3.7.1 #16

Merged
chomp merged 6 commits from 3.7.1 into master 2023-10-14 16:31:18 -04:00
967 changed files with 11215 additions and 6364 deletions

View File

@ -1,4 +1,4 @@
# Mod examples for v3.7.0
# Mod examples for v3.7.1
A collection of example mods that perform typical actions in SPT

View File

@ -13,7 +13,7 @@ This project is designed to streamline the initial setup process for building an
## **NodeJS Setup**
Before you begin, ensure to install NodeJS version `v16.17.1`, which has been tested thoroughly with our mod templates and build scripts. Download it from the [official NodeJS website](https://nodejs.org/).
Before you begin, ensure to install NodeJS version `v18.15.0`, which has been tested thoroughly with our mod templates and build scripts. Download it from the [official NodeJS website](https://nodejs.org/).
After installation, it's advised to reboot your system.

View File

@ -27,7 +27,6 @@ import { LocalisationService } from "../services/LocalisationService";
import { OpenZoneService } from "../services/OpenZoneService";
import { ProfileFixerService } from "../services/ProfileFixerService";
import { SeasonalEventService } from "../services/SeasonalEventService";
import { HashUtil } from "../utils/HashUtil";
import { JsonUtil } from "../utils/JsonUtil";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
@ -36,7 +35,6 @@ export declare class GameController {
protected databaseServer: DatabaseServer;
protected jsonUtil: JsonUtil;
protected timeUtil: TimeUtil;
protected hashUtil: HashUtil;
protected preAkiModLoader: PreAkiModLoader;
protected httpServerHelper: HttpServerHelper;
protected randomUtil: RandomUtil;
@ -51,14 +49,13 @@ export declare class GameController {
protected giftService: GiftService;
protected applicationContext: ApplicationContext;
protected configServer: ConfigServer;
protected os: any;
protected httpConfig: IHttpConfig;
protected coreConfig: ICoreConfig;
protected locationConfig: ILocationConfig;
protected ragfairConfig: IRagfairConfig;
protected pmcConfig: IPmcConfig;
protected lootConfig: ILootConfig;
constructor(logger: ILogger, databaseServer: DatabaseServer, jsonUtil: JsonUtil, timeUtil: TimeUtil, hashUtil: HashUtil, preAkiModLoader: PreAkiModLoader, httpServerHelper: HttpServerHelper, randomUtil: RandomUtil, hideoutHelper: HideoutHelper, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, localisationService: LocalisationService, customLocationWaveService: CustomLocationWaveService, openZoneService: OpenZoneService, seasonalEventService: SeasonalEventService, itemBaseClassService: ItemBaseClassService, giftService: GiftService, applicationContext: ApplicationContext, configServer: ConfigServer);
constructor(logger: ILogger, databaseServer: DatabaseServer, jsonUtil: JsonUtil, timeUtil: TimeUtil, preAkiModLoader: PreAkiModLoader, httpServerHelper: HttpServerHelper, randomUtil: RandomUtil, hideoutHelper: HideoutHelper, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, localisationService: LocalisationService, customLocationWaveService: CustomLocationWaveService, openZoneService: OpenZoneService, seasonalEventService: SeasonalEventService, itemBaseClassService: ItemBaseClassService, giftService: GiftService, applicationContext: ApplicationContext, configServer: ConfigServer);
load(): void;
/**
* Handle client/game/start
@ -67,12 +64,6 @@ export declare class GameController {
protected addCustomLooseLootPositions(): void;
protected adjustLooseLootSpawnProbabilities(): void;
protected setHideoutAreasAndCraftsTo40Secs(): void;
/**
* 3.7.0 moved AIDs to be numeric, old profiles need to be migrated
* We store the old AID value in new field `sessionId`
* @param fullProfile Profile to update
*/
protected fixIncorrectAidValue(fullProfile: IAkiProfile): void;
/** Apply custom limits on bot types as defined in configs/location.json/botTypeLimits */
protected adjustMapBotLimits(): void;
/**

View File

@ -62,10 +62,10 @@ export declare class InraidController {
savePostRaidProgress(offraidData: ISaveProgressRequestData, sessionID: string): void;
/**
* Handle updating player profile post-pmc raid
* @param sessionID session id
* @param offraidData post-raid data
* @param sessionID Session id
* @param postRaidRequest Post-raid data
*/
protected savePmcProgress(sessionID: string, offraidData: ISaveProgressRequestData): void;
protected savePmcProgress(sessionID: string, postRaidRequest: ISaveProgressRequestData): void;
/**
* Make changes to pmc profile after they've died in raid,
* Alter bodypart hp, handle insurance, delete inventory items, remove carried quest items
@ -91,10 +91,10 @@ export declare class InraidController {
protected reducePmcHealthToPercent(pmcData: IPmcData, multipler: number): void;
/**
* Handle updating the profile post-pscav raid
* @param sessionID session id
* @param offraidData post-raid data of raid
* @param sessionID Session id
* @param postRaidRequest Post-raid data of raid
*/
protected savePlayerScavProgress(sessionID: string, offraidData: ISaveProgressRequestData): void;
protected savePlayerScavProgress(sessionID: string, postRaidRequest: ISaveProgressRequestData): void;
/**
* Is the player dead after a raid - dead is anything other than "survived" / "runner"
* @param statusOnExit exit value from offraidData object

View File

@ -4,12 +4,11 @@ import { ProfileHelper } from "../helpers/ProfileHelper";
import { TraderHelper } from "../helpers/TraderHelper";
import { IPmcData } from "../models/eft/common/IPmcData";
import { Item } from "../models/eft/common/tables/IItem";
import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem";
import { IGetInsuranceCostRequestData } from "../models/eft/insurance/IGetInsuranceCostRequestData";
import { IGetInsuranceCostResponseData } from "../models/eft/insurance/IGetInsuranceCostResponseData";
import { IInsureRequestData } from "../models/eft/insurance/IInsureRequestData";
import { IItemEventRouterResponse } from "../models/eft/itemEvent/IItemEventRouterResponse";
import { Insurance } from "../models/eft/profile/IAkiProfile";
import { Insurance, ISystemData } from "../models/eft/profile/IAkiProfile";
import { IInsuranceConfig } from "../models/spt/config/IInsuranceConfig";
import { ILogger } from "../models/spt/utils/ILogger";
import { EventOutputHolder } from "../routers/EventOutputHolder";
@ -67,67 +66,100 @@ export declare class InsuranceController {
*/
protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void;
/**
* Build an array of items to delete from the insured items.
* Remove an insurance package from a profile using the package's system data information.
*
* This method orchestrates several steps:
* - Filters items based on their presence in the database and their raid moddability.
* - Sorts base and independent child items to consider for deletion.
* - Groups child items by their parent for later evaluation.
* - Evaluates grouped child items to decide which should be deleted, based on their value and a random roll.
*
* @param insured - The insured items to build a removal array from.
* @returns An array of IDs representing items that should be deleted.
*/
protected findItemsToDelete(insured: Insurance): string[];
/**
* Filters an item based on its existence in the database, raid moddability, and slot requirements.
*
* @param item The item to be filtered.
* @param parentItemDbDetails The database details of the parent item, or null if the item has no parent.
* @param itemDbDetails A tuple where the first element is a boolean indicating if the item exists in the database,
* and the second element is the item details if it does.
* @returns true if the item exists in the database and neither of the following conditions are met:
* - The item has the RaidModdable property set to false.
* - The item is attached to a required slot in its parent item.
* Otherwise, returns false.
*/
protected filterByRaidModdability(item: Item, parentItemDbDetails: ITemplateItem | null, itemDbDetails: [boolean, ITemplateItem]): boolean;
/**
* Determines if an item is either a base item or a child item that is not equipped to its parent.
*
* @param item The item to check.
* @returns true if the item is a base or an independent child item, otherwise false.
*/
protected isBaseOrIndependentChild(item: Item): boolean;
/**
* Makes a roll to determine if a given item should be deleted. If the roll is successful, the item's ID is added
* to the `toDelete` array.
*
* @param item The item for which the roll is made.
* @param traderId The ID of the trader to consider in the rollForItemDelete method.
* @param toDelete The array accumulating the IDs of items to be deleted.
* @returns true if the item is marked for deletion, otherwise false.
*/
protected makeRollAndMarkForDeletion(item: Item, traderId: string, toDelete: string[]): boolean;
/**
* Groups child items by their parent IDs in a Map data structure.
*
* @param item The child item to be grouped by its parent.
* @param childrenGroupedByParent The Map that holds arrays of children items grouped by their parent IDs.
* @param sessionID The session ID of the profile to remove the package from.
* @param index The array index of the insurance package to remove.
* @returns void
*/
protected groupChildrenByParent(item: Item, childrenGroupedByParent: Map<string, Item[]>): void;
protected removeInsurancePackageFromProfile(sessionID: string, packageInfo: ISystemData): void;
/**
* Sorts the array of children items in descending order by their maximum price. For each child, a roll is made to
* determine if it should be deleted. The method then deletes the most valuable children based on the number of
* successful rolls made.
* Finds the items that should be deleted based on the given Insurance object.
*
* @param children The array of children items to sort and filter.
* @param traderId The ID of the trader to consider in the rollForItemDelete method.
* @param insured The insurance object containing the items to evaluate for deletion.
* @returns A Set containing the IDs of items that should be deleted.
*/
protected findItemsToDelete(insured: Insurance): Set<string>;
/**
* Populate a Map object of items for quick lookup by their ID.
*
* @param insured The insurance object containing the items to populate the map with.
* @returns A Map where the keys are the item IDs and the values are the corresponding Item objects.
*/
protected populateItemsMap(insured: Insurance): Map<string, Item>;
/**
* Initialize a Map object that holds main-parents to all of their attachments. Note that "main-parent" in this
* context refers to the parent item that an attachment is attached to. For example, a suppressor attached to a gun,
* not the backpack that the gun is located in (the gun's parent).
*
* @param insured - The insurance object containing the items to evaluate.
* @param itemsMap - A Map object for quick item look-up by item ID.
* @returns A Map object containing parent item IDs to arrays of their attachment items.
*/
protected populateParentAttachmentsMap(insured: Insurance, itemsMap: Map<string, Item>): Map<string, Item[]>;
/**
* Process "regular" insurance items. Any insured item that is not an attached, attachment is considered a "regular"
* item. This method iterates over them, preforming item deletion rolls to see if they should be deleted. If so,
* they (and their attached, attachments, if any) are marked for deletion in the toDelete Set.
*
* @param insured The insurance object containing the items to evaluate.
* @param toDelete A Set to keep track of items marked for deletion.
* @returns void
*/
protected processRegularItems(insured: Insurance, toDelete: Set<string>): void;
/**
* Process parent items and their attachments, updating the toDelete Set accordingly.
*
* This method iterates over a map of parent items to their attachments and performs evaluations on each.
* It marks items for deletion based on certain conditions and updates the toDelete Set accordingly.
*
* @param mainParentToAttachmentsMap A Map object containing parent item IDs to arrays of their attachment items.
* @param itemsMap A Map object for quick item look-up by item ID.
* @param traderId The trader ID from the Insurance object.
* @param toDelete A Set object to keep track of items marked for deletion.
*/
protected processAttachments(mainParentToAttachmentsMap: Map<string, Item[]>, itemsMap: Map<string, Item>, traderId: string, toDelete: Set<string>): void;
/**
* Takes an array of attachment items that belong to the same main-parent item, sorts them in descending order by
* their maximum price. For each attachment, a roll is made to determine if a deletion should be made. Once the
* number of deletions has been counted, the attachments are added to the toDelete Set, starting with the most
* valuable attachments first.
*
* @param attachments The array of attachment items to sort, filter, and roll.
* @param traderId The ID of the trader to that has ensured these items.
* @param toDelete The array that accumulates the IDs of the items to be deleted.
* @returns void
*/
protected sortAndFilterChildren(children: Item[], traderId: string, toDelete: string[]): void;
protected processAttachmentByParent(attachments: Item[], traderId: string, toDelete: Set<string>): void;
/**
* Sorts the attachment items by their max price in descending order.
*
* @param attachments The array of attachments items.
* @returns An array of items enriched with their max price and common locale-name.
*/
protected sortAttachmentsByPrice(attachments: Item[]): EnrichedItem[];
/**
* Logs the details of each attachment item.
*
* @param attachments The array of attachment items.
*/
protected logAttachmentsDetails(attachments: EnrichedItem[]): void;
/**
* Counts the number of successful rolls for the attachment items.
*
* @param attachments The array of attachment items.
* @param traderId The ID of the trader that has insured these attachments.
* @returns The number of successful rolls.
*/
protected countSuccessfulRolls(attachments: Item[], traderId: string): number;
/**
* Marks the most valuable attachments for deletion based on the number of successful rolls made.
*
* @param attachments The array of attachment items.
* @param successfulRolls The number of successful rolls.
* @param toDelete The array that accumulates the IDs of the items to be deleted.
*/
protected attachmentDeletionByValue(attachments: EnrichedItem[], successfulRolls: number, toDelete: Set<string>): void;
/**
* Remove items from the insured items that should not be returned to the player.
*
@ -135,7 +167,23 @@ export declare class InsuranceController {
* @param toDelete The items that should be deleted.
* @returns void
*/
protected removeItemsFromInsurance(insured: Insurance, toDelete: string[]): void;
protected removeItemsFromInsurance(insured: Insurance, toDelete: Set<string>): void;
/**
* Adopts orphaned items by resetting them as base-level items. Helpful in situations where a parent has been
* deleted from insurance, but any insured items within the parent should remain. This method will remove the
* reference from the children to the parent and set item properties to main-level values.
*
* @param insured Insurance object containing items.
*/
protected adoptOrphanedItems(insured: Insurance): void;
/**
* Fetches the parentId property of an item with a slotId "hideout". Not sure if this is actually dynamic, but this
* method should be a reliable way to fetch it, if it ever does change.
*
* @param items Array of items to search through.
* @returns The parentId of an item with slotId 'hideout'. Empty string if not found.
*/
protected fetchHideoutItemParent(items: Item[]): string;
/**
* Handle sending the insurance message to the user that potentially contains the valid insurance items.
*
@ -146,15 +194,14 @@ export declare class InsuranceController {
*/
protected sendMail(sessionID: string, insurance: Insurance, noItems: boolean): void;
/**
* Determines whether a valid insured item should be removed from the player's inventory based on a random roll and
* Determines whether a insured item should be removed from the player's inventory based on a random roll and
* trader-specific return chance.
*
* @param insuredItem The insured item being evaluated for removal.
* @param traderId The ID of the trader who insured the item.
* @param itemsBeingDeleted List of items that are already slated for removal.
* @param insuredItem Optional. The item to roll for. Only used for logging.
* @returns true if the insured item should be removed from inventory, false otherwise.
*/
protected rollForItemDelete(insuredItem: Item, traderId: string, itemsBeingDeleted: string[]): boolean;
protected rollForDelete(traderId: string, insuredItem?: Item): boolean;
/**
* Handle Insure event
* Add insurance to an item
@ -175,3 +222,8 @@ export declare class InsuranceController {
*/
cost(request: IGetInsuranceCostRequestData, sessionID: string): IGetInsuranceCostResponseData;
}
interface EnrichedItem extends Item {
name: string;
maxPrice: number;
}
export {};

View File

@ -1,4 +1,5 @@
import { ApplicationContext } from "../context/ApplicationContext";
import { LootGenerator } from "../generators/LootGenerator";
import { ProfileHelper } from "../helpers/ProfileHelper";
import { TraderHelper } from "../helpers/TraderHelper";
import { IPmcData } from "../models/eft/common/IPmcData";
@ -12,16 +13,24 @@ import { IJoinMatchResult } from "../models/eft/match/IJoinMatchResult";
import { IInRaidConfig } from "../models/spt/config/IInRaidConfig";
import { IMatchConfig } from "../models/spt/config/IMatchConfig";
import { IPmcConfig } from "../models/spt/config/IPmcConfig";
import { ITraderConfig } from "../models/spt/config/ITraderConfig";
import { ILogger } from "../models/spt/utils/ILogger";
import { ConfigServer } from "../servers/ConfigServer";
import { SaveServer } from "../servers/SaveServer";
import { BotGenerationCacheService } from "../services/BotGenerationCacheService";
import { BotLootCacheService } from "../services/BotLootCacheService";
import { MailSendService } from "../services/MailSendService";
import { MatchLocationService } from "../services/MatchLocationService";
import { ProfileSnapshotService } from "../services/ProfileSnapshotService";
import { HashUtil } from "../utils/HashUtil";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
export declare class MatchController {
protected logger: ILogger;
protected saveServer: SaveServer;
protected timeUtil: TimeUtil;
protected randomUtil: RandomUtil;
protected hashUtil: HashUtil;
protected profileHelper: ProfileHelper;
protected matchLocationService: MatchLocationService;
protected traderHelper: TraderHelper;
@ -29,11 +38,14 @@ export declare class MatchController {
protected configServer: ConfigServer;
protected profileSnapshotService: ProfileSnapshotService;
protected botGenerationCacheService: BotGenerationCacheService;
protected mailSendService: MailSendService;
protected lootGenerator: LootGenerator;
protected applicationContext: ApplicationContext;
protected matchConfig: IMatchConfig;
protected inraidConfig: IInRaidConfig;
protected traderConfig: ITraderConfig;
protected pmcConfig: IPmcConfig;
constructor(logger: ILogger, saveServer: SaveServer, profileHelper: ProfileHelper, matchLocationService: MatchLocationService, traderHelper: TraderHelper, botLootCacheService: BotLootCacheService, configServer: ConfigServer, profileSnapshotService: ProfileSnapshotService, botGenerationCacheService: BotGenerationCacheService, applicationContext: ApplicationContext);
constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, randomUtil: RandomUtil, hashUtil: HashUtil, profileHelper: ProfileHelper, matchLocationService: MatchLocationService, traderHelper: TraderHelper, botLootCacheService: BotLootCacheService, configServer: ConfigServer, profileSnapshotService: ProfileSnapshotService, botGenerationCacheService: BotGenerationCacheService, mailSendService: MailSendService, lootGenerator: LootGenerator, applicationContext: ApplicationContext);
getEnabled(): boolean;
/** Handle raid/profile/list */
getProfile(info: IGetProfileRequestData): IPmcData[];
@ -59,6 +71,13 @@ export declare class MatchController {
protected convertDifficultyDropdownIntoBotDifficulty(botDifficulty: string): string;
/** Handle client/match/offline/end */
endOfflineRaid(info: IEndOfflineRaidRequestData, sessionId: string): void;
/**
* Did player take a COOP extract
* @param extractName Name of extract player took
* @returns True if coop extract
*/
protected extractWasViaCoop(extractName: string): boolean;
protected sendCoopTakenFenceMessage(sessionId: string): void;
/**
* Was extract by car
* @param extractName name of extract

View File

@ -1,81 +1,41 @@
import { HandbookHelper } from "../helpers/HandbookHelper";
import { ItemHelper } from "../helpers/ItemHelper";
import { PresetHelper } from "../helpers/PresetHelper";
import { RepeatableQuestGenerator } from "../generators/RepeatableQuestGenerator";
import { ProfileHelper } from "../helpers/ProfileHelper";
import { RagfairServerHelper } from "../helpers/RagfairServerHelper";
import { RepeatableQuestHelper } from "../helpers/RepeatableQuestHelper";
import { IEmptyRequestData } from "../models/eft/common/IEmptyRequestData";
import { Exit } from "../models/eft/common/ILocationBase";
import { IPmcData } from "../models/eft/common/IPmcData";
import { TraderInfo } from "../models/eft/common/tables/IBotBase";
import { ICompletion, ICompletionAvailableFor, IElimination, IEliminationCondition, IExploration, IExplorationCondition, IPmcDataRepeatableQuest, IRepeatableQuest, IReward, IRewards } from "../models/eft/common/tables/IRepeatableQuests";
import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem";
import { IPmcDataRepeatableQuest } from "../models/eft/common/tables/IRepeatableQuests";
import { IItemEventRouterResponse } from "../models/eft/itemEvent/IItemEventRouterResponse";
import { IRepeatableQuestChangeRequest } from "../models/eft/quests/IRepeatableQuestChangeRequest";
import { ELocationName } from "../models/enums/ELocationName";
import { IEliminationConfig, IQuestConfig, IRepeatableQuestConfig } from "../models/spt/config/IQuestConfig";
import { IQuestConfig, IRepeatableQuestConfig } from "../models/spt/config/IQuestConfig";
import { IQuestTypePool } from "../models/spt/repeatable/IQuestTypePool";
import { ILogger } from "../models/spt/utils/ILogger";
import { EventOutputHolder } from "../routers/EventOutputHolder";
import { ConfigServer } from "../servers/ConfigServer";
import { DatabaseServer } from "../servers/DatabaseServer";
import { ItemFilterService } from "../services/ItemFilterService";
import { LocalisationService } from "../services/LocalisationService";
import { PaymentService } from "../services/PaymentService";
import { ProfileFixerService } from "../services/ProfileFixerService";
import { HttpResponseUtil } from "../utils/HttpResponseUtil";
import { JsonUtil } from "../utils/JsonUtil";
import { MathUtil } from "../utils/MathUtil";
import { ObjectId } from "../utils/ObjectId";
import { ProbabilityObject, ProbabilityObjectArray, RandomUtil } from "../utils/RandomUtil";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
export interface IQuestTypePool {
types: string[];
pool: IQuestPool;
}
export interface IQuestPool {
Exploration: IExplorationPool;
Elimination: IEliminationPool;
}
export interface IExplorationPool {
locations: Partial<Record<ELocationName, string[]>>;
}
export interface IEliminationPool {
targets: IEliminationTargetPool;
}
export interface IEliminationTargetPool {
Savage?: ITargetLocation;
AnyPmc?: ITargetLocation;
bossBully?: ITargetLocation;
bossGluhar?: ITargetLocation;
bossKilla?: ITargetLocation;
bossSanitar?: ITargetLocation;
bossTagilla?: ITargetLocation;
bossKojaniy?: ITargetLocation;
}
export interface ITargetLocation {
locations: string[];
}
export declare class RepeatableQuestController {
protected timeUtil: TimeUtil;
protected logger: ILogger;
protected randomUtil: RandomUtil;
protected httpResponse: HttpResponseUtil;
protected mathUtil: MathUtil;
protected jsonUtil: JsonUtil;
protected databaseServer: DatabaseServer;
protected itemHelper: ItemHelper;
protected presetHelper: PresetHelper;
protected profileHelper: ProfileHelper;
protected profileFixerService: ProfileFixerService;
protected handbookHelper: HandbookHelper;
protected ragfairServerHelper: RagfairServerHelper;
protected eventOutputHolder: EventOutputHolder;
protected localisationService: LocalisationService;
protected paymentService: PaymentService;
protected objectId: ObjectId;
protected itemFilterService: ItemFilterService;
protected repeatableQuestGenerator: RepeatableQuestGenerator;
protected repeatableQuestHelper: RepeatableQuestHelper;
protected configServer: ConfigServer;
protected questConfig: IQuestConfig;
constructor(timeUtil: TimeUtil, logger: ILogger, randomUtil: RandomUtil, httpResponse: HttpResponseUtil, mathUtil: MathUtil, jsonUtil: JsonUtil, databaseServer: DatabaseServer, itemHelper: ItemHelper, presetHelper: PresetHelper, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, handbookHelper: HandbookHelper, ragfairServerHelper: RagfairServerHelper, eventOutputHolder: EventOutputHolder, localisationService: LocalisationService, paymentService: PaymentService, objectId: ObjectId, itemFilterService: ItemFilterService, configServer: ConfigServer);
constructor(timeUtil: TimeUtil, logger: ILogger, randomUtil: RandomUtil, httpResponse: HttpResponseUtil, jsonUtil: JsonUtil, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, ragfairServerHelper: RagfairServerHelper, eventOutputHolder: EventOutputHolder, paymentService: PaymentService, objectId: ObjectId, repeatableQuestGenerator: RepeatableQuestGenerator, repeatableQuestHelper: RepeatableQuestHelper, configServer: ConfigServer);
/**
* Handle client/repeatalbeQuests/activityPeriods
* Returns an array of objects in the format of repeatable quests to the client.
@ -109,103 +69,10 @@ export declare class RepeatableQuestController {
* @returns IPmcDataRepeatableQuest
*/
protected getRepeatableQuestSubTypeFromProfile(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): IPmcDataRepeatableQuest;
/**
* This method is called by GetClientRepeatableQuests and creates one element of quest type format (see assets/database/templates/repeatableQuests.json).
* It randomly draws a quest type (currently Elimination, Completion or Exploration) as well as a trader who is providing the quest
*/
protected generateRepeatableQuest(pmcLevel: number, pmcTraderInfo: Record<string, TraderInfo>, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IRepeatableQuest;
/**
* Just for debug reasons. Draws dailies a random assort of dailies extracted from dumps
*/
generateDebugDailies(dailiesPool: any, factory: any, number: number): any;
/**
* Generates the base object of quest type format given as templates in assets/database/templates/repeatableQuests.json
* The templates include Elimination, Completion and Extraction quest types
*
* @param {string} type quest type: "Elimination", "Completion" or "Extraction"
* @param {string} traderId trader from which the quest will be provided
* @param {string} side scav daily or pmc daily/weekly quest
* @returns {object} a object which contains the base elements for repeatable quests of the requests type
* (needs to be filled with reward and conditions by called to make a valid quest)
*/
protected generateRepeatableTemplate(type: string, traderId: string, side: string): IRepeatableQuest;
/**
* Generates a valid Exploration quest
*
* @param {integer} pmcLevel player's level for reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} questTypePool Pools for quests (used to avoid redundant quests)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Exploration" (see assets/database/templates/repeatableQuests.json)
*/
protected generateExplorationQuest(pmcLevel: number, traderId: string, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IExploration;
/**
* Generates a valid Completion quest
*
* @param {integer} pmcLevel player's level for requested items and reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Completion" (see assets/database/templates/repeatableQuests.json)
*/
protected generateCompletionQuest(pmcLevel: number, traderId: string, repeatableConfig: IRepeatableQuestConfig): ICompletion;
/**
* Generates a valid Elimination quest
*
* @param {integer} pmcLevel player's level for requested items and reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} questTypePool Pools for quests (used to avoid redundant quests)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Elimination" (see assets/database/templates/repeatableQuests.json)
*/
protected generateEliminationQuest(pmcLevel: number, traderId: string, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IElimination;
/**
* Get the relevant elimination config based on the current players PMC level
* @param pmcLevel Level of PMC character
* @param repeatableConfig Main repeatable config
* @returns IEliminationConfig
*/
protected getEliminationConfigByPmcLevel(pmcLevel: number, repeatableConfig: IRepeatableQuestConfig): IEliminationConfig;
/**
* Convert a location into an quest code can read (e.g. factory4_day into 55f2d3fd4bdc2d5f408b4567)
* @param locationKey e.g factory4_day
* @returns guid
*/
protected getQuestLocationByMapId(locationKey: string): string;
/**
* Exploration repeatable quests can specify a required extraction point.
* This method creates the according object which will be appended to the conditions array
*
* @param {string} exit The exit name to generate the condition for
* @returns {object} Exit condition
*/
protected generateExplorationExitCondition(exit: Exit): IExplorationCondition;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateCompletionQuest to create a completion condition (of which a completion quest theoretically can have many)
*
* @param {string} targetItemId id of the item to request
* @param {integer} value amount of items of this specific type to request
* @returns {object} object of "Completion"-condition
*/
protected generateCompletionAvailableForFinish(targetItemId: string, value: number): ICompletionAvailableFor;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateEliminationQuest to create a location condition.
*
* @param {string} location the location on which to fulfill the elimination quest
* @returns {object} object of "Elimination"-location-subcondition
*/
protected generateEliminationLocation(location: string[]): IEliminationCondition;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateEliminationQuest to create a kill condition.
*
* @param {string} target array of target npcs e.g. "AnyPmc", "Savage"
* @param {array} bodyParts array of body parts with which to kill e.g. ["stomach", "thorax"]
* @param {number} distance distance from which to kill (currently only >= supported)
* @returns {object} object of "Elimination"-kill-subcondition
*/
protected generateEliminationCondition(target: string, bodyPart: string[], distance: number): IEliminationCondition;
/**
* Used to create a quest pool during each cycle of repeatable quest generation. The pool will be subsequently
* narrowed down during quest generation to avoid duplicate quests. Like duplicate extractions or elimination quests
@ -215,53 +82,10 @@ export declare class RepeatableQuestController {
* @returns IQuestTypePool
*/
protected generateQuestPool(repeatableConfig: IRepeatableQuestConfig, pmcLevel: number): IQuestTypePool;
/**
* Generate the reward for a mission. A reward can consist of
* - Experience
* - Money
* - Items
* - Trader Reputation
*
* The reward is dependent on the player level as given by the wiki. The exact mapping of pmcLevel to
* experience / money / items / trader reputation can be defined in QuestConfig.js
*
* There's also a random variation of the reward the spread of which can be also defined in the config.
*
* Additonaly, a scaling factor w.r.t. quest difficulty going from 0.2...1 can be used
*
* @param {integer} pmcLevel player's level
* @param {number} difficulty a reward scaling factor goint from 0.2 to 1
* @param {string} traderId the trader for reputation gain (and possible in the future filtering of reward item type based on trader)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of "Reward"-type that can be given for a repeatable mission
*/
protected generateReward(pmcLevel: number, difficulty: number, traderId: string, repeatableConfig: IRepeatableQuestConfig): IRewards;
/**
* Helper to create a reward item structured as required by the client
*
* @param {string} tpl itemId of the rewarded item
* @param {integer} value amount of items to give
* @param {integer} index all rewards will be appended to a list, for unkown reasons the client wants the index
* @returns {object} object of "Reward"-item-type
*/
protected generateRewardItem(tpl: string, value: number, index: number, preset?: any): IReward;
protected createBaseQuestPool(repeatableConfig: IRepeatableQuestConfig): IQuestTypePool;
debugLogRepeatableQuestIds(pmcData: IPmcData): void;
protected probabilityObjectArray<K, V>(configArrayInput: ProbabilityObject<K, V>[]): ProbabilityObjectArray<K, V>;
/**
* Handle RepeatableQuestChange event
*/
changeRepeatableQuest(pmcData: IPmcData, body: IRepeatableQuestChangeRequest, sessionID: string): IItemEventRouterResponse;
/**
* Picks rewardable items from items.json. This means they need to fit into the inventory and they shouldn't be keys (debatable)
* @param repeatableQuestConfig config file
* @returns a list of rewardable items [[_tpl, itemTemplate],...]
*/
protected getRewardableItems(repeatableQuestConfig: IRepeatableQuestConfig): [string, ITemplateItem][];
/**
* Checks if an id is a valid item. Valid meaning that it's an item that may be a reward
* or content of bot loot. Items that are tested as valid may be in a player backpack or stash.
* @param {string} tpl template id of item to check
* @returns boolean: true if item is valid reward
*/
protected isValidRewardItem(tpl: string, repeatableQuestConfig: IRepeatableQuestConfig): boolean;
changeRepeatableQuest(pmcData: IPmcData, changeRequest: IRepeatableQuestChangeRequest, sessionID: string): IItemEventRouterResponse;
}

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
export declare class Serializer {
serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): void;
canHandle(something: string): boolean;

View File

@ -0,0 +1,183 @@
import { HandbookHelper } from "../helpers/HandbookHelper";
import { ItemHelper } from "../helpers/ItemHelper";
import { PresetHelper } from "../helpers/PresetHelper";
import { ProfileHelper } from "../helpers/ProfileHelper";
import { RagfairServerHelper } from "../helpers/RagfairServerHelper";
import { RepeatableQuestHelper } from "../helpers/RepeatableQuestHelper";
import { Exit } from "../models/eft/common/ILocationBase";
import { TraderInfo } from "../models/eft/common/tables/IBotBase";
import { ICompletion, ICompletionAvailableFor, IElimination, IEliminationCondition, IExploration, IExplorationCondition, IRepeatableQuest, IReward, IRewards } from "../models/eft/common/tables/IRepeatableQuests";
import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem";
import { IQuestConfig, IRepeatableQuestConfig } from "../models/spt/config/IQuestConfig";
import { IQuestTypePool } from "../models/spt/repeatable/IQuestTypePool";
import { ILogger } from "../models/spt/utils/ILogger";
import { EventOutputHolder } from "../routers/EventOutputHolder";
import { ConfigServer } from "../servers/ConfigServer";
import { DatabaseServer } from "../servers/DatabaseServer";
import { ItemFilterService } from "../services/ItemFilterService";
import { LocalisationService } from "../services/LocalisationService";
import { PaymentService } from "../services/PaymentService";
import { ProfileFixerService } from "../services/ProfileFixerService";
import { HttpResponseUtil } from "../utils/HttpResponseUtil";
import { JsonUtil } from "../utils/JsonUtil";
import { MathUtil } from "../utils/MathUtil";
import { ObjectId } from "../utils/ObjectId";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
export declare class RepeatableQuestGenerator {
protected timeUtil: TimeUtil;
protected logger: ILogger;
protected randomUtil: RandomUtil;
protected httpResponse: HttpResponseUtil;
protected mathUtil: MathUtil;
protected jsonUtil: JsonUtil;
protected databaseServer: DatabaseServer;
protected itemHelper: ItemHelper;
protected presetHelper: PresetHelper;
protected profileHelper: ProfileHelper;
protected profileFixerService: ProfileFixerService;
protected handbookHelper: HandbookHelper;
protected ragfairServerHelper: RagfairServerHelper;
protected eventOutputHolder: EventOutputHolder;
protected localisationService: LocalisationService;
protected paymentService: PaymentService;
protected objectId: ObjectId;
protected itemFilterService: ItemFilterService;
protected repeatableQuestHelper: RepeatableQuestHelper;
protected configServer: ConfigServer;
protected questConfig: IQuestConfig;
constructor(timeUtil: TimeUtil, logger: ILogger, randomUtil: RandomUtil, httpResponse: HttpResponseUtil, mathUtil: MathUtil, jsonUtil: JsonUtil, databaseServer: DatabaseServer, itemHelper: ItemHelper, presetHelper: PresetHelper, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, handbookHelper: HandbookHelper, ragfairServerHelper: RagfairServerHelper, eventOutputHolder: EventOutputHolder, localisationService: LocalisationService, paymentService: PaymentService, objectId: ObjectId, itemFilterService: ItemFilterService, repeatableQuestHelper: RepeatableQuestHelper, configServer: ConfigServer);
/**
* This method is called by /GetClientRepeatableQuests/ and creates one element of quest type format (see assets/database/templates/repeatableQuests.json).
* It randomly draws a quest type (currently Elimination, Completion or Exploration) as well as a trader who is providing the quest
* @param pmcLevel Player's level for requested items and reward generation
* @param pmcTraderInfo Players traper standing/rep levels
* @param questTypePool Possible quest types pool
* @param repeatableConfig Repeatable quest config
* @returns IRepeatableQuest
*/
generateRepeatableQuest(pmcLevel: number, pmcTraderInfo: Record<string, TraderInfo>, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IRepeatableQuest;
/**
* Generate a randomised Elimination quest
* @param pmcLevel Player's level for requested items and reward generation
* @param traderId Trader from which the quest will be provided
* @param questTypePool Pools for quests (used to avoid redundant quests)
* @param repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns Object of quest type format for "Elimination" (see assets/database/templates/repeatableQuests.json)
*/
protected generateEliminationQuest(pmcLevel: number, traderId: string, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IElimination;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateEliminationQuest to create a location condition.
*
* @param {string} location the location on which to fulfill the elimination quest
* @returns {object} object of "Elimination"-location-subcondition
*/
protected generateEliminationLocation(location: string[], allowedWeapon: string, allowedWeaponCategory: string): IEliminationCondition;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateEliminationQuest to create a kill condition.
*
* @param {string} target array of target npcs e.g. "AnyPmc", "Savage"
* @param {array} bodyParts array of body parts with which to kill e.g. ["stomach", "thorax"]
* @param {number} distance distance from which to kill (currently only >= supported)
* @returns {object} object of "Elimination"-kill-subcondition
*/
protected generateEliminationCondition(target: string, bodyPart: string[], distance: number, allowedWeapon: string, allowedWeaponCategory: string): IEliminationCondition;
/**
* Generates a valid Completion quest
*
* @param {integer} pmcLevel player's level for requested items and reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Completion" (see assets/database/templates/repeatableQuests.json)
*/
protected generateCompletionQuest(pmcLevel: number, traderId: string, repeatableConfig: IRepeatableQuestConfig): ICompletion;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateCompletionQuest to create a completion condition (of which a completion quest theoretically can have many)
*
* @param {string} targetItemId id of the item to request
* @param {integer} value amount of items of this specific type to request
* @returns {object} object of "Completion"-condition
*/
protected generateCompletionAvailableForFinish(targetItemId: string, value: number): ICompletionAvailableFor;
/**
* Generates a valid Exploration quest
*
* @param {integer} pmcLevel player's level for reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} questTypePool Pools for quests (used to avoid redundant quests)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Exploration" (see assets/database/templates/repeatableQuests.json)
*/
protected generateExplorationQuest(pmcLevel: number, traderId: string, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IExploration;
/**
* Convert a location into an quest code can read (e.g. factory4_day into 55f2d3fd4bdc2d5f408b4567)
* @param locationKey e.g factory4_day
* @returns guid
*/
protected getQuestLocationByMapId(locationKey: string): string;
/**
* Exploration repeatable quests can specify a required extraction point.
* This method creates the according object which will be appended to the conditions array
*
* @param {string} exit The exit name to generate the condition for
* @returns {object} Exit condition
*/
protected generateExplorationExitCondition(exit: Exit): IExplorationCondition;
/**
* Generate the reward for a mission. A reward can consist of
* - Experience
* - Money
* - Items
* - Trader Reputation
*
* The reward is dependent on the player level as given by the wiki. The exact mapping of pmcLevel to
* experience / money / items / trader reputation can be defined in QuestConfig.js
*
* There's also a random variation of the reward the spread of which can be also defined in the config.
*
* Additonaly, a scaling factor w.r.t. quest difficulty going from 0.2...1 can be used
*
* @param {integer} pmcLevel player's level
* @param {number} difficulty a reward scaling factor goint from 0.2 to 1
* @param {string} traderId the trader for reputation gain (and possible in the future filtering of reward item type based on trader)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of "Reward"-type that can be given for a repeatable mission
*/
protected generateReward(pmcLevel: number, difficulty: number, traderId: string, repeatableConfig: IRepeatableQuestConfig): IRewards;
/**
* Helper to create a reward item structured as required by the client
*
* @param {string} tpl itemId of the rewarded item
* @param {integer} value amount of items to give
* @param {integer} index all rewards will be appended to a list, for unkown reasons the client wants the index
* @returns {object} object of "Reward"-item-type
*/
protected generateRewardItem(tpl: string, value: number, index: number, preset?: any): IReward;
/**
* Picks rewardable items from items.json. This means they need to fit into the inventory and they shouldn't be keys (debatable)
* @param repeatableQuestConfig config file
* @returns a list of rewardable items [[_tpl, itemTemplate],...]
*/
protected getRewardableItems(repeatableQuestConfig: IRepeatableQuestConfig): [string, ITemplateItem][];
/**
* Checks if an id is a valid item. Valid meaning that it's an item that may be a reward
* or content of bot loot. Items that are tested as valid may be in a player backpack or stash.
* @param {string} tpl template id of item to check
* @returns boolean: true if item is valid reward
*/
protected isValidRewardItem(tpl: string, repeatableQuestConfig: IRepeatableQuestConfig): boolean;
/**
* Generates the base object of quest type format given as templates in assets/database/templates/repeatableQuests.json
* The templates include Elimination, Completion and Extraction quest types
*
* @param {string} type quest type: "Elimination", "Completion" or "Extraction"
* @param {string} traderId trader from which the quest will be provided
* @param {string} side scav daily or pmc daily/weekly quest
* @returns {object} a object which contains the base elements for repeatable quests of the requests type
* (needs to be filled with reward and conditions by called to make a valid quest)
*/
protected generateRepeatableTemplate(type: string, traderId: string, side: string): IRepeatableQuest;
}

View File

@ -111,11 +111,11 @@ export declare class InRaidHelper {
* Add new items found in raid to profile
* Store insurance items in profile
* @param sessionID Session id
* @param pmcData Profile to update
* @param serverProfile Profile to update
* @param postRaidProfile Profile returned by client after a raid
* @returns Updated profile
*/
setInventory(sessionID: string, pmcData: IPmcData, postRaidProfile: IPmcData): IPmcData;
setInventory(sessionID: string, serverProfile: IPmcData, postRaidProfile: IPmcData): IPmcData;
/**
* Clear pmc inventory of all items except those that are exempt
* Used post-raid to remove items after death

View File

@ -225,6 +225,48 @@ declare class ItemHelper {
* @returns true if item is flagged as quest item
*/
isQuestItem(tpl: string): boolean;
/**
* Checks to see if the item is *actually* moddable in-raid. Checks include the items existence in the database, the
* parent items existence in the database, the existence (and value) of the items RaidModdable property, and that
* the parents slot-required property exists, matches that of the item, and it's value.
*
* Note: this function does not preform any checks to see if the item and parent are *actually* related.
*
* @param item The item to be checked
* @param parent The parent of the item to be checked
* @returns True if the item is actually moddable, false if it is not, and null if the check cannot be performed.
*/
isRaidModdable(item: Item, parent: Item): boolean | null;
/**
* Retrieves the main parent item for a given attachment item.
*
* This method traverses up the hierarchy of items starting from a given `itemId`, until it finds the main parent
* item that is not an attached attachment itself. In other words, if you pass it an item id of a suppressor, it
* will traverse up the muzzle brake, barrel, upper receiver, and return the gun that the suppressor is ultimately
* attached to, even if that gun is located within multiple containers.
*
* It's important to note that traversal is expensive, so this method requires that you pass it a Map of the items
* to traverse, where the keys are the item IDs and the values are the corresponding Item objects. This alleviates
* some of the performance concerns, as it allows for quick lookups of items by ID.
*
* To generate the map:
* ```
* const itemsMap = new Map<string, Item>();
* items.forEach(item => itemsMap.set(item._id, item));
* ```
*
* @param itemId - The unique identifier of the item for which to find the main parent.
* @param itemsMap - A Map containing item IDs mapped to their corresponding Item objects for quick lookup.
* @returns The Item object representing the top-most parent of the given item, or `null` if no such parent exists.
*/
getAttachmentMainParent(itemId: string, itemsMap: Map<string, Item>): Item | null;
/**
* Determines if an item is an attachment that is currently attached to it's parent item.
*
* @param item The item to check.
* @returns true if the item is attached attachment, otherwise false.
*/
isAttachmentAttached(item: Item): boolean;
/**
* Get the inventory size of an item
* @param items Item with children
@ -303,6 +345,7 @@ declare class ItemHelper {
* @returns Name of item
*/
getItemName(itemTpl: string): string;
getItemTplsOfBaseType(desiredBaseType: string): string[];
}
declare namespace ItemHelper {
interface ItemSize {

View File

@ -0,0 +1,20 @@
import { IEliminationConfig, IQuestConfig, IRepeatableQuestConfig } from "../models/spt/config/IQuestConfig";
import { ConfigServer } from "../servers/ConfigServer";
import { JsonUtil } from "../utils/JsonUtil";
import { MathUtil } from "../utils/MathUtil";
import { ProbabilityObject, ProbabilityObjectArray } from "../utils/RandomUtil";
export declare class RepeatableQuestHelper {
protected mathUtil: MathUtil;
protected jsonUtil: JsonUtil;
protected configServer: ConfigServer;
protected questConfig: IQuestConfig;
constructor(mathUtil: MathUtil, jsonUtil: JsonUtil, configServer: ConfigServer);
/**
* Get the relevant elimination config based on the current players PMC level
* @param pmcLevel Level of PMC character
* @param repeatableConfig Main repeatable config
* @returns IEliminationConfig
*/
getEliminationConfigByPmcLevel(pmcLevel: number, repeatableConfig: IRepeatableQuestConfig): IEliminationConfig;
probabilityObjectArray<K, V>(configArrayInput: ProbabilityObject<K, V>[]): ProbabilityObjectArray<K, V>;
}

View File

@ -1,4 +1,5 @@
import { DependencyContainer } from "tsyringe";
import { ModDetails } from "../models/eft/profile/IAkiProfile";
import { ICoreConfig } from "../models/spt/config/ICoreConfig";
import { IModLoader } from "../models/spt/mod/IModLoader";
import { IPackageJsonData } from "../models/spt/mod/IPackageJsonData";
@ -35,6 +36,7 @@ export declare class PreAkiModLoader implements IModLoader {
*/
getImportedModsNames(): string[];
getImportedModDetails(): Record<string, IPackageJsonData>;
getProfileModsGroupedByModName(profileMods: ModDetails[]): ModDetails[];
getModPath(mod: string): string;
protected importMods(): Promise<void>;
protected sortMods(prev: string, next: string, missingFromOrderJSON: Record<string, boolean>): number;
@ -64,6 +66,10 @@ export declare class PreAkiModLoader implements IModLoader {
protected isModCombatibleWithAki(mod: IPackageJsonData): boolean;
protected executeMods(container: DependencyContainer): Promise<void>;
sortModsLoadOrder(): string[];
/**
* Compile mod and add into class property "imported"
* @param mod Name of mod to compile/add
*/
protected addMod(mod: string): Promise<void>;
protected autoInstallDependencies(modPath: string, pkg: IPackageJsonData): void;
protected areModDependenciesFulfilled(pkg: IPackageJsonData, loadedMods: Record<string, IPackageJsonData>): boolean;

View File

@ -161,6 +161,8 @@ export interface ICompletionAvailableForProps extends IAvailableForProps {
}
export interface ILocationConditionProps extends IConditionProps {
target: string[];
weapon?: string[];
weaponCategories?: string[];
}
export interface IKillConditionProps extends IConditionProps {
target: string;
@ -168,6 +170,8 @@ export interface IKillConditionProps extends IConditionProps {
savageRole?: string[];
bodyPart?: string[];
distance?: IDistanceCheck;
weapon?: string[];
weaponCategories?: string[];
}
export interface IDistanceCheck {
compareMethod: string;

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
export type HandleFn = (_: string, req: IncomingMessage, resp: ServerResponse) => void;
/**
* Associates handlers, HTTP methods and a base url to a listener using a proxy

View File

@ -52,4 +52,6 @@ export interface AirdropLoot {
itemStackLimits: Record<string, MinMax>;
/** Armor levels to allow inside crate e.g. [4,5,6] */
armorLevelWhitelist?: number[];
/** Should boss items be added to airdrop crate */
allowBossItems: boolean;
}

View File

@ -8,6 +8,8 @@ export interface IInRaidConfig extends IBaseConfig {
save: Save;
/** Names of car extracts */
carExtracts: string[];
/** Names of coop extracts */
coopExtracts: string[];
/** Fene rep gain from a single car extract */
carExtractBaseStandingGain: number;
/** Fence rep gain when successfully extracting as pscav */

View File

@ -23,4 +23,5 @@ export interface ISealedAirdropContainerSettings {
weaponModRewardLimits: Record<string, MinMax>;
rewardTypeLimits: Record<string, MinMax>;
ammoBoxWhitelist: string[];
allowBossItems: boolean;
}

View File

@ -1,5 +1,8 @@
import { IBaseConfig } from "./IBaseConfig";
export interface IItemConfig extends IBaseConfig {
kind: "aki-item";
/** Items that should be globally blacklisted */
blacklist: string[];
/** Items that can only be found on bosses */
bossItems: string[];
}

View File

@ -92,6 +92,12 @@ export interface IEliminationConfig {
minDist: number;
maxKills: number;
minKills: number;
minBossKills: number;
maxBossKills: number;
weaponCategoryRequirementProb: number;
weaponCategoryRequirements: IWeaponRequirement[];
weaponRequirementProb: number;
weaponRequirements: IWeaponRequirement[];
}
export interface ITarget extends IProbabilityObject {
data: IBossInfo;
@ -102,6 +108,9 @@ export interface IBossInfo {
export interface IBodyPart extends IProbabilityObject {
data: string[];
}
export interface IWeaponRequirement extends IProbabilityObject {
data: string[];
}
export interface IProbabilityObject {
key: string;
relativeProbability: number;

View File

@ -6,8 +6,19 @@ export interface IRepairConfig extends IBaseConfig {
applyRandomizeDurabilityLoss: boolean;
weaponSkillRepairGain: number;
armorKitSkillPointGainPerRepairPointMultiplier: number;
/** INT gain multiplier per repaired item type */
repairKitIntellectGainMultiplier: IIntellectGainValues;
maxIntellectGainPerRepair: IMaxIntellectGainValues;
repairKit: RepairKit;
}
export interface IIntellectGainValues {
weapon: number;
armor: number;
}
export interface IMaxIntellectGainValues {
kit: number;
trader: number;
}
export interface RepairKit {
armor: BonusSettings;
weapon: BonusSettings;

View File

@ -9,6 +9,7 @@ export interface IScavCaseConfig extends IBaseConfig {
rewardItemBlacklist: string[];
allowMultipleMoneyRewardsPerRarity: boolean;
allowMultipleAmmoRewardsPerRarity: boolean;
allowBossItemsAsRewards: boolean;
}
export interface MoneyRewards {
moneyRewardChancePercent: number;

View File

@ -1,4 +1,5 @@
import { MinMax } from "../../../models/common/MinMax";
import { LootRequest } from "../services/LootRequest";
import { IBaseConfig } from "./IBaseConfig";
export interface ITraderConfig extends IBaseConfig {
kind: "aki-trader";
@ -35,6 +36,12 @@ export interface FenceConfig {
/** Block seasonal items from appearing when season is inactive */
blacklistSeasonalItems: boolean;
blacklist: string[];
coopExtractGift: CoopExtractReward;
}
export interface CoopExtractReward extends LootRequest {
sendGift: boolean;
messageLocaleIds: string[];
giftExpiryHours: number;
}
export interface DiscountOptions {
assortSize: number;

View File

@ -6,6 +6,9 @@ export interface IPackageJsonData {
author: string;
version: string;
akiVersion: string;
/** We deliberately purge this data */
scripts: Record<string, string>;
devDependencies: Record<string, string>;
licence: string;
main: string;
isBundleMod: boolean;

View File

@ -0,0 +1,31 @@
import { ELocationName } from "../../../models/enums/ELocationName";
export interface IQuestTypePool {
types: string[];
pool: IQuestPool;
}
export interface IQuestPool {
Exploration: IExplorationPool;
Elimination: IEliminationPool;
}
export interface IExplorationPool {
locations: Partial<Record<ELocationName, string[]>>;
}
export interface IEliminationPool {
targets: IEliminationTargetPool;
}
export interface IEliminationTargetPool {
Savage?: ITargetLocation;
AnyPmc?: ITargetLocation;
bossBully?: ITargetLocation;
bossGluhar?: ITargetLocation;
bossKilla?: ITargetLocation;
bossSanitar?: ITargetLocation;
bossTagilla?: ITargetLocation;
bossKnight?: ITargetLocation;
bossZryachiy?: ITargetLocation;
bossBoar?: ITargetLocation;
bossBoarSniper?: ITargetLocation;
}
export interface ITargetLocation {
locations: string[];
}

View File

@ -9,4 +9,5 @@ export interface LootRequest {
itemLimits: Record<string, number>;
itemStackLimits: Record<string, MinMax>;
armorLevelWhitelist: number[];
allowBossItems: boolean;
}

View File

@ -29,7 +29,7 @@ export declare class EventOutputHolder {
/**
* Convert the internal trader data object into an object we can send to the client
* @param traderData server data for traders
* @returns
* @returns dict of trader id + TraderData
*/
protected constructTraderRelations(traderData: Record<string, TraderInfo>): Record<string, TraderData>;
/**

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage } from "http";
import { IncomingMessage } from "node:http";
import { DynamicRouter, Router, StaticRouter } from "../di/Router";
export declare class HttpRouter {
protected staticRouters: StaticRouter[];

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { ImageRouteService } from "../services/mod/image/ImageRouteService";
import { HttpFileUtil } from "../utils/HttpFileUtil";
import { VFS } from "../utils/VFS";

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { Serializer } from "../../di/Serializer";
import { BundleLoader } from "../../loaders/BundleLoader";
import { ILogger } from "../../models/spt/utils/ILogger";

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { Serializer } from "../../di/Serializer";
import { ImageRouter } from "../ImageRouter";
export declare class ImageSerializer extends Serializer {

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { NotifierController } from "../../controllers/NotifierController";
import { Serializer } from "../../di/Serializer";
import { HttpServerHelper } from "../../helpers/HttpServerHelper";

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import http, { IncomingMessage, ServerResponse } from "http";
import http, { IncomingMessage, ServerResponse } from "node:http";
import { ApplicationContext } from "../context/ApplicationContext";
import { HttpServerHelper } from "../helpers/HttpServerHelper";
import { IHttpConfig } from "../models/spt/config/IHttpConfig";

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import http, { IncomingMessage } from "http";
import http, { IncomingMessage } from "node:http";
import WebSocket from "ws";
import { HttpServerHelper } from "../helpers/HttpServerHelper";
import { INotification } from "../models/eft/notifier/INotifier";

View File

@ -1,6 +1,6 @@
/// <reference types="node" />
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { Serializer } from "../../di/Serializer";
import { ILogger } from "../../models/spt/utils/ILogger";
import { HttpRouter } from "../../routers/HttpRouter";

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
export interface IHttpListener {
canHandle(sessionId: string, req: IncomingMessage): boolean;
handle(sessionId: string, req: IncomingMessage, resp: ServerResponse): void;

View File

@ -7,7 +7,6 @@ export declare class ItemFilterService {
protected logger: ILogger;
protected databaseServer: DatabaseServer;
protected configServer: ConfigServer;
protected blacklist: string[];
protected itemConfig: IItemConfig;
constructor(logger: ILogger, databaseServer: DatabaseServer, configServer: ConfigServer);
/**
@ -21,4 +20,15 @@ export declare class ItemFilterService {
* @returns string array of blacklisted tempalte ids
*/
getBlacklistedItems(): string[];
/**
* Check if the provided template id is boss item in config/item.json
* @param tpl template id
* @returns true if boss item
*/
isBossItem(tpl: string): boolean;
/**
* Return boss items in config/item.json
* @returns string array of boss item tempalte ids
*/
getBossItems(): string[];
}

View File

@ -14,6 +14,7 @@ import { IRagfairConfig } from "../models/spt/config/IRagfairConfig";
import { ILogger } from "../models/spt/utils/ILogger";
import { ConfigServer } from "../servers/ConfigServer";
import { DatabaseServer } from "../servers/DatabaseServer";
import { HashUtil } from "../utils/HashUtil";
import { JsonUtil } from "../utils/JsonUtil";
import { TimeUtil } from "../utils/TimeUtil";
import { Watermark } from "../utils/Watermark";
@ -29,11 +30,12 @@ export declare class ProfileFixerService {
protected localisationService: LocalisationService;
protected timeUtil: TimeUtil;
protected jsonUtil: JsonUtil;
protected hashUtil: HashUtil;
protected databaseServer: DatabaseServer;
protected configServer: ConfigServer;
protected coreConfig: ICoreConfig;
protected ragfairConfig: IRagfairConfig;
constructor(logger: ILogger, watermark: Watermark, hideoutHelper: HideoutHelper, inventoryHelper: InventoryHelper, traderHelper: TraderHelper, profileHelper: ProfileHelper, itemHelper: ItemHelper, localisationService: LocalisationService, timeUtil: TimeUtil, jsonUtil: JsonUtil, databaseServer: DatabaseServer, configServer: ConfigServer);
constructor(logger: ILogger, watermark: Watermark, hideoutHelper: HideoutHelper, inventoryHelper: InventoryHelper, traderHelper: TraderHelper, profileHelper: ProfileHelper, itemHelper: ItemHelper, localisationService: LocalisationService, timeUtil: TimeUtil, jsonUtil: JsonUtil, hashUtil: HashUtil, databaseServer: DatabaseServer, configServer: ConfigServer);
/**
* Find issues in the pmc profile data that may cause issues and fix them
* @param pmcProfile profile to check and fix
@ -129,6 +131,17 @@ export declare class ProfileFixerService {
* @param pmcProfile Profile to update
*/
removeLegacyScavCaseProductionCrafts(pmcProfile: IPmcData): void;
/**
* 3.7.0 moved AIDs to be numeric, old profiles need to be migrated
* We store the old AID value in new field `sessionId`
* @param fullProfile Profile to update
*/
fixIncorrectAidValue(fullProfile: IAkiProfile): void;
/**
* Bsg nested `stats` into a sub object called 'eft'
* @param fullProfile Profile to check for and migrate stats data
*/
migrateStatsToNewStructure(fullProfile: IAkiProfile): void;
/**
* 26126 (7th August) requires bonuses to have an ID, these were not included in the default profile presets
* @param pmcProfile Profile to add missing IDs to
@ -136,7 +149,7 @@ export declare class ProfileFixerService {
addMissingIdsToBonuses(pmcProfile: IPmcData): void;
/**
* At some point the property name was changed,migrate data across to new name
* @param pmcProfile
* @param pmcProfile Profile to migrate improvements in
*/
protected migrateImprovements(pmcProfile: IPmcData): void;
}

View File

@ -24,6 +24,8 @@ export declare class SeasonalEventService {
protected seasonalEventConfig: ISeasonalEventConfig;
protected questConfig: IQuestConfig;
protected httpConfig: IHttpConfig;
protected halloweenEventActive: any;
protected christmasEventActive: any;
constructor(logger: ILogger, databaseServer: DatabaseServer, databaseImporter: DatabaseImporter, giftService: GiftService, localisationService: LocalisationService, botHelper: BotHelper, profileHelper: ProfileHelper, configServer: ConfigServer);
protected get christmasEventItems(): string[];
protected get halloweenEventItems(): string[];
@ -51,23 +53,18 @@ export declare class SeasonalEventService {
* @returns array of tpl strings
*/
getAllSeasonalEventItems(): string[];
/**
* Get an array of seasonal items that should be blocked as season is not currently active
* @returns Array of tpl strings
*/
getSeasonalEventItemsToBlock(): string[];
/**
* Is a seasonal event currently active
* @returns true if event is active
*/
seasonalEventEnabled(): boolean;
/**
* Is christmas event active (Globals eventtype array contains even name)
* Is christmas event active
* @returns true if active
*/
christmasEventEnabled(): boolean;
/**
* is halloween event active (Globals eventtype array contains even name)
* is halloween event active
* @returns true if active
*/
halloweenEventEnabled(): boolean;
@ -95,10 +92,11 @@ export declare class SeasonalEventService {
*/
isQuestRelatedToEvent(questId: string, event: SeasonalEventType): boolean;
/**
* Check if current date falls inside any of the seasons events pased in, if so, handle them
* Handle seasonal events
* @param sessionId Players id
*/
checkForAndEnableSeasonalEvents(sessionId: string): void;
enableSeasonalEvents(sessionId: string): void;
protected cacheActiveEvents(): void;
/**
* Iterate through bots inventory and loot to find and remove christmas items (as defined in SeasonalEventService)
* @param nodeInventory Bots inventory to iterate over

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { IHttpListener } from "../../../servers/http/IHttpListener";
export declare class HttpListenerMod implements IHttpListener {
private canHandleOverride;

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { DependencyContainer } from "tsyringe";
export declare class HttpListenerModService {
protected container: DependencyContainer;

View File

@ -12,7 +12,6 @@ export declare class App {
protected onLoadComponents: OnLoad[];
protected onUpdateComponents: OnUpdate[];
protected onUpdateLastRun: {};
protected os: any;
constructor(logger: ILogger, timeUtil: TimeUtil, localisationService: LocalisationService, encodingUtil: EncodingUtil, onLoadComponents: OnLoad[], onUpdateComponents: OnUpdate[]);
load(): Promise<void>;
protected update(onUpdateComponents: OnUpdate[]): Promise<void>;

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import crypto from "crypto";
import crypto from "node:crypto";
import { TimeUtil } from "./TimeUtil";
export declare class HashUtil {
protected timeUtil: TimeUtil;

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { ServerResponse } from "http";
import { ServerResponse } from "node:http";
import { HttpServerHelper } from "../helpers/HttpServerHelper";
export declare class HttpFileUtil {
protected httpServerHelper: HttpServerHelper;

View File

@ -1,6 +1,6 @@
/// <reference types="node" />
/// <reference types="node" />
import fs from "fs";
import fs from "node:fs";
import "reflect-metadata";
import { IAsyncQueue } from "../models/spt/utils/IAsyncQueue";
import { IUUidGenerator } from "../models/spt/utils/IUuidGenerator";

View File

@ -4,12 +4,10 @@ import { ConfigServer } from "../servers/ConfigServer";
import { LocalisationService } from "../services/LocalisationService";
export declare class WatermarkLocale {
protected localisationService: LocalisationService;
protected description: string[];
protected warning: string[];
protected modding: string[];
constructor(localisationService: LocalisationService);
protected watermark: {
description: string[];
warning: string[];
modding: string[];
};
getDescription(): string[];
getWarning(): string[];
getModding(): string[];

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import fs from "fs";
import fs from "node:fs";
import winston from "winston";
import { Daum } from "../../models/eft/itemEvent/IItemEventRouterRequest";
import { LogBackgroundColor } from "../../models/spt/logging/LogBackgroundColor";

View File

@ -13,7 +13,7 @@ This project is designed to streamline the initial setup process for building an
## **NodeJS Setup**
Before you begin, ensure to install NodeJS version `v16.17.1`, which has been tested thoroughly with our mod templates and build scripts. Download it from the [official NodeJS website](https://nodejs.org/).
Before you begin, ensure to install NodeJS version `v18.15.0`, which has been tested thoroughly with our mod templates and build scripts. Download it from the [official NodeJS website](https://nodejs.org/).
After installation, it's advised to reboot your system.

View File

@ -27,7 +27,6 @@ import { LocalisationService } from "../services/LocalisationService";
import { OpenZoneService } from "../services/OpenZoneService";
import { ProfileFixerService } from "../services/ProfileFixerService";
import { SeasonalEventService } from "../services/SeasonalEventService";
import { HashUtil } from "../utils/HashUtil";
import { JsonUtil } from "../utils/JsonUtil";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
@ -36,7 +35,6 @@ export declare class GameController {
protected databaseServer: DatabaseServer;
protected jsonUtil: JsonUtil;
protected timeUtil: TimeUtil;
protected hashUtil: HashUtil;
protected preAkiModLoader: PreAkiModLoader;
protected httpServerHelper: HttpServerHelper;
protected randomUtil: RandomUtil;
@ -51,14 +49,13 @@ export declare class GameController {
protected giftService: GiftService;
protected applicationContext: ApplicationContext;
protected configServer: ConfigServer;
protected os: any;
protected httpConfig: IHttpConfig;
protected coreConfig: ICoreConfig;
protected locationConfig: ILocationConfig;
protected ragfairConfig: IRagfairConfig;
protected pmcConfig: IPmcConfig;
protected lootConfig: ILootConfig;
constructor(logger: ILogger, databaseServer: DatabaseServer, jsonUtil: JsonUtil, timeUtil: TimeUtil, hashUtil: HashUtil, preAkiModLoader: PreAkiModLoader, httpServerHelper: HttpServerHelper, randomUtil: RandomUtil, hideoutHelper: HideoutHelper, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, localisationService: LocalisationService, customLocationWaveService: CustomLocationWaveService, openZoneService: OpenZoneService, seasonalEventService: SeasonalEventService, itemBaseClassService: ItemBaseClassService, giftService: GiftService, applicationContext: ApplicationContext, configServer: ConfigServer);
constructor(logger: ILogger, databaseServer: DatabaseServer, jsonUtil: JsonUtil, timeUtil: TimeUtil, preAkiModLoader: PreAkiModLoader, httpServerHelper: HttpServerHelper, randomUtil: RandomUtil, hideoutHelper: HideoutHelper, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, localisationService: LocalisationService, customLocationWaveService: CustomLocationWaveService, openZoneService: OpenZoneService, seasonalEventService: SeasonalEventService, itemBaseClassService: ItemBaseClassService, giftService: GiftService, applicationContext: ApplicationContext, configServer: ConfigServer);
load(): void;
/**
* Handle client/game/start
@ -67,12 +64,6 @@ export declare class GameController {
protected addCustomLooseLootPositions(): void;
protected adjustLooseLootSpawnProbabilities(): void;
protected setHideoutAreasAndCraftsTo40Secs(): void;
/**
* 3.7.0 moved AIDs to be numeric, old profiles need to be migrated
* We store the old AID value in new field `sessionId`
* @param fullProfile Profile to update
*/
protected fixIncorrectAidValue(fullProfile: IAkiProfile): void;
/** Apply custom limits on bot types as defined in configs/location.json/botTypeLimits */
protected adjustMapBotLimits(): void;
/**

View File

@ -62,10 +62,10 @@ export declare class InraidController {
savePostRaidProgress(offraidData: ISaveProgressRequestData, sessionID: string): void;
/**
* Handle updating player profile post-pmc raid
* @param sessionID session id
* @param offraidData post-raid data
* @param sessionID Session id
* @param postRaidRequest Post-raid data
*/
protected savePmcProgress(sessionID: string, offraidData: ISaveProgressRequestData): void;
protected savePmcProgress(sessionID: string, postRaidRequest: ISaveProgressRequestData): void;
/**
* Make changes to pmc profile after they've died in raid,
* Alter bodypart hp, handle insurance, delete inventory items, remove carried quest items
@ -91,10 +91,10 @@ export declare class InraidController {
protected reducePmcHealthToPercent(pmcData: IPmcData, multipler: number): void;
/**
* Handle updating the profile post-pscav raid
* @param sessionID session id
* @param offraidData post-raid data of raid
* @param sessionID Session id
* @param postRaidRequest Post-raid data of raid
*/
protected savePlayerScavProgress(sessionID: string, offraidData: ISaveProgressRequestData): void;
protected savePlayerScavProgress(sessionID: string, postRaidRequest: ISaveProgressRequestData): void;
/**
* Is the player dead after a raid - dead is anything other than "survived" / "runner"
* @param statusOnExit exit value from offraidData object

View File

@ -4,12 +4,11 @@ import { ProfileHelper } from "../helpers/ProfileHelper";
import { TraderHelper } from "../helpers/TraderHelper";
import { IPmcData } from "../models/eft/common/IPmcData";
import { Item } from "../models/eft/common/tables/IItem";
import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem";
import { IGetInsuranceCostRequestData } from "../models/eft/insurance/IGetInsuranceCostRequestData";
import { IGetInsuranceCostResponseData } from "../models/eft/insurance/IGetInsuranceCostResponseData";
import { IInsureRequestData } from "../models/eft/insurance/IInsureRequestData";
import { IItemEventRouterResponse } from "../models/eft/itemEvent/IItemEventRouterResponse";
import { Insurance } from "../models/eft/profile/IAkiProfile";
import { Insurance, ISystemData } from "../models/eft/profile/IAkiProfile";
import { IInsuranceConfig } from "../models/spt/config/IInsuranceConfig";
import { ILogger } from "../models/spt/utils/ILogger";
import { EventOutputHolder } from "../routers/EventOutputHolder";
@ -67,67 +66,100 @@ export declare class InsuranceController {
*/
protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void;
/**
* Build an array of items to delete from the insured items.
* Remove an insurance package from a profile using the package's system data information.
*
* This method orchestrates several steps:
* - Filters items based on their presence in the database and their raid moddability.
* - Sorts base and independent child items to consider for deletion.
* - Groups child items by their parent for later evaluation.
* - Evaluates grouped child items to decide which should be deleted, based on their value and a random roll.
*
* @param insured - The insured items to build a removal array from.
* @returns An array of IDs representing items that should be deleted.
*/
protected findItemsToDelete(insured: Insurance): string[];
/**
* Filters an item based on its existence in the database, raid moddability, and slot requirements.
*
* @param item The item to be filtered.
* @param parentItemDbDetails The database details of the parent item, or null if the item has no parent.
* @param itemDbDetails A tuple where the first element is a boolean indicating if the item exists in the database,
* and the second element is the item details if it does.
* @returns true if the item exists in the database and neither of the following conditions are met:
* - The item has the RaidModdable property set to false.
* - The item is attached to a required slot in its parent item.
* Otherwise, returns false.
*/
protected filterByRaidModdability(item: Item, parentItemDbDetails: ITemplateItem | null, itemDbDetails: [boolean, ITemplateItem]): boolean;
/**
* Determines if an item is either a base item or a child item that is not equipped to its parent.
*
* @param item The item to check.
* @returns true if the item is a base or an independent child item, otherwise false.
*/
protected isBaseOrIndependentChild(item: Item): boolean;
/**
* Makes a roll to determine if a given item should be deleted. If the roll is successful, the item's ID is added
* to the `toDelete` array.
*
* @param item The item for which the roll is made.
* @param traderId The ID of the trader to consider in the rollForItemDelete method.
* @param toDelete The array accumulating the IDs of items to be deleted.
* @returns true if the item is marked for deletion, otherwise false.
*/
protected makeRollAndMarkForDeletion(item: Item, traderId: string, toDelete: string[]): boolean;
/**
* Groups child items by their parent IDs in a Map data structure.
*
* @param item The child item to be grouped by its parent.
* @param childrenGroupedByParent The Map that holds arrays of children items grouped by their parent IDs.
* @param sessionID The session ID of the profile to remove the package from.
* @param index The array index of the insurance package to remove.
* @returns void
*/
protected groupChildrenByParent(item: Item, childrenGroupedByParent: Map<string, Item[]>): void;
protected removeInsurancePackageFromProfile(sessionID: string, packageInfo: ISystemData): void;
/**
* Sorts the array of children items in descending order by their maximum price. For each child, a roll is made to
* determine if it should be deleted. The method then deletes the most valuable children based on the number of
* successful rolls made.
* Finds the items that should be deleted based on the given Insurance object.
*
* @param children The array of children items to sort and filter.
* @param traderId The ID of the trader to consider in the rollForItemDelete method.
* @param insured The insurance object containing the items to evaluate for deletion.
* @returns A Set containing the IDs of items that should be deleted.
*/
protected findItemsToDelete(insured: Insurance): Set<string>;
/**
* Populate a Map object of items for quick lookup by their ID.
*
* @param insured The insurance object containing the items to populate the map with.
* @returns A Map where the keys are the item IDs and the values are the corresponding Item objects.
*/
protected populateItemsMap(insured: Insurance): Map<string, Item>;
/**
* Initialize a Map object that holds main-parents to all of their attachments. Note that "main-parent" in this
* context refers to the parent item that an attachment is attached to. For example, a suppressor attached to a gun,
* not the backpack that the gun is located in (the gun's parent).
*
* @param insured - The insurance object containing the items to evaluate.
* @param itemsMap - A Map object for quick item look-up by item ID.
* @returns A Map object containing parent item IDs to arrays of their attachment items.
*/
protected populateParentAttachmentsMap(insured: Insurance, itemsMap: Map<string, Item>): Map<string, Item[]>;
/**
* Process "regular" insurance items. Any insured item that is not an attached, attachment is considered a "regular"
* item. This method iterates over them, preforming item deletion rolls to see if they should be deleted. If so,
* they (and their attached, attachments, if any) are marked for deletion in the toDelete Set.
*
* @param insured The insurance object containing the items to evaluate.
* @param toDelete A Set to keep track of items marked for deletion.
* @returns void
*/
protected processRegularItems(insured: Insurance, toDelete: Set<string>): void;
/**
* Process parent items and their attachments, updating the toDelete Set accordingly.
*
* This method iterates over a map of parent items to their attachments and performs evaluations on each.
* It marks items for deletion based on certain conditions and updates the toDelete Set accordingly.
*
* @param mainParentToAttachmentsMap A Map object containing parent item IDs to arrays of their attachment items.
* @param itemsMap A Map object for quick item look-up by item ID.
* @param traderId The trader ID from the Insurance object.
* @param toDelete A Set object to keep track of items marked for deletion.
*/
protected processAttachments(mainParentToAttachmentsMap: Map<string, Item[]>, itemsMap: Map<string, Item>, traderId: string, toDelete: Set<string>): void;
/**
* Takes an array of attachment items that belong to the same main-parent item, sorts them in descending order by
* their maximum price. For each attachment, a roll is made to determine if a deletion should be made. Once the
* number of deletions has been counted, the attachments are added to the toDelete Set, starting with the most
* valuable attachments first.
*
* @param attachments The array of attachment items to sort, filter, and roll.
* @param traderId The ID of the trader to that has ensured these items.
* @param toDelete The array that accumulates the IDs of the items to be deleted.
* @returns void
*/
protected sortAndFilterChildren(children: Item[], traderId: string, toDelete: string[]): void;
protected processAttachmentByParent(attachments: Item[], traderId: string, toDelete: Set<string>): void;
/**
* Sorts the attachment items by their max price in descending order.
*
* @param attachments The array of attachments items.
* @returns An array of items enriched with their max price and common locale-name.
*/
protected sortAttachmentsByPrice(attachments: Item[]): EnrichedItem[];
/**
* Logs the details of each attachment item.
*
* @param attachments The array of attachment items.
*/
protected logAttachmentsDetails(attachments: EnrichedItem[]): void;
/**
* Counts the number of successful rolls for the attachment items.
*
* @param attachments The array of attachment items.
* @param traderId The ID of the trader that has insured these attachments.
* @returns The number of successful rolls.
*/
protected countSuccessfulRolls(attachments: Item[], traderId: string): number;
/**
* Marks the most valuable attachments for deletion based on the number of successful rolls made.
*
* @param attachments The array of attachment items.
* @param successfulRolls The number of successful rolls.
* @param toDelete The array that accumulates the IDs of the items to be deleted.
*/
protected attachmentDeletionByValue(attachments: EnrichedItem[], successfulRolls: number, toDelete: Set<string>): void;
/**
* Remove items from the insured items that should not be returned to the player.
*
@ -135,7 +167,23 @@ export declare class InsuranceController {
* @param toDelete The items that should be deleted.
* @returns void
*/
protected removeItemsFromInsurance(insured: Insurance, toDelete: string[]): void;
protected removeItemsFromInsurance(insured: Insurance, toDelete: Set<string>): void;
/**
* Adopts orphaned items by resetting them as base-level items. Helpful in situations where a parent has been
* deleted from insurance, but any insured items within the parent should remain. This method will remove the
* reference from the children to the parent and set item properties to main-level values.
*
* @param insured Insurance object containing items.
*/
protected adoptOrphanedItems(insured: Insurance): void;
/**
* Fetches the parentId property of an item with a slotId "hideout". Not sure if this is actually dynamic, but this
* method should be a reliable way to fetch it, if it ever does change.
*
* @param items Array of items to search through.
* @returns The parentId of an item with slotId 'hideout'. Empty string if not found.
*/
protected fetchHideoutItemParent(items: Item[]): string;
/**
* Handle sending the insurance message to the user that potentially contains the valid insurance items.
*
@ -146,15 +194,14 @@ export declare class InsuranceController {
*/
protected sendMail(sessionID: string, insurance: Insurance, noItems: boolean): void;
/**
* Determines whether a valid insured item should be removed from the player's inventory based on a random roll and
* Determines whether a insured item should be removed from the player's inventory based on a random roll and
* trader-specific return chance.
*
* @param insuredItem The insured item being evaluated for removal.
* @param traderId The ID of the trader who insured the item.
* @param itemsBeingDeleted List of items that are already slated for removal.
* @param insuredItem Optional. The item to roll for. Only used for logging.
* @returns true if the insured item should be removed from inventory, false otherwise.
*/
protected rollForItemDelete(insuredItem: Item, traderId: string, itemsBeingDeleted: string[]): boolean;
protected rollForDelete(traderId: string, insuredItem?: Item): boolean;
/**
* Handle Insure event
* Add insurance to an item
@ -175,3 +222,8 @@ export declare class InsuranceController {
*/
cost(request: IGetInsuranceCostRequestData, sessionID: string): IGetInsuranceCostResponseData;
}
interface EnrichedItem extends Item {
name: string;
maxPrice: number;
}
export {};

View File

@ -1,4 +1,5 @@
import { ApplicationContext } from "../context/ApplicationContext";
import { LootGenerator } from "../generators/LootGenerator";
import { ProfileHelper } from "../helpers/ProfileHelper";
import { TraderHelper } from "../helpers/TraderHelper";
import { IPmcData } from "../models/eft/common/IPmcData";
@ -12,16 +13,24 @@ import { IJoinMatchResult } from "../models/eft/match/IJoinMatchResult";
import { IInRaidConfig } from "../models/spt/config/IInRaidConfig";
import { IMatchConfig } from "../models/spt/config/IMatchConfig";
import { IPmcConfig } from "../models/spt/config/IPmcConfig";
import { ITraderConfig } from "../models/spt/config/ITraderConfig";
import { ILogger } from "../models/spt/utils/ILogger";
import { ConfigServer } from "../servers/ConfigServer";
import { SaveServer } from "../servers/SaveServer";
import { BotGenerationCacheService } from "../services/BotGenerationCacheService";
import { BotLootCacheService } from "../services/BotLootCacheService";
import { MailSendService } from "../services/MailSendService";
import { MatchLocationService } from "../services/MatchLocationService";
import { ProfileSnapshotService } from "../services/ProfileSnapshotService";
import { HashUtil } from "../utils/HashUtil";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
export declare class MatchController {
protected logger: ILogger;
protected saveServer: SaveServer;
protected timeUtil: TimeUtil;
protected randomUtil: RandomUtil;
protected hashUtil: HashUtil;
protected profileHelper: ProfileHelper;
protected matchLocationService: MatchLocationService;
protected traderHelper: TraderHelper;
@ -29,11 +38,14 @@ export declare class MatchController {
protected configServer: ConfigServer;
protected profileSnapshotService: ProfileSnapshotService;
protected botGenerationCacheService: BotGenerationCacheService;
protected mailSendService: MailSendService;
protected lootGenerator: LootGenerator;
protected applicationContext: ApplicationContext;
protected matchConfig: IMatchConfig;
protected inraidConfig: IInRaidConfig;
protected traderConfig: ITraderConfig;
protected pmcConfig: IPmcConfig;
constructor(logger: ILogger, saveServer: SaveServer, profileHelper: ProfileHelper, matchLocationService: MatchLocationService, traderHelper: TraderHelper, botLootCacheService: BotLootCacheService, configServer: ConfigServer, profileSnapshotService: ProfileSnapshotService, botGenerationCacheService: BotGenerationCacheService, applicationContext: ApplicationContext);
constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, randomUtil: RandomUtil, hashUtil: HashUtil, profileHelper: ProfileHelper, matchLocationService: MatchLocationService, traderHelper: TraderHelper, botLootCacheService: BotLootCacheService, configServer: ConfigServer, profileSnapshotService: ProfileSnapshotService, botGenerationCacheService: BotGenerationCacheService, mailSendService: MailSendService, lootGenerator: LootGenerator, applicationContext: ApplicationContext);
getEnabled(): boolean;
/** Handle raid/profile/list */
getProfile(info: IGetProfileRequestData): IPmcData[];
@ -59,6 +71,13 @@ export declare class MatchController {
protected convertDifficultyDropdownIntoBotDifficulty(botDifficulty: string): string;
/** Handle client/match/offline/end */
endOfflineRaid(info: IEndOfflineRaidRequestData, sessionId: string): void;
/**
* Did player take a COOP extract
* @param extractName Name of extract player took
* @returns True if coop extract
*/
protected extractWasViaCoop(extractName: string): boolean;
protected sendCoopTakenFenceMessage(sessionId: string): void;
/**
* Was extract by car
* @param extractName name of extract

View File

@ -1,81 +1,41 @@
import { HandbookHelper } from "../helpers/HandbookHelper";
import { ItemHelper } from "../helpers/ItemHelper";
import { PresetHelper } from "../helpers/PresetHelper";
import { RepeatableQuestGenerator } from "../generators/RepeatableQuestGenerator";
import { ProfileHelper } from "../helpers/ProfileHelper";
import { RagfairServerHelper } from "../helpers/RagfairServerHelper";
import { RepeatableQuestHelper } from "../helpers/RepeatableQuestHelper";
import { IEmptyRequestData } from "../models/eft/common/IEmptyRequestData";
import { Exit } from "../models/eft/common/ILocationBase";
import { IPmcData } from "../models/eft/common/IPmcData";
import { TraderInfo } from "../models/eft/common/tables/IBotBase";
import { ICompletion, ICompletionAvailableFor, IElimination, IEliminationCondition, IExploration, IExplorationCondition, IPmcDataRepeatableQuest, IRepeatableQuest, IReward, IRewards } from "../models/eft/common/tables/IRepeatableQuests";
import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem";
import { IPmcDataRepeatableQuest } from "../models/eft/common/tables/IRepeatableQuests";
import { IItemEventRouterResponse } from "../models/eft/itemEvent/IItemEventRouterResponse";
import { IRepeatableQuestChangeRequest } from "../models/eft/quests/IRepeatableQuestChangeRequest";
import { ELocationName } from "../models/enums/ELocationName";
import { IEliminationConfig, IQuestConfig, IRepeatableQuestConfig } from "../models/spt/config/IQuestConfig";
import { IQuestConfig, IRepeatableQuestConfig } from "../models/spt/config/IQuestConfig";
import { IQuestTypePool } from "../models/spt/repeatable/IQuestTypePool";
import { ILogger } from "../models/spt/utils/ILogger";
import { EventOutputHolder } from "../routers/EventOutputHolder";
import { ConfigServer } from "../servers/ConfigServer";
import { DatabaseServer } from "../servers/DatabaseServer";
import { ItemFilterService } from "../services/ItemFilterService";
import { LocalisationService } from "../services/LocalisationService";
import { PaymentService } from "../services/PaymentService";
import { ProfileFixerService } from "../services/ProfileFixerService";
import { HttpResponseUtil } from "../utils/HttpResponseUtil";
import { JsonUtil } from "../utils/JsonUtil";
import { MathUtil } from "../utils/MathUtil";
import { ObjectId } from "../utils/ObjectId";
import { ProbabilityObject, ProbabilityObjectArray, RandomUtil } from "../utils/RandomUtil";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
export interface IQuestTypePool {
types: string[];
pool: IQuestPool;
}
export interface IQuestPool {
Exploration: IExplorationPool;
Elimination: IEliminationPool;
}
export interface IExplorationPool {
locations: Partial<Record<ELocationName, string[]>>;
}
export interface IEliminationPool {
targets: IEliminationTargetPool;
}
export interface IEliminationTargetPool {
Savage?: ITargetLocation;
AnyPmc?: ITargetLocation;
bossBully?: ITargetLocation;
bossGluhar?: ITargetLocation;
bossKilla?: ITargetLocation;
bossSanitar?: ITargetLocation;
bossTagilla?: ITargetLocation;
bossKojaniy?: ITargetLocation;
}
export interface ITargetLocation {
locations: string[];
}
export declare class RepeatableQuestController {
protected timeUtil: TimeUtil;
protected logger: ILogger;
protected randomUtil: RandomUtil;
protected httpResponse: HttpResponseUtil;
protected mathUtil: MathUtil;
protected jsonUtil: JsonUtil;
protected databaseServer: DatabaseServer;
protected itemHelper: ItemHelper;
protected presetHelper: PresetHelper;
protected profileHelper: ProfileHelper;
protected profileFixerService: ProfileFixerService;
protected handbookHelper: HandbookHelper;
protected ragfairServerHelper: RagfairServerHelper;
protected eventOutputHolder: EventOutputHolder;
protected localisationService: LocalisationService;
protected paymentService: PaymentService;
protected objectId: ObjectId;
protected itemFilterService: ItemFilterService;
protected repeatableQuestGenerator: RepeatableQuestGenerator;
protected repeatableQuestHelper: RepeatableQuestHelper;
protected configServer: ConfigServer;
protected questConfig: IQuestConfig;
constructor(timeUtil: TimeUtil, logger: ILogger, randomUtil: RandomUtil, httpResponse: HttpResponseUtil, mathUtil: MathUtil, jsonUtil: JsonUtil, databaseServer: DatabaseServer, itemHelper: ItemHelper, presetHelper: PresetHelper, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, handbookHelper: HandbookHelper, ragfairServerHelper: RagfairServerHelper, eventOutputHolder: EventOutputHolder, localisationService: LocalisationService, paymentService: PaymentService, objectId: ObjectId, itemFilterService: ItemFilterService, configServer: ConfigServer);
constructor(timeUtil: TimeUtil, logger: ILogger, randomUtil: RandomUtil, httpResponse: HttpResponseUtil, jsonUtil: JsonUtil, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, ragfairServerHelper: RagfairServerHelper, eventOutputHolder: EventOutputHolder, paymentService: PaymentService, objectId: ObjectId, repeatableQuestGenerator: RepeatableQuestGenerator, repeatableQuestHelper: RepeatableQuestHelper, configServer: ConfigServer);
/**
* Handle client/repeatalbeQuests/activityPeriods
* Returns an array of objects in the format of repeatable quests to the client.
@ -109,103 +69,10 @@ export declare class RepeatableQuestController {
* @returns IPmcDataRepeatableQuest
*/
protected getRepeatableQuestSubTypeFromProfile(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): IPmcDataRepeatableQuest;
/**
* This method is called by GetClientRepeatableQuests and creates one element of quest type format (see assets/database/templates/repeatableQuests.json).
* It randomly draws a quest type (currently Elimination, Completion or Exploration) as well as a trader who is providing the quest
*/
protected generateRepeatableQuest(pmcLevel: number, pmcTraderInfo: Record<string, TraderInfo>, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IRepeatableQuest;
/**
* Just for debug reasons. Draws dailies a random assort of dailies extracted from dumps
*/
generateDebugDailies(dailiesPool: any, factory: any, number: number): any;
/**
* Generates the base object of quest type format given as templates in assets/database/templates/repeatableQuests.json
* The templates include Elimination, Completion and Extraction quest types
*
* @param {string} type quest type: "Elimination", "Completion" or "Extraction"
* @param {string} traderId trader from which the quest will be provided
* @param {string} side scav daily or pmc daily/weekly quest
* @returns {object} a object which contains the base elements for repeatable quests of the requests type
* (needs to be filled with reward and conditions by called to make a valid quest)
*/
protected generateRepeatableTemplate(type: string, traderId: string, side: string): IRepeatableQuest;
/**
* Generates a valid Exploration quest
*
* @param {integer} pmcLevel player's level for reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} questTypePool Pools for quests (used to avoid redundant quests)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Exploration" (see assets/database/templates/repeatableQuests.json)
*/
protected generateExplorationQuest(pmcLevel: number, traderId: string, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IExploration;
/**
* Generates a valid Completion quest
*
* @param {integer} pmcLevel player's level for requested items and reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Completion" (see assets/database/templates/repeatableQuests.json)
*/
protected generateCompletionQuest(pmcLevel: number, traderId: string, repeatableConfig: IRepeatableQuestConfig): ICompletion;
/**
* Generates a valid Elimination quest
*
* @param {integer} pmcLevel player's level for requested items and reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} questTypePool Pools for quests (used to avoid redundant quests)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Elimination" (see assets/database/templates/repeatableQuests.json)
*/
protected generateEliminationQuest(pmcLevel: number, traderId: string, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IElimination;
/**
* Get the relevant elimination config based on the current players PMC level
* @param pmcLevel Level of PMC character
* @param repeatableConfig Main repeatable config
* @returns IEliminationConfig
*/
protected getEliminationConfigByPmcLevel(pmcLevel: number, repeatableConfig: IRepeatableQuestConfig): IEliminationConfig;
/**
* Convert a location into an quest code can read (e.g. factory4_day into 55f2d3fd4bdc2d5f408b4567)
* @param locationKey e.g factory4_day
* @returns guid
*/
protected getQuestLocationByMapId(locationKey: string): string;
/**
* Exploration repeatable quests can specify a required extraction point.
* This method creates the according object which will be appended to the conditions array
*
* @param {string} exit The exit name to generate the condition for
* @returns {object} Exit condition
*/
protected generateExplorationExitCondition(exit: Exit): IExplorationCondition;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateCompletionQuest to create a completion condition (of which a completion quest theoretically can have many)
*
* @param {string} targetItemId id of the item to request
* @param {integer} value amount of items of this specific type to request
* @returns {object} object of "Completion"-condition
*/
protected generateCompletionAvailableForFinish(targetItemId: string, value: number): ICompletionAvailableFor;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateEliminationQuest to create a location condition.
*
* @param {string} location the location on which to fulfill the elimination quest
* @returns {object} object of "Elimination"-location-subcondition
*/
protected generateEliminationLocation(location: string[]): IEliminationCondition;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateEliminationQuest to create a kill condition.
*
* @param {string} target array of target npcs e.g. "AnyPmc", "Savage"
* @param {array} bodyParts array of body parts with which to kill e.g. ["stomach", "thorax"]
* @param {number} distance distance from which to kill (currently only >= supported)
* @returns {object} object of "Elimination"-kill-subcondition
*/
protected generateEliminationCondition(target: string, bodyPart: string[], distance: number): IEliminationCondition;
/**
* Used to create a quest pool during each cycle of repeatable quest generation. The pool will be subsequently
* narrowed down during quest generation to avoid duplicate quests. Like duplicate extractions or elimination quests
@ -215,53 +82,10 @@ export declare class RepeatableQuestController {
* @returns IQuestTypePool
*/
protected generateQuestPool(repeatableConfig: IRepeatableQuestConfig, pmcLevel: number): IQuestTypePool;
/**
* Generate the reward for a mission. A reward can consist of
* - Experience
* - Money
* - Items
* - Trader Reputation
*
* The reward is dependent on the player level as given by the wiki. The exact mapping of pmcLevel to
* experience / money / items / trader reputation can be defined in QuestConfig.js
*
* There's also a random variation of the reward the spread of which can be also defined in the config.
*
* Additonaly, a scaling factor w.r.t. quest difficulty going from 0.2...1 can be used
*
* @param {integer} pmcLevel player's level
* @param {number} difficulty a reward scaling factor goint from 0.2 to 1
* @param {string} traderId the trader for reputation gain (and possible in the future filtering of reward item type based on trader)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of "Reward"-type that can be given for a repeatable mission
*/
protected generateReward(pmcLevel: number, difficulty: number, traderId: string, repeatableConfig: IRepeatableQuestConfig): IRewards;
/**
* Helper to create a reward item structured as required by the client
*
* @param {string} tpl itemId of the rewarded item
* @param {integer} value amount of items to give
* @param {integer} index all rewards will be appended to a list, for unkown reasons the client wants the index
* @returns {object} object of "Reward"-item-type
*/
protected generateRewardItem(tpl: string, value: number, index: number, preset?: any): IReward;
protected createBaseQuestPool(repeatableConfig: IRepeatableQuestConfig): IQuestTypePool;
debugLogRepeatableQuestIds(pmcData: IPmcData): void;
protected probabilityObjectArray<K, V>(configArrayInput: ProbabilityObject<K, V>[]): ProbabilityObjectArray<K, V>;
/**
* Handle RepeatableQuestChange event
*/
changeRepeatableQuest(pmcData: IPmcData, body: IRepeatableQuestChangeRequest, sessionID: string): IItemEventRouterResponse;
/**
* Picks rewardable items from items.json. This means they need to fit into the inventory and they shouldn't be keys (debatable)
* @param repeatableQuestConfig config file
* @returns a list of rewardable items [[_tpl, itemTemplate],...]
*/
protected getRewardableItems(repeatableQuestConfig: IRepeatableQuestConfig): [string, ITemplateItem][];
/**
* Checks if an id is a valid item. Valid meaning that it's an item that may be a reward
* or content of bot loot. Items that are tested as valid may be in a player backpack or stash.
* @param {string} tpl template id of item to check
* @returns boolean: true if item is valid reward
*/
protected isValidRewardItem(tpl: string, repeatableQuestConfig: IRepeatableQuestConfig): boolean;
changeRepeatableQuest(pmcData: IPmcData, changeRequest: IRepeatableQuestChangeRequest, sessionID: string): IItemEventRouterResponse;
}

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
export declare class Serializer {
serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): void;
canHandle(something: string): boolean;

View File

@ -0,0 +1,183 @@
import { HandbookHelper } from "../helpers/HandbookHelper";
import { ItemHelper } from "../helpers/ItemHelper";
import { PresetHelper } from "../helpers/PresetHelper";
import { ProfileHelper } from "../helpers/ProfileHelper";
import { RagfairServerHelper } from "../helpers/RagfairServerHelper";
import { RepeatableQuestHelper } from "../helpers/RepeatableQuestHelper";
import { Exit } from "../models/eft/common/ILocationBase";
import { TraderInfo } from "../models/eft/common/tables/IBotBase";
import { ICompletion, ICompletionAvailableFor, IElimination, IEliminationCondition, IExploration, IExplorationCondition, IRepeatableQuest, IReward, IRewards } from "../models/eft/common/tables/IRepeatableQuests";
import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem";
import { IQuestConfig, IRepeatableQuestConfig } from "../models/spt/config/IQuestConfig";
import { IQuestTypePool } from "../models/spt/repeatable/IQuestTypePool";
import { ILogger } from "../models/spt/utils/ILogger";
import { EventOutputHolder } from "../routers/EventOutputHolder";
import { ConfigServer } from "../servers/ConfigServer";
import { DatabaseServer } from "../servers/DatabaseServer";
import { ItemFilterService } from "../services/ItemFilterService";
import { LocalisationService } from "../services/LocalisationService";
import { PaymentService } from "../services/PaymentService";
import { ProfileFixerService } from "../services/ProfileFixerService";
import { HttpResponseUtil } from "../utils/HttpResponseUtil";
import { JsonUtil } from "../utils/JsonUtil";
import { MathUtil } from "../utils/MathUtil";
import { ObjectId } from "../utils/ObjectId";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
export declare class RepeatableQuestGenerator {
protected timeUtil: TimeUtil;
protected logger: ILogger;
protected randomUtil: RandomUtil;
protected httpResponse: HttpResponseUtil;
protected mathUtil: MathUtil;
protected jsonUtil: JsonUtil;
protected databaseServer: DatabaseServer;
protected itemHelper: ItemHelper;
protected presetHelper: PresetHelper;
protected profileHelper: ProfileHelper;
protected profileFixerService: ProfileFixerService;
protected handbookHelper: HandbookHelper;
protected ragfairServerHelper: RagfairServerHelper;
protected eventOutputHolder: EventOutputHolder;
protected localisationService: LocalisationService;
protected paymentService: PaymentService;
protected objectId: ObjectId;
protected itemFilterService: ItemFilterService;
protected repeatableQuestHelper: RepeatableQuestHelper;
protected configServer: ConfigServer;
protected questConfig: IQuestConfig;
constructor(timeUtil: TimeUtil, logger: ILogger, randomUtil: RandomUtil, httpResponse: HttpResponseUtil, mathUtil: MathUtil, jsonUtil: JsonUtil, databaseServer: DatabaseServer, itemHelper: ItemHelper, presetHelper: PresetHelper, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, handbookHelper: HandbookHelper, ragfairServerHelper: RagfairServerHelper, eventOutputHolder: EventOutputHolder, localisationService: LocalisationService, paymentService: PaymentService, objectId: ObjectId, itemFilterService: ItemFilterService, repeatableQuestHelper: RepeatableQuestHelper, configServer: ConfigServer);
/**
* This method is called by /GetClientRepeatableQuests/ and creates one element of quest type format (see assets/database/templates/repeatableQuests.json).
* It randomly draws a quest type (currently Elimination, Completion or Exploration) as well as a trader who is providing the quest
* @param pmcLevel Player's level for requested items and reward generation
* @param pmcTraderInfo Players traper standing/rep levels
* @param questTypePool Possible quest types pool
* @param repeatableConfig Repeatable quest config
* @returns IRepeatableQuest
*/
generateRepeatableQuest(pmcLevel: number, pmcTraderInfo: Record<string, TraderInfo>, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IRepeatableQuest;
/**
* Generate a randomised Elimination quest
* @param pmcLevel Player's level for requested items and reward generation
* @param traderId Trader from which the quest will be provided
* @param questTypePool Pools for quests (used to avoid redundant quests)
* @param repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns Object of quest type format for "Elimination" (see assets/database/templates/repeatableQuests.json)
*/
protected generateEliminationQuest(pmcLevel: number, traderId: string, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IElimination;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateEliminationQuest to create a location condition.
*
* @param {string} location the location on which to fulfill the elimination quest
* @returns {object} object of "Elimination"-location-subcondition
*/
protected generateEliminationLocation(location: string[], allowedWeapon: string, allowedWeaponCategory: string): IEliminationCondition;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateEliminationQuest to create a kill condition.
*
* @param {string} target array of target npcs e.g. "AnyPmc", "Savage"
* @param {array} bodyParts array of body parts with which to kill e.g. ["stomach", "thorax"]
* @param {number} distance distance from which to kill (currently only >= supported)
* @returns {object} object of "Elimination"-kill-subcondition
*/
protected generateEliminationCondition(target: string, bodyPart: string[], distance: number, allowedWeapon: string, allowedWeaponCategory: string): IEliminationCondition;
/**
* Generates a valid Completion quest
*
* @param {integer} pmcLevel player's level for requested items and reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Completion" (see assets/database/templates/repeatableQuests.json)
*/
protected generateCompletionQuest(pmcLevel: number, traderId: string, repeatableConfig: IRepeatableQuestConfig): ICompletion;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateCompletionQuest to create a completion condition (of which a completion quest theoretically can have many)
*
* @param {string} targetItemId id of the item to request
* @param {integer} value amount of items of this specific type to request
* @returns {object} object of "Completion"-condition
*/
protected generateCompletionAvailableForFinish(targetItemId: string, value: number): ICompletionAvailableFor;
/**
* Generates a valid Exploration quest
*
* @param {integer} pmcLevel player's level for reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} questTypePool Pools for quests (used to avoid redundant quests)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Exploration" (see assets/database/templates/repeatableQuests.json)
*/
protected generateExplorationQuest(pmcLevel: number, traderId: string, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IExploration;
/**
* Convert a location into an quest code can read (e.g. factory4_day into 55f2d3fd4bdc2d5f408b4567)
* @param locationKey e.g factory4_day
* @returns guid
*/
protected getQuestLocationByMapId(locationKey: string): string;
/**
* Exploration repeatable quests can specify a required extraction point.
* This method creates the according object which will be appended to the conditions array
*
* @param {string} exit The exit name to generate the condition for
* @returns {object} Exit condition
*/
protected generateExplorationExitCondition(exit: Exit): IExplorationCondition;
/**
* Generate the reward for a mission. A reward can consist of
* - Experience
* - Money
* - Items
* - Trader Reputation
*
* The reward is dependent on the player level as given by the wiki. The exact mapping of pmcLevel to
* experience / money / items / trader reputation can be defined in QuestConfig.js
*
* There's also a random variation of the reward the spread of which can be also defined in the config.
*
* Additonaly, a scaling factor w.r.t. quest difficulty going from 0.2...1 can be used
*
* @param {integer} pmcLevel player's level
* @param {number} difficulty a reward scaling factor goint from 0.2 to 1
* @param {string} traderId the trader for reputation gain (and possible in the future filtering of reward item type based on trader)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of "Reward"-type that can be given for a repeatable mission
*/
protected generateReward(pmcLevel: number, difficulty: number, traderId: string, repeatableConfig: IRepeatableQuestConfig): IRewards;
/**
* Helper to create a reward item structured as required by the client
*
* @param {string} tpl itemId of the rewarded item
* @param {integer} value amount of items to give
* @param {integer} index all rewards will be appended to a list, for unkown reasons the client wants the index
* @returns {object} object of "Reward"-item-type
*/
protected generateRewardItem(tpl: string, value: number, index: number, preset?: any): IReward;
/**
* Picks rewardable items from items.json. This means they need to fit into the inventory and they shouldn't be keys (debatable)
* @param repeatableQuestConfig config file
* @returns a list of rewardable items [[_tpl, itemTemplate],...]
*/
protected getRewardableItems(repeatableQuestConfig: IRepeatableQuestConfig): [string, ITemplateItem][];
/**
* Checks if an id is a valid item. Valid meaning that it's an item that may be a reward
* or content of bot loot. Items that are tested as valid may be in a player backpack or stash.
* @param {string} tpl template id of item to check
* @returns boolean: true if item is valid reward
*/
protected isValidRewardItem(tpl: string, repeatableQuestConfig: IRepeatableQuestConfig): boolean;
/**
* Generates the base object of quest type format given as templates in assets/database/templates/repeatableQuests.json
* The templates include Elimination, Completion and Extraction quest types
*
* @param {string} type quest type: "Elimination", "Completion" or "Extraction"
* @param {string} traderId trader from which the quest will be provided
* @param {string} side scav daily or pmc daily/weekly quest
* @returns {object} a object which contains the base elements for repeatable quests of the requests type
* (needs to be filled with reward and conditions by called to make a valid quest)
*/
protected generateRepeatableTemplate(type: string, traderId: string, side: string): IRepeatableQuest;
}

View File

@ -111,11 +111,11 @@ export declare class InRaidHelper {
* Add new items found in raid to profile
* Store insurance items in profile
* @param sessionID Session id
* @param pmcData Profile to update
* @param serverProfile Profile to update
* @param postRaidProfile Profile returned by client after a raid
* @returns Updated profile
*/
setInventory(sessionID: string, pmcData: IPmcData, postRaidProfile: IPmcData): IPmcData;
setInventory(sessionID: string, serverProfile: IPmcData, postRaidProfile: IPmcData): IPmcData;
/**
* Clear pmc inventory of all items except those that are exempt
* Used post-raid to remove items after death

View File

@ -225,6 +225,48 @@ declare class ItemHelper {
* @returns true if item is flagged as quest item
*/
isQuestItem(tpl: string): boolean;
/**
* Checks to see if the item is *actually* moddable in-raid. Checks include the items existence in the database, the
* parent items existence in the database, the existence (and value) of the items RaidModdable property, and that
* the parents slot-required property exists, matches that of the item, and it's value.
*
* Note: this function does not preform any checks to see if the item and parent are *actually* related.
*
* @param item The item to be checked
* @param parent The parent of the item to be checked
* @returns True if the item is actually moddable, false if it is not, and null if the check cannot be performed.
*/
isRaidModdable(item: Item, parent: Item): boolean | null;
/**
* Retrieves the main parent item for a given attachment item.
*
* This method traverses up the hierarchy of items starting from a given `itemId`, until it finds the main parent
* item that is not an attached attachment itself. In other words, if you pass it an item id of a suppressor, it
* will traverse up the muzzle brake, barrel, upper receiver, and return the gun that the suppressor is ultimately
* attached to, even if that gun is located within multiple containers.
*
* It's important to note that traversal is expensive, so this method requires that you pass it a Map of the items
* to traverse, where the keys are the item IDs and the values are the corresponding Item objects. This alleviates
* some of the performance concerns, as it allows for quick lookups of items by ID.
*
* To generate the map:
* ```
* const itemsMap = new Map<string, Item>();
* items.forEach(item => itemsMap.set(item._id, item));
* ```
*
* @param itemId - The unique identifier of the item for which to find the main parent.
* @param itemsMap - A Map containing item IDs mapped to their corresponding Item objects for quick lookup.
* @returns The Item object representing the top-most parent of the given item, or `null` if no such parent exists.
*/
getAttachmentMainParent(itemId: string, itemsMap: Map<string, Item>): Item | null;
/**
* Determines if an item is an attachment that is currently attached to it's parent item.
*
* @param item The item to check.
* @returns true if the item is attached attachment, otherwise false.
*/
isAttachmentAttached(item: Item): boolean;
/**
* Get the inventory size of an item
* @param items Item with children
@ -303,6 +345,7 @@ declare class ItemHelper {
* @returns Name of item
*/
getItemName(itemTpl: string): string;
getItemTplsOfBaseType(desiredBaseType: string): string[];
}
declare namespace ItemHelper {
interface ItemSize {

View File

@ -0,0 +1,20 @@
import { IEliminationConfig, IQuestConfig, IRepeatableQuestConfig } from "../models/spt/config/IQuestConfig";
import { ConfigServer } from "../servers/ConfigServer";
import { JsonUtil } from "../utils/JsonUtil";
import { MathUtil } from "../utils/MathUtil";
import { ProbabilityObject, ProbabilityObjectArray } from "../utils/RandomUtil";
export declare class RepeatableQuestHelper {
protected mathUtil: MathUtil;
protected jsonUtil: JsonUtil;
protected configServer: ConfigServer;
protected questConfig: IQuestConfig;
constructor(mathUtil: MathUtil, jsonUtil: JsonUtil, configServer: ConfigServer);
/**
* Get the relevant elimination config based on the current players PMC level
* @param pmcLevel Level of PMC character
* @param repeatableConfig Main repeatable config
* @returns IEliminationConfig
*/
getEliminationConfigByPmcLevel(pmcLevel: number, repeatableConfig: IRepeatableQuestConfig): IEliminationConfig;
probabilityObjectArray<K, V>(configArrayInput: ProbabilityObject<K, V>[]): ProbabilityObjectArray<K, V>;
}

View File

@ -1,4 +1,5 @@
import { DependencyContainer } from "tsyringe";
import { ModDetails } from "../models/eft/profile/IAkiProfile";
import { ICoreConfig } from "../models/spt/config/ICoreConfig";
import { IModLoader } from "../models/spt/mod/IModLoader";
import { IPackageJsonData } from "../models/spt/mod/IPackageJsonData";
@ -35,6 +36,7 @@ export declare class PreAkiModLoader implements IModLoader {
*/
getImportedModsNames(): string[];
getImportedModDetails(): Record<string, IPackageJsonData>;
getProfileModsGroupedByModName(profileMods: ModDetails[]): ModDetails[];
getModPath(mod: string): string;
protected importMods(): Promise<void>;
protected sortMods(prev: string, next: string, missingFromOrderJSON: Record<string, boolean>): number;
@ -64,6 +66,10 @@ export declare class PreAkiModLoader implements IModLoader {
protected isModCombatibleWithAki(mod: IPackageJsonData): boolean;
protected executeMods(container: DependencyContainer): Promise<void>;
sortModsLoadOrder(): string[];
/**
* Compile mod and add into class property "imported"
* @param mod Name of mod to compile/add
*/
protected addMod(mod: string): Promise<void>;
protected autoInstallDependencies(modPath: string, pkg: IPackageJsonData): void;
protected areModDependenciesFulfilled(pkg: IPackageJsonData, loadedMods: Record<string, IPackageJsonData>): boolean;

View File

@ -161,6 +161,8 @@ export interface ICompletionAvailableForProps extends IAvailableForProps {
}
export interface ILocationConditionProps extends IConditionProps {
target: string[];
weapon?: string[];
weaponCategories?: string[];
}
export interface IKillConditionProps extends IConditionProps {
target: string;
@ -168,6 +170,8 @@ export interface IKillConditionProps extends IConditionProps {
savageRole?: string[];
bodyPart?: string[];
distance?: IDistanceCheck;
weapon?: string[];
weaponCategories?: string[];
}
export interface IDistanceCheck {
compareMethod: string;

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
export type HandleFn = (_: string, req: IncomingMessage, resp: ServerResponse) => void;
/**
* Associates handlers, HTTP methods and a base url to a listener using a proxy

View File

@ -52,4 +52,6 @@ export interface AirdropLoot {
itemStackLimits: Record<string, MinMax>;
/** Armor levels to allow inside crate e.g. [4,5,6] */
armorLevelWhitelist?: number[];
/** Should boss items be added to airdrop crate */
allowBossItems: boolean;
}

View File

@ -8,6 +8,8 @@ export interface IInRaidConfig extends IBaseConfig {
save: Save;
/** Names of car extracts */
carExtracts: string[];
/** Names of coop extracts */
coopExtracts: string[];
/** Fene rep gain from a single car extract */
carExtractBaseStandingGain: number;
/** Fence rep gain when successfully extracting as pscav */

View File

@ -23,4 +23,5 @@ export interface ISealedAirdropContainerSettings {
weaponModRewardLimits: Record<string, MinMax>;
rewardTypeLimits: Record<string, MinMax>;
ammoBoxWhitelist: string[];
allowBossItems: boolean;
}

View File

@ -1,5 +1,8 @@
import { IBaseConfig } from "./IBaseConfig";
export interface IItemConfig extends IBaseConfig {
kind: "aki-item";
/** Items that should be globally blacklisted */
blacklist: string[];
/** Items that can only be found on bosses */
bossItems: string[];
}

View File

@ -92,6 +92,12 @@ export interface IEliminationConfig {
minDist: number;
maxKills: number;
minKills: number;
minBossKills: number;
maxBossKills: number;
weaponCategoryRequirementProb: number;
weaponCategoryRequirements: IWeaponRequirement[];
weaponRequirementProb: number;
weaponRequirements: IWeaponRequirement[];
}
export interface ITarget extends IProbabilityObject {
data: IBossInfo;
@ -102,6 +108,9 @@ export interface IBossInfo {
export interface IBodyPart extends IProbabilityObject {
data: string[];
}
export interface IWeaponRequirement extends IProbabilityObject {
data: string[];
}
export interface IProbabilityObject {
key: string;
relativeProbability: number;

View File

@ -6,8 +6,19 @@ export interface IRepairConfig extends IBaseConfig {
applyRandomizeDurabilityLoss: boolean;
weaponSkillRepairGain: number;
armorKitSkillPointGainPerRepairPointMultiplier: number;
/** INT gain multiplier per repaired item type */
repairKitIntellectGainMultiplier: IIntellectGainValues;
maxIntellectGainPerRepair: IMaxIntellectGainValues;
repairKit: RepairKit;
}
export interface IIntellectGainValues {
weapon: number;
armor: number;
}
export interface IMaxIntellectGainValues {
kit: number;
trader: number;
}
export interface RepairKit {
armor: BonusSettings;
weapon: BonusSettings;

View File

@ -9,6 +9,7 @@ export interface IScavCaseConfig extends IBaseConfig {
rewardItemBlacklist: string[];
allowMultipleMoneyRewardsPerRarity: boolean;
allowMultipleAmmoRewardsPerRarity: boolean;
allowBossItemsAsRewards: boolean;
}
export interface MoneyRewards {
moneyRewardChancePercent: number;

View File

@ -1,4 +1,5 @@
import { MinMax } from "../../../models/common/MinMax";
import { LootRequest } from "../services/LootRequest";
import { IBaseConfig } from "./IBaseConfig";
export interface ITraderConfig extends IBaseConfig {
kind: "aki-trader";
@ -35,6 +36,12 @@ export interface FenceConfig {
/** Block seasonal items from appearing when season is inactive */
blacklistSeasonalItems: boolean;
blacklist: string[];
coopExtractGift: CoopExtractReward;
}
export interface CoopExtractReward extends LootRequest {
sendGift: boolean;
messageLocaleIds: string[];
giftExpiryHours: number;
}
export interface DiscountOptions {
assortSize: number;

View File

@ -6,6 +6,9 @@ export interface IPackageJsonData {
author: string;
version: string;
akiVersion: string;
/** We deliberately purge this data */
scripts: Record<string, string>;
devDependencies: Record<string, string>;
licence: string;
main: string;
isBundleMod: boolean;

View File

@ -0,0 +1,31 @@
import { ELocationName } from "../../../models/enums/ELocationName";
export interface IQuestTypePool {
types: string[];
pool: IQuestPool;
}
export interface IQuestPool {
Exploration: IExplorationPool;
Elimination: IEliminationPool;
}
export interface IExplorationPool {
locations: Partial<Record<ELocationName, string[]>>;
}
export interface IEliminationPool {
targets: IEliminationTargetPool;
}
export interface IEliminationTargetPool {
Savage?: ITargetLocation;
AnyPmc?: ITargetLocation;
bossBully?: ITargetLocation;
bossGluhar?: ITargetLocation;
bossKilla?: ITargetLocation;
bossSanitar?: ITargetLocation;
bossTagilla?: ITargetLocation;
bossKnight?: ITargetLocation;
bossZryachiy?: ITargetLocation;
bossBoar?: ITargetLocation;
bossBoarSniper?: ITargetLocation;
}
export interface ITargetLocation {
locations: string[];
}

View File

@ -9,4 +9,5 @@ export interface LootRequest {
itemLimits: Record<string, number>;
itemStackLimits: Record<string, MinMax>;
armorLevelWhitelist: number[];
allowBossItems: boolean;
}

View File

@ -29,7 +29,7 @@ export declare class EventOutputHolder {
/**
* Convert the internal trader data object into an object we can send to the client
* @param traderData server data for traders
* @returns
* @returns dict of trader id + TraderData
*/
protected constructTraderRelations(traderData: Record<string, TraderInfo>): Record<string, TraderData>;
/**

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage } from "http";
import { IncomingMessage } from "node:http";
import { DynamicRouter, Router, StaticRouter } from "../di/Router";
export declare class HttpRouter {
protected staticRouters: StaticRouter[];

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { ImageRouteService } from "../services/mod/image/ImageRouteService";
import { HttpFileUtil } from "../utils/HttpFileUtil";
import { VFS } from "../utils/VFS";

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { Serializer } from "../../di/Serializer";
import { BundleLoader } from "../../loaders/BundleLoader";
import { ILogger } from "../../models/spt/utils/ILogger";

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { Serializer } from "../../di/Serializer";
import { ImageRouter } from "../ImageRouter";
export declare class ImageSerializer extends Serializer {

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { NotifierController } from "../../controllers/NotifierController";
import { Serializer } from "../../di/Serializer";
import { HttpServerHelper } from "../../helpers/HttpServerHelper";

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import http, { IncomingMessage, ServerResponse } from "http";
import http, { IncomingMessage, ServerResponse } from "node:http";
import { ApplicationContext } from "../context/ApplicationContext";
import { HttpServerHelper } from "../helpers/HttpServerHelper";
import { IHttpConfig } from "../models/spt/config/IHttpConfig";

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import http, { IncomingMessage } from "http";
import http, { IncomingMessage } from "node:http";
import WebSocket from "ws";
import { HttpServerHelper } from "../helpers/HttpServerHelper";
import { INotification } from "../models/eft/notifier/INotifier";

View File

@ -1,6 +1,6 @@
/// <reference types="node" />
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { Serializer } from "../../di/Serializer";
import { ILogger } from "../../models/spt/utils/ILogger";
import { HttpRouter } from "../../routers/HttpRouter";

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
export interface IHttpListener {
canHandle(sessionId: string, req: IncomingMessage): boolean;
handle(sessionId: string, req: IncomingMessage, resp: ServerResponse): void;

View File

@ -7,7 +7,6 @@ export declare class ItemFilterService {
protected logger: ILogger;
protected databaseServer: DatabaseServer;
protected configServer: ConfigServer;
protected blacklist: string[];
protected itemConfig: IItemConfig;
constructor(logger: ILogger, databaseServer: DatabaseServer, configServer: ConfigServer);
/**
@ -21,4 +20,15 @@ export declare class ItemFilterService {
* @returns string array of blacklisted tempalte ids
*/
getBlacklistedItems(): string[];
/**
* Check if the provided template id is boss item in config/item.json
* @param tpl template id
* @returns true if boss item
*/
isBossItem(tpl: string): boolean;
/**
* Return boss items in config/item.json
* @returns string array of boss item tempalte ids
*/
getBossItems(): string[];
}

View File

@ -14,6 +14,7 @@ import { IRagfairConfig } from "../models/spt/config/IRagfairConfig";
import { ILogger } from "../models/spt/utils/ILogger";
import { ConfigServer } from "../servers/ConfigServer";
import { DatabaseServer } from "../servers/DatabaseServer";
import { HashUtil } from "../utils/HashUtil";
import { JsonUtil } from "../utils/JsonUtil";
import { TimeUtil } from "../utils/TimeUtil";
import { Watermark } from "../utils/Watermark";
@ -29,11 +30,12 @@ export declare class ProfileFixerService {
protected localisationService: LocalisationService;
protected timeUtil: TimeUtil;
protected jsonUtil: JsonUtil;
protected hashUtil: HashUtil;
protected databaseServer: DatabaseServer;
protected configServer: ConfigServer;
protected coreConfig: ICoreConfig;
protected ragfairConfig: IRagfairConfig;
constructor(logger: ILogger, watermark: Watermark, hideoutHelper: HideoutHelper, inventoryHelper: InventoryHelper, traderHelper: TraderHelper, profileHelper: ProfileHelper, itemHelper: ItemHelper, localisationService: LocalisationService, timeUtil: TimeUtil, jsonUtil: JsonUtil, databaseServer: DatabaseServer, configServer: ConfigServer);
constructor(logger: ILogger, watermark: Watermark, hideoutHelper: HideoutHelper, inventoryHelper: InventoryHelper, traderHelper: TraderHelper, profileHelper: ProfileHelper, itemHelper: ItemHelper, localisationService: LocalisationService, timeUtil: TimeUtil, jsonUtil: JsonUtil, hashUtil: HashUtil, databaseServer: DatabaseServer, configServer: ConfigServer);
/**
* Find issues in the pmc profile data that may cause issues and fix them
* @param pmcProfile profile to check and fix
@ -129,6 +131,17 @@ export declare class ProfileFixerService {
* @param pmcProfile Profile to update
*/
removeLegacyScavCaseProductionCrafts(pmcProfile: IPmcData): void;
/**
* 3.7.0 moved AIDs to be numeric, old profiles need to be migrated
* We store the old AID value in new field `sessionId`
* @param fullProfile Profile to update
*/
fixIncorrectAidValue(fullProfile: IAkiProfile): void;
/**
* Bsg nested `stats` into a sub object called 'eft'
* @param fullProfile Profile to check for and migrate stats data
*/
migrateStatsToNewStructure(fullProfile: IAkiProfile): void;
/**
* 26126 (7th August) requires bonuses to have an ID, these were not included in the default profile presets
* @param pmcProfile Profile to add missing IDs to
@ -136,7 +149,7 @@ export declare class ProfileFixerService {
addMissingIdsToBonuses(pmcProfile: IPmcData): void;
/**
* At some point the property name was changed,migrate data across to new name
* @param pmcProfile
* @param pmcProfile Profile to migrate improvements in
*/
protected migrateImprovements(pmcProfile: IPmcData): void;
}

View File

@ -24,6 +24,8 @@ export declare class SeasonalEventService {
protected seasonalEventConfig: ISeasonalEventConfig;
protected questConfig: IQuestConfig;
protected httpConfig: IHttpConfig;
protected halloweenEventActive: any;
protected christmasEventActive: any;
constructor(logger: ILogger, databaseServer: DatabaseServer, databaseImporter: DatabaseImporter, giftService: GiftService, localisationService: LocalisationService, botHelper: BotHelper, profileHelper: ProfileHelper, configServer: ConfigServer);
protected get christmasEventItems(): string[];
protected get halloweenEventItems(): string[];
@ -51,23 +53,18 @@ export declare class SeasonalEventService {
* @returns array of tpl strings
*/
getAllSeasonalEventItems(): string[];
/**
* Get an array of seasonal items that should be blocked as season is not currently active
* @returns Array of tpl strings
*/
getSeasonalEventItemsToBlock(): string[];
/**
* Is a seasonal event currently active
* @returns true if event is active
*/
seasonalEventEnabled(): boolean;
/**
* Is christmas event active (Globals eventtype array contains even name)
* Is christmas event active
* @returns true if active
*/
christmasEventEnabled(): boolean;
/**
* is halloween event active (Globals eventtype array contains even name)
* is halloween event active
* @returns true if active
*/
halloweenEventEnabled(): boolean;
@ -95,10 +92,11 @@ export declare class SeasonalEventService {
*/
isQuestRelatedToEvent(questId: string, event: SeasonalEventType): boolean;
/**
* Check if current date falls inside any of the seasons events pased in, if so, handle them
* Handle seasonal events
* @param sessionId Players id
*/
checkForAndEnableSeasonalEvents(sessionId: string): void;
enableSeasonalEvents(sessionId: string): void;
protected cacheActiveEvents(): void;
/**
* Iterate through bots inventory and loot to find and remove christmas items (as defined in SeasonalEventService)
* @param nodeInventory Bots inventory to iterate over

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { IHttpListener } from "../../../servers/http/IHttpListener";
export declare class HttpListenerMod implements IHttpListener {
private canHandleOverride;

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
import { DependencyContainer } from "tsyringe";
export declare class HttpListenerModService {
protected container: DependencyContainer;

View File

@ -12,7 +12,6 @@ export declare class App {
protected onLoadComponents: OnLoad[];
protected onUpdateComponents: OnUpdate[];
protected onUpdateLastRun: {};
protected os: any;
constructor(logger: ILogger, timeUtil: TimeUtil, localisationService: LocalisationService, encodingUtil: EncodingUtil, onLoadComponents: OnLoad[], onUpdateComponents: OnUpdate[]);
load(): Promise<void>;
protected update(onUpdateComponents: OnUpdate[]): Promise<void>;

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import crypto from "crypto";
import crypto from "node:crypto";
import { TimeUtil } from "./TimeUtil";
export declare class HashUtil {
protected timeUtil: TimeUtil;

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { ServerResponse } from "http";
import { ServerResponse } from "node:http";
import { HttpServerHelper } from "../helpers/HttpServerHelper";
export declare class HttpFileUtil {
protected httpServerHelper: HttpServerHelper;

View File

@ -1,6 +1,6 @@
/// <reference types="node" />
/// <reference types="node" />
import fs from "fs";
import fs from "node:fs";
import "reflect-metadata";
import { IAsyncQueue } from "../models/spt/utils/IAsyncQueue";
import { IUUidGenerator } from "../models/spt/utils/IUuidGenerator";

View File

@ -4,12 +4,10 @@ import { ConfigServer } from "../servers/ConfigServer";
import { LocalisationService } from "../services/LocalisationService";
export declare class WatermarkLocale {
protected localisationService: LocalisationService;
protected description: string[];
protected warning: string[];
protected modding: string[];
constructor(localisationService: LocalisationService);
protected watermark: {
description: string[];
warning: string[];
modding: string[];
};
getDescription(): string[];
getWarning(): string[];
getModding(): string[];

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import fs from "fs";
import fs from "node:fs";
import winston from "winston";
import { Daum } from "../../models/eft/itemEvent/IItemEventRouterRequest";
import { LogBackgroundColor } from "../../models/spt/logging/LogBackgroundColor";

View File

@ -13,7 +13,7 @@ This project is designed to streamline the initial setup process for building an
## **NodeJS Setup**
Before you begin, ensure to install NodeJS version `v16.17.1`, which has been tested thoroughly with our mod templates and build scripts. Download it from the [official NodeJS website](https://nodejs.org/).
Before you begin, ensure to install NodeJS version `v18.15.0`, which has been tested thoroughly with our mod templates and build scripts. Download it from the [official NodeJS website](https://nodejs.org/).
After installation, it's advised to reboot your system.

View File

@ -27,7 +27,6 @@ import { LocalisationService } from "../services/LocalisationService";
import { OpenZoneService } from "../services/OpenZoneService";
import { ProfileFixerService } from "../services/ProfileFixerService";
import { SeasonalEventService } from "../services/SeasonalEventService";
import { HashUtil } from "../utils/HashUtil";
import { JsonUtil } from "../utils/JsonUtil";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
@ -36,7 +35,6 @@ export declare class GameController {
protected databaseServer: DatabaseServer;
protected jsonUtil: JsonUtil;
protected timeUtil: TimeUtil;
protected hashUtil: HashUtil;
protected preAkiModLoader: PreAkiModLoader;
protected httpServerHelper: HttpServerHelper;
protected randomUtil: RandomUtil;
@ -51,14 +49,13 @@ export declare class GameController {
protected giftService: GiftService;
protected applicationContext: ApplicationContext;
protected configServer: ConfigServer;
protected os: any;
protected httpConfig: IHttpConfig;
protected coreConfig: ICoreConfig;
protected locationConfig: ILocationConfig;
protected ragfairConfig: IRagfairConfig;
protected pmcConfig: IPmcConfig;
protected lootConfig: ILootConfig;
constructor(logger: ILogger, databaseServer: DatabaseServer, jsonUtil: JsonUtil, timeUtil: TimeUtil, hashUtil: HashUtil, preAkiModLoader: PreAkiModLoader, httpServerHelper: HttpServerHelper, randomUtil: RandomUtil, hideoutHelper: HideoutHelper, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, localisationService: LocalisationService, customLocationWaveService: CustomLocationWaveService, openZoneService: OpenZoneService, seasonalEventService: SeasonalEventService, itemBaseClassService: ItemBaseClassService, giftService: GiftService, applicationContext: ApplicationContext, configServer: ConfigServer);
constructor(logger: ILogger, databaseServer: DatabaseServer, jsonUtil: JsonUtil, timeUtil: TimeUtil, preAkiModLoader: PreAkiModLoader, httpServerHelper: HttpServerHelper, randomUtil: RandomUtil, hideoutHelper: HideoutHelper, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, localisationService: LocalisationService, customLocationWaveService: CustomLocationWaveService, openZoneService: OpenZoneService, seasonalEventService: SeasonalEventService, itemBaseClassService: ItemBaseClassService, giftService: GiftService, applicationContext: ApplicationContext, configServer: ConfigServer);
load(): void;
/**
* Handle client/game/start
@ -67,12 +64,6 @@ export declare class GameController {
protected addCustomLooseLootPositions(): void;
protected adjustLooseLootSpawnProbabilities(): void;
protected setHideoutAreasAndCraftsTo40Secs(): void;
/**
* 3.7.0 moved AIDs to be numeric, old profiles need to be migrated
* We store the old AID value in new field `sessionId`
* @param fullProfile Profile to update
*/
protected fixIncorrectAidValue(fullProfile: IAkiProfile): void;
/** Apply custom limits on bot types as defined in configs/location.json/botTypeLimits */
protected adjustMapBotLimits(): void;
/**

View File

@ -62,10 +62,10 @@ export declare class InraidController {
savePostRaidProgress(offraidData: ISaveProgressRequestData, sessionID: string): void;
/**
* Handle updating player profile post-pmc raid
* @param sessionID session id
* @param offraidData post-raid data
* @param sessionID Session id
* @param postRaidRequest Post-raid data
*/
protected savePmcProgress(sessionID: string, offraidData: ISaveProgressRequestData): void;
protected savePmcProgress(sessionID: string, postRaidRequest: ISaveProgressRequestData): void;
/**
* Make changes to pmc profile after they've died in raid,
* Alter bodypart hp, handle insurance, delete inventory items, remove carried quest items
@ -91,10 +91,10 @@ export declare class InraidController {
protected reducePmcHealthToPercent(pmcData: IPmcData, multipler: number): void;
/**
* Handle updating the profile post-pscav raid
* @param sessionID session id
* @param offraidData post-raid data of raid
* @param sessionID Session id
* @param postRaidRequest Post-raid data of raid
*/
protected savePlayerScavProgress(sessionID: string, offraidData: ISaveProgressRequestData): void;
protected savePlayerScavProgress(sessionID: string, postRaidRequest: ISaveProgressRequestData): void;
/**
* Is the player dead after a raid - dead is anything other than "survived" / "runner"
* @param statusOnExit exit value from offraidData object

View File

@ -4,12 +4,11 @@ import { ProfileHelper } from "../helpers/ProfileHelper";
import { TraderHelper } from "../helpers/TraderHelper";
import { IPmcData } from "../models/eft/common/IPmcData";
import { Item } from "../models/eft/common/tables/IItem";
import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem";
import { IGetInsuranceCostRequestData } from "../models/eft/insurance/IGetInsuranceCostRequestData";
import { IGetInsuranceCostResponseData } from "../models/eft/insurance/IGetInsuranceCostResponseData";
import { IInsureRequestData } from "../models/eft/insurance/IInsureRequestData";
import { IItemEventRouterResponse } from "../models/eft/itemEvent/IItemEventRouterResponse";
import { Insurance } from "../models/eft/profile/IAkiProfile";
import { Insurance, ISystemData } from "../models/eft/profile/IAkiProfile";
import { IInsuranceConfig } from "../models/spt/config/IInsuranceConfig";
import { ILogger } from "../models/spt/utils/ILogger";
import { EventOutputHolder } from "../routers/EventOutputHolder";
@ -67,67 +66,100 @@ export declare class InsuranceController {
*/
protected processInsuredItems(insuranceDetails: Insurance[], sessionID: string): void;
/**
* Build an array of items to delete from the insured items.
* Remove an insurance package from a profile using the package's system data information.
*
* This method orchestrates several steps:
* - Filters items based on their presence in the database and their raid moddability.
* - Sorts base and independent child items to consider for deletion.
* - Groups child items by their parent for later evaluation.
* - Evaluates grouped child items to decide which should be deleted, based on their value and a random roll.
*
* @param insured - The insured items to build a removal array from.
* @returns An array of IDs representing items that should be deleted.
*/
protected findItemsToDelete(insured: Insurance): string[];
/**
* Filters an item based on its existence in the database, raid moddability, and slot requirements.
*
* @param item The item to be filtered.
* @param parentItemDbDetails The database details of the parent item, or null if the item has no parent.
* @param itemDbDetails A tuple where the first element is a boolean indicating if the item exists in the database,
* and the second element is the item details if it does.
* @returns true if the item exists in the database and neither of the following conditions are met:
* - The item has the RaidModdable property set to false.
* - The item is attached to a required slot in its parent item.
* Otherwise, returns false.
*/
protected filterByRaidModdability(item: Item, parentItemDbDetails: ITemplateItem | null, itemDbDetails: [boolean, ITemplateItem]): boolean;
/**
* Determines if an item is either a base item or a child item that is not equipped to its parent.
*
* @param item The item to check.
* @returns true if the item is a base or an independent child item, otherwise false.
*/
protected isBaseOrIndependentChild(item: Item): boolean;
/**
* Makes a roll to determine if a given item should be deleted. If the roll is successful, the item's ID is added
* to the `toDelete` array.
*
* @param item The item for which the roll is made.
* @param traderId The ID of the trader to consider in the rollForItemDelete method.
* @param toDelete The array accumulating the IDs of items to be deleted.
* @returns true if the item is marked for deletion, otherwise false.
*/
protected makeRollAndMarkForDeletion(item: Item, traderId: string, toDelete: string[]): boolean;
/**
* Groups child items by their parent IDs in a Map data structure.
*
* @param item The child item to be grouped by its parent.
* @param childrenGroupedByParent The Map that holds arrays of children items grouped by their parent IDs.
* @param sessionID The session ID of the profile to remove the package from.
* @param index The array index of the insurance package to remove.
* @returns void
*/
protected groupChildrenByParent(item: Item, childrenGroupedByParent: Map<string, Item[]>): void;
protected removeInsurancePackageFromProfile(sessionID: string, packageInfo: ISystemData): void;
/**
* Sorts the array of children items in descending order by their maximum price. For each child, a roll is made to
* determine if it should be deleted. The method then deletes the most valuable children based on the number of
* successful rolls made.
* Finds the items that should be deleted based on the given Insurance object.
*
* @param children The array of children items to sort and filter.
* @param traderId The ID of the trader to consider in the rollForItemDelete method.
* @param insured The insurance object containing the items to evaluate for deletion.
* @returns A Set containing the IDs of items that should be deleted.
*/
protected findItemsToDelete(insured: Insurance): Set<string>;
/**
* Populate a Map object of items for quick lookup by their ID.
*
* @param insured The insurance object containing the items to populate the map with.
* @returns A Map where the keys are the item IDs and the values are the corresponding Item objects.
*/
protected populateItemsMap(insured: Insurance): Map<string, Item>;
/**
* Initialize a Map object that holds main-parents to all of their attachments. Note that "main-parent" in this
* context refers to the parent item that an attachment is attached to. For example, a suppressor attached to a gun,
* not the backpack that the gun is located in (the gun's parent).
*
* @param insured - The insurance object containing the items to evaluate.
* @param itemsMap - A Map object for quick item look-up by item ID.
* @returns A Map object containing parent item IDs to arrays of their attachment items.
*/
protected populateParentAttachmentsMap(insured: Insurance, itemsMap: Map<string, Item>): Map<string, Item[]>;
/**
* Process "regular" insurance items. Any insured item that is not an attached, attachment is considered a "regular"
* item. This method iterates over them, preforming item deletion rolls to see if they should be deleted. If so,
* they (and their attached, attachments, if any) are marked for deletion in the toDelete Set.
*
* @param insured The insurance object containing the items to evaluate.
* @param toDelete A Set to keep track of items marked for deletion.
* @returns void
*/
protected processRegularItems(insured: Insurance, toDelete: Set<string>): void;
/**
* Process parent items and their attachments, updating the toDelete Set accordingly.
*
* This method iterates over a map of parent items to their attachments and performs evaluations on each.
* It marks items for deletion based on certain conditions and updates the toDelete Set accordingly.
*
* @param mainParentToAttachmentsMap A Map object containing parent item IDs to arrays of their attachment items.
* @param itemsMap A Map object for quick item look-up by item ID.
* @param traderId The trader ID from the Insurance object.
* @param toDelete A Set object to keep track of items marked for deletion.
*/
protected processAttachments(mainParentToAttachmentsMap: Map<string, Item[]>, itemsMap: Map<string, Item>, traderId: string, toDelete: Set<string>): void;
/**
* Takes an array of attachment items that belong to the same main-parent item, sorts them in descending order by
* their maximum price. For each attachment, a roll is made to determine if a deletion should be made. Once the
* number of deletions has been counted, the attachments are added to the toDelete Set, starting with the most
* valuable attachments first.
*
* @param attachments The array of attachment items to sort, filter, and roll.
* @param traderId The ID of the trader to that has ensured these items.
* @param toDelete The array that accumulates the IDs of the items to be deleted.
* @returns void
*/
protected sortAndFilterChildren(children: Item[], traderId: string, toDelete: string[]): void;
protected processAttachmentByParent(attachments: Item[], traderId: string, toDelete: Set<string>): void;
/**
* Sorts the attachment items by their max price in descending order.
*
* @param attachments The array of attachments items.
* @returns An array of items enriched with their max price and common locale-name.
*/
protected sortAttachmentsByPrice(attachments: Item[]): EnrichedItem[];
/**
* Logs the details of each attachment item.
*
* @param attachments The array of attachment items.
*/
protected logAttachmentsDetails(attachments: EnrichedItem[]): void;
/**
* Counts the number of successful rolls for the attachment items.
*
* @param attachments The array of attachment items.
* @param traderId The ID of the trader that has insured these attachments.
* @returns The number of successful rolls.
*/
protected countSuccessfulRolls(attachments: Item[], traderId: string): number;
/**
* Marks the most valuable attachments for deletion based on the number of successful rolls made.
*
* @param attachments The array of attachment items.
* @param successfulRolls The number of successful rolls.
* @param toDelete The array that accumulates the IDs of the items to be deleted.
*/
protected attachmentDeletionByValue(attachments: EnrichedItem[], successfulRolls: number, toDelete: Set<string>): void;
/**
* Remove items from the insured items that should not be returned to the player.
*
@ -135,7 +167,23 @@ export declare class InsuranceController {
* @param toDelete The items that should be deleted.
* @returns void
*/
protected removeItemsFromInsurance(insured: Insurance, toDelete: string[]): void;
protected removeItemsFromInsurance(insured: Insurance, toDelete: Set<string>): void;
/**
* Adopts orphaned items by resetting them as base-level items. Helpful in situations where a parent has been
* deleted from insurance, but any insured items within the parent should remain. This method will remove the
* reference from the children to the parent and set item properties to main-level values.
*
* @param insured Insurance object containing items.
*/
protected adoptOrphanedItems(insured: Insurance): void;
/**
* Fetches the parentId property of an item with a slotId "hideout". Not sure if this is actually dynamic, but this
* method should be a reliable way to fetch it, if it ever does change.
*
* @param items Array of items to search through.
* @returns The parentId of an item with slotId 'hideout'. Empty string if not found.
*/
protected fetchHideoutItemParent(items: Item[]): string;
/**
* Handle sending the insurance message to the user that potentially contains the valid insurance items.
*
@ -146,15 +194,14 @@ export declare class InsuranceController {
*/
protected sendMail(sessionID: string, insurance: Insurance, noItems: boolean): void;
/**
* Determines whether a valid insured item should be removed from the player's inventory based on a random roll and
* Determines whether a insured item should be removed from the player's inventory based on a random roll and
* trader-specific return chance.
*
* @param insuredItem The insured item being evaluated for removal.
* @param traderId The ID of the trader who insured the item.
* @param itemsBeingDeleted List of items that are already slated for removal.
* @param insuredItem Optional. The item to roll for. Only used for logging.
* @returns true if the insured item should be removed from inventory, false otherwise.
*/
protected rollForItemDelete(insuredItem: Item, traderId: string, itemsBeingDeleted: string[]): boolean;
protected rollForDelete(traderId: string, insuredItem?: Item): boolean;
/**
* Handle Insure event
* Add insurance to an item
@ -175,3 +222,8 @@ export declare class InsuranceController {
*/
cost(request: IGetInsuranceCostRequestData, sessionID: string): IGetInsuranceCostResponseData;
}
interface EnrichedItem extends Item {
name: string;
maxPrice: number;
}
export {};

View File

@ -1,4 +1,5 @@
import { ApplicationContext } from "../context/ApplicationContext";
import { LootGenerator } from "../generators/LootGenerator";
import { ProfileHelper } from "../helpers/ProfileHelper";
import { TraderHelper } from "../helpers/TraderHelper";
import { IPmcData } from "../models/eft/common/IPmcData";
@ -12,16 +13,24 @@ import { IJoinMatchResult } from "../models/eft/match/IJoinMatchResult";
import { IInRaidConfig } from "../models/spt/config/IInRaidConfig";
import { IMatchConfig } from "../models/spt/config/IMatchConfig";
import { IPmcConfig } from "../models/spt/config/IPmcConfig";
import { ITraderConfig } from "../models/spt/config/ITraderConfig";
import { ILogger } from "../models/spt/utils/ILogger";
import { ConfigServer } from "../servers/ConfigServer";
import { SaveServer } from "../servers/SaveServer";
import { BotGenerationCacheService } from "../services/BotGenerationCacheService";
import { BotLootCacheService } from "../services/BotLootCacheService";
import { MailSendService } from "../services/MailSendService";
import { MatchLocationService } from "../services/MatchLocationService";
import { ProfileSnapshotService } from "../services/ProfileSnapshotService";
import { HashUtil } from "../utils/HashUtil";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
export declare class MatchController {
protected logger: ILogger;
protected saveServer: SaveServer;
protected timeUtil: TimeUtil;
protected randomUtil: RandomUtil;
protected hashUtil: HashUtil;
protected profileHelper: ProfileHelper;
protected matchLocationService: MatchLocationService;
protected traderHelper: TraderHelper;
@ -29,11 +38,14 @@ export declare class MatchController {
protected configServer: ConfigServer;
protected profileSnapshotService: ProfileSnapshotService;
protected botGenerationCacheService: BotGenerationCacheService;
protected mailSendService: MailSendService;
protected lootGenerator: LootGenerator;
protected applicationContext: ApplicationContext;
protected matchConfig: IMatchConfig;
protected inraidConfig: IInRaidConfig;
protected traderConfig: ITraderConfig;
protected pmcConfig: IPmcConfig;
constructor(logger: ILogger, saveServer: SaveServer, profileHelper: ProfileHelper, matchLocationService: MatchLocationService, traderHelper: TraderHelper, botLootCacheService: BotLootCacheService, configServer: ConfigServer, profileSnapshotService: ProfileSnapshotService, botGenerationCacheService: BotGenerationCacheService, applicationContext: ApplicationContext);
constructor(logger: ILogger, saveServer: SaveServer, timeUtil: TimeUtil, randomUtil: RandomUtil, hashUtil: HashUtil, profileHelper: ProfileHelper, matchLocationService: MatchLocationService, traderHelper: TraderHelper, botLootCacheService: BotLootCacheService, configServer: ConfigServer, profileSnapshotService: ProfileSnapshotService, botGenerationCacheService: BotGenerationCacheService, mailSendService: MailSendService, lootGenerator: LootGenerator, applicationContext: ApplicationContext);
getEnabled(): boolean;
/** Handle raid/profile/list */
getProfile(info: IGetProfileRequestData): IPmcData[];
@ -59,6 +71,13 @@ export declare class MatchController {
protected convertDifficultyDropdownIntoBotDifficulty(botDifficulty: string): string;
/** Handle client/match/offline/end */
endOfflineRaid(info: IEndOfflineRaidRequestData, sessionId: string): void;
/**
* Did player take a COOP extract
* @param extractName Name of extract player took
* @returns True if coop extract
*/
protected extractWasViaCoop(extractName: string): boolean;
protected sendCoopTakenFenceMessage(sessionId: string): void;
/**
* Was extract by car
* @param extractName name of extract

View File

@ -1,81 +1,41 @@
import { HandbookHelper } from "../helpers/HandbookHelper";
import { ItemHelper } from "../helpers/ItemHelper";
import { PresetHelper } from "../helpers/PresetHelper";
import { RepeatableQuestGenerator } from "../generators/RepeatableQuestGenerator";
import { ProfileHelper } from "../helpers/ProfileHelper";
import { RagfairServerHelper } from "../helpers/RagfairServerHelper";
import { RepeatableQuestHelper } from "../helpers/RepeatableQuestHelper";
import { IEmptyRequestData } from "../models/eft/common/IEmptyRequestData";
import { Exit } from "../models/eft/common/ILocationBase";
import { IPmcData } from "../models/eft/common/IPmcData";
import { TraderInfo } from "../models/eft/common/tables/IBotBase";
import { ICompletion, ICompletionAvailableFor, IElimination, IEliminationCondition, IExploration, IExplorationCondition, IPmcDataRepeatableQuest, IRepeatableQuest, IReward, IRewards } from "../models/eft/common/tables/IRepeatableQuests";
import { ITemplateItem } from "../models/eft/common/tables/ITemplateItem";
import { IPmcDataRepeatableQuest } from "../models/eft/common/tables/IRepeatableQuests";
import { IItemEventRouterResponse } from "../models/eft/itemEvent/IItemEventRouterResponse";
import { IRepeatableQuestChangeRequest } from "../models/eft/quests/IRepeatableQuestChangeRequest";
import { ELocationName } from "../models/enums/ELocationName";
import { IEliminationConfig, IQuestConfig, IRepeatableQuestConfig } from "../models/spt/config/IQuestConfig";
import { IQuestConfig, IRepeatableQuestConfig } from "../models/spt/config/IQuestConfig";
import { IQuestTypePool } from "../models/spt/repeatable/IQuestTypePool";
import { ILogger } from "../models/spt/utils/ILogger";
import { EventOutputHolder } from "../routers/EventOutputHolder";
import { ConfigServer } from "../servers/ConfigServer";
import { DatabaseServer } from "../servers/DatabaseServer";
import { ItemFilterService } from "../services/ItemFilterService";
import { LocalisationService } from "../services/LocalisationService";
import { PaymentService } from "../services/PaymentService";
import { ProfileFixerService } from "../services/ProfileFixerService";
import { HttpResponseUtil } from "../utils/HttpResponseUtil";
import { JsonUtil } from "../utils/JsonUtil";
import { MathUtil } from "../utils/MathUtil";
import { ObjectId } from "../utils/ObjectId";
import { ProbabilityObject, ProbabilityObjectArray, RandomUtil } from "../utils/RandomUtil";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
export interface IQuestTypePool {
types: string[];
pool: IQuestPool;
}
export interface IQuestPool {
Exploration: IExplorationPool;
Elimination: IEliminationPool;
}
export interface IExplorationPool {
locations: Partial<Record<ELocationName, string[]>>;
}
export interface IEliminationPool {
targets: IEliminationTargetPool;
}
export interface IEliminationTargetPool {
Savage?: ITargetLocation;
AnyPmc?: ITargetLocation;
bossBully?: ITargetLocation;
bossGluhar?: ITargetLocation;
bossKilla?: ITargetLocation;
bossSanitar?: ITargetLocation;
bossTagilla?: ITargetLocation;
bossKojaniy?: ITargetLocation;
}
export interface ITargetLocation {
locations: string[];
}
export declare class RepeatableQuestController {
protected timeUtil: TimeUtil;
protected logger: ILogger;
protected randomUtil: RandomUtil;
protected httpResponse: HttpResponseUtil;
protected mathUtil: MathUtil;
protected jsonUtil: JsonUtil;
protected databaseServer: DatabaseServer;
protected itemHelper: ItemHelper;
protected presetHelper: PresetHelper;
protected profileHelper: ProfileHelper;
protected profileFixerService: ProfileFixerService;
protected handbookHelper: HandbookHelper;
protected ragfairServerHelper: RagfairServerHelper;
protected eventOutputHolder: EventOutputHolder;
protected localisationService: LocalisationService;
protected paymentService: PaymentService;
protected objectId: ObjectId;
protected itemFilterService: ItemFilterService;
protected repeatableQuestGenerator: RepeatableQuestGenerator;
protected repeatableQuestHelper: RepeatableQuestHelper;
protected configServer: ConfigServer;
protected questConfig: IQuestConfig;
constructor(timeUtil: TimeUtil, logger: ILogger, randomUtil: RandomUtil, httpResponse: HttpResponseUtil, mathUtil: MathUtil, jsonUtil: JsonUtil, databaseServer: DatabaseServer, itemHelper: ItemHelper, presetHelper: PresetHelper, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, handbookHelper: HandbookHelper, ragfairServerHelper: RagfairServerHelper, eventOutputHolder: EventOutputHolder, localisationService: LocalisationService, paymentService: PaymentService, objectId: ObjectId, itemFilterService: ItemFilterService, configServer: ConfigServer);
constructor(timeUtil: TimeUtil, logger: ILogger, randomUtil: RandomUtil, httpResponse: HttpResponseUtil, jsonUtil: JsonUtil, profileHelper: ProfileHelper, profileFixerService: ProfileFixerService, ragfairServerHelper: RagfairServerHelper, eventOutputHolder: EventOutputHolder, paymentService: PaymentService, objectId: ObjectId, repeatableQuestGenerator: RepeatableQuestGenerator, repeatableQuestHelper: RepeatableQuestHelper, configServer: ConfigServer);
/**
* Handle client/repeatalbeQuests/activityPeriods
* Returns an array of objects in the format of repeatable quests to the client.
@ -109,103 +69,10 @@ export declare class RepeatableQuestController {
* @returns IPmcDataRepeatableQuest
*/
protected getRepeatableQuestSubTypeFromProfile(repeatableConfig: IRepeatableQuestConfig, pmcData: IPmcData): IPmcDataRepeatableQuest;
/**
* This method is called by GetClientRepeatableQuests and creates one element of quest type format (see assets/database/templates/repeatableQuests.json).
* It randomly draws a quest type (currently Elimination, Completion or Exploration) as well as a trader who is providing the quest
*/
protected generateRepeatableQuest(pmcLevel: number, pmcTraderInfo: Record<string, TraderInfo>, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IRepeatableQuest;
/**
* Just for debug reasons. Draws dailies a random assort of dailies extracted from dumps
*/
generateDebugDailies(dailiesPool: any, factory: any, number: number): any;
/**
* Generates the base object of quest type format given as templates in assets/database/templates/repeatableQuests.json
* The templates include Elimination, Completion and Extraction quest types
*
* @param {string} type quest type: "Elimination", "Completion" or "Extraction"
* @param {string} traderId trader from which the quest will be provided
* @param {string} side scav daily or pmc daily/weekly quest
* @returns {object} a object which contains the base elements for repeatable quests of the requests type
* (needs to be filled with reward and conditions by called to make a valid quest)
*/
protected generateRepeatableTemplate(type: string, traderId: string, side: string): IRepeatableQuest;
/**
* Generates a valid Exploration quest
*
* @param {integer} pmcLevel player's level for reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} questTypePool Pools for quests (used to avoid redundant quests)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Exploration" (see assets/database/templates/repeatableQuests.json)
*/
protected generateExplorationQuest(pmcLevel: number, traderId: string, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IExploration;
/**
* Generates a valid Completion quest
*
* @param {integer} pmcLevel player's level for requested items and reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Completion" (see assets/database/templates/repeatableQuests.json)
*/
protected generateCompletionQuest(pmcLevel: number, traderId: string, repeatableConfig: IRepeatableQuestConfig): ICompletion;
/**
* Generates a valid Elimination quest
*
* @param {integer} pmcLevel player's level for requested items and reward generation
* @param {string} traderId trader from which the quest will be provided
* @param {object} questTypePool Pools for quests (used to avoid redundant quests)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of quest type format for "Elimination" (see assets/database/templates/repeatableQuests.json)
*/
protected generateEliminationQuest(pmcLevel: number, traderId: string, questTypePool: IQuestTypePool, repeatableConfig: IRepeatableQuestConfig): IElimination;
/**
* Get the relevant elimination config based on the current players PMC level
* @param pmcLevel Level of PMC character
* @param repeatableConfig Main repeatable config
* @returns IEliminationConfig
*/
protected getEliminationConfigByPmcLevel(pmcLevel: number, repeatableConfig: IRepeatableQuestConfig): IEliminationConfig;
/**
* Convert a location into an quest code can read (e.g. factory4_day into 55f2d3fd4bdc2d5f408b4567)
* @param locationKey e.g factory4_day
* @returns guid
*/
protected getQuestLocationByMapId(locationKey: string): string;
/**
* Exploration repeatable quests can specify a required extraction point.
* This method creates the according object which will be appended to the conditions array
*
* @param {string} exit The exit name to generate the condition for
* @returns {object} Exit condition
*/
protected generateExplorationExitCondition(exit: Exit): IExplorationCondition;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateCompletionQuest to create a completion condition (of which a completion quest theoretically can have many)
*
* @param {string} targetItemId id of the item to request
* @param {integer} value amount of items of this specific type to request
* @returns {object} object of "Completion"-condition
*/
protected generateCompletionAvailableForFinish(targetItemId: string, value: number): ICompletionAvailableFor;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateEliminationQuest to create a location condition.
*
* @param {string} location the location on which to fulfill the elimination quest
* @returns {object} object of "Elimination"-location-subcondition
*/
protected generateEliminationLocation(location: string[]): IEliminationCondition;
/**
* A repeatable quest, besides some more or less static components, exists of reward and condition (see assets/database/templates/repeatableQuests.json)
* This is a helper method for GenerateEliminationQuest to create a kill condition.
*
* @param {string} target array of target npcs e.g. "AnyPmc", "Savage"
* @param {array} bodyParts array of body parts with which to kill e.g. ["stomach", "thorax"]
* @param {number} distance distance from which to kill (currently only >= supported)
* @returns {object} object of "Elimination"-kill-subcondition
*/
protected generateEliminationCondition(target: string, bodyPart: string[], distance: number): IEliminationCondition;
/**
* Used to create a quest pool during each cycle of repeatable quest generation. The pool will be subsequently
* narrowed down during quest generation to avoid duplicate quests. Like duplicate extractions or elimination quests
@ -215,53 +82,10 @@ export declare class RepeatableQuestController {
* @returns IQuestTypePool
*/
protected generateQuestPool(repeatableConfig: IRepeatableQuestConfig, pmcLevel: number): IQuestTypePool;
/**
* Generate the reward for a mission. A reward can consist of
* - Experience
* - Money
* - Items
* - Trader Reputation
*
* The reward is dependent on the player level as given by the wiki. The exact mapping of pmcLevel to
* experience / money / items / trader reputation can be defined in QuestConfig.js
*
* There's also a random variation of the reward the spread of which can be also defined in the config.
*
* Additonaly, a scaling factor w.r.t. quest difficulty going from 0.2...1 can be used
*
* @param {integer} pmcLevel player's level
* @param {number} difficulty a reward scaling factor goint from 0.2 to 1
* @param {string} traderId the trader for reputation gain (and possible in the future filtering of reward item type based on trader)
* @param {object} repeatableConfig The configuration for the repeatably kind (daily, weekly) as configured in QuestConfig for the requestd quest
* @returns {object} object of "Reward"-type that can be given for a repeatable mission
*/
protected generateReward(pmcLevel: number, difficulty: number, traderId: string, repeatableConfig: IRepeatableQuestConfig): IRewards;
/**
* Helper to create a reward item structured as required by the client
*
* @param {string} tpl itemId of the rewarded item
* @param {integer} value amount of items to give
* @param {integer} index all rewards will be appended to a list, for unkown reasons the client wants the index
* @returns {object} object of "Reward"-item-type
*/
protected generateRewardItem(tpl: string, value: number, index: number, preset?: any): IReward;
protected createBaseQuestPool(repeatableConfig: IRepeatableQuestConfig): IQuestTypePool;
debugLogRepeatableQuestIds(pmcData: IPmcData): void;
protected probabilityObjectArray<K, V>(configArrayInput: ProbabilityObject<K, V>[]): ProbabilityObjectArray<K, V>;
/**
* Handle RepeatableQuestChange event
*/
changeRepeatableQuest(pmcData: IPmcData, body: IRepeatableQuestChangeRequest, sessionID: string): IItemEventRouterResponse;
/**
* Picks rewardable items from items.json. This means they need to fit into the inventory and they shouldn't be keys (debatable)
* @param repeatableQuestConfig config file
* @returns a list of rewardable items [[_tpl, itemTemplate],...]
*/
protected getRewardableItems(repeatableQuestConfig: IRepeatableQuestConfig): [string, ITemplateItem][];
/**
* Checks if an id is a valid item. Valid meaning that it's an item that may be a reward
* or content of bot loot. Items that are tested as valid may be in a player backpack or stash.
* @param {string} tpl template id of item to check
* @returns boolean: true if item is valid reward
*/
protected isValidRewardItem(tpl: string, repeatableQuestConfig: IRepeatableQuestConfig): boolean;
changeRepeatableQuest(pmcData: IPmcData, changeRequest: IRepeatableQuestChangeRequest, sessionID: string): IItemEventRouterResponse;
}

View File

@ -1,5 +1,5 @@
/// <reference types="node" />
import { IncomingMessage, ServerResponse } from "http";
import { IncomingMessage, ServerResponse } from "node:http";
export declare class Serializer {
serialize(sessionID: string, req: IncomingMessage, resp: ServerResponse, body: any): void;
canHandle(something: string): boolean;

Some files were not shown because too many files have changed in this diff Show More