Formatting

Have you been using the recommended VSCode extensions? ;)
This commit is contained in:
Refringe 2024-02-02 13:54:07 -05:00
parent 8227e06d77
commit 90492f3aa2
No known key found for this signature in database
GPG Key ID: 64E03E5F892C6F9E
141 changed files with 1687 additions and 1467 deletions

View File

@ -20,22 +20,27 @@ export class AchievementCallbacks
/**
* Handle client/achievement/list
*
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getAchievements(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IGetAchievementsResponse>
public getAchievements(
url: string,
info: IEmptyRequestData,
sessionID: string,
): IGetBodyResponseData<IGetAchievementsResponse>
{
return this.httpResponse.getBody(this.achievementController.getAchievements(sessionID));
}
/**
* Handle client/achievement/statistic
*
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public statistic(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<ICompletedAchievementsResponse>
public statistic(
url: string,
info: IEmptyRequestData,
sessionID: string,
): IGetBodyResponseData<ICompletedAchievementsResponse>
{
return this.httpResponse.getBody(this.achievementController.getAchievementStatistics(sessionID));
}
}
}

View File

@ -33,7 +33,7 @@ export class BuildsCallbacks
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public createMagazineTemplate(url: string, request: ISetMagazineRequest, sessionID: string): INullResponseData
{
this.buildController.createMagazineTemplate(sessionID, request)
this.buildController.createMagazineTemplate(sessionID, request);
return this.httpResponse.nullResponse();
}

View File

@ -28,10 +28,7 @@ export class CustomizationCallbacks
*/
public getSuits(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IGetSuitsResponse>
{
const result: IGetSuitsResponse = {
_id: sessionID,
suites: this.saveServer.getProfile(sessionID).suits,
};
const result: IGetSuitsResponse = { _id: sessionID, suites: this.saveServer.getProfile(sessionID).suits };
return this.httpResponse.getBody(result);
}

View File

@ -150,7 +150,7 @@ export class GameCallbacks implements OnLoad
return this.httpResponse.nullResponse();
}
/**
/**
* Handle singleplayer/settings/getRaidTime
* @returns string
*/

View File

@ -97,7 +97,7 @@ export class InraidCallbacks
*/
public getTraderServices(url: string, info: IEmptyRequestData, sessionId: string): string
{
const lastSlashPos = url.lastIndexOf('/');
const lastSlashPos = url.lastIndexOf("/");
const traderId = url.substring(lastSlashPos + 1);
return this.httpResponse.noBody(this.inraidController.getTraderServices(sessionId, traderId));
}

View File

@ -30,7 +30,7 @@ export class InventoryCallbacks
{
constructor(
@inject("InventoryController") protected inventoryController: InventoryController,
@inject("QuestController") protected questController: QuestController
@inject("QuestController") protected questController: QuestController,
)
{}
@ -164,17 +164,16 @@ export class InventoryCallbacks
return this.inventoryController.openRandomLootContainer(pmcData, body, sessionID);
}
public redeemProfileReward(pmcData: IPmcData,
public redeemProfileReward(
pmcData: IPmcData,
body: IRedeemProfileRequestData,
sessionId: string
sessionId: string,
): IItemEventRouterResponse
{
return this.inventoryController.redeemProfileReward(pmcData, body, sessionId)
return this.inventoryController.redeemProfileReward(pmcData, body, sessionId);
}
public setFavoriteItem(pmcData: IPmcData,
body: ISetFavoriteItems,
sessionId: string): IItemEventRouterResponse
public setFavoriteItem(pmcData: IPmcData, body: ISetFavoriteItems, sessionId: string): IItemEventRouterResponse
{
return this.inventoryController.setFavoriteItem(pmcData, body, sessionId);
}

View File

@ -160,7 +160,11 @@ export class MatchCallbacks
* @returns
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getGroupStatus(url: string, info: IGetGroupStatusRequestData, sessionID: string): IGetBodyResponseData<IGetGroupStatusResponse>
public getGroupStatus(
url: string,
info: IGetGroupStatusRequestData,
sessionID: string,
): IGetBodyResponseData<IGetGroupStatusResponse>
{
return this.httpResponse.getBody(this.matchController.getGroupStatus(info));
}

View File

@ -34,7 +34,11 @@ export class ProfileCallbacks
/**
* Handle client/game/profile/create
*/
public createProfile(url: string, info: IProfileCreateRequestData, sessionID: string): IGetBodyResponseData<ICreateProfileResponse>
public createProfile(
url: string,
info: IProfileCreateRequestData,
sessionID: string,
): IGetBodyResponseData<ICreateProfileResponse>
{
const id = this.profileController.createProfile(info, sessionID);

View File

@ -69,7 +69,7 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
// Process all offers / expire offers
await this.ragfairServer.update();
return true;
}
return false;
@ -142,7 +142,11 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
}
/** Handle client/ragfair/offer/findbyid */
public getFleaOfferById(url: string, request: IGetRagfairOfferByIdRequest, sessionID: string): IGetBodyResponseData<IRagfairOffer>
public getFleaOfferById(
url: string,
request: IGetRagfairOfferByIdRequest,
sessionID: string,
): IGetBodyResponseData<IRagfairOffer>
{
return this.httpResponse.getBody(this.ragfairController.getOfferById(sessionID, request));
}

View File

@ -1,4 +1,5 @@
export enum ContextVariableType {
export enum ContextVariableType
{
/** Logged in users session id */
SESSION_ID = 0,
/** Currently acive raid information */

View File

@ -23,7 +23,7 @@ export class AchievementController
*/
public getAchievements(sessionID: string): IGetAchievementsResponse
{
return {elements: this.databaseServer.getTables().templates.achievements };
return { elements: this.databaseServer.getTables().templates.achievements };
}
/**
@ -34,13 +34,13 @@ export class AchievementController
public getAchievementStatistics(sessionId: string): ICompletedAchievementsResponse
{
const achievements = this.databaseServer.getTables().templates.achievements;
const stats = {}
const stats = {};
for (const achievement of achievements)
{
stats[achievement.id] = 0;
}
return {elements: stats};
return { elements: stats };
}
}

View File

@ -153,7 +153,7 @@ export class BotController
{
return this.generateBotsFirstTime(info, pmcProfile, sessionId);
}
return this.returnSingleBotFromCache(sessionId, info);
}
@ -162,11 +162,17 @@ export class BotController
* @param request Bot generation request object
* @param pmcProfile Player profile
* @param sessionId Session id
* @returns
* @returns
*/
protected generateBotsFirstTime(request: IGenerateBotsRequestData, pmcProfile: IPmcData, sessionId: string): IBotBase[]
protected generateBotsFirstTime(
request: IGenerateBotsRequestData,
pmcProfile: IPmcData,
sessionId: string,
): IBotBase[]
{
const allPmcsHaveSameNameAsPlayer = this.randomUtil.getChance100(this.pmcConfig.allPMCsHavePlayerNameWithRandomPrefixChance);
const allPmcsHaveSameNameAsPlayer = this.randomUtil.getChance100(
this.pmcConfig.allPMCsHavePlayerNameWithRandomPrefixChance,
);
for (const condition of request.conditions)
{
const botGenerationDetails: BotGenerationDetails = {
@ -180,7 +186,7 @@ export class BotController
botCountToGenerate: this.botConfig.presetBatch[condition.Role],
botDifficulty: condition.Difficulty,
isPlayerScav: false,
allPmcsHaveSameNameAsPlayer: allPmcsHaveSameNameAsPlayer
allPmcsHaveSameNameAsPlayer: allPmcsHaveSameNameAsPlayer,
};
// Event bots need special actions to occur, set data up for them
@ -190,7 +196,7 @@ export class BotController
// Add eventRole data + reassign role property to be base type
botGenerationDetails.eventRole = condition.Role;
botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot(
botGenerationDetails.eventRole
botGenerationDetails.eventRole,
);
}
@ -338,9 +344,9 @@ export class BotController
public getBotCap(): number
{
const defaultMapCapId = "default";
const raidConfig = this.applicationContext
.getLatestValue(ContextVariableType.RAID_CONFIGURATION)
.getValue<IGetRaidConfigurationRequestData>();
const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<
IGetRaidConfigurationRequestData
>();
if (!raidConfig)
{
@ -366,9 +372,10 @@ export class BotController
public getAiBotBrainTypes(): any
{
return {
return {
pmc: this.pmcConfig.pmcType,
assault: this.botConfig.assaultBrainType,
playerScav: this.botConfig.playerScavBrainType};
playerScav: this.botConfig.playerScavBrainType,
};
}
}

View File

@ -71,10 +71,7 @@ export class BuildController
}
/** Handle client/builds/weapon/save */
public saveWeaponBuild(
sessionId: string,
body: IPresetBuildActionRequestData,
): void
public saveWeaponBuild(sessionId: string, body: IPresetBuildActionRequestData): void
{
const pmcData = this.profileHelper.getPmcProfile(sessionId);
@ -84,12 +81,7 @@ export class BuildController
body.Root = body.Items[0]._id;
// Create new object ready to save into profile userbuilds.weaponBuilds
const newBuild: IWeaponBuild = {
Id: body.Id,
Name: body.Name,
Root: body.Root,
Items: body.Items,
};
const newBuild: IWeaponBuild = { Id: body.Id, Name: body.Name, Root: body.Root, Items: body.Items };
const savedWeaponBuilds = this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds;
const existingBuild = savedWeaponBuilds.find((x) => x.Id === body.Id);
@ -110,15 +102,13 @@ export class BuildController
}
/** Handle client/builds/equipment/save event */
public saveEquipmentBuild(
sessionID: string,
request: IPresetBuildActionRequestData,
): void
public saveEquipmentBuild(sessionID: string, request: IPresetBuildActionRequestData): void
{
const buildType = "equipmentBuilds";
const pmcData = this.profileHelper.getPmcProfile(sessionID);
const existingSavedEquipmentBuilds: IEquipmentBuild[] = this.saveServer.getProfile(sessionID).userbuilds[buildType];
const existingSavedEquipmentBuilds: IEquipmentBuild[] =
this.saveServer.getProfile(sessionID).userbuilds[buildType];
// Replace duplicate ID's. The first item is the base item.
// Root ID and the base item ID need to match.
@ -214,16 +204,15 @@ export class BuildController
profile.userbuilds.magazineBuilds = [];
}
const existingArrayId = profile.userbuilds.magazineBuilds.findIndex(item => item.Name === request.Name);
const existingArrayId = profile.userbuilds.magazineBuilds.findIndex((item) => item.Name === request.Name);
if (existingArrayId === -1)
{
profile.userbuilds.magazineBuilds.push(result);
}
else
{
profile.userbuilds.magazineBuilds.splice(existingArrayId, 1, result)
profile.userbuilds.magazineBuilds.splice(existingArrayId, 1, result);
}
}
}

View File

@ -131,7 +131,7 @@ export class GameController
this.checkTraderRepairValuesExist();
this.adjustLocationBotValues()
this.adjustLocationBotValues();
// repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in
// offraidData). Since we don't want to clutter the Quests list, we need to remove all completed (failed or
@ -151,7 +151,7 @@ export class GameController
if (this.coreConfig.fixes.fixProfileBreakingInventoryItemIssues)
{
this.profileFixerService.fixProfileBreakingInventoryItemIssues(pmcProfile)
this.profileFixerService.fixProfileBreakingInventoryItemIssues(pmcProfile);
}
if (pmcProfile.Health)
@ -439,7 +439,10 @@ export class GameController
public getGameConfig(sessionID: string): IGameConfigResponse
{
const profile = this.profileHelper.getPmcProfile(sessionID);
const gameTime = profile.Stats?.Eft.OverallCounters.Items?.find(counter => counter.Key.includes("LifeTime") && counter.Key.includes("Pmc"))?.Value ?? 0;
const gameTime =
profile.Stats?.Eft.OverallCounters.Items?.find((counter) =>
counter.Key.includes("LifeTime") && counter.Key.includes("Pmc")
)?.Value ?? 0;
const config: IGameConfigResponse = {
languages: this.databaseServer.getTables().locales.languages,
@ -574,10 +577,8 @@ export class GameController
(sum, curr) => sum + curr.value,
0,
);
hydrationRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === BonusType.HYDRATION_REGENERATION).reduce(
(sum, curr) => sum + curr.value,
0,
);
hydrationRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === BonusType.HYDRATION_REGENERATION)
.reduce((sum, curr) => sum + curr.value, 0);
hpRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === BonusType.HEALTH_REGENERATION).reduce(
(sum, curr) => sum + curr.value,
0,
@ -822,7 +823,7 @@ export class GameController
dateAdded: Date.now(),
name: modDetails.name,
version: modDetails.version,
url: modDetails.url
url: modDetails.url,
});
}
}

View File

@ -8,7 +8,13 @@ import { PaymentHelper } from "@spt-aki/helpers/PaymentHelper";
import { PresetHelper } from "@spt-aki/helpers/PresetHelper";
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
import { HideoutArea, ITaskConditionCounter, Product, Production, ScavCase } from "@spt-aki/models/eft/common/tables/IBotBase";
import {
HideoutArea,
ITaskConditionCounter,
Product,
Production,
ScavCase,
} from "@spt-aki/models/eft/common/tables/IBotBase";
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { HideoutUpgradeCompleteRequestData } from "@spt-aki/models/eft/hideout/HideoutUpgradeCompleteRequestData";
import { IHandleQTEEventRequestData } from "@spt-aki/models/eft/hideout/IHandleQTEEventRequestData";
@ -503,9 +509,9 @@ export class HideoutController
itemWithModsToAdd: [itemToReturn],
foundInRaid: !!itemToReturn.upd.SpawnedInSession,
callback: null,
useSortingTable: false
}
useSortingTable: false,
};
this.inventoryHelper.addItemToStash(sessionID, request, pmcData, output);
if (output.warnings && output.warnings.length > 0)
{
@ -787,10 +793,7 @@ export class HideoutController
const preset = this.presetHelper.getDefaultPreset(recipe.endProduct);
// Ensure preset has unique ids and is cloned so we don't alter the preset data stored in memory
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
null,
this.jsonUtil.clone(preset._items),
);
const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(preset._items));
this.itemHelper.remapRootItemId(presetAndMods);
@ -805,9 +808,7 @@ export class HideoutController
const rewardToAdd: Item = {
_id: this.hashUtil.generate(),
_tpl: recipe.endProduct,
upd: {
StackObjectsCount: recipe.count
}
upd: { StackObjectsCount: recipe.count },
};
// Split item into separate items with acceptable stack sizes
@ -821,10 +822,7 @@ export class HideoutController
// Add the first reward item to array when not a preset (first preset added above earlier)
if (!rewardIsPreset)
{
itemAndChildrenToSendToPlayer.push([{
_id: this.hashUtil.generate(),
_tpl: recipe.endProduct
}]);
itemAndChildrenToSendToPlayer.push([{ _id: this.hashUtil.generate(), _tpl: recipe.endProduct }]);
}
// Add multiple of item if recipe requests it
@ -846,10 +844,9 @@ export class HideoutController
{
for (const reward of itemAndChildrenToSendToPlayer)
{
if (!reward[0].upd)
{
reward[0].upd = {}
reward[0].upd = {};
}
reward[0].upd.RecodableComponent = { IsEncoded: true };
@ -916,7 +913,7 @@ export class HideoutController
itemsWithModsToAdd: itemAndChildrenToSendToPlayer,
foundInRaid: true,
useSortingTable: false,
callback: null
callback: null,
};
// Add FiR crafted items(s) to player inventory
@ -946,13 +943,8 @@ export class HideoutController
const intellectAmountToGive = 0.5 * (Math.round(craftingExpAmount / 15));
if (intellectAmountToGive > 0)
{
this.profileHelper.addSkillPointsToPlayer(
pmcData,
SkillTypes.INTELLECT,
intellectAmountToGive,
);
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.INTELLECT, intellectAmountToGive);
}
}
area.lastRecipe = request.recipeId;
@ -1042,7 +1034,7 @@ export class HideoutController
itemsWithModsToAdd: scavCaseRewards,
foundInRaid: true,
callback: null,
useSortingTable: false
useSortingTable: false,
};
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);

View File

@ -348,7 +348,7 @@ export class InraidController
{
for (const quest of scavProfile.Quests)
{
const pmcQuest = pmcProfile.Quests.find(x => x.qid === quest.qid);
const pmcQuest = pmcProfile.Quests.find((x) => x.qid === quest.qid);
if (!pmcQuest)
{
this.logger.warning(`No PMC quest found for ID: ${quest.qid}`);
@ -546,9 +546,9 @@ export class InraidController
// Remove any items that were returned by the item delivery, but also insured, from the player's insurance list
// This is to stop items being duplicated by being returned from both the item delivery, and insurance
const deliveredItemIds = items.map(x => x._id);
pmcData.InsuredItems = pmcData.InsuredItems.filter(x => !deliveredItemIds.includes(x.itemId));
const deliveredItemIds = items.map((x) => x._id);
pmcData.InsuredItems = pmcData.InsuredItems.filter((x) => !deliveredItemIds.includes(x.itemId));
// Send the items to the player
this.mailSendService.sendLocalisedNpcMessageToPlayer(
sessionId,

View File

@ -121,8 +121,7 @@ export class InventoryController
}
// Item is moving into or out of place of fame dogtag slot
if (moveRequest.to.container.startsWith("dogtag")
|| originalLocationSlotId.startsWith("dogtag"))
if (moveRequest.to.container.startsWith("dogtag") || originalLocationSlotId.startsWith("dogtag"))
{
this.hideoutHelper.applyPlaceOfFameDogtagBonus(pmcData);
}
@ -177,11 +176,7 @@ export class InventoryController
if (body.fromOwner?.type === "Mail")
{
this.inventoryHelper.removeItemAndChildrenFromMailRewards(
sessionID,
body,
output,
);
this.inventoryHelper.removeItemAndChildrenFromMailRewards(sessionID, body, output);
return;
}
@ -190,12 +185,7 @@ export class InventoryController
? pmcData
: this.profileHelper.getFullProfile(sessionID).characters.scav;
return this.inventoryHelper.removeItem(
profileToRemoveItemFrom,
body.item,
sessionID,
output,
);
return this.inventoryHelper.removeItem(profileToRemoveItemFrom, body.item, sessionID, output);
}
/**
@ -527,9 +517,7 @@ export class InventoryController
return this.eventOutputHolder.getOutput(sessionID);
}
this.logger.warning(
this.localisationService.getText("inventory-unable_to_toggle_item_not_found", body.item),
);
this.logger.warning(this.localisationService.getText("inventory-unable_to_toggle_item_not_found", body.item));
return { warnings: [], profileChanges: {} };
}
@ -685,7 +673,7 @@ export class InventoryController
const item = this.databaseServer.getTables().templates.items[itemTpl];
if (!item)
{
this.logger.warning(`Unable to find item with id ${itemTpl}, skipping inspection`)
this.logger.warning(`Unable to find item with id ${itemTpl}, skipping inspection`);
return;
}
@ -932,7 +920,7 @@ export class InventoryController
if (containerSettings.foundInRaid)
{
foundInRaid = containerSettings.foundInRaid
foundInRaid = containerSettings.foundInRaid;
}
}
else
@ -950,8 +938,8 @@ export class InventoryController
itemsWithModsToAdd: rewards,
foundInRaid: foundInRaid,
callback: null,
useSortingTable: true
}
useSortingTable: true,
};
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);
if (output.warnings.length > 0)
{
@ -964,7 +952,11 @@ export class InventoryController
return output;
}
public redeemProfileReward(pmcData: IPmcData, request: IRedeemProfileRequestData, sessionId: string): IItemEventRouterResponse
public redeemProfileReward(
pmcData: IPmcData,
request: IRedeemProfileRequestData,
sessionId: string,
): IItemEventRouterResponse
{
const output = this.eventOutputHolder.getOutput(sessionId);
@ -974,8 +966,8 @@ export class InventoryController
// Hard coded to `SYSTEM` for now
// TODO: make this dynamic
const dialog = fullprofile.dialogues["59e7125688a45068a6249071"];
const mail = dialog.messages.find(x => x._id === event.MessageId);
const mailEvent = mail.profileChangeEvents.find(x => x._id === event.EventId);
const mail = dialog.messages.find((x) => x._id === event.MessageId);
const mailEvent = mail.profileChangeEvents.find((x) => x._id === event.EventId);
switch (mailEvent.Type)
{
@ -994,15 +986,15 @@ export class InventoryController
break;
case "SkillPoints":
{
const profileSkill = pmcData.Skills.Common.find(x => x.Id === mailEvent.entity);
const profileSkill = pmcData.Skills.Common.find((x) => x.Id === mailEvent.entity);
profileSkill.Progress = mailEvent.value;
this.logger.success(`Set profile skill: ${mailEvent.entity} to: ${mailEvent.value}`);
break;
}
case "ExamineAllItems":
{
const itemsToInspect = this.itemHelper.getItems().filter(x => x._type !== "Node");
this.flagItemsAsInspectedAndRewardXp(itemsToInspect.map(x => x._id), pmcData);
const itemsToInspect = this.itemHelper.getItems().filter((x) => x._type !== "Node");
this.flagItemsAsInspectedAndRewardXp(itemsToInspect.map((x) => x._id), pmcData);
this.logger.success(`Flagged ${itemsToInspect.length} items as examined`);
break;
}
@ -1031,7 +1023,7 @@ export class InventoryController
for (const itemId of request.items)
{
// If id already exists in array, we're removing it
const indexOfItemAlreadyFavorited = pmcData.Inventory.favoriteItems.findIndex(x => x === itemId);
const indexOfItemAlreadyFavorited = pmcData.Inventory.favoriteItems.findIndex((x) => x === itemId);
if (indexOfItemAlreadyFavorited > -1)
{
pmcData.Inventory.favoriteItems.splice(indexOfItemAlreadyFavorited, 1);
@ -1041,7 +1033,7 @@ export class InventoryController
pmcData.Inventory.favoriteItems.push(itemId);
}
}
return output;
}
}

View File

@ -132,7 +132,7 @@ export class LauncherController
return profileId;
}
protected generateProfileId(): string
{
const timestamp = this.timeUtil.getTimestamp();
@ -142,8 +142,8 @@ export class LauncherController
protected formatID(timeStamp: number, counter: number): string
{
const timeStampStr = timeStamp.toString(16).padStart(8, '0');
const counterStr = counter.toString(16).padStart(16, '0');
const timeStampStr = timeStamp.toString(16).padStart(8, "0");
const counterStr = counter.toString(16).padStart(16, "0");
return timeStampStr.toLowerCase() + counterStr.toLowerCase();
}

View File

@ -91,7 +91,9 @@ export class LocationController
// Check for a loot multipler adjustment in app context and apply if one is found
let locationConfigCopy: ILocationConfig;
const raidAdjustments = this.applicationContext.getLatestValue(ContextVariableType.RAID_ADJUSTMENTS)?.getValue<IRaidChanges>();
const raidAdjustments = this.applicationContext.getLatestValue(ContextVariableType.RAID_ADJUSTMENTS)?.getValue<
IRaidChanges
>();
if (raidAdjustments)
{
locationConfigCopy = this.jsonUtil.clone(this.locationConfig); // Clone values so they can be used to reset originals later

View File

@ -185,7 +185,7 @@ export class ProfileController
inraid: {} as Inraid,
insurance: [],
traderPurchases: {},
achievements: {}
achievements: {},
};
this.profileFixerService.checkForAndFixPmcProfileIssues(profileDetails.characters.pmc);
@ -391,7 +391,7 @@ export class ProfileController
return [{ _id: this.hashUtil.generate(), Info: { Level: 1, Side: "Bear", Nickname: info.nickname } }];
}
/**
/**
* Handle client/profile/status
*/
public getProfileStatus(sessionId: string): GetProfileStatusResponseData
@ -399,20 +399,14 @@ export class ProfileController
const account = this.saveServer.getProfile(sessionId).info;
const response: GetProfileStatusResponseData = {
maxPveCountExceeded: false,
profiles: [{
profileid: account.scavId,
profileToken: null,
status: "Free",
sid: "",
ip: "",
port: 0,
}, {
profiles: [{ profileid: account.scavId, profileToken: null, status: "Free", sid: "", ip: "", port: 0 }, {
profileid: account.id,
profileToken: null,
status: "Free",
sid: "",
ip: "",
port: 0 }],
port: 0,
}],
};
return response;
@ -422,7 +416,7 @@ export class ProfileController
{
const player = this.profileHelper.getFullProfile(sessionId);
const playerPmc = player.characters.pmc;
// return player for now
return {
id: playerPmc._id,
@ -434,34 +428,34 @@ export class ProfileController
memberCategory: playerPmc.Info.MemberCategory,
bannedState: playerPmc.Info.BannedState,
bannedUntil: playerPmc.Info.BannedUntil,
registrationDate: playerPmc.Info.RegistrationDate
registrationDate: playerPmc.Info.RegistrationDate,
},
customization: {
head: playerPmc.Customization.Head,
body: playerPmc.Customization.Body,
feet: playerPmc.Customization.Feet,
hands: playerPmc.Customization.Hands
hands: playerPmc.Customization.Hands,
},
skills: playerPmc.Skills,
equipment: {
// Default inventory tpl
Id: playerPmc.Inventory.items.find(x => x._tpl === "55d7217a4bdc2d86028b456d")._id,
Items: playerPmc.Inventory.items
Id: playerPmc.Inventory.items.find((x) => x._tpl === "55d7217a4bdc2d86028b456d")._id,
Items: playerPmc.Inventory.items,
},
achievements: playerPmc.Achievements,
favoriteItems: playerPmc.Inventory.favoriteItems ?? [],
pmcStats: {
eft: {
totalInGameTime: playerPmc.Stats.Eft.TotalInGameTime,
overAllCounters: playerPmc.Stats.Eft.OverallCounters
}
overAllCounters: playerPmc.Stats.Eft.OverallCounters,
},
},
scavStats: {
eft: {
totalInGameTime: player.characters.scav.Stats.Eft.TotalInGameTime,
overAllCounters: player.characters.scav.Stats.Eft.OverallCounters
}
}
overAllCounters: player.characters.scav.Stats.Eft.OverallCounters,
},
},
};
}
}

View File

@ -108,7 +108,9 @@ export class QuestController
const trader = profile.TradersInfo[quest.traderId];
if (!trader)
{
this.logger.debug(`Unable to show quest: ${quest.QuestName} as its for a trader: ${quest.traderId} that no longer exists.`);
this.logger.debug(
`Unable to show quest: ${quest.QuestName} as its for a trader: ${quest.traderId} that no longer exists.`,
);
continue;
}
@ -135,7 +137,9 @@ export class QuestController
for (const conditionToFulfil of questRequirements)
{
// If the previous quest isn't in the user profile, it hasn't been completed or started
const prerequisiteQuest = profile.Quests.find((profileQuest) => conditionToFulfil.target.includes(profileQuest.qid));
const prerequisiteQuest = profile.Quests.find((profileQuest) =>
conditionToFulfil.target.includes(profileQuest.qid)
);
if (!prerequisiteQuest)
{
haveCompletedPreviousQuest = false;
@ -307,10 +311,10 @@ export class QuestController
): IItemEventRouterResponse
{
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
// Does quest exist in profile
// Restarting a failed quest can mean quest exists in profile
const existingQuestStatus = pmcData.Quests.find((x) => x.qid === acceptedQuest.qid)
const existingQuestStatus = pmcData.Quests.find((x) => x.qid === acceptedQuest.qid);
if (existingQuestStatus)
{
// Update existing
@ -356,9 +360,10 @@ export class QuestController
);
// Having accepted new quest, look for newly unlocked quests and inform client of them
acceptQuestResponse.profileChanges[sessionID].quests.push(...this.questHelper
.getNewlyAccessibleQuestsWhenStartingQuest(acceptedQuest.qid, sessionID));
acceptQuestResponse.profileChanges[sessionID].quests.push(
...this.questHelper.getNewlyAccessibleQuestsWhenStartingQuest(acceptedQuest.qid, sessionID),
);
return acceptQuestResponse;
}
@ -380,7 +385,11 @@ export class QuestController
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
// Create and store quest status object inside player profile
const newRepeatableQuest = this.questHelper.getQuestReadyForProfile(pmcData, QuestStatus.Started, acceptedQuest);
const newRepeatableQuest = this.questHelper.getQuestReadyForProfile(
pmcData,
QuestStatus.Started,
acceptedQuest,
);
pmcData.Quests.push(newRepeatableQuest);
// Look for the generated quest cache in profile.RepeatableQuests
@ -431,7 +440,7 @@ export class QuestController
if (!acceptQuestResponse.profileChanges[sessionID].repeatableQuests)
{
acceptQuestResponse.profileChanges[sessionID].repeatableQuests = []
acceptQuestResponse.profileChanges[sessionID].repeatableQuests = [];
}
acceptQuestResponse.profileChanges[sessionID].repeatableQuests.push(responseData);
@ -510,7 +519,9 @@ export class QuestController
// Check if it's a repeatable quest. If so, remove from Quests
for (const currentRepeatable of pmcData.RepeatableQuests)
{
const repeatableQuest = currentRepeatable.activeQuests.find((activeRepeatable) => activeRepeatable._id === completedQuestId);
const repeatableQuest = currentRepeatable.activeQuests.find((activeRepeatable) =>
activeRepeatable._id === completedQuestId
);
if (repeatableQuest)
{
// Need to remove redundant scav quest object as its no longer necessary, is tracked in pmc profile
@ -633,8 +644,7 @@ export class QuestController
if (nextQuestWaitCondition)
{
// Now + wait time
const availableAfterTimestamp = this.timeUtil.getTimestamp()
+ nextQuestWaitCondition.availableAfter;
const availableAfterTimestamp = this.timeUtil.getTimestamp() + nextQuestWaitCondition.availableAfter;
// Update quest in profile with status of AvailableAfter
const existingQuestInProfile = pmcData.Quests.find((x) => x.qid === quest._id);
@ -912,7 +922,8 @@ export class QuestController
id: conditionId,
sourceId: questId,
type: "HandoverItem",
value: counterValue };
value: counterValue,
};
}
/**

View File

@ -97,7 +97,7 @@ export class RagfairController
const pmcProfile = this.profileHelper.getPmcProfile(sessionID);
result.offers = this.getOffersForSearchType(searchRequest, itemsToAdd, traderAssorts, pmcProfile);
if (searchRequest.updateOfferCount)
{
result.categories = this.getSpecificCategories(pmcProfile, searchRequest, result.offers);
@ -158,7 +158,7 @@ export class RagfairController
public getOfferById(sessionId: string, request: IGetRagfairOfferByIdRequest): IRagfairOffer
{
const offers = this.ragfairOfferService.getOffers();
const offerToReturn = offers.find(x => x.intId === request.id);
const offerToReturn = offers.find((x) => x.intId === request.id);
return offerToReturn;
}
@ -199,10 +199,15 @@ export class RagfairController
* @param offers ragfair offers to get categories for
* @returns record with templates + counts
*/
protected getSpecificCategories(pmcProfile: IPmcData, searchRequest: ISearchRequestData, offers: IRagfairOffer[]): Record<string, number>
protected getSpecificCategories(
pmcProfile: IPmcData,
searchRequest: ISearchRequestData,
offers: IRagfairOffer[],
): Record<string, number>
{
// Linked/required search categories
const playerHasFleaUnlocked = pmcProfile.Info.Level >= this.databaseServer.getTables().globals.config.RagFair.minUserLevel;
const playerHasFleaUnlocked =
pmcProfile.Info.Level >= this.databaseServer.getTables().globals.config.RagFair.minUserLevel;
let offerPool = [];
if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest))
{
@ -698,7 +703,9 @@ export class RagfairController
if (playerOfferIndex === -1)
{
this.logger.error(
this.localisationService.getText("ragfair-offer_not_found_in_profile", { offerId: removeRequest.offerId }),
this.localisationService.getText("ragfair-offer_not_found_in_profile", {
offerId: removeRequest.offerId,
}),
);
return this.httpResponse.appendErrorToOutput(
output,
@ -735,7 +742,9 @@ export class RagfairController
if (playerOfferIndex === -1)
{
this.logger.warning(
this.localisationService.getText("ragfair-offer_not_found_in_profile", { offerId: extendRequest.offerId }),
this.localisationService.getText("ragfair-offer_not_found_in_profile", {
offerId: extendRequest.offerId,
}),
);
return this.httpResponse.appendErrorToOutput(
output,

View File

@ -83,7 +83,7 @@ export class RepeatableQuestController
*
* @param {string} _info Request from client
* @param {string} sessionID Player's session id
*
*
* @returns {array} Array of "repeatableQuestObjects" as descibed above
*/
public getClientRepeatableQuests(_info: IEmptyRequestData, sessionID: string): IPmcDataRepeatableQuest[]
@ -120,7 +120,7 @@ export class RepeatableQuestController
for (const activeQuest of currentRepeatableQuestType.activeQuests)
{
// Keep finished quests in list so player can hand in
const quest = pmcData.Quests.find(quest => quest.qid === activeQuest._id);
const quest = pmcData.Quests.find((quest) => quest.qid === activeQuest._id);
if (quest)
{
if (quest.status === QuestStatus.AvailableForFinish)
@ -136,7 +136,7 @@ export class RepeatableQuestController
this.profileFixerService.removeDanglingConditionCounters(pmcData);
// Remove expired quest from pmc.quest array
pmcData.Quests = pmcData.Quests.filter(quest => quest.qid !== activeQuest._id);
pmcData.Quests = pmcData.Quests.filter((quest) => quest.qid !== activeQuest._id);
currentRepeatableQuestType.inactiveQuests.push(activeQuest);
}
currentRepeatableQuestType.activeQuests = questsToKeep;

View File

@ -11,7 +11,10 @@ import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEve
import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer";
import { IProcessBaseTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBaseTradeRequestData";
import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData";
import { IOfferRequest, IProcessRagfairTradeRequestData } from "@spt-aki/models/eft/trade/IProcessRagfairTradeRequestData";
import {
IOfferRequest,
IProcessRagfairTradeRequestData,
} from "@spt-aki/models/eft/trade/IProcessRagfairTradeRequestData";
import { IProcessSellTradeRequestData } from "@spt-aki/models/eft/trade/IProcessSellTradeRequestData";
import { ISellScavItemsToFenceRequestData } from "@spt-aki/models/eft/trade/ISellScavItemsToFenceRequestData";
import { BackendErrorCodes } from "@spt-aki/models/enums/BackendErrorCodes";
@ -146,14 +149,20 @@ export class TradeController
* @param requestOffer request data from client
* @param output Output to send back to client
*/
protected buyTraderItemFromRagfair(sessionId: string, pmcData: IPmcData, fleaOffer: IRagfairOffer, requestOffer: IOfferRequest, output: IItemEventRouterResponse): void
protected buyTraderItemFromRagfair(
sessionId: string,
pmcData: IPmcData,
fleaOffer: IRagfairOffer,
requestOffer: IOfferRequest,
output: IItemEventRouterResponse,
): void
{
// Skip buying items when player doesn't have needed loyalty
if (this.playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer, pmcData))
{
const errorMessage = `Unable to buy item: ${
fleaOffer.items[0]._tpl
} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`
} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`;
this.logger.debug(errorMessage);
this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.RAGFAIRUNAVAILABLE);
@ -182,7 +191,13 @@ export class TradeController
* @param requestOffer Request data from client
* @param output Output to send back to client
*/
protected buyPmcItemFromRagfair(sessionId: string, pmcData: IPmcData, fleaOffer: IRagfairOffer, requestOffer: IOfferRequest, output: IItemEventRouterResponse): void
protected buyPmcItemFromRagfair(
sessionId: string,
pmcData: IPmcData,
fleaOffer: IRagfairOffer,
requestOffer: IOfferRequest,
output: IItemEventRouterResponse,
): void
{
const buyData: IProcessBuyTradeRequestData = {
Action: "TradingConfirm",
@ -198,7 +213,13 @@ export class TradeController
};
// buyItem() must occur prior to removing the offer stack, otherwise item inside offer doesn't exist for confirmTrading() to use
this.tradeHelper.buyItem(pmcData, buyData, sessionId, this.ragfairConfig.dynamic.purchasesAreFoundInRaid, output);
this.tradeHelper.buyItem(
pmcData,
buyData,
sessionId,
this.ragfairConfig.dynamic.purchasesAreFoundInRaid,
output,
);
if (output.warnings.length > 0)
{
return;
@ -231,10 +252,7 @@ export class TradeController
const scavProfile = this.profileHelper.getFullProfile(sessionId)?.characters?.scav;
if (!scavProfile)
{
return this.httpResponse.appendErrorToOutput(
output,
`Profile ${request.fromOwner.id} has no scav account`,
);
return this.httpResponse.appendErrorToOutput(output, `Profile ${request.fromOwner.id} has no scav account`);
}
this.sellInventoryToTrader(sessionId, scavProfile, pmcData, Traders.FENCE, output);
@ -256,7 +274,7 @@ export class TradeController
profileWithItemsToSell: IPmcData,
profileThatGetsMoney: IPmcData,
trader: Traders,
output: IItemEventRouterResponse
output: IItemEventRouterResponse,
): void
{
const handbookPrices = this.ragfairPriceService.getAllStaticPrices();
@ -335,4 +353,4 @@ export class TradeController
return totalPrice;
}
}
}

View File

@ -29,7 +29,8 @@ export class WeatherController
time: "",
date: "",
weather: null,
winterEventEnabled: this.weatherConfig.forceWinterEvent };
winterEventEnabled: this.weatherConfig.forceWinterEvent,
};
result = this.weatherGenerator.calculateGameTime(result);
result.weather = this.weatherGenerator.generateWeather();

View File

@ -485,7 +485,9 @@ export class Container
depContainer.register<RagfairStaticRouter>("RagfairStaticRouter", { useClass: RagfairStaticRouter });
depContainer.register<TraderStaticRouter>("TraderStaticRouter", { useClass: TraderStaticRouter });
depContainer.register<WeatherStaticRouter>("WeatherStaticRouter", { useClass: WeatherStaticRouter });
depContainer.register<AchievementStaticRouter>("AchievementStaticRouter", { useClass: AchievementStaticRouter });
depContainer.register<AchievementStaticRouter>("AchievementStaticRouter", {
useClass: AchievementStaticRouter,
});
depContainer.register<BuildsStaticRouter>("BuildsStaticRouter", { useClass: BuildsStaticRouter });
}

View File

@ -80,7 +80,9 @@ export class BotEquipmentModGenerator
const compatibleModsPool = settings.modPool[parentTemplate._id];
if (!compatibleModsPool)
{
this.logger.warning(`bot: ${settings.botRole} lacks a mod slot pool for item: ${parentTemplate._id} ${parentTemplate._name}`);
this.logger.warning(
`bot: ${settings.botRole} lacks a mod slot pool for item: ${parentTemplate._id} ${parentTemplate._name}`,
);
}
// Iterate over mod pool and choose mods to add to item
@ -94,12 +96,16 @@ export class BotEquipmentModGenerator
modSlot: modSlotName,
parentId: parentTemplate._id,
parentName: parentTemplate._name,
botRole: settings.botRole
botRole: settings.botRole,
}),
);
continue;
}
const modSpawnResult = this.shouldModBeSpawned(itemSlotTemplate, modSlotName.toLowerCase(), settings.spawnChances.equipmentMods);
const modSpawnResult = this.shouldModBeSpawned(
itemSlotTemplate,
modSlotName.toLowerCase(),
settings.spawnChances.equipmentMods,
);
if (modSpawnResult === ModSpawn.SKIP && !forceSpawn)
{
continue;
@ -112,37 +118,48 @@ export class BotEquipmentModGenerator
}
let modPoolToChooseFrom = compatibleModsPool[modSlotName];
if (settings.botEquipmentConfig.filterPlatesByLevel && this.itemHelper.isRemovablePlateSlot(modSlotName.toLowerCase()))
if (
settings.botEquipmentConfig.filterPlatesByLevel
&& this.itemHelper.isRemovablePlateSlot(modSlotName.toLowerCase())
)
{
const outcome = this.filterPlateModsForSlotByLevel(settings, modSlotName.toLowerCase(), compatibleModsPool[modSlotName], parentTemplate);
const outcome = this.filterPlateModsForSlotByLevel(
settings,
modSlotName.toLowerCase(),
compatibleModsPool[modSlotName],
parentTemplate,
);
if ([Result.UNKNOWN_FAILURE, Result.NO_DEFAULT_FILTER].includes(outcome.result))
{
this.logger.debug(`Plate slot: ${modSlotName} selection for armor: ${parentTemplate._id} failed: ${Result[outcome.result]}, skipping`);
this.logger.debug(
`Plate slot: ${modSlotName} selection for armor: ${parentTemplate._id} failed: ${
Result[outcome.result]
}, skipping`,
);
continue;
}
if ([Result.LACKS_PLATE_WEIGHTS].includes(outcome.result))
{
this.logger.warning(`Plate slot: ${modSlotName} lacks weights for armor: ${parentTemplate._id}, unable to adjust plate choice, using existing data`);
this.logger.warning(
`Plate slot: ${modSlotName} lacks weights for armor: ${parentTemplate._id}, unable to adjust plate choice, using existing data`,
);
}
modPoolToChooseFrom = outcome.plateModTpls;
}
// Find random mod and check its compatible
let modTpl: string;
let found = false;
const exhaustableModPool = new ExhaustableArray(
modPoolToChooseFrom,
this.randomUtil,
this.jsonUtil,
);
const exhaustableModPool = new ExhaustableArray(modPoolToChooseFrom, this.randomUtil, this.jsonUtil);
while (exhaustableModPool.hasValues())
{
modTpl = exhaustableModPool.getRandomValue();
if (
!this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(equipment, modTpl, modSlotName).incompatible
!this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(equipment, modTpl, modSlotName)
.incompatible
)
{
found = true;
@ -178,13 +195,7 @@ export class BotEquipmentModGenerator
if (Object.keys(settings.modPool).includes(modTpl))
{
// Call self recursively with item being checkced item we just added to bot
this.generateModsForEquipment(
equipment,
modId,
modTemplate[1],
settings,
forceSpawn,
);
this.generateModsForEquipment(equipment, modId, modTemplate[1], settings, forceSpawn);
}
}
@ -196,15 +207,17 @@ export class BotEquipmentModGenerator
* @param settings Bot equipment generation settings
* @param modSlot Armor slot being filtered
* @param existingPlateTplPool Plates tpls to choose from
* @param armorItem
* @param armorItem
* @returns Array of plate tpls to choose from
*/
protected filterPlateModsForSlotByLevel(settings: IGenerateEquipmentProperties, modSlot: string, existingPlateTplPool: string[], armorItem: ITemplateItem): IFilterPlateModsForSlotByLevelResult
protected filterPlateModsForSlotByLevel(
settings: IGenerateEquipmentProperties,
modSlot: string,
existingPlateTplPool: string[],
armorItem: ITemplateItem,
): IFilterPlateModsForSlotByLevelResult
{
const result: IFilterPlateModsForSlotByLevelResult = {
result: Result.UNKNOWN_FAILURE,
plateModTpls: null
};
const result: IFilterPlateModsForSlotByLevelResult = { result: Result.UNKNOWN_FAILURE, plateModTpls: null };
// Not pmc or not a plate slot, return original mod pool array
if (!this.itemHelper.isRemovablePlateSlot(modSlot))
@ -216,10 +229,9 @@ export class BotEquipmentModGenerator
}
// Get the front/back/side weights based on bots level
const plateSlotWeights = settings
.botEquipmentConfig
?.armorPlateWeighting
?.find(x => settings.botLevel >= x.levelRange.min && settings.botLevel <= x.levelRange.max);
const plateSlotWeights = settings.botEquipmentConfig?.armorPlateWeighting?.find((x) =>
settings.botLevel >= x.levelRange.min && settings.botLevel <= x.levelRange.max
);
if (!plateSlotWeights)
{
// No weights, return original array of plate tpls
@ -242,18 +254,20 @@ export class BotEquipmentModGenerator
// Choose a plate level based on weighting
const chosenArmorPlateLevel = this.weightedRandomHelper.getWeightedValue<string>(plateWeights);
// Convert the array of ids into database items
const platesFromDb = existingPlateTplPool.map(x => this.itemHelper.getItem(x)[1]);
const platesFromDb = existingPlateTplPool.map((x) => this.itemHelper.getItem(x)[1]);
// Filter plates to the chosen level based on its armorClass property
const filteredPlates = platesFromDb.filter(x => x._props.armorClass === chosenArmorPlateLevel);
const filteredPlates = platesFromDb.filter((x) => x._props.armorClass === chosenArmorPlateLevel);
if (filteredPlates.length === 0)
{
this.logger.debug(`Plate filter was too restrictive for armor: ${armorItem._id}, unable to find plates of level: ${chosenArmorPlateLevel}. Using mod items default plate`);
this.logger.debug(
`Plate filter was too restrictive for armor: ${armorItem._id}, unable to find plates of level: ${chosenArmorPlateLevel}. Using mod items default plate`,
);
const relatedItemDbModSlot = armorItem._props.Slots.find(slot => slot._name.toLowerCase() === modSlot);
const defaultPlate = relatedItemDbModSlot._props.filters[0].Plate
const relatedItemDbModSlot = armorItem._props.Slots.find((slot) => slot._name.toLowerCase() === modSlot);
const defaultPlate = relatedItemDbModSlot._props.filters[0].Plate;
if (!defaultPlate)
{
// No relevant plate found after filtering AND no default plate
@ -262,7 +276,9 @@ export class BotEquipmentModGenerator
const defaultPreset = this.presetHelper.getDefaultPreset(armorItem._id);
if (defaultPreset)
{
const relatedPresetSlot = defaultPreset._items.find(item => item.slotId?.toLowerCase() === modSlot);
const relatedPresetSlot = defaultPreset._items.find((item) =>
item.slotId?.toLowerCase() === modSlot
);
if (relatedPresetSlot)
{
result.result = Result.SUCCESS;
@ -285,7 +301,7 @@ export class BotEquipmentModGenerator
// Only return the items ids
result.result = Result.SUCCESS;
result.plateModTpls = filteredPlates.map(x => x._id);
result.plateModTpls = filteredPlates.map((x) => x._id);
return result;
}
@ -333,7 +349,7 @@ export class BotEquipmentModGenerator
this.localisationService.getText("bot-unable_to_add_mods_to_weapon_missing_ammo_slot", {
weaponName: parentTemplate._name,
weaponId: parentTemplate._id,
botRole: botRole
botRole: botRole,
}),
);
@ -385,7 +401,7 @@ export class BotEquipmentModGenerator
weapon,
ammoTpl,
parentTemplate,
modSpawnResult
modSpawnResult,
);
// Compatible mod not found
@ -469,9 +485,7 @@ export class BotEquipmentModGenerator
}
const modId = this.hashUtil.generate();
weapon.push(
this.createModItem(modId, modToAddTemplate._id, weaponId, modSlot, modToAddTemplate, botRole),
);
weapon.push(this.createModItem(modId, modToAddTemplate._id, weaponId, modSlot, modToAddTemplate, botRole));
// I first thought we could use the recursive generateModsForItems as previously for cylinder magazines.
// However, the recursion doesn't go over the slots of the parent mod but over the modPool which is given by the bot config
@ -699,7 +713,7 @@ export class BotEquipmentModGenerator
const slotRequired = itemSlot._required;
if (this.getAmmoContainers().includes(modSlot))
{
return ModSpawn.SPAWN
return ModSpawn.SPAWN;
}
const spawnMod = this.probabilityHelper.rollChance(modSpawnChances[modSlot]);
if (!spawnMod && slotRequired)
@ -708,9 +722,7 @@ export class BotEquipmentModGenerator
return ModSpawn.DEFAULT_MOD;
}
return spawnMod
? ModSpawn.SPAWN
: ModSpawn.SKIP;
return spawnMod ? ModSpawn.SPAWN : ModSpawn.SKIP;
}
/**
@ -733,7 +745,7 @@ export class BotEquipmentModGenerator
weapon: Item[],
ammoTpl: string,
parentTemplate: ITemplateItem,
modSpawnResult: ModSpawn
modSpawnResult: ModSpawn,
): [boolean, ITemplateItem]
{
/** Slot mod will fill */
@ -743,17 +755,23 @@ export class BotEquipmentModGenerator
// It's ammo, use predefined ammo parameter
if (this.getAmmoContainers().includes(modSlot) && modSlot !== "mod_magazine")
{
return this.itemHelper.getItem(ammoTpl)
return this.itemHelper.getItem(ammoTpl);
}
// Ensure there's a pool of mods to pick from
let modPool = this.getModPoolForSlot(itemModPool, modSpawnResult, parentTemplate, weaponTemplate, modSlot, botEquipBlacklist, isRandomisableSlot);
let modPool = this.getModPoolForSlot(
itemModPool,
modSpawnResult,
parentTemplate,
weaponTemplate,
modSlot,
botEquipBlacklist,
isRandomisableSlot,
);
if (!(modPool || parentSlot._required))
{
// Nothing in mod pool + item not required
this.logger.debug(
`Mod pool for slot: ${modSlot} on item: ${parentTemplate._name} was empty, skipping mod`,
);
this.logger.debug(`Mod pool for slot: ${modSlot} on item: ${parentTemplate._name} was empty, skipping mod`);
return null;
}
@ -763,16 +781,18 @@ export class BotEquipmentModGenerator
// scope pool has more than one scope
if (modPool.length > 1)
{
modPool = this.filterSightsByWeaponType(
weapon[0],
modPool,
botWeaponSightWhitelist,
);
modPool = this.filterSightsByWeaponType(weapon[0], modPool, botWeaponSightWhitelist);
}
}
// Pick random mod that's compatible
const chosenModResult = this.pickWeaponModTplForSlotFromPool(modPool, parentSlot, modSpawnResult, weapon, modSlot);
const chosenModResult = this.pickWeaponModTplForSlotFromPool(
modPool,
parentSlot,
modSpawnResult,
weapon,
modSlot,
);
if (chosenModResult.slotBlocked && !parentSlot._required)
{
// Don't bother trying to fit mod, slot is completely blocked
@ -783,7 +803,7 @@ export class BotEquipmentModGenerator
if (chosenModResult.incompatible && parentSlot._required)
{
this.logger.debug(chosenModResult.reason);
//this.logger.debug(`Weapon: ${weapon.map(x => `${x._tpl} ${x.slotId ?? ""}`).join(",")}`)
// this.logger.debug(`Weapon: ${weapon.map(x => `${x._tpl} ${x.slotId ?? ""}`).join(",")}`)
}
// Get random mod to attach from items db for required slots if none found above
@ -821,15 +841,12 @@ export class BotEquipmentModGenerator
parentSlot: Slot,
modSpawnResult: ModSpawn,
weapon: Item[],
modSlotname: string): IChooseRandomCompatibleModResult
modSlotname: string,
): IChooseRandomCompatibleModResult
{
let chosenTpl: string;
const exhaustableModPool = new ExhaustableArray(modPool, this.randomUtil, this.jsonUtil);
let chosenModResult: IChooseRandomCompatibleModResult = {
incompatible: true,
found: false,
reason: "unknown",
};
let chosenModResult: IChooseRandomCompatibleModResult = { incompatible: true, found: false, reason: "unknown" };
const modParentFilterList = parentSlot._props.filters[0].Filter;
// How many times can a mod for the slot be blocked before we stop trying
@ -876,7 +893,7 @@ export class BotEquipmentModGenerator
// Try again
continue;
}
// Some mod combos will never work, make sure this isnt the case
if (!chosenModResult.incompatible && !this.weaponModComboIsIncompatible(weapon, chosenTpl))
{
@ -902,9 +919,9 @@ export class BotEquipmentModGenerator
* @param parentTemplate Mods parent
* @param weaponTemplate Mods root parent (weapon/equipment)
* @param modSlot name of mod slot to choose for
* @param botEquipBlacklist
* @param botEquipBlacklist
* @param isRandomisableSlot is flagged as a randomisable slot
* @returns
* @returns
*/
protected getModPoolForSlot(
itemModPool: Record<string, string[]>,
@ -913,13 +930,16 @@ export class BotEquipmentModGenerator
weaponTemplate: ITemplateItem,
modSlot: string,
botEquipBlacklist: EquipmentFilterDetails,
isRandomisableSlot: boolean): string[]
isRandomisableSlot: boolean,
): string[]
{
// Mod is flagged as being default only, try and find it in globals
if (modSpawnResult === ModSpawn.DEFAULT_MOD)
{
const defaultWeaponPreset = this.presetHelper.getDefaultPreset(weaponTemplate._id)
const matchingMod = defaultWeaponPreset._items.find(item => item?.slotId?.toLowerCase() === modSlot.toLowerCase());
const defaultWeaponPreset = this.presetHelper.getDefaultPreset(weaponTemplate._id);
const matchingMod = defaultWeaponPreset._items.find((item) =>
item?.slotId?.toLowerCase() === modSlot.toLowerCase()
);
// Only filter mods down to single default item if it already exists in existing itemModPool, OR the default item has no children
// Filtering mod pool to item that wasnt already there can have problems;
@ -1053,7 +1073,7 @@ export class BotEquipmentModGenerator
slotAddedToTemplate: Slot,
modSlot: string,
parentTemplate: ITemplateItem,
botRole: string
botRole: string,
): boolean
{
const modBeingAddedTemplate = modToAdd[1];
@ -1083,7 +1103,7 @@ export class BotEquipmentModGenerator
itemName: modBeingAddedTemplate._name,
modSlot: modSlot,
parentItemName: parentTemplate._name,
botRole: botRole
botRole: botRole,
}),
);
}

View File

@ -110,9 +110,7 @@ export class BotGenerator
// Get raw json data for bot (Cloned)
const botJsonTemplate = this.jsonUtil.clone(
this.botHelper.getBotTemplate((botGenerationDetails.isPmc)
? bot.Info.Side
: botGenerationDetails.role),
this.botHelper.getBotTemplate((botGenerationDetails.isPmc) ? bot.Info.Side : botGenerationDetails.role),
);
bot = this.generateBot(sessionId, bot, botJsonTemplate, botGenerationDetails);
@ -161,12 +159,7 @@ export class BotGenerator
);
}
bot.Info.Nickname = this.generateBotNickname(
botJsonTemplate,
botGenerationDetails,
botRole,
sessionId,
);
bot.Info.Nickname = this.generateBotNickname(botJsonTemplate, botGenerationDetails, botRole, sessionId);
if (!this.seasonalEventService.christmasEventEnabled())
{

View File

@ -12,7 +12,12 @@ import { Chances, Generation, IBotType, Inventory, Mods } from "@spt-aki/models/
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { EquipmentSlots } from "@spt-aki/models/enums/EquipmentSlots";
import { EquipmentFilterDetails, EquipmentFilters, IBotConfig, RandomisationDetails } from "@spt-aki/models/spt/config/IBotConfig";
import {
EquipmentFilterDetails,
EquipmentFilters,
IBotConfig,
RandomisationDetails,
} from "@spt-aki/models/spt/config/IBotConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
@ -70,13 +75,7 @@ export class BotInventoryGenerator
// Generate base inventory with no items
const botInventory = this.generateInventoryBase();
this.generateAndAddEquipmentToBot(
templateInventory,
wornItemChances,
botRole,
botInventory,
botLevel,
);
this.generateAndAddEquipmentToBot(templateInventory, wornItemChances, botRole, botInventory, botLevel);
// Roll weapon spawns (primary/secondary/holster) and generate a weapon for each roll that passed
this.generateAndAddWeaponsToBot(
@ -91,13 +90,7 @@ export class BotInventoryGenerator
);
// Pick loot and add to bots containers (rig/backpack/pockets/secure)
this.botLootGenerator.generateLoot(
sessionId,
botJsonTemplate,
isPmc,
botRole,
botInventory,
botLevel);
this.botLootGenerator.generateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
return botInventory;
}
@ -138,7 +131,7 @@ export class BotInventoryGenerator
sortingTable: sortingTableId,
hideoutAreaStashes: {},
fastPanel: {},
favoriteItems: []
favoriteItems: [],
};
}
@ -189,7 +182,7 @@ export class BotInventoryGenerator
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
randomisationDetails: randomistionDetails,
});
}
@ -203,7 +196,7 @@ export class BotInventoryGenerator
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
randomisationDetails: randomistionDetails,
});
this.generateEquipment({
rootEquipmentSlot: EquipmentSlots.HEADWEAR,
@ -214,7 +207,7 @@ export class BotInventoryGenerator
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
randomisationDetails: randomistionDetails,
});
this.generateEquipment({
rootEquipmentSlot: EquipmentSlots.EARPIECE,
@ -225,7 +218,7 @@ export class BotInventoryGenerator
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
randomisationDetails: randomistionDetails,
});
this.generateEquipment({
rootEquipmentSlot: EquipmentSlots.ARMOR_VEST,
@ -236,11 +229,11 @@ export class BotInventoryGenerator
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
randomisationDetails: randomistionDetails,
});
// Bot has no armor vest and flagged to be foreced to wear armored rig in this event
const hasArmorVest = botInventory.items.some(item => item.slotId === "ArmorVest")
const hasArmorVest = botInventory.items.some((item) => item.slotId === "ArmorVest");
if (botEquipConfig.forceOnlyArmoredRigWhenNoArmor && !hasArmorVest)
{
// Filter rigs down to only those with armor
@ -263,44 +256,48 @@ export class BotInventoryGenerator
botLevel: botLevel,
inventory: botInventory,
botEquipmentConfig: botEquipConfig,
randomisationDetails: randomistionDetails
randomisationDetails: randomistionDetails,
});
}
/**
* Remove non-armored rigs from parameter data
* @param templateInventory
* @param templateInventory
*/
protected filterRigsToThoseWithProtection(templateInventory: Inventory): void
{
const tacVestsWithArmor = Object.entries(templateInventory.equipment.TacticalVest)
.reduce((newVestDictionary, [tplKey]) =>
const tacVestsWithArmor = Object.entries(templateInventory.equipment.TacticalVest).reduce(
(newVestDictionary, [tplKey]) =>
{
if (this.itemHelper.getItem(tplKey)[1]._props.Slots?.length > 0)
{
newVestDictionary[tplKey] = templateInventory.equipment.TacticalVest[tplKey];
}
return newVestDictionary;
}, {});
},
{},
);
templateInventory.equipment.TacticalVest = tacVestsWithArmor;
}
/**
* Remove armored rigs from parameter data
* @param templateInventory
* @param templateInventory
*/
protected filterRigsToThoseWithoutProtection(templateInventory: Inventory): void
{
const tacVestsWithoutArmor = Object.entries(templateInventory.equipment.TacticalVest)
.reduce((newVestDictionary, [tplKey]) =>
const tacVestsWithoutArmor = Object.entries(templateInventory.equipment.TacticalVest).reduce(
(newVestDictionary, [tplKey]) =>
{
if (this.itemHelper.getItem(tplKey)[1]._props.Slots?.length === 0)
{
newVestDictionary[tplKey] = templateInventory.equipment.TacticalVest[tplKey];
}
return newVestDictionary;
}, {});
},
{},
);
templateInventory.equipment.TacticalVest = tacVestsWithoutArmor;
}
@ -312,13 +309,18 @@ export class BotInventoryGenerator
protected generateEquipment(settings: IGenerateEquipmentProperties): void
{
const spawnChance =
([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]).includes(settings.rootEquipmentSlot)
([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]).includes(
settings.rootEquipmentSlot,
)
? 100
: settings.spawnChances.equipment[settings.rootEquipmentSlot];
if (typeof spawnChance === "undefined")
{
this.logger.warning(
this.localisationService.getText("bot-no_spawn_chance_defined_for_equipment_slot", settings.rootEquipmentSlot),
this.localisationService.getText(
"bot-no_spawn_chance_defined_for_equipment_slot",
settings.rootEquipmentSlot,
),
);
return;
@ -358,7 +360,8 @@ export class BotInventoryGenerator
const compatabilityResult = this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(
settings.inventory.items,
chosenItemTpl,
settings.rootEquipmentSlot);
settings.rootEquipmentSlot,
);
if (compatabilityResult.incompatible)
{
// Tried x different items that failed, stop
@ -404,13 +407,13 @@ export class BotInventoryGenerator
}
// Item has slots, fill them
if ( pickedItemDb._props.Slots?.length > 0 )
if (pickedItemDb._props.Slots?.length > 0)
{
const items = this.botEquipmentModGenerator.generateModsForEquipment(
[item],
id,
pickedItemDb,
settings
settings,
);
settings.inventory.items.push(...items);
}
@ -558,20 +561,20 @@ export class BotInventoryGenerator
}
export interface IGenerateEquipmentProperties
{
/** Root Slot being generated */
rootEquipmentSlot: string,
/** Equipment pool for root slot being generated */
rootEquipmentPool: Record<string, number>,
modPool: Mods,
/** Dictionary of mod items and their chance to spawn for this bot type */
spawnChances: Chances,
/** Role being generated for */
botRole: string,
/** Level of bot being generated */
botLevel: number,
inventory: PmcInventory,
botEquipmentConfig: EquipmentFilters,
/** Settings from bot.json to adjust how item is generated */
randomisationDetails: RandomisationDetails
}
{
/** Root Slot being generated */
rootEquipmentSlot: string;
/** Equipment pool for root slot being generated */
rootEquipmentPool: Record<string, number>;
modPool: Mods;
/** Dictionary of mod items and their chance to spawn for this bot type */
spawnChances: Chances;
/** Role being generated for */
botRole: string;
/** Level of bot being generated */
botLevel: number;
inventory: PmcInventory;
botEquipmentConfig: EquipmentFilters;
/** Settings from bot.json to adjust how item is generated */
randomisationDetails: RandomisationDetails;
}

View File

@ -43,7 +43,8 @@ export class BotLevelGenerator
botGenerationDetails.playerLevel,
botGenerationDetails.botRelativeLevelDeltaMin,
levelDetails,
expTable);
expTable,
);
// Get random level based on the exp table.
let exp = 0;

View File

@ -341,7 +341,6 @@ export class BotLootGenerator
}
}
this.addRequiredChildItemsToParent(itemToAddTemplate, itemWithChildrenToAdd, isPmc);
// Attempt to add item to container(s)
@ -359,7 +358,9 @@ export class BotLootGenerator
if (itemAddedResult === ItemAddedResult.NO_CONTAINERS)
{
// Bot has no container to put item in, exit
this.logger.debug(`Unable to add: ${totalItemCount} items to bot as it lacks a container to include them`);
this.logger.debug(
`Unable to add: ${totalItemCount} items to bot as it lacks a container to include them`,
);
break;
}
@ -367,7 +368,11 @@ export class BotLootGenerator
if (fitItemIntoContainerAttempts >= 4)
{
this.logger.debug(
`Failed to place item ${i} of ${totalItemCount} items into ${botRole} containers: ${equipmentSlots.join(",")}. Tried ${fitItemIntoContainerAttempts} times, reason: ${ItemAddedResult[itemAddedResult]}, skipping`,
`Failed to place item ${i} of ${totalItemCount} items into ${botRole} containers: ${
equipmentSlots.join(",")
}. Tried ${fitItemIntoContainerAttempts} times, reason: ${
ItemAddedResult[itemAddedResult]
}, skipping`,
);
break;
@ -399,7 +404,11 @@ export class BotLootGenerator
* @param itemToAddChildrenTo Item to add children to
* @param isPmc Is the item being generated for a pmc (affects money/ammo stack sizes)
*/
protected addRequiredChildItemsToParent(itemToAddTemplate: ITemplateItem, itemToAddChildrenTo: Item[], isPmc: boolean): void
protected addRequiredChildItemsToParent(
itemToAddTemplate: ITemplateItem,
itemToAddChildrenTo: Item[],
isPmc: boolean,
): void
{
// Fill ammo box
if (this.itemHelper.isOfBaseclass(itemToAddTemplate._id, BaseClasses.AMMO_BOX))
@ -477,7 +486,11 @@ export class BotLootGenerator
if (result !== ItemAddedResult.SUCCESS)
{
this.logger.debug(`Failed to add additional weapon ${generatedWeapon.weapon[0]._id} to bot backpack, reason: ${ItemAddedResult[result]}`);
this.logger.debug(
`Failed to add additional weapon ${generatedWeapon.weapon[0]._id} to bot backpack, reason: ${
ItemAddedResult[result]
}`,
);
}
}
}
@ -631,7 +644,10 @@ export class BotLootGenerator
{
const randomSize = itemTemplate._props.StackMaxSize === 1
? 1
: this.randomUtil.getInt(itemTemplate._props.StackMinRandom, Math.min(itemTemplate._props.StackMaxRandom, 60));
: this.randomUtil.getInt(
itemTemplate._props.StackMinRandom,
Math.min(itemTemplate._props.StackMaxRandom, 60),
);
if (!ammoItem.upd)
{

View File

@ -212,7 +212,7 @@ export class BotWeaponGenerator
)
{
// Guns have variety of possible Chamber ids, patron_in_weapon/patron_in_weapon_000/patron_in_weapon_001
const chamberSlotNames = weaponItemTemplate._props.Chambers.map(x => x._name);
const chamberSlotNames = weaponItemTemplate._props.Chambers.map((x) => x._name);
this.addCartridgeToChamber(weaponWithModsArray, ammoTpl, chamberSlotNames);
}
@ -311,7 +311,10 @@ export class BotWeaponGenerator
{
// Invalid weapon generated, fallback to preset
this.logger.warning(
this.localisationService.getText("bot-weapon_generated_incorrect_using_default", `${weaponTpl} ${itemTemplate._name}`),
this.localisationService.getText(
"bot-weapon_generated_incorrect_using_default",
`${weaponTpl} ${itemTemplate._name}`,
),
);
const weaponMods = [];
@ -364,10 +367,12 @@ export class BotWeaponGenerator
}
// Iterate over required slots in db item, check mod exists for that slot
for (const modSlotTemplate of modTemplate._props.Slots.filter(slot => slot._required))
for (const modSlotTemplate of modTemplate._props.Slots.filter((slot) => slot._required))
{
const slotName = modSlotTemplate._name;
const weaponSlotItem = weaponItemArray.find((weaponItem) => weaponItem.parentId === mod._id && weaponItem.slotId === slotName);
const weaponSlotItem = weaponItemArray.find((weaponItem) =>
weaponItem.parentId === mod._id && weaponItem.slotId === slotName
);
if (!weaponSlotItem)
{
this.logger.warning(
@ -547,7 +552,10 @@ export class BotWeaponGenerator
{
// Shouldn't happen
this.logger.warning(
this.localisationService.getText("bot-weapon_missing_magazine_or_chamber", {weaponId: weaponTemplate._id, botRole: botRole}),
this.localisationService.getText("bot-weapon_missing_magazine_or_chamber", {
weaponId: weaponTemplate._id,
botRole: botRole,
}),
);
}
@ -594,9 +602,10 @@ export class BotWeaponGenerator
while (!chosenAmmoTpl)
{
const possibleAmmo = this.weightedRandomHelper.getWeightedValue<string>(compatibleCartridges);
// Weapon has chamber but does not support cartridge
if (weaponTemplate._props.Chambers[0]
if (
weaponTemplate._props.Chambers[0]
&& !weaponTemplate._props.Chambers[0]._props.filters[0].Filter.includes(possibleAmmo)
)
{

View File

@ -64,7 +64,8 @@ export class FenceBaseAssortGenerator
// Item base type blacklisted
if (this.traderConfig.fence.blacklist.length > 0)
{
if (this.traderConfig.fence.blacklist.includes(rootItemDb._id)
if (
this.traderConfig.fence.blacklist.includes(rootItemDb._id)
|| this.itemHelper.isOfBaseclasses(rootItemDb._id, this.traderConfig.fence.blacklist)
)
{
@ -123,10 +124,7 @@ export class FenceBaseAssortGenerator
}
// Construct preset + mods
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
null,
this.jsonUtil.clone(defaultPreset._items),
);
const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(defaultPreset._items));
// Find root item and add some properties to it
for (let i = 0; i < presetAndMods.length; i++)
@ -156,7 +154,10 @@ export class FenceBaseAssortGenerator
// Multiply weapon+mods rouble price by multipler in config
baseFenceAssort.barter_scheme[presetAndMods[0]._id] = [[]];
baseFenceAssort.barter_scheme[presetAndMods[0]._id][0][0] = { _tpl: Money.ROUBLES, count: Math.round(price) * this.traderConfig.fence.presetPriceMult };
baseFenceAssort.barter_scheme[presetAndMods[0]._id][0][0] = {
_tpl: Money.ROUBLES,
count: Math.round(price) * this.traderConfig.fence.presetPriceMult,
};
baseFenceAssort.loyal_level_items[presetAndMods[0]._id] = 1;
}
@ -177,7 +178,7 @@ export class FenceBaseAssortGenerator
}
// Check for and add required soft inserts to armors
const requiredSlots = itemDbDetails._props.Slots.filter(slot => slot._required);
const requiredSlots = itemDbDetails._props.Slots.filter((slot) => slot._required);
const hasRequiredSlots = requiredSlots.length > 0;
if (hasRequiredSlots)
{
@ -199,9 +200,9 @@ export class FenceBaseAssortGenerator
upd: {
Repairable: {
Durability: modItemDbDetails._props.MaxDurability,
MaxDurability: modItemDbDetails._props.MaxDurability
}
}
MaxDurability: modItemDbDetails._props.MaxDurability,
},
},
};
armor.push(mod);
@ -209,12 +210,14 @@ export class FenceBaseAssortGenerator
}
// Check for and add plate items
const plateSlots = itemDbDetails._props.Slots.filter(slot => this.itemHelper.isRemovablePlateSlot(slot._name));
const plateSlots = itemDbDetails._props.Slots.filter((slot) =>
this.itemHelper.isRemovablePlateSlot(slot._name)
);
if (plateSlots.length > 0)
{
for (const plateSlot of plateSlots)
{
const plateTpl = plateSlot._props.filters[0].Plate
const plateTpl = plateSlot._props.filters[0].Plate;
if (!plateTpl)
{
// Bsg data lacks a default plate, skip adding mod
@ -229,9 +232,9 @@ export class FenceBaseAssortGenerator
upd: {
Repairable: {
Durability: modItemDbDetails._props.MaxDurability,
MaxDurability: modItemDbDetails._props.MaxDurability
}
}
MaxDurability: modItemDbDetails._props.MaxDurability,
},
},
});
}
}
@ -247,7 +250,7 @@ export class FenceBaseAssortGenerator
let price = 0;
for (const item of itemWithChildren)
{
price += this.handbookHelper.getTemplatePrice(item._tpl);
price += this.handbookHelper.getTemplatePrice(item._tpl);
}
return price;

View File

@ -1,14 +1,14 @@
export interface IFilterPlateModsForSlotByLevelResult
{
result: Result
plateModTpls: string[]
result: Result;
plateModTpls: string[];
}
export enum Result {
export enum Result
{
UNKNOWN_FAILURE = -1,
SUCCESS = 1,
NO_DEFAULT_FILTER = 2,
NOT_PLATE_HOLDING_SLOT = 3,
LACKS_PLATE_WEIGHTS = 4
}
LACKS_PLATE_WEIGHTS = 4,
}

View File

@ -169,7 +169,7 @@ export class LocationGenerator
const staticContainerGroupData: IStaticContainer = db.locations[locationId].statics;
if (!staticContainerGroupData)
{
this.logger.warning(`Map: ${locationId} lacks a statics file, skipping container generation.`)
this.logger.warning(`Map: ${locationId} lacks a statics file, skipping container generation.`);
return result;
}
@ -248,7 +248,6 @@ export class LocationGenerator
staticContainerCount++;
staticLootItemCount += containerWithLoot.template.Items.length;
}
}
@ -430,7 +429,7 @@ export class LocationGenerator
itemCountToAdd,
this.locationConfig.allowDuplicateItemsInStaticContainers,
locklist,
).filter(x => !tplsForced.includes(x));
).filter((x) => !tplsForced.includes(x));
// Add forced loot to chosen item pool
const tplsToAddToContainer = tplsForced.concat(chosenTpls);
@ -516,7 +515,9 @@ export class LocationGenerator
const countDistribution = staticLootDist[containerTypeId]?.itemcountDistribution;
if (!countDistribution)
{
this.logger.warning(`Unable to acquire count distrubution for container: ${containerTypeId} on: ${locationName}. defaulting to 0`);
this.logger.warning(
`Unable to acquire count distrubution for container: ${containerTypeId} on: ${locationName}. defaulting to 0`,
);
return 0;
}
@ -603,7 +604,10 @@ export class LocationGenerator
// Draw from random distribution
const desiredSpawnpointCount = Math.round(
this.getLooseLootMultiplerForLocation(locationName)
* this.randomUtil.getNormallyDistributedRandomNumber(dynamicLootDist.spawnpointCount.mean, dynamicLootDist.spawnpointCount.std),
* this.randomUtil.getNormallyDistributedRandomNumber(
dynamicLootDist.spawnpointCount.mean,
dynamicLootDist.spawnpointCount.std,
),
);
// Positions not in forced but have 100% chance to spawn
@ -823,7 +827,7 @@ export class LocationGenerator
{
const chosenItem = spawnPoint.template.Items.find((x) => x._id === chosenComposedKey);
const chosenTpl = chosenItem._tpl;
const itemTemplate = this.itemHelper.getItem(chosenTpl)[1];
const itemTemplate = this.itemHelper.getItem(chosenTpl)[1];
// Item array to return
let itemWithMods: Item[] = [];
@ -864,18 +868,19 @@ export class LocationGenerator
this.locationConfig.minFillLooseMagazinePercent / 100,
);
}
itemWithMods.push(...magazineItem);
}
else if (this.itemHelper.armorItemCanHoldMods(chosenTpl))
{
itemWithMods.push({
_id: this.objectId.generate(),
_tpl: chosenTpl,
});
itemWithMods.push({ _id: this.objectId.generate(), _tpl: chosenTpl });
if (itemTemplate._props.Slots?.length > 0)
{
itemWithMods = this.itemHelper.addChildSlotItems(itemWithMods, itemTemplate, this.locationConfig.equipmentLootSettings.modSpawnChancePercent);
itemWithMods = this.itemHelper.addChildSlotItems(
itemWithMods,
itemTemplate,
this.locationConfig.equipmentLootSettings.modSpawnChancePercent,
);
}
}
else
@ -1004,7 +1009,10 @@ export class LocationGenerator
if (!rootItem)
{
this.logger.error(
this.localisationService.getText("location-missing_root_item", { tpl: chosenTpl, parentId: parentId }),
this.localisationService.getText("location-missing_root_item", {
tpl: chosenTpl,
parentId: parentId,
}),
);
throw new Error(this.localisationService.getText("location-critical_error_see_log"));
@ -1085,14 +1093,14 @@ export class LocationGenerator
// We make base item above, at start of function, no need to do it here
if (itemTemplate._props.Slots?.length > 0)
{
items = this.itemHelper.addChildSlotItems(items, itemTemplate, this.locationConfig.equipmentLootSettings.modSpawnChancePercent);
items = this.itemHelper.addChildSlotItems(
items,
itemTemplate,
this.locationConfig.equipmentLootSettings.modSpawnChancePercent,
);
}
}
return {
items: items,
width: width,
height: height
};
return { items: items, width: width, height: height };
}
}

View File

@ -112,30 +112,50 @@ export class LootGenerator
const itemBlacklistArray = Array.from(itemBlacklist);
// Filter default presets to just weapons
const randomisedWeaponPresetCount = this.randomUtil.getInt(options.weaponPresetCount.min, options.weaponPresetCount.max);
const randomisedWeaponPresetCount = this.randomUtil.getInt(
options.weaponPresetCount.min,
options.weaponPresetCount.max,
);
if (randomisedWeaponPresetCount > 0)
{
const weaponDefaultPresets = globalDefaultPresets.filter(preset => this.itemHelper.isOfBaseclass(preset._encyclopedia, BaseClasses.WEAPON));
const weaponDefaultPresets = globalDefaultPresets.filter((preset) =>
this.itemHelper.isOfBaseclass(preset._encyclopedia, BaseClasses.WEAPON)
);
for (let index = 0; index < randomisedWeaponPresetCount; index++)
{
if (!this.findAndAddRandomPresetToLoot(weaponDefaultPresets, itemTypeCounts, itemBlacklistArray, result))
if (
!this.findAndAddRandomPresetToLoot(weaponDefaultPresets, itemTypeCounts, itemBlacklistArray, result)
)
{
index--;
}
}
}
// Filter default presets to just armors and then filter again by protection level
const randomisedArmorPresetCount = this.randomUtil.getInt(options.armorPresetCount.min, options.armorPresetCount.max);
const randomisedArmorPresetCount = this.randomUtil.getInt(
options.armorPresetCount.min,
options.armorPresetCount.max,
);
if (randomisedArmorPresetCount > 0)
{
const armorDefaultPresets = globalDefaultPresets.filter(preset => this.itemHelper.armorItemCanHoldMods(preset._encyclopedia));
const levelFilteredArmorPresets = armorDefaultPresets.filter(armor => this.armorIsDesiredProtectionLevel(armor, options));
const armorDefaultPresets = globalDefaultPresets.filter((preset) =>
this.itemHelper.armorItemCanHoldMods(preset._encyclopedia)
);
const levelFilteredArmorPresets = armorDefaultPresets.filter((armor) =>
this.armorIsDesiredProtectionLevel(armor, options)
);
for (let index = 0; index < randomisedArmorPresetCount; index++)
{
if (!this.findAndAddRandomPresetToLoot(levelFilteredArmorPresets, itemTypeCounts, itemBlacklistArray, result))
if (
!this.findAndAddRandomPresetToLoot(
levelFilteredArmorPresets,
itemTypeCounts,
itemBlacklistArray,
result,
)
)
{
index--;
}
@ -153,21 +173,21 @@ export class LootGenerator
*/
protected armorIsDesiredProtectionLevel(armor: IPreset, options: LootRequest): boolean
{
const frontPlate = armor._items.find(mod => mod?.slotId?.toLowerCase() === "front_plate");
const frontPlate = armor._items.find((mod) => mod?.slotId?.toLowerCase() === "front_plate");
if (frontPlate)
{
const plateDb = this.itemHelper.getItem(frontPlate._tpl);
return options.armorLevelWhitelist.includes(Number.parseInt(plateDb[1]._props.armorClass as any));
}
const helmetTop = armor._items.find(mod => mod?.slotId?.toLowerCase() === "helmet_top");
const helmetTop = armor._items.find((mod) => mod?.slotId?.toLowerCase() === "helmet_top");
if (helmetTop)
{
const plateDb = this.itemHelper.getItem(helmetTop._tpl);
return options.armorLevelWhitelist.includes(Number.parseInt(plateDb[1]._props.armorClass as any));
}
const softArmorFront = armor._items.find(mod => mod?.slotId?.toLowerCase() === "soft_armor_front");
const softArmorFront = armor._items.find((mod) => mod?.slotId?.toLowerCase() === "soft_armor_front");
if (softArmorFront)
{
const plateDb = this.itemHelper.getItem(softArmorFront._tpl);
@ -369,10 +389,7 @@ export class LootGenerator
chosenWeaponPreset = this.randomUtil.getArrayValue(this.presetHelper.getPresets(chosenWeaponTpl));
}
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
null,
this.jsonUtil.clone(chosenWeaponPreset._items),
);
const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(chosenWeaponPreset._items));
this.itemHelper.remapRootItemId(presetAndMods);
// Add preset to return object
@ -436,12 +453,7 @@ export class LootGenerator
for (let index = 0; index < rewardCount; index++)
{
const chosenAmmoBox = this.randomUtil.getArrayValue(ammoBoxesMatchingCaliber);
const ammoBoxItem: Item[] = [
{
_id: this.hashUtil.generate(),
_tpl: chosenAmmoBox._id
}
]
const ammoBoxItem: Item[] = [{ _id: this.hashUtil.generate(), _tpl: chosenAmmoBox._id }];
this.itemHelper.addCartridgesToAmmoBox(ammoBoxItem, chosenAmmoBox);
rewards.push(ammoBoxItem);
}
@ -469,14 +481,9 @@ export class LootGenerator
{
// Choose a random item from pool
const chosenRewardItem = this.randomUtil.getArrayValue(rewardItemPool);
const rewardItem: Item[] = [
{
_id: this.hashUtil.generate(),
_tpl: chosenRewardItem._id
}
]
const rewardItem: Item[] = [{ _id: this.hashUtil.generate(), _tpl: chosenRewardItem._id }];
rewards.push(rewardItem)
rewards.push(rewardItem);
}
}
@ -524,12 +531,9 @@ export class LootGenerator
for (let index = 0; index < rewardCount; index++)
{
const chosenItem = this.randomUtil.drawRandomFromList(relatedItems);
const item: Item[] = [{
_id: this.hashUtil.generate(),
_tpl: chosenItem[0]._id
}];
const item: Item[] = [{ _id: this.hashUtil.generate(), _tpl: chosenItem[0]._id }];
modRewards.push(item)
modRewards.push(item);
}
}
@ -552,13 +556,8 @@ export class LootGenerator
const chosenRewardItemTpl = this.weightedRandomHelper.getWeightedValue<string>(
rewardContainerDetails.rewardTplPool,
);
const rewardItem: Item[] = [
{
_id: this.hashUtil.generate(),
_tpl: chosenRewardItemTpl
}
]
itemsToReturn.push(rewardItem)
const rewardItem: Item[] = [{ _id: this.hashUtil.generate(), _tpl: chosenRewardItemTpl }];
itemsToReturn.push(rewardItem);
}
return itemsToReturn;

View File

@ -114,7 +114,7 @@ export class PlayerScavGenerator
scavData.Info.Level = this.getScavLevel(existingScavData);
scavData.Info.Experience = this.getScavExperience(existingScavData);
scavData.Quests = existingScavData.Quests ?? [];
scavData.TaskConditionCounters = existingScavData.TaskConditionCounters ?? { };
scavData.TaskConditionCounters = existingScavData.TaskConditionCounters ?? {};
scavData.Notes = existingScavData.Notes ?? { Notes: [] };
scavData.WishList = existingScavData.WishList ?? [];

View File

@ -75,21 +75,18 @@ export class RagfairAssortGenerator
const results: Item[][] = [];
/** Get cloned items from db */
const dbItemsClone = this.itemHelper.getItems().filter(item => item._type !== "Node");
const dbItemsClone = this.itemHelper.getItems().filter((item) => item._type !== "Node");
/** Store processed preset tpls so we dont add them when procesing non-preset items */
const processedArmorItems: string[] = [];
const seasonalEventActive = this.seasonalEventService.seasonalEventEnabled();
const seasonalItemTplBlacklist = this.seasonalEventService.getInactiveSeasonalEventItems();
const presets = this.getPresetsToAdd();
for (const preset of presets)
{
// Update Ids and clone
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
null,
this.jsonUtil.clone(preset._items),
);
const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(preset._items));
this.itemHelper.remapRootItemId(presetAndMods);
// Add presets base item tpl to the processed list so its skipped later on when processing items
@ -97,9 +94,9 @@ export class RagfairAssortGenerator
presetAndMods[0].parentId = "hideout";
presetAndMods[0].slotId = "hideout";
presetAndMods[0].upd = { StackObjectsCount: 99999999, UnlimitedCount: true, sptPresetId: preset._id};
presetAndMods[0].upd = { StackObjectsCount: 99999999, UnlimitedCount: true, sptPresetId: preset._id };
results.push(presetAndMods);
results.push(presetAndMods);
}
for (const item of dbItemsClone)
@ -126,7 +123,7 @@ export class RagfairAssortGenerator
const ragfairAssort = this.createRagfairAssortRootItem(item._id, item._id); // tplid and id must be the same so hideout recipe rewards work
results.push([ragfairAssort]);
results.push([ragfairAssort]);
}
return results;
@ -140,8 +137,8 @@ export class RagfairAssortGenerator
protected getPresetsToAdd(): IPreset[]
{
return (this.ragfairConfig.dynamic.showDefaultPresetsOnly)
? Object.values(this.presetHelper.getDefaultPresets())
: this.presetHelper.getAllPresets()
? Object.values(this.presetHelper.getDefaultPresets())
: this.presetHelper.getAllPresets();
}
/**

View File

@ -288,7 +288,8 @@ export class RagfairOfferGenerator
if (this.ragfairServerHelper.isPlayer(userID))
{
// Player offer = current time + offerDurationTimeInHour;
const offerDurationTimeHours = this.databaseServer.getTables().globals.config.RagFair.offerDurationTimeInHour;
const offerDurationTimeHours =
this.databaseServer.getTables().globals.config.RagFair.offerDurationTimeInHour;
return this.timeUtil.getTimestamp() + Math.round(offerDurationTimeHours * TimeUtil.ONE_HOUR_AS_SECONDS);
}
@ -357,7 +358,10 @@ export class RagfairOfferGenerator
// Armor presets can hold plates above the allowed flea level, remove if necessary
if (isPreset && this.ragfairConfig.dynamic.blacklist.enableBsgList)
{
this.removeBannedPlatesFromPreset(assortItemWithChildren, this.ragfairConfig.dynamic.blacklist.armorPlateMaxProtectionLevel);
this.removeBannedPlatesFromPreset(
assortItemWithChildren,
this.ragfairConfig.dynamic.blacklist.armorPlateMaxProtectionLevel,
);
}
// Get number of offers to create
@ -375,11 +379,13 @@ export class RagfairOfferGenerator
// Presets get unique id generated during getPresetItems() earlier + would require regenerating all children to match
assortItemWithChildren[0]._id = this.hashUtil.generate();
}
delete assortItemWithChildren[0].parentId;
delete assortItemWithChildren[0].slotId;
assortSingleOfferProcesses.push(this.createSingleOfferForItem(assortItemWithChildren, isPreset, itemDetails));
assortSingleOfferProcesses.push(
this.createSingleOfferForItem(assortItemWithChildren, isPreset, itemDetails),
);
}
await Promise.all(assortSingleOfferProcesses);
@ -399,7 +405,9 @@ export class RagfairOfferGenerator
return false;
}
const plateSlots = presetWithChildren.filter(item => this.itemHelper.getRemovablePlateSlotIds().includes(item.slotId?.toLowerCase()));
const plateSlots = presetWithChildren.filter((item) =>
this.itemHelper.getRemovablePlateSlotIds().includes(item.slotId?.toLowerCase())
);
if (plateSlots.length === 0)
{
// Has no plate slots e.g. "left_side_plate", exit
@ -409,7 +417,8 @@ export class RagfairOfferGenerator
let removedPlate = false;
for (const plateSlot of plateSlots)
{
const plateArmorLevel = Number.parseInt(<string>this.itemHelper.getItem(plateSlot._tpl)[1]._props.armorClass) ?? 0;
const plateArmorLevel =
Number.parseInt(<string>this.itemHelper.getItem(plateSlot._tpl)[1]._props.armorClass) ?? 0;
if (plateArmorLevel > plateProtectionLimit)
{
presetWithChildren.splice(presetWithChildren.indexOf(plateSlot), 1);
@ -417,7 +426,7 @@ export class RagfairOfferGenerator
}
}
return removedPlate
return removedPlate;
}
/**
@ -434,13 +443,19 @@ export class RagfairOfferGenerator
): Promise<void>
{
// Set stack size to random value
itemWithChildren[0].upd.StackObjectsCount = this.ragfairServerHelper.calculateDynamicStackCount(itemWithChildren[0]._tpl, isPreset);
itemWithChildren[0].upd.StackObjectsCount = this.ragfairServerHelper.calculateDynamicStackCount(
itemWithChildren[0]._tpl,
isPreset,
);
const isBarterOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.barter.chancePercent);
const isPackOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.pack.chancePercent)
&& !isBarterOffer
&& itemWithChildren.length === 1
&& this.itemHelper.isOfBaseclasses(itemWithChildren[0]._tpl, this.ragfairConfig.dynamic.pack.itemTypeWhitelist);
&& this.itemHelper.isOfBaseclasses(
itemWithChildren[0]._tpl,
this.ragfairConfig.dynamic.pack.itemTypeWhitelist,
);
const randomUserId = this.hashUtil.generate();
let barterScheme: IBarterScheme[];
@ -614,22 +629,29 @@ export class RagfairOfferGenerator
* @param itemWithMods Item to adjust condition details of
* @param itemDetails db item details of first item in array
*/
protected randomiseItemCondition(conditionSettingsId: string, itemWithMods: Item[], itemDetails: ITemplateItem): void
protected randomiseItemCondition(
conditionSettingsId: string,
itemWithMods: Item[],
itemDetails: ITemplateItem,
): void
{
const rootItem = itemWithMods[0];
const itemConditionValues = this.ragfairConfig.dynamic.condition[conditionSettingsId];
const multiplier = this.randomUtil.getFloat(
itemConditionValues.min,
itemConditionValues.max,
);
const multiplier = this.randomUtil.getFloat(itemConditionValues.min, itemConditionValues.max);
// Randomise armor + plates + armor related things
if (this.itemHelper.armorItemCanHoldMods(rootItem._tpl)
|| this.itemHelper.isOfBaseclasses(rootItem._tpl, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT]))
if (
this.itemHelper.armorItemCanHoldMods(rootItem._tpl)
|| this.itemHelper.isOfBaseclasses(rootItem._tpl, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT])
)
{
// Chance to not adjust armor
if (!this.randomUtil.getChance100(this.ragfairConfig.dynamic.condition[BaseClasses.ARMORED_EQUIPMENT].conditionChance * 100))
if (
!this.randomUtil.getChance100(
this.ragfairConfig.dynamic.condition[BaseClasses.ARMORED_EQUIPMENT].conditionChance * 100,
)
)
{
return;
}
@ -637,18 +659,17 @@ export class RagfairOfferGenerator
this.randomiseArmorDurabilityValues(itemWithMods);
// Add hits to visor
const visorMod = itemWithMods.find(item => item.parentId === BaseClasses.ARMORED_EQUIPMENT && item.slotId === "mod_equipment_000");
if (this.randomUtil.getChance100(25)
&& visorMod)
const visorMod = itemWithMods.find((item) =>
item.parentId === BaseClasses.ARMORED_EQUIPMENT && item.slotId === "mod_equipment_000"
);
if (this.randomUtil.getChance100(25) && visorMod)
{
if (!visorMod.upd)
{
visorMod.upd = {};
}
visorMod.upd.FaceShield = {
Hits: this.randomUtil.getInt(1,3)
}
visorMod.upd.FaceShield = { Hits: this.randomUtil.getInt(1, 3) };
}
return;
@ -673,7 +694,8 @@ export class RagfairOfferGenerator
if (rootItem.upd.Key && itemDetails._props.MaximumNumberOfUsage > 1)
{
// randomize key uses
rootItem.upd.Key.NumberOfUsages = Math.round(itemDetails._props.MaximumNumberOfUsage * (1 - multiplier)) || 0;
rootItem.upd.Key.NumberOfUsages = Math.round(itemDetails._props.MaximumNumberOfUsage * (1 - multiplier))
|| 0;
return;
}
@ -752,22 +774,26 @@ export class RagfairOfferGenerator
// Store mod types durabiltiy multiplier for later use in current/max durability value calculation
if (!childMultiplerValues[itemDbDetails._parent])
{
childMultiplerValues[itemDbDetails._parent] = this.randomUtil.getFloat(
itemDurabilityConfigDict[itemDbDetails._parent].min,
itemDurabilityConfigDict[itemDbDetails._parent].max,
) / itemDurabilityConfigDict[itemDbDetails._parent].max;
childMultiplerValues[itemDbDetails._parent] =
this.randomUtil.getFloat(
itemDurabilityConfigDict[itemDbDetails._parent].min,
itemDurabilityConfigDict[itemDbDetails._parent].max,
) / itemDurabilityConfigDict[itemDbDetails._parent].max;
}
const modMultipler = childMultiplerValues[itemDbDetails._parent];
const maxDurability = Math.round(
this.randomUtil.getFloat(itemDbDetails._props.MaxDurability * this.randomUtil.getFloat(modMultipler, 1), itemDbDetails._props.MaxDurability),
this.randomUtil.getFloat(
itemDbDetails._props.MaxDurability * this.randomUtil.getFloat(modMultipler, 1),
itemDbDetails._props.MaxDurability,
),
);
const durability = Math.round(
this.randomUtil.getFloat(maxDurability * this.randomUtil.getFloat(modMultipler, 1), maxDurability),
);
item.upd.Repairable = {
Durability: durability || 1, // Never let value become 0
MaxDurability: maxDurability
MaxDurability: maxDurability,
};
}
}
@ -896,7 +922,11 @@ export class RagfairOfferGenerator
* @param multipler What to multiply the resulting price by
* @returns Barter scheme for offer
*/
protected createCurrencyBarterScheme(offerWithChildren: Item[], isPackOffer: boolean, multipler = 1): IBarterScheme[]
protected createCurrencyBarterScheme(
offerWithChildren: Item[],
isPackOffer: boolean,
multipler = 1,
): IBarterScheme[]
{
const currency = this.ragfairServerHelper.getDynamicOfferCurrency();
const price = this.ragfairPriceService.getDynamicOfferPriceForOffer(offerWithChildren, currency, isPackOffer)

View File

@ -9,7 +9,12 @@ import { RepeatableQuestHelper } from "@spt-aki/helpers/RepeatableQuestHelper";
import { Exit, ILocationBase } from "@spt-aki/models/eft/common/ILocationBase";
import { TraderInfo } from "@spt-aki/models/eft/common/tables/IBotBase";
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { IQuestCondition, IQuestConditionCounterCondition, IQuestReward, IQuestRewards } from "@spt-aki/models/eft/common/tables/IQuest";
import {
IQuestCondition,
IQuestConditionCounterCondition,
IQuestReward,
IQuestRewards,
} from "@spt-aki/models/eft/common/tables/IQuest";
import { IRepeatableQuest } from "@spt-aki/models/eft/common/tables/IRepeatableQuests";
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
@ -292,11 +297,15 @@ export class RepeatableQuestGenerator
// Filter out close range weapons from far distance requirement
if (distance > 50)
{
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter(category => ["Shotgun", "Pistol"].includes(category.key));
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter((category) =>
["Shotgun", "Pistol"].includes(category.key)
);
}
else if (distance < 20) // Filter out far range weapons from close distance requirement
{
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter(category => ["MarksmanRifle", "DMR"].includes(category.key));
else if (distance < 20)
{ // Filter out far range weapons from close distance requirement
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter((category) =>
["MarksmanRifle", "DMR"].includes(category.key)
);
}
// Pick a weighted weapon category
@ -418,7 +427,7 @@ export class RepeatableQuestGenerator
id: this.objectId.generate(),
dynamicLocale: true,
target: location,
conditionType: "Location"
conditionType: "Location",
};
return propsObject;
@ -477,7 +486,7 @@ export class RepeatableQuestGenerator
if (allowedWeaponCategory?.length > 0)
{
// TODO - fix - does weaponCategories exist?
//killConditionProps.weaponCategories = [allowedWeaponCategory];
// killConditionProps.weaponCategories = [allowedWeaponCategory];
}
return killConditionProps;
@ -568,8 +577,8 @@ export class RepeatableQuestGenerator
}
// Draw items to ask player to retrieve
let isAmmo = 0
const randomNumbersUsed = [];
let isAmmo = 0;
const randomNumbersUsed = [];
for (let i = 0; i < distinctItemsToRetrieveCount; i++)
{
let randomNumber = this.randomUtil.randInt(itemSelection.length);
@ -694,7 +703,8 @@ export class RepeatableQuestGenerator
): IRepeatableQuest
{
const explorationConfig = repeatableConfig.questConfig.Exploration;
const requiresSpecificExtract = Math.random() < repeatableConfig.questConfig.Exploration.specificExits.probability;
const requiresSpecificExtract =
Math.random() < repeatableConfig.questConfig.Exploration.specificExits.probability;
if (Object.keys(questTypePool.pool.Exploration.locations).length === 0)
{
@ -731,7 +741,7 @@ export class RepeatableQuestGenerator
dynamicLocale: true,
target: locationTarget,
};
quest.conditions.AvailableForFinish[0].counter.id = this.objectId.generate();
quest.conditions.AvailableForFinish[0].counter.conditions = [exitStatusCondition, locationCondition];
quest.conditions.AvailableForFinish[0].value = numExtracts;
@ -742,27 +752,30 @@ export class RepeatableQuestGenerator
{
// Filter by whitelist, it's also possible that the field "PassageRequirement" does not exist (e.g. Shoreline)
let mapExits = this.getLocationExitsForSide(locationKey, repeatableConfig.side);
// Exclude scav coop exits when choosing pmc exit
if (repeatableConfig.side === "Pmc")
{
mapExits = mapExits.filter(exit => exit.PassageRequirement !== "ScavCooperation")
mapExits = mapExits.filter((exit) => exit.PassageRequirement !== "ScavCooperation");
}
// Only get exits that have a greater than 0% chance to spawn
const exitPool = mapExits.filter(exit => exit.Chance > 0);
const exitPool = mapExits.filter((exit) => exit.Chance > 0);
// Exclude exits with a requirement to leave (e.g. car extracts)
const possibleExits = exitPool.filter((exit) =>
(!("PassageRequirement" in exit)
|| repeatableConfig.questConfig.Exploration.specificExits.passageRequirementWhitelist.includes(
exit.PassageRequirement
))
const possibleExits = exitPool.filter((
exit,
) => (!("PassageRequirement" in exit)
|| repeatableConfig.questConfig.Exploration.specificExits.passageRequirementWhitelist.includes(
exit.PassageRequirement,
))
);
if (possibleExits.length === 0)
{
this.logger.error(`Unable to choose specific exit on map: ${locationKey}, Possible exit pool was empty`);
this.logger.error(
`Unable to choose specific exit on map: ${locationKey}, Possible exit pool was empty`,
);
}
else
{
@ -792,13 +805,12 @@ export class RepeatableQuestGenerator
protected getLocationExitsForSide(locationKey: string, playerSide: string): Exit[]
{
const mapBase = this.databaseServer.getTables().locations[locationKey.toLowerCase()].base as ILocationBase;
const infilPointsOfSameSide = new Set<string>();
for (const spawnPoint of mapBase.SpawnPointParams)
{
// Same side, add infil to list
if (spawnPoint.Sides.includes(playerSide)
|| spawnPoint.Sides.includes("All"))
if (spawnPoint.Sides.includes(playerSide) || spawnPoint.Sides.includes("All"))
{
// Has specific start location
if (spawnPoint.Infiltration.length > 0)
@ -811,7 +823,9 @@ export class RepeatableQuestGenerator
// use list of allowed infils to figure out side of exits
const infilPointsArray = Array.from(infilPointsOfSameSide);
return mapBase.exits.filter(exit => exit.EntryPoints.split(",").some(entryPoint => infilPointsArray.includes(entryPoint)));
return mapBase.exits.filter((exit) =>
exit.EntryPoints.split(",").some((entryPoint) => infilPointsArray.includes(entryPoint))
);
}
protected generatePickupQuest(
@ -838,16 +852,16 @@ export class RepeatableQuestGenerator
findCondition.target = [itemTypeToFetchWithCount.itemType];
findCondition.value = itemCountToFetch;
const counterCreatorCondition = quest.conditions.AvailableForFinish.find((x) => x.conditionType === "CounterCreator");
const counterCreatorCondition = quest.conditions.AvailableForFinish.find((x) =>
x.conditionType === "CounterCreator"
);
// const locationCondition = counterCreatorCondition._props.counter.conditions.find(x => x._parent === "Location");
// (locationCondition._props as ILocationConditionProps).target = [...locationTarget];
const equipmentCondition = counterCreatorCondition.counter.conditions.find((x) =>
x.conditionType === "Equipment"
);
equipmentCondition.equipmentInclusive = [[
itemTypeToFetchWithCount.itemType,
]];
equipmentCondition.equipmentInclusive = [[itemTypeToFetchWithCount.itemType]];
// Add rewards
quest.rewards = this.generateReward(pmcLevel, 1, traderId, repeatableConfig, pickupConfig);
@ -874,12 +888,7 @@ export class RepeatableQuestGenerator
*/
protected generateExplorationExitCondition(exit: Exit): IQuestConditionCounterCondition
{
return {
conditionType: "ExitName",
exitName: exit.Name,
id: this.objectId.generate(),
dynamicLocale: true,
};
return { conditionType: "ExitName", exitName: exit.Name, id: this.objectId.generate(), dynamicLocale: true };
}
/**
@ -951,11 +960,7 @@ export class RepeatableQuestGenerator
let roublesBudget = rewardRoubles;
let rewardItemPool = this.chooseRewardItemsWithinBudget(repeatableConfig, roublesBudget, traderId);
const rewards: IQuestRewards = {
Started: [],
Success: [],
Fail: [],
};
const rewards: IQuestRewards = { Started: [], Success: [], Fail: [] };
let rewardIndex = 0;
// Add xp reward
@ -970,7 +975,11 @@ export class RepeatableQuestGenerator
{
// convert to equivalent dollars
rewards.Success.push(
this.generateRewardItem(Money.EUROS, this.handbookHelper.fromRUB(rewardRoubles, Money.EUROS), rewardIndex),
this.generateRewardItem(
Money.EUROS,
this.handbookHelper.fromRUB(rewardRoubles, Money.EUROS),
rewardIndex,
),
);
}
else
@ -980,14 +989,19 @@ export class RepeatableQuestGenerator
rewardIndex++;
const traderWhitelistDetails = repeatableConfig.traderWhitelist.find((x) => x.traderId === traderId);
if (traderWhitelistDetails.rewardCanBeWeapon && this.randomUtil.getChance100(traderWhitelistDetails.weaponRewardChancePercent))
if (
traderWhitelistDetails.rewardCanBeWeapon
&& this.randomUtil.getChance100(traderWhitelistDetails.weaponRewardChancePercent)
)
{
// Add a random default preset weapon as reward
const defaultPresets = Object.values(this.presetHelper.getDefaultPresets());
const defaultPreset = this.jsonUtil.clone(this.randomUtil.getArrayValue(defaultPresets));
// use _encyclopedia as its always the base items _tpl, items[0] isnt guaranteed to be base item
rewards.Success.push(this.generateRewardItem(defaultPreset._encyclopedia, 1, rewardIndex, defaultPreset._items));
rewards.Success.push(
this.generateRewardItem(defaultPreset._encyclopedia, 1, rewardIndex, defaultPreset._items),
);
rewardIndex++;
}
@ -1059,7 +1073,8 @@ export class RepeatableQuestGenerator
target: traderId,
value: rewardReputation,
type: QuestRewardType.TRADER_STANDING,
index: rewardIndex };
index: rewardIndex,
};
rewards.Success.push(reward);
rewardIndex++;
}
@ -1164,7 +1179,7 @@ export class RepeatableQuestGenerator
if (preset)
{
const rootItem = preset.find(x => x._tpl === tpl);
const rootItem = preset.find((x) => x._tpl === tpl);
rewardItem.target = rootItem._id; // Target property and root items id must match
rewardItem.items = this.itemHelper.reparentItemAndChildren(rootItem, preset);
}

View File

@ -108,7 +108,7 @@ export class ScavCaseRewardGenerator
{
return false;
}
if (item._props.QuestItem)
{
return false;
@ -289,13 +289,7 @@ export class ScavCaseRewardGenerator
const result: Item[][] = [];
for (const rewardItemDb of rewardItems)
{
let resultItem: Item[] = [
{
_id: this.hashUtil.generate(),
_tpl: rewardItemDb._id,
upd: undefined
}
];
let resultItem: Item[] = [{ _id: this.hashUtil.generate(), _tpl: rewardItemDb._id, upd: undefined }];
const rootItem = resultItem[0];
if (this.itemHelper.isOfBaseclass(rewardItemDb._id, BaseClasses.AMMO_BOX))
@ -303,8 +297,10 @@ export class ScavCaseRewardGenerator
this.itemHelper.addCartridgesToAmmoBox(resultItem, rewardItemDb);
}
// Armor or weapon = use default preset from globals.json
else if (this.itemHelper.armorItemCanHoldMods(rewardItemDb._id)
|| this.itemHelper.isOfBaseclass(rewardItemDb._id, BaseClasses.WEAPON))
else if (
this.itemHelper.armorItemCanHoldMods(rewardItemDb._id)
|| this.itemHelper.isOfBaseclass(rewardItemDb._id, BaseClasses.WEAPON)
)
{
const preset = this.presetHelper.getDefaultPreset(rewardItemDb._id);
if (!preset)
@ -313,12 +309,9 @@ export class ScavCaseRewardGenerator
continue;
}
// Ensure preset has unique ids and is cloned so we don't alter the preset data stored in memory
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
null,
this.jsonUtil.clone(preset._items),
);
const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(preset._items));
this.itemHelper.remapRootItemId(presetAndMods);
resultItem = presetAndMods;
@ -334,7 +327,7 @@ export class ScavCaseRewardGenerator
delete rootItem.upd;
}
result.push(resultItem)
result.push(resultItem);
}
return result;

View File

@ -19,7 +19,7 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
@inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("BotWeaponGeneratorHelper") protected botWeaponGeneratorHelper: BotWeaponGeneratorHelper,
@inject("RandomUtil") protected randomUtil: RandomUtil
@inject("RandomUtil") protected randomUtil: RandomUtil,
)
{}
@ -75,7 +75,9 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
// Prevent infinite loop by only allowing 5 attempts at fitting a magazine into inventory
if (fitAttempts > 5)
{
this.logger.debug(`Failed ${fitAttempts} times to add magazine ${magazineTpl} to bot inventory, stopping`);
this.logger.debug(
`Failed ${fitAttempts} times to add magazine ${magazineTpl} to bot inventory, stopping`,
);
break;
}
@ -108,10 +110,15 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
// Edge case - some weapons (SKS) have an internal magazine as default, choose random non-internal magazine to add to bot instead
if (magTemplate._props.ReloadMagType === "InternalMagazine")
{
const result = this.getRandomExternalMagazineForInternalMagazineGun(inventoryMagGen.getWeaponTemplate()._id, attemptedMagBlacklist);
const result = this.getRandomExternalMagazineForInternalMagazineGun(
inventoryMagGen.getWeaponTemplate()._id,
attemptedMagBlacklist,
);
if (!result?._id)
{
this.logger.debug(`Unable to add additional magazine into bot inventory for weapon: ${weapon._name}, attempted: ${fitAttempts} times`);
this.logger.debug(
`Unable to add additional magazine into bot inventory for weapon: ${weapon._name}, attempted: ${fitAttempts} times`,
);
break;
}
@ -138,24 +145,29 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
* @param weaponTpl Weapon to get mag for
* @returns tpl of magazine
*/
protected getRandomExternalMagazineForInternalMagazineGun(weaponTpl: string, magazineBlacklist: string[]): ITemplateItem
protected getRandomExternalMagazineForInternalMagazineGun(
weaponTpl: string,
magazineBlacklist: string[],
): ITemplateItem
{
// The mag Slot data for the weapon
const magSlot = this.itemHelper.getItem(weaponTpl)[1]._props.Slots.find(x => x._name === "mod_magazine");
const magSlot = this.itemHelper.getItem(weaponTpl)[1]._props.Slots.find((x) => x._name === "mod_magazine");
if (!magSlot)
{
return null;
}
// All possible mags that fit into the weapon excluding blacklisted
const magazinePool = magSlot._props.filters[0].Filter.filter(x => !magazineBlacklist.includes(x)).map((x) => this.itemHelper.getItem(x)[1]);
const magazinePool = magSlot._props.filters[0].Filter.filter((x) => !magazineBlacklist.includes(x)).map((x) =>
this.itemHelper.getItem(x)[1]
);
if (!magazinePool)
{
return null;
}
// Non-internal magazines that fit into the weapon
const externalMagazineOnlyPool = magazinePool.filter(x => x._props.ReloadMagType !== "InternalMagazine");
const externalMagazineOnlyPool = magazinePool.filter((x) => x._props.ReloadMagType !== "InternalMagazine");
if (!externalMagazineOnlyPool || externalMagazineOnlyPool?.length === 0)
{
return null;

View File

@ -296,11 +296,7 @@ export class BotGeneratorHelper
}),
);
return {
incompatible: true,
found: false,
reason: `item: ${tplToCheck} does not exist in the database`
};
return { incompatible: true, found: false, reason: `item: ${tplToCheck} does not exist in the database` };
}
// No props property
@ -314,11 +310,7 @@ export class BotGeneratorHelper
}),
);
return {
incompatible: true,
found: false,
reason: `item: ${tplToCheck} does not have a _props field`
};
return { incompatible: true, found: false, reason: `item: ${tplToCheck} does not have a _props field` };
}
// Check if any of the current weapon mod templates have the incoming item defined as incompatible
@ -346,10 +338,7 @@ export class BotGeneratorHelper
};
}
return {
incompatible: false,
reason: ""
};
return { incompatible: false, reason: "" };
}
/**
@ -385,7 +374,7 @@ export class BotGeneratorHelper
}),
);
return { incompatible: true, found: false, reason: `item: ${tplToCheck} does not exist in the database` };
return { incompatible: true, found: false, reason: `item: ${tplToCheck} does not exist in the database` };
}
if (!itemToEquip._props)
@ -398,11 +387,7 @@ export class BotGeneratorHelper
}),
);
return {
incompatible: true,
found: false,
reason: `item: ${tplToCheck} does not have a _props field`
};
return { incompatible: true, found: false, reason: `item: ${tplToCheck} does not have a _props field` };
}
// Does an equipped item have a property that blocks the desired item - check for prop "BlocksX" .e.g BlocksEarpiece / BlocksFaceCover
@ -436,7 +421,7 @@ export class BotGeneratorHelper
// Does item being checked get blocked/block existing item
if (itemToEquip._props.BlocksHeadwear)
{
const existingHeadwear = itemsEquipped.find(x => x.slotId === "Headwear");
const existingHeadwear = itemsEquipped.find((x) => x.slotId === "Headwear");
if (existingHeadwear)
{
return {
@ -448,11 +433,11 @@ export class BotGeneratorHelper
};
}
}
// Does item being checked get blocked/block existing item
if (itemToEquip._props.BlocksFaceCover)
{
const existingFaceCover = itemsEquipped.find(item => item.slotId === "FaceCover");
const existingFaceCover = itemsEquipped.find((item) => item.slotId === "FaceCover");
if (existingFaceCover)
{
return {
@ -468,7 +453,7 @@ export class BotGeneratorHelper
// Does item being checked get blocked/block existing item
if (itemToEquip._props.BlocksEarpiece)
{
const existingEarpiece = itemsEquipped.find(item => item.slotId === "Earpiece");
const existingEarpiece = itemsEquipped.find((item) => item.slotId === "Earpiece");
if (existingEarpiece)
{
return {
@ -484,7 +469,7 @@ export class BotGeneratorHelper
// Does item being checked get blocked/block existing item
if (itemToEquip._props.BlocksArmorVest)
{
const existingArmorVest = itemsEquipped.find(item => item.slotId === "ArmorVest");
const existingArmorVest = itemsEquipped.find((item) => item.slotId === "ArmorVest");
if (existingArmorVest)
{
return {

View File

@ -186,12 +186,12 @@ export class BotWeaponGeneratorHelper
{
// Bot doesnt have any containers we want to add item to
this.logger.debug(
`Unable to add item: ${
itemWithChildren[0]._tpl
} to bot as it lacks the following containers: ${equipmentSlots.join(",")}`,
`Unable to add item: ${itemWithChildren[0]._tpl} to bot as it lacks the following containers: ${
equipmentSlots.join(",")
}`,
);
return ItemAddedResult.NO_CONTAINERS
return ItemAddedResult.NO_CONTAINERS;
}
// No container of desired type found, skip to next container type

View File

@ -105,9 +105,8 @@ export class GiveSptCommand implements ISptCommand
{
// Make sure IDs are unique before adding to array - prevent collisions
const presetToSend = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(preset._items));
itemsToSend.push(... presetToSend);
itemsToSend.push(...presetToSend);
}
}
else if (this.itemHelper.isOfBaseclass(checkedItem[1]._id, BaseClasses.AMMO_BOX))
{
@ -124,10 +123,7 @@ export class GiveSptCommand implements ISptCommand
const item: Item = {
_id: this.hashUtil.generate(),
_tpl: checkedItem[1]._id,
upd: {
StackObjectsCount: +quantity,
SpawnedInSession: true
},
upd: { StackObjectsCount: +quantity, SpawnedInSession: true },
};
itemsToSend.push(...this.itemHelper.splitStack(item));
}

View File

@ -22,7 +22,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
@inject("RandomUtil") protected randomUtil: RandomUtil,
@inject("MailSendService") protected mailSendService: MailSendService,
@inject("GiftService") protected giftService: GiftService,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
@ -64,7 +64,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
"Hey! you got the right code!",
"A secret code, how exciting!",
"You found a gift code!",
])
]),
);
return;
@ -75,7 +75,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
this.mailSendService.sendUserMessageToPlayer(
sessionId,
sptFriendUser,
this.randomUtil.getArrayValue(["Looks like you already used that code", "You already have that!!"])
this.randomUtil.getArrayValue(["Looks like you already used that code", "You already have that!!"]),
);
return;
@ -91,7 +91,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
"I love you too buddy :3!",
"uwu",
`love you too ${sender?.Info?.Nickname}`,
])
]),
);
}
@ -100,7 +100,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
this.mailSendService.sendUserMessageToPlayer(
sessionId,
sptFriendUser,
this.randomUtil.getArrayValue(["Its me!!", "spt? i've heard of that project"])
this.randomUtil.getArrayValue(["Its me!!", "spt? i've heard of that project"]),
);
}
@ -120,7 +120,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
"Heyyyyy",
"Hey there",
`Hello ${sender?.Info?.Nickname}`,
])
]),
);
}
@ -134,7 +134,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
"Cool guy, he made EFT!",
"Legend",
"Remember when he said webel-webel-webel-webel, classic nikita moment",
])
]),
);
}
@ -143,7 +143,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
this.mailSendService.sendUserMessageToPlayer(
sessionId,
sptFriendUser,
this.randomUtil.getArrayValue(["beep boop", "**sad boop**", "probably", "sometimes", "yeah lol"])
this.randomUtil.getArrayValue(["beep boop", "**sad boop**", "probably", "sometimes", "yeah lol"]),
);
}

View File

@ -4,10 +4,7 @@ import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
import { NotificationSendHelper } from "@spt-aki/helpers/NotificationSendHelper";
import { NotifierHelper } from "@spt-aki/helpers/NotifierHelper";
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import {
Dialogue,
MessagePreview,
} from "@spt-aki/models/eft/profile/IAkiProfile";
import { Dialogue, MessagePreview } from "@spt-aki/models/eft/profile/IAkiProfile";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { SaveServer } from "@spt-aki/servers/SaveServer";

View File

@ -37,7 +37,7 @@ export class HandbookHelper
constructor(
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("JsonUtil") protected jsonUtil: JsonUtil
@inject("JsonUtil") protected jsonUtil: JsonUtil,
)
{}
@ -168,6 +168,6 @@ export class HandbookHelper
public getCategoryById(handbookId: string): Category
{
return this.databaseServer.getTables().templates.handbook.Categories.find(x => x.Id === handbookId);
return this.databaseServer.getTables().templates.handbook.Categories.find((x) => x.Id === handbookId);
}
}

View File

@ -420,11 +420,11 @@ export class HideoutHelper
// 10-10-2021 From wiki, 1 resource last 12 minutes 38 seconds, 1/12.63333/60 = 0.00131
let fuelDrainRate = this.databaseServer.getTables().hideout.settings.generatorFuelFlowRate
* this.getTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn);
const fuelBonus = pmcData.Bonuses.find((bonus) => bonus.type === BonusType.FUEL_CONSUMPTION);
const fuelBonusPercent = 1.0 - (fuelBonus ? Math.abs(fuelBonus.value) : 0) / 100;
fuelDrainRate *= fuelBonusPercent;
// Hideout management resource consumption bonus:
const hideoutManagementConsumptionBonus = 1.0 - this.getHideoutManagementConsumptionBonus(pmcData);
fuelDrainRate *= hideoutManagementConsumptionBonus;
@ -452,13 +452,13 @@ export class HideoutHelper
// No fuel left, skip
continue;
}
// Undefined fuel, fresh fuel item and needs its max fuel amount looked up
if (!fuelRemaining)
{
const fuelItemTemplate = this.itemHelper.getItem(fuelItemInSlot._tpl)[1];
pointsConsumed = fuelDrainRate;
fuelRemaining = fuelItemTemplate._props.MaxResource - fuelDrainRate;
fuelRemaining = fuelItemTemplate._props.MaxResource - fuelDrainRate;
}
else
{
@ -481,7 +481,9 @@ export class HideoutHelper
{
fuelItemInSlot.upd = this.getAreaUpdObject(1, fuelRemaining, pointsConsumed);
this.logger.debug(`$Profile: ${pmcData._id} Generator has: ${fuelRemaining} fuel left in slot ${i + 1}`);
this.logger.debug(
`$Profile: ${pmcData._id} Generator has: ${fuelRemaining} fuel left in slot ${i + 1}`,
);
hasFuelRemaining = true;
break; // Break here to avoid updating all the fuel tanks
@ -955,7 +957,7 @@ export class HideoutHelper
pmcData: IPmcData,
request: IHideoutTakeProductionRequestData,
sessionId: string,
output: IItemEventRouterResponse
output: IItemEventRouterResponse,
): void
{
// Get how many coins were crafted and ready to pick up
@ -973,15 +975,11 @@ export class HideoutHelper
const itemsToAdd: Item[][] = [];
for (let index = 0; index < craftedCoinCount; index++)
{
itemsToAdd.push(
[{
_id: this.hashUtil.generate(),
_tpl: HideoutHelper.bitcoinTpl,
upd: {
StackObjectsCount: 1
}
}]
);
itemsToAdd.push([{
_id: this.hashUtil.generate(),
_tpl: HideoutHelper.bitcoinTpl,
upd: { StackObjectsCount: 1 },
}]);
}
// Create request for what we want to add to stash
@ -989,7 +987,7 @@ export class HideoutHelper
itemsWithModsToAdd: itemsToAdd,
foundInRaid: true,
useSortingTable: false,
callback: null
callback: null,
};
// Add FiR coins to player inventory
@ -1070,24 +1068,29 @@ export class HideoutHelper
*/
public applyPlaceOfFameDogtagBonus(pmcData: IPmcData): void
{
const fameAreaProfile = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.PLACE_OF_FAME);
const fameAreaProfile = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.PLACE_OF_FAME);
// Get hideout area 16 bonus array
const fameAreaDb = this.databaseServer.getTables().hideout.areas.find((area) => area.type === HideoutAreas.PLACE_OF_FAME);
const fameAreaDb = this.databaseServer.getTables().hideout.areas.find((area) =>
area.type === HideoutAreas.PLACE_OF_FAME
);
// Get SkillGroupLevelingBoost object
const combatBoostBonusDb = fameAreaDb.stages[fameAreaProfile.level].bonuses.find(bonus => bonus.type === "SkillGroupLevelingBoost");
const combatBoostBonusDb = fameAreaDb.stages[fameAreaProfile.level].bonuses.find((bonus) =>
bonus.type === "SkillGroupLevelingBoost"
);
// Get SkillGroupLevelingBoost object in profile
const combatBonusProfile = pmcData.Bonuses.find(bonus => bonus.id === combatBoostBonusDb.id);
const combatBonusProfile = pmcData.Bonuses.find((bonus) => bonus.id === combatBoostBonusDb.id);
// Get all slotted dogtag items
const activeDogtags = pmcData.Inventory.items.filter(item => item?.slotId?.startsWith("dogtag"));
const activeDogtags = pmcData.Inventory.items.filter((item) => item?.slotId?.startsWith("dogtag"));
// Calculate bonus percent (apply hideoutManagement bonus)
const hideoutManagementSkill = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.HIDEOUT_MANAGEMENT);
const hideoutManagementSkillBonusPercent = 1 + (hideoutManagementSkill.Progress / 10000); // 5100 becomes 0.51, add 1 to it, 1.51
const bonus = this.getDogtagCombatSkillBonusPercent(pmcData, activeDogtags) * hideoutManagementSkillBonusPercent;
const bonus = this.getDogtagCombatSkillBonusPercent(pmcData, activeDogtags)
* hideoutManagementSkillBonusPercent;
// Update bonus value to above calcualted value
combatBonusProfile.value = Number.parseFloat(bonus.toFixed(2));
@ -1112,8 +1115,7 @@ export class HideoutHelper
continue;
}
if (Number.parseInt(dogtag.upd.Dogtag?.AccountId) === pmcData.aid
)
if (Number.parseInt(dogtag.upd.Dogtag?.AccountId) === pmcData.aid)
{
continue;
}

View File

@ -45,7 +45,7 @@ export class InRaidHelper
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ProfileFixerService") protected profileFixerService: ProfileFixerService,
@inject("ConfigServer") protected configServer: ConfigServer,
@inject("RandomUtil") protected randomUtil: RandomUtil
@inject("RandomUtil") protected randomUtil: RandomUtil,
)
{
this.lostOnDeathConfig = this.configServer.getConfig(ConfigTypes.LOST_ON_DEATH);
@ -68,7 +68,7 @@ export class InRaidHelper
*/
public addUpdToMoneyFromRaid(items: Item[]): void
{
for (const item of items.filter(x => this.paymentHelper.isMoneyTpl(x._tpl)))
for (const item of items.filter((x) => this.paymentHelper.isMoneyTpl(x._tpl)))
{
if (!item.upd)
{
@ -129,9 +129,9 @@ export class InRaidHelper
let pmcStandingForKill = botTypes[victim.Side.toLowerCase()]?.experience?.standingForKill;
const pmcKillProbabilityForScavGain = this.inRaidConfig.pmcKillProbabilityForScavGain;
if(this.randomUtil.rollForChanceProbability(pmcKillProbabilityForScavGain))
if (this.randomUtil.rollForChanceProbability(pmcKillProbabilityForScavGain))
{
pmcStandingForKill += this.inRaidConfig.scavExtractGain
pmcStandingForKill += this.inRaidConfig.scavExtractGain;
}
return pmcStandingForKill;
@ -151,7 +151,7 @@ export class InRaidHelper
saveProgressRequest: ISaveProgressRequestData,
sessionID: string,
): void
{
{
// Remove skill fatigue values
this.resetSkillPointsEarnedDuringRaid(saveProgressRequest.profile);
@ -214,19 +214,23 @@ export class InRaidHelper
if (matchingPreRaidCounter.value !== postRaidValue)
{
this.logger.error(
`TaskConditionCounters: ${backendCounterKey} value is different post raid, old: ${matchingPreRaidCounter.value} new: ${postRaidValue}`
`TaskConditionCounters: ${backendCounterKey} value is different post raid, old: ${matchingPreRaidCounter.value} new: ${postRaidValue}`,
);
}
}
}
/**
* Update various serverPMC profile values; quests/limb hp/trader standing with values post-raic
* Update various serverPMC profile values; quests/limb hp/trader standing with values post-raic
* @param pmcData Server PMC profile
* @param saveProgressRequest Post-raid request data
* @param sessionId Session id
*/
public updatePmcProfileDataPostRaid(pmcData: IPmcData, saveProgressRequest: ISaveProgressRequestData, sessionId: string): void
public updatePmcProfileDataPostRaid(
pmcData: IPmcData,
saveProgressRequest: ISaveProgressRequestData,
sessionId: string,
): void
{
// Process failed quests then copy everything
this.processAlteredQuests(sessionId, pmcData, pmcData.Quests, saveProgressRequest.profile);
@ -250,13 +254,19 @@ export class InRaidHelper
* @param saveProgressRequest Post-raid request data
* @param sessionId Session id
*/
public updateScavProfileDataPostRaid(scavData: IPmcData, saveProgressRequest: ISaveProgressRequestData, sessionId: string): void
public updateScavProfileDataPostRaid(
scavData: IPmcData,
saveProgressRequest: ISaveProgressRequestData,
sessionId: string,
): void
{
// Only copy active quests into scav profile // Progress will later to copied over to PMC profile
const existingActiveQuestIds = scavData.Quests?.filter(x => x.status !== QuestStatus.AvailableForStart).map(x => x.qid);
const existingActiveQuestIds = scavData.Quests?.filter((x) => x.status !== QuestStatus.AvailableForStart).map((
x,
) => x.qid);
if (existingActiveQuestIds)
{
scavData.Quests = saveProgressRequest.profile.Quests.filter(x => existingActiveQuestIds.includes(x.qid));
scavData.Quests = saveProgressRequest.profile.Quests.filter((x) => existingActiveQuestIds.includes(x.qid));
}
this.profileFixerService.checkForAndFixScavProfileIssues(scavData);
@ -305,7 +315,7 @@ export class InRaidHelper
}
// Already completed/failed before raid, skip
if ([QuestStatus.Fail, QuestStatus.Success].includes(preRaidQuest.status) )
if ([QuestStatus.Fail, QuestStatus.Success].includes(preRaidQuest.status))
{
// Daily quests get their status altered in-raid to "AvailableForStart",
// Copy pre-raid status to post raid data
@ -323,7 +333,9 @@ export class InRaidHelper
}
// Quest with time-gate has unlocked
if (postRaidQuestStatus === "AvailableAfter" && postRaidQuest.availableAfter <= this.timeUtil.getTimestamp())
if (
postRaidQuestStatus === "AvailableAfter" && postRaidQuest.availableAfter <= this.timeUtil.getTimestamp()
)
{
// Flag as ready to complete
postRaidQuest.status = QuestStatus.AvailableForStart;
@ -351,7 +363,9 @@ export class InRaidHelper
// Does failed quest have requirement to collect items from raid
const questDbData = this.questHelper.getQuestFromDb(postRaidQuest.qid, pmcData);
// AvailableForFinish
const matchingAffFindConditions = questDbData.conditions.AvailableForFinish.filter(condition => condition.conditionType === "FindItem");
const matchingAffFindConditions = questDbData.conditions.AvailableForFinish.filter((condition) =>
condition.conditionType === "FindItem"
);
const itemsToCollect: string[] = [];
if (matchingAffFindConditions)
{
@ -364,21 +378,25 @@ export class InRaidHelper
// Remove quest items from profile as quest has failed and may still be alive
// Required as restarting the quest from main menu does not remove value from CarriedQuestItems array
postRaidProfile.Stats.Eft.CarriedQuestItems = postRaidProfile.Stats.Eft.CarriedQuestItems.filter(carriedQuestItem => !itemsToCollect.includes(carriedQuestItem))
postRaidProfile.Stats.Eft.CarriedQuestItems = postRaidProfile.Stats.Eft.CarriedQuestItems.filter((
carriedQuestItem,
) => !itemsToCollect.includes(carriedQuestItem));
// Remove quest item from profile now quest is failed
// updateProfileBaseStats() has already passed by ref EFT.Stats, all changes applied to postRaid profile also apply to server profile
for (const itemTpl of itemsToCollect)
{
// Look for sessioncounter and remove it
const counterIndex = postRaidProfile.Stats.Eft.SessionCounters.Items.findIndex(x => x.Key.includes(itemTpl) && x.Key.includes("LootItem"));
const counterIndex = postRaidProfile.Stats.Eft.SessionCounters.Items.findIndex((x) =>
x.Key.includes(itemTpl) && x.Key.includes("LootItem")
);
if (counterIndex > -1)
{
postRaidProfile.Stats.Eft.SessionCounters.Items.splice(counterIndex, 1);
}
// Look for quest item and remove it
const inventoryItemIndex = postRaidProfile.Inventory.items.findIndex(x => x._tpl === itemTpl);
const inventoryItemIndex = postRaidProfile.Inventory.items.findIndex((x) => x._tpl === itemTpl);
if (inventoryItemIndex > -1)
{
postRaidProfile.Inventory.items.splice(inventoryItemIndex, 1);
@ -399,13 +417,15 @@ export class InRaidHelper
const dbQuest = this.questHelper.getQuestFromDb(lockedQuest.qid, null);
if (!dbQuest)
{
this.logger.warning(`Unable to adjust locked quest: ${lockedQuest.qid} as it wasnt found in db. It may not become available later on`);
this.logger.warning(
`Unable to adjust locked quest: ${lockedQuest.qid} as it wasnt found in db. It may not become available later on`,
);
continue;
}
// Find the time requirement in AvailableForStart array (assuming there is one as quest in locked state === its time-gated)
const afsRequirement = dbQuest.conditions.AvailableForStart.find(x => x.conditionType === "Quest");
const afsRequirement = dbQuest.conditions.AvailableForStart.find((x) => x.conditionType === "Quest");
if (afsRequirement && afsRequirement.availableAfter > 0)
{
// Prereq quest has a wait

View File

@ -75,16 +75,18 @@ export class InventoryHelper
* @param pmcData Player profile
* @param output Client response object
*/
public addItemsToStash(sessionId: string, request: IAddItemsDirectRequest, pmcData: IPmcData, output: IItemEventRouterResponse): void
public addItemsToStash(
sessionId: string,
request: IAddItemsDirectRequest,
pmcData: IPmcData,
output: IItemEventRouterResponse,
): void
{
// Check all items fit into inventory before adding
if (!this.canPlaceItemsInInventory(sessionId, request.itemsWithModsToAdd))
{
// No space, exit
this.httpResponse.appendErrorToOutput(
output,
this.localisationService.getText("inventory-no_stash_space"),
)
this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("inventory-no_stash_space"));
return;
}
@ -95,7 +97,7 @@ export class InventoryHelper
itemWithModsToAdd: itemToAdd,
foundInRaid: request.foundInRaid,
useSortingTable: request.useSortingTable,
callback: request.callback
callback: request.callback,
};
// Add to player inventory
@ -114,7 +116,12 @@ export class InventoryHelper
* @param pmcData Player profile
* @param output Client response object
*/
public addItemToStash(sessionId: string, request: IAddItemDirectRequest, pmcData: IPmcData, output: IItemEventRouterResponse): void
public addItemToStash(
sessionId: string,
request: IAddItemDirectRequest,
pmcData: IPmcData,
output: IItemEventRouterResponse,
): void
{
const itemWithModsToAddClone = this.jsonUtil.clone(request.itemWithModsToAdd);
@ -167,7 +174,11 @@ export class InventoryHelper
output.profileChanges[sessionId].items.new.push(...itemWithModsToAddClone);
pmcData.Inventory.items.push(...itemWithModsToAddClone);
this.logger.debug(`Added ${itemWithModsToAddClone[0].upd?.StackObjectsCount ?? 1} item: ${itemWithModsToAddClone[0]._tpl} with: ${itemWithModsToAddClone.length - 1} mods to inventory`);
this.logger.debug(
`Added ${itemWithModsToAddClone[0].upd?.StackObjectsCount ?? 1} item: ${
itemWithModsToAddClone[0]._tpl
} with: ${itemWithModsToAddClone.length - 1} mods to inventory`,
);
}
/**
@ -189,7 +200,6 @@ export class InventoryHelper
{
item.upd.SpawnedInSession = foundInRaid;
}
else
{
if (delete item.upd.SpawnedInSession)
@ -222,10 +232,7 @@ export class InventoryHelper
}
}
public canPlaceItemsInInventory(
sessionId: string,
itemsWithChildren: Item[][]
): boolean
public canPlaceItemsInInventory(sessionId: string, itemsWithChildren: Item[][]): boolean
{
const pmcData = this.profileHelper.getPmcProfile(sessionId);
@ -241,10 +248,7 @@ export class InventoryHelper
return true;
}
public canPlaceItemInInventory(
stashFS2D: number[][],
itemWithChildren: Item[]
): boolean
public canPlaceItemInInventory(stashFS2D: number[][], itemWithChildren: Item[]): boolean
{
// Get x/y size of item
const rootItem = itemWithChildren[0];
@ -288,7 +292,8 @@ export class InventoryHelper
itemWithChildren: Item[],
playerInventory: Inventory,
useSortingTable: boolean,
output: IItemEventRouterResponse): void
output: IItemEventRouterResponse,
): void
{
// Get x/y size of item
const rootItem = itemWithChildren[0];
@ -317,9 +322,7 @@ export class InventoryHelper
}
catch (err)
{
const errorText = (typeof err === "string")
? ` -> ${err}`
: "";
const errorText = (typeof err === "string") ? ` -> ${err}` : "";
this.logger.error(this.localisationService.getText("inventory-fill_container_failed", errorText));
this.httpResponse.appendErrorToOutput(
@ -388,12 +391,9 @@ export class InventoryHelper
}
else
{
this.httpResponse.appendErrorToOutput(
output,
this.localisationService.getText("inventory-no_stash_space"),
);
this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("inventory-no_stash_space"));
return
return;
}
}
@ -896,7 +896,7 @@ export class InventoryHelper
{
continue;
}
if (childFoldable && rootFolded && childFolded)
{
continue;

View File

@ -107,7 +107,7 @@ export class ItemHelper
*/
public armorItemCanHoldMods(itemTpl: string): boolean
{
return this.isOfBaseclasses(itemTpl, [BaseClasses.HEADWEAR, BaseClasses.VEST, BaseClasses.ARMOR]);
return this.isOfBaseclasses(itemTpl, [BaseClasses.HEADWEAR, BaseClasses.VEST, BaseClasses.ARMOR]);
}
/**
@ -137,15 +137,24 @@ export class ItemHelper
}
// Check if item has slots that match soft insert name ids
const softInsertSlotIds = ["groin", "soft_armor_back", "soft_armor_front", "soft_armor_left", "soft_armor_right", "shoulder_l", "shoulder_r", "collar"];
if (itemDbDetails[1]._props.Slots.find(slot => softInsertSlotIds.includes(slot._name.toLowerCase())))
const softInsertSlotIds = [
"groin",
"soft_armor_back",
"soft_armor_front",
"soft_armor_left",
"soft_armor_right",
"shoulder_l",
"shoulder_r",
"collar",
];
if (itemDbDetails[1]._props.Slots.find((slot) => softInsertSlotIds.includes(slot._name.toLowerCase())))
{
return true;
}
// Also classified as BUILT_IN_INSERTS
const helmetInsertSlotIds = ["helmet_top", "helmet_back", "helmet_ears"]
if (itemDbDetails[1]._props.Slots.find(slot => helmetInsertSlotIds.includes(slot._name.toLowerCase())))
const helmetInsertSlotIds = ["helmet_top", "helmet_back", "helmet_ears"];
if (itemDbDetails[1]._props.Slots.find((slot) => helmetInsertSlotIds.includes(slot._name.toLowerCase())))
{
return true;
}
@ -446,8 +455,7 @@ export class ItemHelper
for (const itemFromAssort of assort)
{
if (itemFromAssort.parentId === itemIdToFind
&& !list.find((item) => itemFromAssort._id === item._id))
if (itemFromAssort.parentId === itemIdToFind && !list.find((item) => itemFromAssort._id === item._id))
{
list.push(itemFromAssort);
list = list.concat(this.findAndReturnChildrenByAssort(itemFromAssort._id, assort));
@ -555,7 +563,7 @@ export class ItemHelper
/**
* Turn items like money into separate stacks that adhere to max stack size
* @param itemToSplit Item to split into smaller stacks
* @returns
* @returns
*/
public splitStackIntoSeparateItems(itemToSplit: Item): Item[][]
{
@ -604,9 +612,7 @@ export class ItemHelper
{
const filterResult = itemsToSearch.filter((item) =>
{
return by === "tpl"
? (item._tpl === barterId)
: (item._id === barterId);
return by === "tpl" ? (item._tpl === barterId) : (item._id === barterId);
});
matchingItems.push(...filterResult);
@ -978,7 +984,15 @@ export class ItemHelper
const cartridgeCountToAdd = (remainingSpace < maxPerStack) ? remainingSpace : maxPerStack;
// Add cartridge item into items array
ammoBox.push(this.createCartridges(ammoBox[0]._id, cartridgeTpl, cartridgeCountToAdd, location, ammoBox[0].upd?.SpawnedInSession));
ammoBox.push(
this.createCartridges(
ammoBox[0]._id,
cartridgeTpl,
cartridgeCountToAdd,
location,
ammoBox[0].upd?.SpawnedInSession,
),
);
currentStoredCartridgeCount += cartridgeCountToAdd;
location++;
@ -1068,7 +1082,9 @@ export class ItemHelper
if (!magazineCartridgeMaxCount)
{
this.logger.warning(`Magazine: ${magTemplate._id} ${magTemplate._name} lacks a Cartridges array, unable to fill magazine with ammo`);
this.logger.warning(
`Magazine: ${magTemplate._id} ${magTemplate._name} lacks a Cartridges array, unable to fill magazine with ammo`,
);
return;
}
@ -1104,10 +1120,11 @@ export class ItemHelper
magazineWithChildCartridges.push(
this.createCartridges(
magazineWithChildCartridges[0]._id,
cartridgeTpl, cartridgeCountToAdd,
cartridgeTpl,
cartridgeCountToAdd,
location,
magazineWithChildCartridges[0].upd?.SpawnedInSession
)
magazineWithChildCartridges[0].upd?.SpawnedInSession,
),
);
currentStoredCartridgeCount += cartridgeCountToAdd;
@ -1170,7 +1187,13 @@ export class ItemHelper
* @param foundInRaid OPTIONAL - Are cartridges found in raid (SpawnedInSession)
* @returns Item
*/
public createCartridges(parentId: string, ammoTpl: string, stackCount: number, location: number, foundInRaid = false): Item
public createCartridges(
parentId: string,
ammoTpl: string,
stackCount: number,
location: number,
foundInRaid = false,
): Item
{
return {
_id: this.objectId.generate(),
@ -1178,10 +1201,7 @@ export class ItemHelper
parentId: parentId,
slotId: "cartridges",
location: location,
upd: {
StackObjectsCount: stackCount,
SpawnedInSession: foundInRaid
},
upd: { StackObjectsCount: stackCount, SpawnedInSession: foundInRaid },
};
}
@ -1225,7 +1245,12 @@ export class ItemHelper
* @param requiredOnly Only add required mods
* @returns Item with children
*/
public addChildSlotItems(itemToAdd: Item[], itemToAddTemplate: ITemplateItem, modSpawnChanceDict: Record<string, number> = null, requiredOnly = false): Item[]
public addChildSlotItems(
itemToAdd: Item[],
itemToAddTemplate: ITemplateItem,
modSpawnChanceDict: Record<string, number> = null,
requiredOnly = false,
): Item[]
{
const result = itemToAdd;
const incompatibleModTpls: Set<string> = new Set();
@ -1251,11 +1276,13 @@ export class ItemHelper
}
}
const itemPool = slot._props.filters[0].Filter ?? [];
const itemPool = slot._props.filters[0].Filter ?? [];
const chosenTpl = this.getCompatibleTplFromArray(itemPool, incompatibleModTpls);
if (!chosenTpl)
{
this.logger.debug(`Unable to add mod to item: ${itemToAddTemplate._id} ${itemToAddTemplate._name} slot: ${slot._name} as no compatible tpl could be found in pool of ${itemPool.length}, skipping`);
this.logger.debug(
`Unable to add mod to item: ${itemToAddTemplate._id} ${itemToAddTemplate._name} slot: ${slot._name} as no compatible tpl could be found in pool of ${itemPool.length}, skipping`,
);
continue;
}
@ -1264,7 +1291,7 @@ export class ItemHelper
_id: this.hashUtil.generate(),
_tpl: chosenTpl,
parentId: result[0]._id,
slotId: slot._name
slotId: slot._name,
};
result.push(modItemToAdd);
@ -1300,7 +1327,7 @@ export class ItemHelper
if (incompatibleModTpls.has(tpl))
{
// Incompatible tpl was chosen, try again
count++
count++;
if (count >= possibleTpls.length)
{
return null;

View File

@ -285,8 +285,8 @@ export class QuestHelper
item.upd.SpawnedInSession = true;
// Separate base item from mods, fix stacks
if (item._id === questReward.target) // Is base reward item
{
if (item._id === questReward.target)
{ // Is base reward item
if (
(item.parentId !== undefined) && (item.parentId === "hideout") // Has parentId of hideout
&& (item.upd !== undefined) && (item.upd.StackObjectsCount !== undefined) // Has upd with stackobject count
@ -350,12 +350,14 @@ export class QuestHelper
questReward.items = this.jsonUtil.clone(defaultPreset._items);
// Remap target id to the new presets id
questReward.target = questReward.items.find(item => item._tpl === originalRewardRootItem._tpl)._id;
questReward.target = questReward.items.find((item) => item._tpl === originalRewardRootItem._tpl)._id;
return;
}
this.logger.warning(`Unable to find default preset for armor ${originalRewardRootItem._tpl}, adding mods manually`);
this.logger.warning(
`Unable to find default preset for armor ${originalRewardRootItem._tpl}, adding mods manually`,
);
const itemDbData = this.itemHelper.getItem(originalRewardRootItem._tpl)[1];
// Hydrate reward with only 'required' mods - necessary for things like helmets otherwise you end up with nvgs/visors etc
@ -372,9 +374,7 @@ export class QuestHelper
{
// Iterate over all rewards with the desired status, flatten out items that have a type of Item
const questRewards = quest.rewards[QuestStatus[status]].flatMap((reward: IQuestReward) =>
reward.type === "Item"
? this.processReward(reward)
: []
reward.type === "Item" ? this.processReward(reward) : []
);
return questRewards;
@ -654,7 +654,9 @@ export class QuestHelper
public getQuestWithOnlyLevelRequirementStartCondition(quest: IQuest): IQuest
{
quest = this.jsonUtil.clone(quest);
quest.conditions.AvailableForStart = quest.conditions.AvailableForStart.filter((q) => q.conditionType === "Level");
quest.conditions.AvailableForStart = quest.conditions.AvailableForStart.filter((q) =>
q.conditionType === "Level"
);
return quest;
}
@ -686,7 +688,7 @@ export class QuestHelper
// Create a dialog message for completing the quest.
const quest = this.getQuestFromDb(failRequest.qid, pmcData);
const questIsRepeatable = pmcData.RepeatableQuests.some(quest => quest.id === failRequest.qid);
const questIsRepeatable = pmcData.RepeatableQuests.some((quest) => quest.id === failRequest.qid);
if (!questIsRepeatable)
{
this.mailSendService.sendLocalisedNpcMessageToPlayer(
@ -1007,9 +1009,7 @@ export class QuestHelper
const questInDb = allQuests.find((x) => x._id === questId);
if (!questInDb)
{
this.logger.debug(
`Unable to find quest: ${questId} in db, cannot get 'FindItem' condition, skipping`,
);
this.logger.debug(`Unable to find quest: ${questId} in db, cannot get 'FindItem' condition, skipping`);
continue;
}

View File

@ -565,7 +565,10 @@ export class RagfairOfferHelper
// Filter out presets when search request has multiple buildItems
// Assuming 1 build item = single item e.g. gun
if (searchRequest.buildCount && this.presetHelper.hasPreset(offerRootItem._tpl) && Object.keys(searchRequest.buildItems).length > 1)
if (
searchRequest.buildCount && this.presetHelper.hasPreset(offerRootItem._tpl)
&& Object.keys(searchRequest.buildItems).length > 1
)
{
// Don't include preset offer
return false;
@ -610,7 +613,8 @@ export class RagfairOfferHelper
return false;
}
if (this.isConditionItem(offerRootItem)
if (
this.isConditionItem(offerRootItem)
&& !this.itemQualityInRange(offerRootItem, searchRequest.conditionFrom, searchRequest.conditionTo)
)
{
@ -700,9 +704,10 @@ export class RagfairOfferHelper
*/
protected isConditionItem(item: Item): boolean
{
// thanks typescript, undefined assertion is not returnable since it
// thanks typescript, undefined assertion is not returnable since it
// tries to return a multitype object
return (item.upd.MedKit || item.upd.Repairable || item.upd.Resource || item.upd.FoodDrink || item.upd.Key || item.upd.RepairKit)
return (item.upd.MedKit || item.upd.Repairable || item.upd.Resource || item.upd.FoodDrink || item.upd.Key
|| item.upd.RepairKit)
? true
: false;
}

View File

@ -42,11 +42,11 @@ export class RagfairSellHelper
// Base sell chance modified by items quality
const baseSellChancePercent = sellConfig.base * qualityMultiplier;
// Modfier gets applied twice to either penalize or incentivize over/under pricing (Probably a cleaner way to do this)
const sellModifier = (averageOfferPriceRub / playerListedPriceRub) * sellConfig.sellMultiplier;
let sellChance = Math.round((baseSellChancePercent * sellModifier) * sellModifier);
// Adjust sell chance if below config value
if (sellChance < sellConfig.minSellChancePercent)
{
@ -73,7 +73,10 @@ export class RagfairSellHelper
const startTime = this.timeUtil.getTimestamp();
// Get a time in future to stop simulating sell chances at
const endTime = startTime + this.timeUtil.getHoursAsSeconds(this.databaseServer.getTables().globals.config.RagFair.offerDurationTimeInHour);
const endTime = startTime
+ this.timeUtil.getHoursAsSeconds(
this.databaseServer.getTables().globals.config.RagFair.offerDurationTimeInHour,
);
let sellTime = startTime;
let remainingCount = itemSellCount;
@ -106,7 +109,10 @@ export class RagfairSellHelper
const weighting = (100 - sellChancePercent) / 100;
let maximumTime = weighting * (this.ragfairConfig.sell.time.max * 60);
const minimumTime = this.ragfairConfig.sell.time.min * 60;
if (maximumTime < minimumTime) maximumTime = minimumTime + 5;
if (maximumTime < minimumTime)
{
maximumTime = minimumTime + 5;
}
// Sell time will be random between min/max
sellTime += Math.floor(Math.random() * (maximumTime - minimumTime) + minimumTime);

View File

@ -160,7 +160,9 @@ export class RagfairServerHelper
MessageType.MESSAGE_WITH_ITEMS,
RagfairServerHelper.goodsReturnedTemplate,
returnedItems,
this.timeUtil.getHoursAsSeconds(this.databaseServer.getTables().globals.config.RagFair.yourOfferDidNotSellMaxStorageTimeInHour),
this.timeUtil.getHoursAsSeconds(
this.databaseServer.getTables().globals.config.RagFair.yourOfferDidNotSellMaxStorageTimeInHour,
),
);
}
@ -278,7 +280,7 @@ export class RagfairServerHelper
/**
* Possible bug, returns all items associated with an items tpl, could be multiple presets from globals.json
* @param item Preset item
* @returns
* @returns
*/
public getPresetItemsByTpl(item: Item): Item[]
{

View File

@ -45,7 +45,8 @@ export class TradeHelper
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
@inject("RagfairServer") protected ragfairServer: RagfairServer,
@inject("TraderAssortHelper") protected traderAssortHelper: TraderAssortHelper,
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService: TraderPurchasePersisterService,
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService:
TraderPurchasePersisterService,
@inject("ConfigServer") protected configServer: ConfigServer,
)
{
@ -71,7 +72,7 @@ export class TradeHelper
): void
{
let offerItems: Item[] = [];
let buyCallback: { (buyCount: number) };
let buyCallback: { (buyCount: number); };
if (buyRequestData.tid.toLocaleLowerCase() === "ragfair")
{
buyCallback = (buyCount: number) =>
@ -81,23 +82,26 @@ export class TradeHelper
// We store ragfair offerid in buyRequestData.item_id
const offerWithItem = allOffers.find((x) => x._id === buyRequestData.item_id);
const itemPurchased = offerWithItem.items[0];
// Ensure purchase does not exceed trader item limit
const assortHasBuyRestrictions = this.itemHelper.hasBuyRestrictions(itemPurchased);
if (assortHasBuyRestrictions)
{
this.checkPurchaseIsWithinTraderItemLimit(sessionID, buyRequestData.tid, itemPurchased, buyRequestData.item_id, buyCount);
this.checkPurchaseIsWithinTraderItemLimit(
sessionID,
buyRequestData.tid,
itemPurchased,
buyRequestData.item_id,
buyCount,
);
}
// Decrement trader item count
if (this.traderConfig.persistPurchaseDataInProfile && assortHasBuyRestrictions)
{
const itemPurchaseDat = {
items: [{
itemId: buyRequestData.item_id,
count: buyCount
}],
traderId: buyRequestData.tid
items: [{ itemId: buyRequestData.item_id, count: buyCount }],
traderId: buyRequestData.tid,
};
this.traderHelper.addTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDat);
}
@ -121,10 +125,10 @@ export class TradeHelper
// Update assort/flea item values
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(buyRequestData.tid).items;
const itemPurchased = traderAssorts.find((assort) => assort._id === buyRequestData.item_id);
// Decrement trader item count
itemPurchased.upd.StackObjectsCount -= buyCount;
this.fenceService.amendOrRemoveFenceOffer(buyRequestData.item_id, buyCount);
};
@ -150,12 +154,17 @@ export class TradeHelper
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(buyRequestData.tid).items;
const itemPurchased = traderAssorts.find((x) => x._id === buyRequestData.item_id);
// Ensure purchase does not exceed trader item limit
const assortHasBuyRestrictions = this.itemHelper.hasBuyRestrictions(itemPurchased);
if (assortHasBuyRestrictions)
{
this.checkPurchaseIsWithinTraderItemLimit(sessionID, buyRequestData.tid, itemPurchased, buyRequestData.item_id, buyCount);
this.checkPurchaseIsWithinTraderItemLimit(
sessionID,
buyRequestData.tid,
itemPurchased,
buyRequestData.item_id,
buyCount,
);
}
// Decrement trader item count
@ -164,11 +173,8 @@ export class TradeHelper
if (this.traderConfig.persistPurchaseDataInProfile && assortHasBuyRestrictions)
{
const itemPurchaseDat = {
items: [{
itemId: buyRequestData.item_id,
count: buyCount
}],
traderId: buyRequestData.tid
items: [{ itemId: buyRequestData.item_id, count: buyCount }],
traderId: buyRequestData.tid,
};
this.traderHelper.addTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDat);
}
@ -182,7 +188,7 @@ export class TradeHelper
// Get all trader assort items
const traderItems = this.traderAssortHelper.getAssort(sessionID, buyRequestData.tid).items;
// Get item + children for purchase
const relevantItems = this.itemHelper.findAndReturnChildrenAsItems(traderItems, buyRequestData.item_id);
offerItems.push(...relevantItems);
@ -209,7 +215,7 @@ export class TradeHelper
itemWithModsToAdd: this.itemHelper.reparentItemAndChildren(offerItems[0], offerItems),
foundInRaid: foundInRaid,
callback: buyCallback,
useSortingTable: false
useSortingTable: false,
};
// Add item + children to stash
@ -299,9 +305,19 @@ export class TradeHelper
* @param assortId Id of assort being purchased
* @param count How many of the item are being bought
*/
protected checkPurchaseIsWithinTraderItemLimit(sessionId: string, traderId: string, assortBeingPurchased: Item, assortId: string, count: number): void
protected checkPurchaseIsWithinTraderItemLimit(
sessionId: string,
traderId: string,
assortBeingPurchased: Item,
assortId: string,
count: number,
): void
{
const traderPurchaseData = this.traderPurchasePersisterService.getProfileTraderPurchase(sessionId, traderId, assortBeingPurchased._id);
const traderPurchaseData = this.traderPurchasePersisterService.getProfileTraderPurchase(
sessionId,
traderId,
assortBeingPurchased._id,
);
if ((traderPurchaseData?.count ?? 0 + count) > assortBeingPurchased.upd?.BuyRestrictionMax)
{
throw new Error(

View File

@ -148,7 +148,7 @@ export class TraderAssortHelper
protected resetBuyRestrictionCurrentValue(assortItems: Item[]): void
{
// iterate over root items
for (const assort of assortItems.filter(item => item.slotId === "hideout"))
for (const assort of assortItems.filter((item) => item.slotId === "hideout"))
{
// no value to adjust
if (!assort.upd.BuyRestrictionCurrent)

View File

@ -146,10 +146,12 @@ export class PreAkiModLoader implements IModLoader
const modOrder = this.vfs.readFile(this.modOrderPath, { encoding: "utf8" });
try
{
this.jsonUtil.deserialize<any>(modOrder, this.modOrderPath).order.forEach((mod: string, index: number) =>
{
this.order[mod] = index;
});
this.jsonUtil.deserialize<any>(modOrder, this.modOrderPath).order.forEach(
(mod: string, index: number) =>
{
this.order[mod] = index;
},
);
}
catch (error)
{
@ -429,7 +431,7 @@ export class PreAkiModLoader implements IModLoader
public sortModsLoadOrder(): string[]
{
// if loadorder.json exists: load it, otherwise generate load order
const loadOrderPath = `${this.basepath}loadorder.json`
const loadOrderPath = `${this.basepath}loadorder.json`;
if (this.vfs.exists(loadOrderPath))
{
return this.jsonUtil.deserialize(this.vfs.readFile(loadOrderPath), loadOrderPath);

View File

@ -1,11 +1,11 @@
import { IMagazineTemplateAmmoItem } from "../profile/IAkiProfile"
import { IMagazineTemplateAmmoItem } from "../profile/IAkiProfile";
export interface ISetMagazineRequest
{
Id: string
Name: string
Caliber: string
Items: IMagazineTemplateAmmoItem[]
TopCount: number
BottomCount: number
}
Id: string;
Name: string;
Caliber: string;
Items: IMagazineTemplateAmmoItem[];
TopCount: number;
BottomCount: number;
}

View File

@ -77,9 +77,9 @@ export interface IConfig
SkillFatigueReset: number;
DiscardLimitsEnabled: boolean;
EventSettings: IEventSettings;
FavoriteItemsSettings: IFavoriteItemsSettings
VaultingSettings: IVaultingSettings
BTRSettings: IBTRSettings
FavoriteItemsSettings: IFavoriteItemsSettings;
VaultingSettings: IVaultingSettings;
BTRSettings: IBTRSettings;
EventType: string[];
WalkSpeed: Ixyz;
SprintSpeed: Ixyz;
@ -1036,99 +1036,98 @@ export interface IRestrictionsInRaid
export interface IFavoriteItemsSettings
{
WeaponStandMaxItemsCount: number
PlaceOfFameMaxItemsCount: number
WeaponStandMaxItemsCount: number;
PlaceOfFameMaxItemsCount: number;
}
export interface IVaultingSettings
{
IsActive: boolean
VaultingInputTime: number
GridSettings: IVaultingGridSettings
MovesSettings: IVaultingMovesSettings
IsActive: boolean;
VaultingInputTime: number;
GridSettings: IVaultingGridSettings;
MovesSettings: IVaultingMovesSettings;
}
export interface IVaultingGridSettings
{
GridSizeX: number
GridSizeY: number
GridSizeZ: number
SteppingLengthX: number
SteppingLengthY: number
SteppingLengthZ: number
GridOffsetX: number
GridOffsetY: number
GridOffsetZ: number
OffsetFactor: number
GridSizeX: number;
GridSizeY: number;
GridSizeZ: number;
SteppingLengthX: number;
SteppingLengthY: number;
SteppingLengthZ: number;
GridOffsetX: number;
GridOffsetY: number;
GridOffsetZ: number;
OffsetFactor: number;
}
export interface IVaultingMovesSettings
{
VaultSettings: IVaultingSubMoveSettings
ClimbSettings: IVaultingSubMoveSettings
VaultSettings: IVaultingSubMoveSettings;
ClimbSettings: IVaultingSubMoveSettings;
}
export interface IVaultingSubMoveSettings
{
IsActive: boolean
MaxWithoutHandHeight: number
SpeedRange: Ixyz
MoveRestrictions: IMoveRestrictions
AutoMoveRestrictions: IMoveRestrictions
IsActive: boolean;
MaxWithoutHandHeight: number;
SpeedRange: Ixyz;
MoveRestrictions: IMoveRestrictions;
AutoMoveRestrictions: IMoveRestrictions;
}
export interface IMoveRestrictions
{
IsActive: boolean
MinDistantToInteract: number
MinHeight: number
MaxHeight: number
MinLength: number
MaxLength: number
IsActive: boolean;
MinDistantToInteract: number;
MinHeight: number;
MaxHeight: number;
MinLength: number;
MaxLength: number;
}
export interface IBTRSettings
{
LocationsWithBTR: string[]
BasePriceTaxi: number
AddPriceTaxi: number
CleanUpPrice: number
DeliveryPrice: number
ModDeliveryCost: number
BearPriceMod: number
UsecPriceMod: number
ScavPriceMod: number
CoefficientDiscountCharisma: number
DeliveryMinPrice: number
TaxiMinPrice: number
BotCoverMinPrice: number
MapsConfigs: Record<string, IBtrMapConfig>
DiameterWheel: number
HeightWheel: number
HeightWheelMaxPosLimit: number
HeightWheelMinPosLimit: number
SnapToSurfaceWheelsSpeed: number
CheckSurfaceForWheelsTimer: number
HeightWheelOffset: number
LocationsWithBTR: string[];
BasePriceTaxi: number;
AddPriceTaxi: number;
CleanUpPrice: number;
DeliveryPrice: number;
ModDeliveryCost: number;
BearPriceMod: number;
UsecPriceMod: number;
ScavPriceMod: number;
CoefficientDiscountCharisma: number;
DeliveryMinPrice: number;
TaxiMinPrice: number;
BotCoverMinPrice: number;
MapsConfigs: Record<string, IBtrMapConfig>;
DiameterWheel: number;
HeightWheel: number;
HeightWheelMaxPosLimit: number;
HeightWheelMinPosLimit: number;
SnapToSurfaceWheelsSpeed: number;
CheckSurfaceForWheelsTimer: number;
HeightWheelOffset: number;
}
export interface IBtrMapConfig
{
mapID: string
pathsConfigurations: IBtrMapConfig[]
mapID: string;
pathsConfigurations: IBtrMapConfig[];
}
export interface IBtrMapConfig
{
id: string
enterPoint: string
exitPoint: string
pathPoints: string[]
once: boolean
circle: boolean
circleCount: number
}
id: string;
enterPoint: string;
exitPoint: string;
pathPoints: string[];
once: boolean;
circle: boolean;
circleCount: number;
}
export interface ISquadSettings
{
@ -1643,11 +1642,11 @@ export interface IFenceLevel
BotHelpChance: number;
BotSpreadoutChance: number;
BotStopChance: number;
PriceModTaxi: number
PriceModDelivery: number
PriceModCleanUp: number
DeliveryGridSize: Ixyz
CanInteractWithBtr: boolean
PriceModTaxi: number;
PriceModDelivery: number;
PriceModCleanUp: number;
DeliveryGridSize: Ixyz;
CanInteractWithBtr: boolean;
}
export interface IInertia
@ -1766,14 +1765,15 @@ export interface IAudioGroupPreset
export interface IEnvironmentSettings
{
SnowStepsVolumeMultiplier: number
SurfaceMultipliers: ISurfaceMultiplier[]
SnowStepsVolumeMultiplier: number;
SurfaceMultipliers: ISurfaceMultiplier[];
}
export interface ISurfaceMultiplier {
SurfaceType: string
VolumeMult: number
}
export interface ISurfaceMultiplier
{
SurfaceType: string;
VolumeMult: number;
}
export interface IBotWeaponScattering
{

View File

@ -229,7 +229,7 @@ export interface Exit
Chance: number;
Count: number;
EntryPoints: string;
EventAvailable: boolean
EventAvailable: boolean;
ExfiltrationTime: number;
ExfiltrationType: string;
RequiredSlot?: string;

View File

@ -5,12 +5,12 @@ export interface IPmcData extends IBotBase
export interface IPostRaidPmcData extends IBotBase
{
Stats: IPostRaidStats
Stats: IPostRaidStats;
}
export interface IPostRaidStats
{
Eft: IEftStats
Eft: IEftStats;
/** Only found in profile we get from client post raid */
Arena: IEftStats
}
Arena: IEftStats;
}

View File

@ -1,20 +1,20 @@
import { IQuestConditionTypes, IQuestRewards } from "./IQuest"
import { IQuestConditionTypes, IQuestRewards } from "./IQuest";
export interface IAchievement
{
id: string
imageUrl: string
assetPath: string
rewards: IQuestRewards
conditions: IQuestConditionTypes
instantComplete: boolean
showNotificationsInGame: boolean
showProgress: boolean
prefab: string
rarity: string
hidden: boolean
showConditions: boolean
progressBarEnabled: boolean
side: string
index: number
}
id: string;
imageUrl: string;
assetPath: string;
rewards: IQuestRewards;
conditions: IQuestConditionTypes;
instantComplete: boolean;
showNotificationsInGame: boolean;
showProgress: boolean;
prefab: string;
rarity: string;
hidden: boolean;
showConditions: boolean;
progressBarEnabled: boolean;
side: string;
index: number;
}

View File

@ -29,7 +29,7 @@ export interface IBotBase
UnlockedInfo: IUnlockedInfo;
RagfairInfo: RagfairInfo;
/** Achievement id and timestamp */
Achievements: Record<string, number>
Achievements: Record<string, number>;
RepeatableQuests: IPmcDataRepeatableQuest[];
Bonuses: Bonus[];
Notes: Notes;
@ -43,11 +43,11 @@ export interface IBotBase
export interface ITaskConditionCounter
{
id: string
type: string
value: number
id: string;
type: string;
value: number;
/** Quest id */
sourceId: string
sourceId: string;
}
export interface IUnlockedInfo
@ -165,7 +165,7 @@ export interface Inventory
/** Key is hideout area enum numeric as string e.g. "24", value is area _id */
hideoutAreaStashes: Record<string, string>;
fastPanel: Record<string, string>;
favoriteItems: string[]
favoriteItems: string[];
}
export interface IBaseJsonSkills
@ -510,4 +510,4 @@ export interface Note
{
Time: number;
Text: string;
}
}

View File

@ -54,10 +54,10 @@ export interface IQuestCondition
{
id: string;
index?: number;
compareMethod?: string
compareMethod?: string;
dynamicLocale: boolean;
visibilityConditions?: VisibilityCondition[];
globalQuestCounterId?: string
globalQuestCounterId?: string;
parentId?: string;
target: string[] | string;
value?: string | number;
@ -75,7 +75,7 @@ export interface IQuestCondition
plantTime?: number;
zoneId?: string;
countInRaid?: boolean;
completeInSeconds?: number
completeInSeconds?: number;
isEncoded?: boolean;
conditionType?: string;
}
@ -89,48 +89,48 @@ export interface IQuestConditionCounter
export interface IQuestConditionCounterCondition
{
id: string;
dynamicLocale: boolean
dynamicLocale: boolean;
target?: string[] | string; // TODO: some objects have an array and some are just strings, thanks bsg very cool
completeInSeconds?: number
energy?: IValueCompare
completeInSeconds?: number;
energy?: IValueCompare;
exitName?: string;
hydration?: IValueCompare
time?: IValueCompare
hydration?: IValueCompare;
time?: IValueCompare;
compareMethod?: string;
value?: number;
weapon?: string[];
distance?: ICounterConditionDistance
distance?: ICounterConditionDistance;
equipmentInclusive?: string[][];
weaponModsInclusive?: string[][];
weaponModsExclusive?: string[][];
enemyEquipmentInclusive?: string[][];
enemyEquipmentExclusive?: string[][];
weaponCaliber?: string[]
savageRole?: string[]
weaponCaliber?: string[];
savageRole?: string[];
status?: string[];
bodyPart?: string[];
daytime?: IDaytimeCounter;
conditionType?: string
enemyHealthEffects?: IEnemyHealthEffect[]
resetOnSessionEnd?: boolean
conditionType?: string;
enemyHealthEffects?: IEnemyHealthEffect[];
resetOnSessionEnd?: boolean;
}
export interface IEnemyHealthEffect
{
bodyParts: string[]
effects: string[]
bodyParts: string[];
effects: string[];
}
export interface IValueCompare
{
compareMethod: string
value: number
compareMethod: string;
value: number;
}
export interface ICounterConditionDistance
{
value: number
compareMethod: string
value: number;
compareMethod: string;
}
export interface IDaytimeCounter
@ -142,7 +142,7 @@ export interface IDaytimeCounter
export interface VisibilityCondition
{
id: string;
target: string
target: string;
value?: number;
dynamicLocale?: boolean;
oneSessionOnly: boolean;

View File

@ -2,9 +2,9 @@ import { IQuest, IQuestConditionTypes, IQuestRewards } from "./IQuest";
export interface IRepeatableQuest extends IQuest
{
changeCost: IChangeCost[]
changeCost: IChangeCost[];
changeStandingCost: number;
sptRepatableGroupName: string
sptRepatableGroupName: string;
}
export interface IRepeatableQuestDatabase

View File

@ -86,7 +86,7 @@ export interface Props
EffectiveDistance?: number;
Ergonomics?: number;
Velocity?: number;
WithAnimatorAiming?: boolean
WithAnimatorAiming?: boolean;
RaidModdable?: boolean;
ToolModdable?: boolean;
UniqueAnimationModID?: number;
@ -188,9 +188,9 @@ export interface Props
weapUseType?: string;
ammoCaliber?: string;
OperatingResource?: number;
PostRecoilHorizontalRangeHandRotation?: Ixyz
PostRecoilVerticalRangeHandRotation?: Ixyz
ProgressRecoilAngleOnStable?: Ixyz
PostRecoilHorizontalRangeHandRotation?: Ixyz;
PostRecoilVerticalRangeHandRotation?: Ixyz;
ProgressRecoilAngleOnStable?: Ixyz;
RepairComplexity?: number;
durabSpawnMin?: number;
durabSpawnMax?: number;
@ -198,7 +198,7 @@ export interface Props
RecoilForceUp?: number;
RecoilForceBack?: number;
RecoilAngle?: number;
RecoilCamera?: number
RecoilCamera?: number;
weapFireType?: string[];
RecolDispersion?: number;
SingleFireRate?: number;
@ -206,7 +206,7 @@ export interface Props
bFirerate?: number;
bEffDist?: number;
bHearDist?: number;
blockLeftStance?: boolean
blockLeftStance?: boolean;
isChamberLoad?: boolean;
chamberAmmoCount?: number;
isBoltCatch?: boolean;
@ -216,8 +216,8 @@ export interface Props
shotgunDispersion?: number;
Chambers?: Slot[];
CameraSnap?: number;
CameraToWeaponAngleSpeedRange?: Ixyz
CameraToWeaponAngleStep?: number
CameraToWeaponAngleSpeedRange?: Ixyz;
CameraToWeaponAngleStep?: number;
ReloadMode?: string;
AimPlane?: number;
TacticalReloadStiffnes?: Ixyz;
@ -225,7 +225,7 @@ export interface Props
RecoilCenter?: Ixyz;
RotationCenter?: Ixyz;
RotationCenterNoStock?: Ixyz;
ShotsGroupSettings?: IShotsGroupSettings[]
ShotsGroupSettings?: IShotsGroupSettings[];
FoldedSlot?: string;
CompactHandling?: boolean;
MinRepairDegradation?: number;
@ -257,11 +257,11 @@ export interface Props
AllowOverheat?: boolean;
DoubleActionAccuracyPenalty?: number;
RecoilPosZMult?: number;
RecoilReturnPathDampingHandRotation?: number
RecoilReturnPathOffsetHandRotation?: number
RecoilReturnSpeedHandRotation?: number
RecoilStableAngleIncreaseStep?: number
RecoilStableIndexShot?: number
RecoilReturnPathDampingHandRotation?: number;
RecoilReturnPathOffsetHandRotation?: number;
RecoilReturnSpeedHandRotation?: number;
RecoilStableAngleIncreaseStep?: number;
RecoilStableIndexShot?: number;
MinRepairKitDegradation?: number;
MaxRepairKitDegradation?: number;
BlocksEarpiece?: boolean;
@ -560,9 +560,9 @@ export interface IColor
export interface IShotsGroupSettings
{
EndShotIndex: number
ShotRecoilPositionStrength: Ixyz
ShotRecoilRadianRange: Ixyz
ShotRecoilRotationStrength: Ixyz
StartShotIndex: number
}
EndShotIndex: number;
ShotRecoilPositionStrength: Ixyz;
ShotRecoilRadianRange: Ixyz;
ShotRecoilRotationStrength: Ixyz;
StartShotIndex: number;
}

View File

@ -1,5 +1,5 @@
export interface IGetRaidTimeRequest
{
Side: string,
Location: string
}
Side: string;
Location: string;
}

View File

@ -1,15 +1,15 @@
export interface IGetRaidTimeResponse
{
RaidTimeMinutes: number
NewSurviveTimeSeconds: number
OriginalSurvivalTimeSeconds: number
ExitChanges: ExtractChange[]
RaidTimeMinutes: number;
NewSurviveTimeSeconds: number;
OriginalSurvivalTimeSeconds: number;
ExitChanges: ExtractChange[];
}
export interface ExtractChange
{
Name: string
MinTime?: number
MaxTime?: number
Chance?: number
}
Name: string;
MinTime?: number;
MaxTime?: number;
Chance?: number;
}

View File

@ -91,4 +91,4 @@ export interface StageBonus
/** CHANGES PER DUMP */
id?: string;
templateId?: string;
}
}

View File

@ -17,27 +17,30 @@ export interface IQteData
area: HideoutAreas;
areaLevel: number;
quickTimeEvents: IQuickTimeEvent[];
requirements: (IAreaRequirement
| IItemRequirement
| ITraderUnlockRequirement
| ITraderLoyaltyRequirement
| ISkillRequirement
| IResourceRequirement
| IToolRequirement
| IQuestRequirement
| IHealthRequirement
| IBodyPartBuffRequirement)[];
requirements:
(
| IAreaRequirement
| IItemRequirement
| ITraderUnlockRequirement
| ITraderLoyaltyRequirement
| ISkillRequirement
| IResourceRequirement
| IToolRequirement
| IQuestRequirement
| IHealthRequirement
| IBodyPartBuffRequirement
)[];
results: Record<QteEffectType, IQteResult>;
}
export interface IQuickTimeEvent
{
type: QteType;
position: { x: number, y: number };
position: { x: number; y: number; };
startDelay: number;
endDelay: number;
speed: number;
successRange: { x: number, y: number };
successRange: { x: number; y: number; };
key: string;
}
@ -71,73 +74,73 @@ export interface ISkillLevelMultiplier
export interface IAreaRequirement extends IQteRequirement
{
type: RequirementType.AREA,
areaType: HideoutAreas,
requiredLevel: number,
type: RequirementType.AREA;
areaType: HideoutAreas;
requiredLevel: number;
}
export interface ITraderUnlockRequirement extends IQteRequirement
{
type: RequirementType.TRADER_UNLOCK,
type: RequirementType.TRADER_UNLOCK;
traderId: Traders;
}
export interface ITraderLoyaltyRequirement extends IQteRequirement
{
type: RequirementType.TRADER_LOYALTY,
type: RequirementType.TRADER_LOYALTY;
traderId: Traders;
loyaltyLevel: number;
}
export interface ISkillRequirement extends IQteRequirement
{
type: RequirementType.SKILL,
type: RequirementType.SKILL;
skillName: SkillTypes;
skillLevel: number;
}
export interface IResourceRequirement extends IQteRequirement
{
type: RequirementType.RESOURCE,
templateId: string,
resource: number,
type: RequirementType.RESOURCE;
templateId: string;
resource: number;
}
export interface IItemRequirement extends IQteRequirement
{
type: RequirementType.ITEM,
templateId: string,
count: number,
isFunctional: boolean,
isEncoded: boolean,
type: RequirementType.ITEM;
templateId: string;
count: number;
isFunctional: boolean;
isEncoded: boolean;
}
export interface IToolRequirement extends IQteRequirement
{
type: RequirementType.TOOL,
templateId: string,
count: number,
isFunctional: boolean,
isEncoded: boolean,
type: RequirementType.TOOL;
templateId: string;
count: number;
isFunctional: boolean;
isEncoded: boolean;
}
export interface IQuestRequirement extends IQteRequirement
{
type: RequirementType.QUEST_COMPLETE,
questId: string,
type: RequirementType.QUEST_COMPLETE;
questId: string;
}
export interface IHealthRequirement extends IQteRequirement
{
type: RequirementType.HEALTH,
energy: number,
hydration: number,
type: RequirementType.HEALTH;
energy: number;
hydration: number;
}
export interface IBodyPartBuffRequirement extends IQteRequirement
{
type: RequirementType.BODY_PART_BUFF,
effectName: Effect,
bodyPart: BodyPart,
excluded: boolean,
type: RequirementType.BODY_PART_BUFF;
effectName: Effect;
bodyPart: BodyPart;
excluded: boolean;
}

View File

@ -2,6 +2,6 @@ import { Item } from "../common/tables/IItem";
export interface IItemDeliveryRequestData
{
items: Item[],
traderId: string
}
items: Item[];
traderId: string;
}

View File

@ -1,4 +1,4 @@
import { Item } from "../common/tables/IItem"
import { Item } from "../common/tables/IItem";
export interface IAddItemDirectRequest
{
@ -7,4 +7,4 @@ export interface IAddItemDirectRequest
foundInRaid: boolean;
callback: (buyCount: number) => void;
useSortingTable: boolean;
}
}

View File

@ -9,4 +9,4 @@ export interface IAddItemsDirectRequest
callback: (buyCount: number) => void;
/** Should sorting table be used when no space found in stash */
useSortingTable: boolean;
}
}

View File

@ -1,8 +1,8 @@
import { IInventoryBaseActionRequestData } from "@spt-aki/models/eft/inventory/IInventoryBaseActionRequestData";
export interface IInventoryUnbindRequestData extends IInventoryBaseActionRequestData
export interface IInventoryUnbindRequestData extends IInventoryBaseActionRequestData
{
Action: "Unbind"
item: string
index: number
}
Action: "Unbind";
item: string;
index: number;
}

View File

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

View File

@ -4,5 +4,5 @@ export interface ISetFavoriteItems extends IInventoryBaseActionRequestData
{
Action: "SetFavoriteItems";
items: any[];
timestamp: number
}
timestamp: number;
}

View File

@ -1,19 +1,19 @@
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory"
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory";
export interface IGetGroupStatusResponse
{
players: IPlayer[]
maxPveCountExceeded: boolean
players: IPlayer[];
maxPveCountExceeded: boolean;
}
export interface IPlayer
{
aid: string
_id: string
lookingGroup: boolean
IsLeader: boolean
IsReady: boolean
Info: ICurrentGroupMemberInfo
aid: string;
_id: string;
lookingGroup: boolean;
IsLeader: boolean;
IsReady: boolean;
Info: ICurrentGroupMemberInfo;
}
export interface ICurrentGroupMemberInfo
@ -22,4 +22,4 @@ export interface ICurrentGroupMemberInfo
Side: string;
Level: string;
MemberCategory: MemberCategory;
}
}

View File

@ -1,4 +1,4 @@
export interface IRemoveBuildRequestData
{
id: string;
}
}

View File

@ -7,7 +7,6 @@ import { IProfileChangeEvent } from "@spt-aki/models/spt/dialog/ISendMessageDeta
export interface IAkiProfile
{
info: Info;
characters: Characters;
/** Clothing purchases */
@ -53,7 +52,7 @@ export interface IUserBuilds
{
weaponBuilds: IWeaponBuild[];
equipmentBuilds: IEquipmentBuild[];
magazineBuilds: IMagazineBuild[]
magazineBuilds: IMagazineBuild[];
}
export interface IUserBuild
@ -64,7 +63,6 @@ export interface IUserBuild
export interface IWeaponBuild extends IUserBuild
{
Root: string;
Items: Item[]; // Same as PMC inventory items
}
@ -78,25 +76,25 @@ export interface IEquipmentBuild extends IUserBuild
export interface IMagazineBuild extends IUserBuild
{
Caliber: string
TopCount: number
BottomCount: number
Items: IMagazineTemplateAmmoItem[]
Caliber: string;
TopCount: number;
BottomCount: number;
Items: IMagazineTemplateAmmoItem[];
}
export interface IMagazineTemplateAmmoItem
{
TemplateId: string
Count: number
TemplateId: string;
Count: number;
}
/** Used by defaultEquipmentPresets.json */
export interface IDefaultEquipmentPreset extends IUserBuild
{
Items: Item[]
Root: string
BuildType: EquipmentBuildType
type: string
Items: Item[];
Root: string;
BuildType: EquipmentBuildType;
type: string;
}
export interface Dialogue
@ -291,8 +289,8 @@ export interface Insurance
traderId: string;
maxStorageTime: number;
systemData: ISystemData;
messageType: MessageType
messageTemplateId: string
messageType: MessageType;
messageTemplateId: string;
items: Item[];
}

View File

@ -1,4 +1,4 @@
export interface ICompletedAchievementsResponse
{
elements: Record<string, number>
}
elements: Record<string, number>;
}

View File

@ -1,4 +1,4 @@
export interface ICreateProfileResponse
{
uid: string
}
uid: string;
}

View File

@ -2,5 +2,5 @@ import { IAchievement } from "../common/tables/IAchievement";
export interface IGetAchievementsResponse
{
elements: IAchievement[]
}
elements: IAchievement[];
}

View File

@ -1,4 +1,4 @@
export interface IGetOtherProfileRequest
{
accountId: string;
}
}

View File

@ -1,30 +1,29 @@
import { OverallCounters, Skills } from "@spt-aki/models/eft/common/tables/IBotBase"
import { Item } from "../common/tables/IItem"
import { OverallCounters, Skills } from "@spt-aki/models/eft/common/tables/IBotBase";
import { Item } from "../common/tables/IItem";
export interface IGetOtherProfileResponse
{
id: string
aid: number
info: IOtherProfileInfo
customization: IOtherProfileCustomization
skills: Skills
equipment: IOtherProfileEquipment
achievements: Record<string, number>
favoriteItems: string[]
pmcStats: IOtherProfileStats
scavStats: IOtherProfileStats
id: string;
aid: number;
info: IOtherProfileInfo;
customization: IOtherProfileCustomization;
skills: Skills;
equipment: IOtherProfileEquipment;
achievements: Record<string, number>;
favoriteItems: string[];
pmcStats: IOtherProfileStats;
scavStats: IOtherProfileStats;
}
export interface IOtherProfileInfo
{
nickname: string
side: string
experience: number
memberCategory: number
bannedState: boolean
bannedUntil: number
registrationDate: number
nickname: string;
side: string;
experience: number;
memberCategory: number;
bannedState: boolean;
bannedUntil: number;
registrationDate: number;
}
export interface IOtherProfileCustomization
@ -48,6 +47,6 @@ export interface IOtherProfileStats
export interface IOtherProfileSubStats
{
totalInGameTime: number
overAllCounters: OverallCounters
}
totalInGameTime: number;
overAllCounters: OverallCounters;
}

View File

@ -1,4 +1,4 @@
export interface IGetRagfairOfferByIdRequest
{
id: number
}
id: number;
}

View File

@ -6,7 +6,7 @@ export interface IWeatherData
time: string;
date: string;
weather?: IWeather;
winterEventEnabled: boolean
winterEventEnabled: boolean;
}
export interface IWeather

View File

@ -103,6 +103,6 @@ export enum BaseClasses
STACKABLE_ITEM = "5661632d4bdc2d903d8b456b",
BUILT_IN_INSERTS = "65649eb40bf0ed77b8044453",
ARMOR_PLATE = "644120aa86ffbe10ee032b6f",
CULTIST_AMULET= "64b69b0c8f3be32ed22682f8",
RADIO_TRANSMITTER = "62e9103049c018f425059f38"
CULTIST_AMULET = "64b69b0c8f3be32ed22682f8",
RADIO_TRANSMITTER = "62e9103049c018f425059f38",
}

View File

@ -4,5 +4,5 @@ export enum BonusSkillType
COMBAT = "Combat",
SPECIAL = "Special",
PRACTICAL = "Practical",
MENTAL = "Mental"
}
MENTAL = "Mental",
}

View File

@ -5,11 +5,11 @@ export enum BonusType
HEALTH_REGENERATION = "HealthRegeneration",
EXPERIENCE_RATE = "ExperienceRate",
QUEST_MONEY_REWARD = "QuestMoneyReward",
SCAV_COOLDOWN_TIMER = "ScavCooldownTimer",
UNLOCK_ITEM_CRAFT = "UnlockItemCraft",
UNLOCK_ITEM_PASSIVE_CREATION = "UnlockItemPassiveCreation",
UNLOCK_RANDOM_ITEM_CREATION = "UnlockRandomItemCreation",
SKILL_LEVELING_BOOST = "SkillLevelingBoost",
SCAV_COOLDOWN_TIMER = "ScavCooldownTimer",
UNLOCK_ITEM_CRAFT = "UnlockItemCraft",
UNLOCK_ITEM_PASSIVE_CREATION = "UnlockItemPassiveCreation",
UNLOCK_RANDOM_ITEM_CREATION = "UnlockRandomItemCreation",
SKILL_LEVELING_BOOST = "SkillLevelingBoost",
DEBUFF_END_DELAY = "DebuffEndDelay",
RAGFAIR_COMMISSION = "RagfairCommission",
INSURANCE_RETURN_TIME = "InsuranceReturnTime",
@ -19,15 +19,15 @@ export enum BonusType
UNLOCK_ITEM_CHARGE = "UnlockItemCharge",
RECEIVE_ITEM_BONUS = "ReceiveItemBonus",
UNLOCK_UNIQUE_ID = "UnlockUniqueId",
INCREASE_CANISTER_SLOTS = "IncreaseCanisterSlots",
ADDITIONAL_SLOTS = "AdditionalSlots",
FUEL_CONSUMPTION = "FuelConsumption",
REPAIR_WEAPON_BONUS = "RepairWeaponBonus",
REPAIR_ARMOR_BONUS = "RepairArmorBonus",
UNLOCK_WEAPON_REPAIR = "UnlockWeaponRepair",
UNLOCK_ARMOR_REPAIR = "UnlockArmorRepair",
INCREASE_CANISTER_SLOTS = "IncreaseCanisterSlots",
ADDITIONAL_SLOTS = "AdditionalSlots",
FUEL_CONSUMPTION = "FuelConsumption",
REPAIR_WEAPON_BONUS = "RepairWeaponBonus",
REPAIR_ARMOR_BONUS = "RepairArmorBonus",
UNLOCK_WEAPON_REPAIR = "UnlockWeaponRepair",
UNLOCK_ARMOR_REPAIR = "UnlockArmorRepair",
STASH_SIZE = "StashSize",
MAXIMUM_ENERGY_RESERVE = "MaximumEnergyReserve",
TEXT_BONUS = "TextBonus",
SKILL_GROUP_LEVELING_BOOST = "SkillGroupLevelingBoost"
}
SKILL_GROUP_LEVELING_BOOST = "SkillGroupLevelingBoost",
}

View File

@ -1,4 +1,5 @@
export enum ConfigTypes {
export enum ConfigTypes
{
AIRDROP = "aki-airdrop",
BOT = "aki-bot",
PMC = "aki-pmc",
@ -25,5 +26,5 @@ export enum ConfigTypes {
SEASONAL_EVENT = "aki-seasonalevents",
LOST_ON_DEATH = "aki-lostondeath",
GIFTS = "aki-gifts",
BTR = "aki-btr"
BTR = "aki-btr",
}

View File

@ -1,4 +1,5 @@
export enum HideoutAreas {
export enum HideoutAreas
{
NOTSET = -1,
VENTS = 0,
SECURITY = 1,
@ -25,5 +26,5 @@ export enum HideoutAreas {
EMERGENCY_WALL = 22,
GYM = 23,
WEAPON_STAND = 24,
WEAPON_STAND_SECONDARY = 25
WEAPON_STAND_SECONDARY = 25,
}

View File

@ -1,4 +1,5 @@
export enum ItemAddedResult {
export enum ItemAddedResult
{
UNKNOWN = -1,
SUCCESS = 1,
NO_SPACE = 2,

View File

@ -1,4 +1,5 @@
export enum ItemEventActions {
export enum ItemEventActions
{
MOVE = "Move",
REMOVE = "Remove",
SPLIT = "Split",
@ -25,5 +26,5 @@ export enum ItemEventActions {
REMOVE_EQUIPMENT_BUILD = "RemoveEquipmentBuild",
REDEEM_PROFILE_REWARD = "RedeemProfileReward",
SET_FAVORITE_ITEMS = "SetFavoriteItems",
QUEST_FAIL = "QuestFail"
QUEST_FAIL = "QuestFail",
}

View File

@ -1,5 +1,6 @@
export enum ModSpawn {
export enum ModSpawn
{
DEFAULT_MOD,
SPAWN,
SKIP
}
SKIP,
}

View File

@ -5,5 +5,5 @@ export enum SeasonalEventType
HALLOWEEN = "Halloween",
NEW_YEARS = "NewYears",
PROMO = "Promo",
SNOW = "Snow"
SNOW = "Snow",
}

View File

@ -5,5 +5,5 @@ export enum TraderServiceType
CULTISTS_AID = "CultistsAid",
BTR_ITEMS_DELIVERY = "BtrItemsDelivery",
PLAYER_TAXI = "PlayerTaxi",
BTR_BOT_COVER = "BtrBotCover"
}
BTR_BOT_COVER = "BtrBotCover",
}

View File

@ -1,3 +1,4 @@
export enum QteActivityType {
export enum QteActivityType
{
GYM = 0,
}

View File

@ -1,4 +1,5 @@
export enum QteEffectType {
export enum QteEffectType
{
FINISH_EFFECT = "FinishEffect",
SINGLE_SUCCESS_EFFECT = "SingleSuccessEffect",
SINGLE_FAIL_EFFECT = "SingleFailEffect",

View File

@ -1,4 +1,5 @@
export enum QteResultType {
export enum QteResultType
{
NONE = "None",
EXIT = "Exit",
}
}

View File

@ -1,6 +1,7 @@
export enum QteRewardType {
export enum QteRewardType
{
SKILL = "Skill",
HEALTH_EFFECT = "HealthEffect",
MUSCLE_PAIN = "MusclePain",
GYM_ARM_TRAUMA = "GymArmTrauma",
}
}

View File

@ -1,3 +1,4 @@
export enum QteType {
export enum QteType
{
SHRINKING_CIRCLE = 0,
}

View File

@ -1,4 +1,5 @@
export enum RequirementType {
export enum RequirementType
{
AREA = "Area",
ITEM = "Item",
TRADER_UNLOCK = "TraderUnlock",
@ -9,4 +10,4 @@ export enum RequirementType {
QUEST_COMPLETE = "QuestComplete",
HEALTH = "Health",
BODY_PART_BUFF = "BodyPartBuff",
}
}

View File

@ -5,4 +5,4 @@ export interface IChooseRandomCompatibleModResult
chosenTpl?: string;
reason: string;
slotBlocked?: boolean;
}
}

View File

@ -9,7 +9,7 @@ export interface IBTRConfig extends IBaseConfig
/** How long the cover fire service lasts for */
coverFireTime: number;
/** How long the BTR waits at every point in its path */
pointWaitTime: MinMax,
pointWaitTime: MinMax;
/** How long after purchasing the taxi service before the BTR leaves */
taxiWaitTime: number;
}

View File

@ -103,7 +103,7 @@ export interface EquipmentFilters
/** Chance gun laser is active during the day */
laserIsActiveChancePercent?: number;
/** Should plates be filtered by level */
filterPlatesByLevel?:boolean
filterPlatesByLevel?: boolean;
/** Chance NODS are down/active during the day */
nvgIsActiveChanceDayPercent?: number;
/** Chance NODS are down/active during the night */
@ -121,7 +121,7 @@ export interface EquipmentFilters
weightingAdjustmentsByPlayerLevel?: WeightingAdjustmentDetails[];
/** Should the stock mod be forced to spawn on bot */
forceStock: boolean;
armorPlateWeighting?: IArmorPlateWeights[]
armorPlateWeighting?: IArmorPlateWeights[];
}
export interface ModLimits
@ -169,7 +169,6 @@ export interface WeightingAdjustmentDetails
equipment?: IAdjustmentDetails;
/** Key: clothing slot e.g. feet, value: item tpl + weight */
clothing?: IAdjustmentDetails;
}
export interface IAdjustmentDetails

View File

@ -43,13 +43,13 @@ export interface ILocationConfig extends IBaseConfig
/** Key: map, value: settings to control how long scav raids are*/
scavRaidTimeSettings: IScavRaidTimeSettings;
/** Settings to adjust mods for lootable equipment in raid */
equipmentLootSettings: IEquipmentLootSettings
equipmentLootSettings: IEquipmentLootSettings;
}
export interface IEquipmentLootSettings
{
// Percentage chance item will be added to equipment
modSpawnChancePercent: Record<string, number>
modSpawnChancePercent: Record<string, number>;
}
export interface IFixEmptyBotWavesSettings
@ -118,13 +118,13 @@ export interface IContainerRandomistionSettings
export interface IScavRaidTimeSettings
{
settings: IScavRaidTimeConfigSettings
maps: Record<string, IScavRaidTimeLocationSettings>
settings: IScavRaidTimeConfigSettings;
maps: Record<string, IScavRaidTimeLocationSettings>;
}
export interface IScavRaidTimeConfigSettings
{
trainArrivalDelayObservedSeconds: number
trainArrivalDelayObservedSeconds: number;
}
export interface IScavRaidTimeLocationSettings
@ -133,7 +133,7 @@ export interface IScavRaidTimeLocationSettings
reduceLootByPercent: boolean;
/** Smallest % of container loot that should be spawned */
minStaticLootPercent: number;
/** Smallest % of loose loot that should be spawned */
/** Smallest % of loose loot that should be spawned */
minDynamicLootPercent: number;
/** Chance raid time is reduced */
reducedChancePercent: number;

View File

@ -77,8 +77,8 @@ export interface ITraderWhitelist
traderId: string;
questTypes: string[];
rewardBaseWhitelist: string[];
rewardCanBeWeapon: boolean
weaponRewardChancePercent: number
rewardCanBeWeapon: boolean;
weaponRewardChancePercent: number;
}
export interface IRepeatableQuestTypesConfig
@ -91,9 +91,9 @@ export interface IRepeatableQuestTypesConfig
export interface IExploration extends IBaseQuestConfig
{
maxExtracts: number
maxExtractsWithSpecificExit: number
specificExits: ISpecificExits
maxExtracts: number;
maxExtractsWithSpecificExit: number;
specificExits: ISpecificExits;
}
export interface ISpecificExits
@ -106,7 +106,7 @@ export interface ICompletion extends IBaseQuestConfig
{
minRequestedAmount: number;
maxRequestedAmount: number;
uniqueItemCount: number
uniqueItemCount: number;
minRequestedBulletAmount: number;
maxRequestedBulletAmount: number;
useWhitelist: boolean;

View File

@ -33,8 +33,8 @@ export interface ISendMessageDetails
export interface IProfileChangeEvent
{
_id: string
Type: "TraderSalesSum" | "TraderStanding" | "ProfileLevel" | "SkillPoints" | "ExamineAllItems" | "UnlockTrader"
value: number
entity?: string
_id: string;
Type: "TraderSalesSum" | "TraderStanding" | "ProfileLevel" | "SkillPoints" | "ExamineAllItems" | "UnlockTrader";
value: number;
entity?: string;
}

View File

@ -1,9 +1,9 @@
export interface IRaidChanges
{
/** What percentage of dynamic loot should the map contain */
dynamicLootPercent: number
dynamicLootPercent: number;
/** What percentage of static loot should the map contain */
staticLootPercent: number
staticLootPercent: number;
/** How many seconds into the raid is the player simulated to spawn in at */
simulatedRaidStartSeconds: number
}
simulatedRaidStartSeconds: number;
}

View File

@ -55,7 +55,7 @@ export interface IDatabaseTables
defaultEquipmentPresets: IDefaultEquipmentPreset[];
/** Achievements */
achievements: IAchievement[]
achievements: IAchievement[];
};
traders?: Record<string, ITrader>;

View File

@ -2,7 +2,7 @@ import { TraderServiceType } from "@spt-aki/models/enums/TraderServiceType";
export interface ITraderServiceModel
{
serviceType: TraderServiceType,
itemsToPay?: Record<string, number>[],
subServices?: Record<string, number>[],
}
serviceType: TraderServiceType;
itemsToPay?: Record<string, number>[];
subServices?: Record<string, number>[];
}

View File

@ -184,7 +184,7 @@ export class EventOutputHolder
}
// Return null if there's no crafts to send to client to match live behaviour
return (Object.keys(productions).length > 0) ? productions : null
return (Object.keys(productions).length > 0) ? productions : null;
}
/**

View File

@ -71,7 +71,7 @@ export class GameStaticRouter extends StaticRouter
{
return this.gameCallbacks.getRaidTime(url, info, sessionID);
},
)
),
]);
}
}

View File

@ -53,7 +53,7 @@ export class InraidStaticRouter extends StaticRouter
(url: string, info: any, sessionID: string, output: string): any =>
{
return this.inraidCallbacks.itemDelivery(url, info, sessionID);
}
},
),
]);
}

View File

@ -83,7 +83,11 @@ export class RagfairServer
return Object.keys(this.ragfairConfig.traders).filter((x) => this.ragfairConfig.traders[x]);
}
public getAllActiveCategories(fleaUnlocked: boolean, searchRequestData: ISearchRequestData, offers: IRagfairOffer[]): Record<string, number>
public getAllActiveCategories(
fleaUnlocked: boolean,
searchRequestData: ISearchRequestData,
offers: IRagfairOffer[],
): Record<string, number>
{
return this.ragfairCategoriesService.getCategoriesFromOffers(offers, searchRequestData, fleaUnlocked);
}

View File

@ -27,7 +27,7 @@ export class SaveServer
@inject("HashUtil") protected hashUtil: HashUtil,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("WinstonLogger") protected logger: ILogger,
@inject("ConfigServer") protected configServer: ConfigServer
@inject("ConfigServer") protected configServer: ConfigServer,
)
{}
@ -207,7 +207,10 @@ export class SaveServer
}
const start = performance.now();
const jsonProfile = this.jsonUtil.serialize(this.profiles[sessionID], !this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE).features.compressProfile);
const jsonProfile = this.jsonUtil.serialize(
this.profiles[sessionID],
!this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE).features.compressProfile,
);
const fmd5 = this.hashUtil.generateMd5ForData(jsonProfile);
if (typeof (this.saveMd5[sessionID]) !== "string" || this.saveMd5[sessionID] !== fmd5)
{

View File

@ -201,7 +201,13 @@ export class BotEquipmentModPoolService
protected generateGearPool(): void
{
const gear = Object.values(this.databaseServer.getTables().templates.items).filter((x) =>
x._type === "Item" && this.itemHelper.isOfBaseclasses(x._id, [BaseClasses.ARMORED_EQUIPMENT, BaseClasses.VEST, BaseClasses.ARMOR, BaseClasses.HEADWEAR])
x._type === "Item"
&& this.itemHelper.isOfBaseclasses(x._id, [
BaseClasses.ARMORED_EQUIPMENT,
BaseClasses.VEST,
BaseClasses.ARMOR,
BaseClasses.HEADWEAR,
])
);
this.generatePool(gear, "gear");

View File

@ -408,9 +408,9 @@ export class FenceService
{
const baseFenceAssort = this.databaseServer.getTables().traders[Traders.FENCE].assort;
const itemTypeCounts = this.initItemLimitCounter(this.traderConfig.fence.itemTypeLimits);
this.addItemAssorts(assortCount, assorts, baseFenceAssort, itemTypeCounts, loyaltyLevel);
// Add presets
const maxPresetCount = Math.round(assortCount * (this.traderConfig.fence.maxPresetsPercent / 100));
const randomisedPresetCount = this.randomUtil.getInt(0, maxPresetCount);
@ -426,17 +426,21 @@ export class FenceService
): void
{
const priceLimits = this.traderConfig.fence.itemCategoryRoublePriceLimit;
const assortRootItems = baseFenceAssort.items.filter(x => x.parentId === "hideout" && !x.upd?.sptPresetId);
const assortRootItems = baseFenceAssort.items.filter((x) => x.parentId === "hideout" && !x.upd?.sptPresetId);
for (let i = 0; i < assortCount; i++)
{
const chosenBaseAssortRoot = this.randomUtil.getArrayValue(assortRootItems);
if (!chosenBaseAssortRoot)
{
this.logger.error(this.localisationService.getText("fence-unable_to_find_assort_by_id", chosenBaseAssortRoot._id));
this.logger.error(
this.localisationService.getText("fence-unable_to_find_assort_by_id", chosenBaseAssortRoot._id),
);
continue;
}
const desiredAssortItemAndChildrenClone = this.jsonUtil.clone(this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, chosenBaseAssortRoot._id));
const desiredAssortItemAndChildrenClone = this.jsonUtil.clone(
this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, chosenBaseAssortRoot._id),
);
const itemDbDetails = this.itemHelper.getItem(chosenBaseAssortRoot._tpl)[1];
const itemLimitCount = itemTypeCounts[itemDbDetails._parent];
@ -446,7 +450,7 @@ export class FenceService
i--;
continue;
}
const itemIsPreset = this.presetHelper.isPreset(chosenBaseAssortRoot._id);
const price = baseFenceAssort.barter_scheme[chosenBaseAssortRoot._id][0][0].count;
@ -456,7 +460,7 @@ export class FenceService
i--;
continue;
}
if (price > priceLimits[itemDbDetails._parent])
{
// Too expensive for fence, try another item
@ -478,8 +482,8 @@ export class FenceService
this.randomiseItemUpdProperties(itemDbDetails, rootItemBeingAdded);
rootItemBeingAdded.upd.StackObjectsCount = this.getSingleItemStackCount(itemDbDetails);
//rootItemBeingAdded.upd.BuyRestrictionCurrent = 0;
//rootItemBeingAdded.upd.UnlimitedCount = false;
// rootItemBeingAdded.upd.BuyRestrictionCurrent = 0;
// rootItemBeingAdded.upd.UnlimitedCount = false;
// Need to add mods to armors so they dont show as red in the trade screen
if (this.itemHelper.itemRequiresSoftInserts(rootItemBeingAdded._tpl))
@ -495,12 +499,17 @@ export class FenceService
/**
* Find presets in base fence assort and add desired number to 'assorts' parameter
* @param desiredPresetCount
* @param assorts
* @param baseFenceAssort
* @param desiredPresetCount
* @param assorts
* @param baseFenceAssort
* @param loyaltyLevel Which loyalty level is required to see/buy item
*/
protected addPresetsToAssort(desiredPresetCount: number, assorts: ITraderAssort, baseFenceAssort: ITraderAssort, loyaltyLevel: number): void
protected addPresetsToAssort(
desiredPresetCount: number,
assorts: ITraderAssort,
baseFenceAssort: ITraderAssort,
loyaltyLevel: number,
): void
{
let presetsAddedCount = 0;
if (desiredPresetCount <= 0)
@ -508,12 +517,14 @@ export class FenceService
return;
}
const presetRootItems = baseFenceAssort.items.filter(item => item.upd?.sptPresetId);
const presetRootItems = baseFenceAssort.items.filter((item) => item.upd?.sptPresetId);
while (presetsAddedCount < desiredPresetCount)
{
const randomPresetRoot = this.randomUtil.getArrayValue(presetRootItems);
const rootItemDb = this.itemHelper.getItem(randomPresetRoot._tpl)[1];
const presetWithChildrenClone = this.jsonUtil.clone(this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, randomPresetRoot._id));
const presetWithChildrenClone = this.jsonUtil.clone(
this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, randomPresetRoot._id),
);
// Need to add mods to armors so they dont show as red in the trade screen
if (this.itemHelper.itemRequiresSoftInserts(randomPresetRoot._tpl))
@ -557,7 +568,7 @@ export class FenceService
}
// Check for and adjust soft insert durability values
const requiredSlots = itemDbDetails._props.Slots.filter(slot => slot._required);
const requiredSlots = itemDbDetails._props.Slots.filter((slot) => slot._required);
const hasRequiredSlots = requiredSlots.length > 0;
if (hasRequiredSlots)
{
@ -566,7 +577,8 @@ export class FenceService
const modItemDbDetails = this.itemHelper.getItem(requiredSlot._props.filters[0].Plate)[1];
const durabilityValues = this.getRandomisedArmorDurabilityValues(
modItemDbDetails,
this.traderConfig.fence.armorMaxDurabilityPercentMinMax);
this.traderConfig.fence.armorMaxDurabilityPercentMinMax,
);
const plateTpl = requiredSlot._props.filters[0].Plate; // `Plate` property appears to be the 'default' item for slot
if (plateTpl === "")
{
@ -575,36 +587,41 @@ export class FenceService
}
// Find items mod to apply dura changes to
const modItemToAdjust = armor.find(mod => mod.slotId.toLowerCase() === requiredSlot._name.toLowerCase());
const modItemToAdjust = armor.find((mod) =>
mod.slotId.toLowerCase() === requiredSlot._name.toLowerCase()
);
if (!modItemToAdjust.upd)
{
modItemToAdjust.upd = {}
modItemToAdjust.upd = {};
}
if (!modItemToAdjust.upd.Repairable)
{
modItemToAdjust.upd.Repairable = {
Durability: modItemDbDetails._props.MaxDurability,
MaxDurability: modItemDbDetails._props.MaxDurability
MaxDurability: modItemDbDetails._props.MaxDurability,
};
}
modItemToAdjust.upd.Repairable.Durability = durabilityValues.Durability;
modItemToAdjust.upd.Repairable.MaxDurability = durabilityValues.MaxDurability;
// 25% chance to add shots to visor when its below max durability
if (this.randomUtil.getChance100(25)
&& modItemToAdjust.parentId === BaseClasses.ARMORED_EQUIPMENT && modItemToAdjust.slotId === "mod_equipment_000"
&& modItemToAdjust.upd.Repairable.Durability < modItemDbDetails._props.MaxDurability) // Is damaged
{
modItemToAdjust.upd.FaceShield = {
Hits: this.randomUtil.getInt(1,3)
}
if (
this.randomUtil.getChance100(25)
&& modItemToAdjust.parentId === BaseClasses.ARMORED_EQUIPMENT
&& modItemToAdjust.slotId === "mod_equipment_000"
&& modItemToAdjust.upd.Repairable.Durability < modItemDbDetails._props.MaxDurability
)
{ // Is damaged
modItemToAdjust.upd.FaceShield = { Hits: this.randomUtil.getInt(1, 3) };
}
}
}
// Check for and adjust plate durability values
const plateSlots = itemDbDetails._props.Slots.filter(slot => this.itemHelper.isRemovablePlateSlot(slot._name));
const plateSlots = itemDbDetails._props.Slots.filter((slot) =>
this.itemHelper.isRemovablePlateSlot(slot._name)
);
if (plateSlots.length > 0)
{
for (const plateSlot of plateSlots)
@ -615,7 +632,7 @@ export class FenceService
continue;
}
const plateTpl = plateSlot._props.filters[0].Plate
const plateTpl = plateSlot._props.filters[0].Plate;
if (!plateTpl)
{
// Bsg data lacks a default plate, skip adding mod
@ -624,20 +641,21 @@ export class FenceService
const modItemDbDetails = this.itemHelper.getItem(plateTpl)[1];
const durabilityValues = this.getRandomisedArmorDurabilityValues(
modItemDbDetails,
this.traderConfig.fence.armorMaxDurabilityPercentMinMax);
this.traderConfig.fence.armorMaxDurabilityPercentMinMax,
);
// Find items mod to apply dura changes to
const modItemToAdjust = armor.find(mod => mod.slotId.toLowerCase() === plateSlot._name.toLowerCase());
const modItemToAdjust = armor.find((mod) => mod.slotId.toLowerCase() === plateSlot._name.toLowerCase());
if (!modItemToAdjust.upd)
{
modItemToAdjust.upd = {}
modItemToAdjust.upd = {};
}
if (!modItemToAdjust.upd.Repairable)
{
modItemToAdjust.upd.Repairable = {
Durability: modItemDbDetails._props.MaxDurability,
MaxDurability: modItemDbDetails._props.MaxDurability
MaxDurability: modItemDbDetails._props.MaxDurability,
};
}
@ -755,11 +773,13 @@ export class FenceService
if (
(itemDetails._parent === BaseClasses.ARMORED_EQUIPMENT
|| itemDetails._parent === BaseClasses.FACECOVER
|| itemDetails._parent === BaseClasses.ARMOR_PLATE
) && itemDetails._props.MaxDurability > 0
|| itemDetails._parent === BaseClasses.ARMOR_PLATE) && itemDetails._props.MaxDurability > 0
)
{
const values = this.getRandomisedArmorDurabilityValues(itemDetails, this.traderConfig.fence.armorMaxDurabilityPercentMinMax);
const values = this.getRandomisedArmorDurabilityValues(
itemDetails,
this.traderConfig.fence.armorMaxDurabilityPercentMinMax,
);
itemToAdjust.upd.Repairable = { Durability: values.Durability, MaxDurability: values.MaxDurability };
return;
@ -816,7 +836,10 @@ export class FenceService
* @param maxDurabilityMinMaxPercent Max durabiltiy percent min/max values
* @returns Durability + MaxDurability values
*/
protected getRandomisedArmorDurabilityValues(itemDetails: ITemplateItem, maxDurabilityMinMaxPercent: MinMax): Repairable
protected getRandomisedArmorDurabilityValues(
itemDetails: ITemplateItem,
maxDurabilityMinMaxPercent: MinMax,
): Repairable
{
const duraMin = maxDurabilityMinMaxPercent.min / 100 * itemDetails._props.MaxDurability;
const duraMax = maxDurabilityMinMaxPercent.max / 100 * itemDetails._props.MaxDurability;
@ -905,11 +928,11 @@ export class FenceService
public amendOrRemoveFenceOffer(assortId: string, buyCount: number): void
{
let isNormalAssort = true;
let fenceAssortItem = this.fenceAssort.items.find(item => item._id === assortId);
let fenceAssortItem = this.fenceAssort.items.find((item) => item._id === assortId);
if (!fenceAssortItem)
{
// Not in main assorts, check secondary section
fenceAssortItem = this.fenceDiscountAssort.items.find(item => item._id === assortId);
fenceAssortItem = this.fenceDiscountAssort.items.find((item) => item._id === assortId);
if (!fenceAssortItem)
{
this.logger.error(`Offer with id: ${assortId} not found`);
@ -937,7 +960,7 @@ export class FenceService
const itemWithChildrenToRemove = this.itemHelper.findAndReturnChildrenAsItems(assorts, assortId);
for (const itemToRemove of itemWithChildrenToRemove)
{
let indexToRemove = assorts.findIndex(item => item._id === itemToRemove._id);
let indexToRemove = assorts.findIndex((item) => item._id === itemToRemove._id);
// No offer found in main assort, check discount items
if (indexToRemove === -1)
@ -947,7 +970,9 @@ export class FenceService
if (indexToRemove === -1)
{
this.logger.warning(`unable to remove fence assort item: ${itemToRemove._id} tpl: ${itemToRemove._tpl}`)
this.logger.warning(
`unable to remove fence assort item: ${itemToRemove._id} tpl: ${itemToRemove._tpl}`,
);
}
return;

View File

@ -137,7 +137,8 @@ export class GiftService
senderDetails: {
_id: this.getSenderId(giftData),
aid: 1234567, // TODO - pass proper aid value
Info: null },
Info: null,
},
messageText: giftData.messageText,
items: giftData.items,
itemsMaxStorageLifetimeSeconds: this.timeUtil.getHoursAsSeconds(giftData.collectionTimeHours),

View File

@ -108,7 +108,7 @@ export class InsuranceService
const systemData = {
date: this.timeUtil.getDateMailFormat(),
time: this.timeUtil.getTimeMailFormat(),
location: mapId
location: mapId,
};
// Send "i will go look for your stuff" message from trader to player
this.mailSendService.sendLocalisedNpcMessageToPlayer(
@ -118,7 +118,7 @@ export class InsuranceService
this.randomUtil.getArrayValue(dialogueTemplates.insuranceStart),
null,
null,
systemData
systemData,
);
// Store insurance to send to player later in profile
@ -247,7 +247,7 @@ export class InsuranceService
}
// Item iterated on could have already been processed previously (as a child of another item)
if (equipmentToSendToPlayer.some(item => item.itemsToReturnToPlayer._id === insuredItem.itemId))
if (equipmentToSendToPlayer.some((item) => item.itemsToReturnToPlayer._id === insuredItem.itemId))
{
continue;
}
@ -256,14 +256,17 @@ export class InsuranceService
// Catches both events: player died with item on + player survived but dropped item in raid
if (!offRaidGearHash[insuredItem.itemId] || playerDied)
{
equipmentToSendToPlayer.push(
{
equipmentToSendToPlayer.push({
pmcData: pmcData,
itemsToReturnToPlayer: this.getInsuredItemDetails(
pmcData,
this.itemHelper.findAndReturnChildrenAsItems(Object.values(preRaidGearHash), preRaidItem._id, true),
offraidData.insurance,
pmcData,
this.itemHelper.findAndReturnChildrenAsItems(
Object.values(preRaidGearHash),
preRaidItem._id,
true,
),
offraidData.insurance,
),
traderId: insuredItem.tid,
sessionID: sessionID,
});
@ -287,13 +290,13 @@ export class InsuranceService
protected getInsuredItemDetails(
pmcData: IPmcData,
preRaidItemWithChildren: Item[],
allItemsFromClient: IInsuredItemsData[]
allItemsFromClient: IInsuredItemsData[],
): Item[]
{
const itemsToReturn: Item[] = [];
for (const preRaidItem of preRaidItemWithChildren)
{
const isInsured = pmcData.InsuredItems.some(item => item.itemId === preRaidItem._id);
const isInsured = pmcData.InsuredItems.some((item) => item.itemId === preRaidItem._id);
const itemClientInsuranceData = allItemsFromClient?.find((x) => x.id === preRaidItem._id);
const itemIsSoftInsert = this.itemHelper.isOfBaseclass(preRaidItem._tpl, BaseClasses.BUILT_IN_INSERTS);
@ -437,7 +440,7 @@ export class InsuranceService
this.addInsuranceItemToArray(sessionId, traderId, itemsToReturnToPlayer);
// Remove item from insured items array as its been processed
const returnedItemIds = itemsToReturnToPlayer.map(item => item._id);
const returnedItemIds = itemsToReturnToPlayer.map((item) => item._id);
pmcData.InsuredItems = pmcData.InsuredItems.filter((item) => !returnedItemIds.includes(item.itemId));
}

View File

@ -113,9 +113,7 @@ export class LocaleService
return platformLocale.language;
}
this.logger.warning(
`Unsupported system langauge found: ${localeCode}, falling back to english`,
);
this.logger.warning(`Unsupported system langauge found: ${localeCode}, falling back to english`);
return "en";
}
@ -140,9 +138,7 @@ export class LocaleService
const langaugeCode = platformLocale.language.toLowerCase();
if (!this.localeConfig.serverSupportedLocales.includes(langaugeCode))
{
this.logger.warning(
`Unsupported system langauge found: ${langaugeCode}, falling back to english`,
);
this.logger.warning(`Unsupported system langauge found: ${langaugeCode}, falling back to english`);
return "en";
}

View File

@ -23,7 +23,6 @@ export class LocalisationService
@inject("LocaleService") protected localeService: LocaleService,
)
{
const localeFileDirectory = path.join(
process.cwd(),
globalThis.G_RELEASE_CONFIGURATION

View File

@ -17,7 +17,6 @@ export class MatchLocationService
public createGroup(sessionID: string, info: ICreateGroupRequestData): any
{
const account = this.saveServer.getProfile(sessionID).info;
const groupID = "test";
@ -31,13 +30,7 @@ export class MatchLocationService
isSavage: false,
timeShift: "CURR",
dt: this.timeUtil.getTimestamp(),
players: [{
_id: account.id,
region: "EUR",
ip: "127.0.0.1",
savageId: account.scavId,
accessKeyId: "",
}],
players: [{ _id: account.id, region: "EUR", ip: "127.0.0.1", savageId: account.scavId, accessKeyId: "" }],
customDataCenter: [],
};

View File

@ -222,9 +222,7 @@ export class PaymentService
const rootCurrencyReward = {
_id: this.hashUtil.generate(),
_tpl: currency,
upd: {
StackObjectsCount: calcAmount
}
upd: { StackObjectsCount: calcAmount },
};
const rewards = this.itemHelper.splitStackIntoSeparateItems(rootCurrencyReward);
@ -234,7 +232,7 @@ export class PaymentService
itemsWithModsToAdd: rewards,
foundInRaid: false,
callback: null,
useSortingTable: true
useSortingTable: true,
};
this.inventoryHelper.addItemsToStash(sessionID, addItemToStashRequest, pmcData, output);
}

View File

@ -64,7 +64,7 @@ export class ProfileFixerService
this.addMissingRepeatableQuestsProperty(pmcProfile);
this.addLighthouseKeeperIfMissing(pmcProfile);
this.addUnlockedInfoObjectIfMissing(pmcProfile);
this.removeOrphanedQuests(pmcProfile);
this.removeOrphanedQuests(pmcProfile);
if (pmcProfile.Inventory)
{
@ -243,13 +243,8 @@ export class ProfileFixerService
if (!stashItem)
{
// Stand inventory stash item doesnt exist, add it
pmcProfile.Inventory.items.push(
{
_id: hideoutStandAreaDb._id,
_tpl: stageCurrentAt.container
}
)
stashItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandAreaDb._id)
pmcProfile.Inventory.items.push({ _id: hideoutStandAreaDb._id, _tpl: stageCurrentAt.container });
stashItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandAreaDb._id);
}
// `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected
@ -266,13 +261,8 @@ export class ProfileFixerService
if (!stashSecondaryItem)
{
// Stand inventory stash item doesnt exist, add it
pmcProfile.Inventory.items.push(
{
_id: hideoutStandSecondaryAreaDb._id,
_tpl: stageCurrentAt.container
}
)
stashSecondaryItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandSecondaryAreaDb._id)
pmcProfile.Inventory.items.push({ _id: hideoutStandSecondaryAreaDb._id, _tpl: stageCurrentAt.container });
stashSecondaryItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandSecondaryAreaDb._id);
}
// `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected
@ -330,13 +320,8 @@ export class ProfileFixerService
if (!stashItem)
{
// Stand inventory stash item doesnt exist, add it
pmcProfile.Inventory.items.push(
{
_id: placeOfFameAreaDb._id,
_tpl: stageCurrentlyAt.container
}
)
stashItem = pmcProfile.Inventory.items?.find((x) => x._id === placeOfFameAreaDb._id)
pmcProfile.Inventory.items.push({ _id: placeOfFameAreaDb._id, _tpl: stageCurrentlyAt.container });
stashItem = pmcProfile.Inventory.items?.find((x) => x._id === placeOfFameAreaDb._id);
}
// `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected
@ -521,16 +506,22 @@ export class ProfileFixerService
const taskConditionKeysToRemove: string[] = [];
const activeRepeatableQuests = this.getActiveRepeatableQuests(pmcProfile.RepeatableQuests);
const achievements = this.databaseServer.getTables().templates.achievements;
// Loop over TaskConditionCounters objects and add once we want to remove to counterKeysToRemove
for (const [key, taskConditionCounter] of Object.entries(pmcProfile.TaskConditionCounters))
{
// Only check if profile has repeatable quests
if (pmcProfile.RepeatableQuests && activeRepeatableQuests.length > 0)
{
const existsInActiveRepeatableQuests = activeRepeatableQuests.some((quest) => quest._id === taskConditionCounter.sourceId);
const existsInQuests = pmcProfile.Quests.some((quest) => quest.qid === taskConditionCounter.sourceId);
const isAchievementTracker = achievements.some((quest) => quest.id === taskConditionCounter.sourceId);
const existsInActiveRepeatableQuests = activeRepeatableQuests.some((quest) =>
quest._id === taskConditionCounter.sourceId
);
const existsInQuests = pmcProfile.Quests.some((quest) =>
quest.qid === taskConditionCounter.sourceId
);
const isAchievementTracker = achievements.some((quest) =>
quest.id === taskConditionCounter.sourceId
);
// If task conditions id is neither in activeQuests, quests or achievements - it's stale and should be cleaned up
if (!(existsInActiveRepeatableQuests || existsInQuests || isAchievementTracker))
@ -1119,7 +1110,7 @@ export class ProfileFixerService
if (itemAJson === itemBJson)
{
// Both items match, we can safely delete one
const indexOfItemToRemove = pmcProfile.Inventory.items.findIndex(x => x._id === key);
const indexOfItemToRemove = pmcProfile.Inventory.items.findIndex((x) => x._id === key);
pmcProfile.Inventory.items.splice(indexOfItemToRemove, 1);
this.logger.warning(`Deleted duplicate item: ${key}`);
}
@ -1127,10 +1118,10 @@ export class ProfileFixerService
{
// Items are different, replace ID with unique value
// Only replace ID if items have no children, we dont want orphaned children
const itemsHaveChildren = pmcProfile.Inventory.items.some(x => x.parentId === key);
const itemsHaveChildren = pmcProfile.Inventory.items.some((x) => x.parentId === key);
if (!itemsHaveChildren)
{
const itemToAdjustId = pmcProfile.Inventory.items.find(x => x._id === key);
const itemToAdjustId = pmcProfile.Inventory.items.find((x) => x._id === key);
itemToAdjustId._id = this.hashUtil.generate();
this.logger.warning(`Replace duplicate item Id: ${key} with ${itemToAdjustId._id}`);
}
@ -1138,7 +1129,7 @@ export class ProfileFixerService
}
// Iterate over all inventory items
for (const item of pmcProfile.Inventory.items.filter(x => x.slotId))
for (const item of pmcProfile.Inventory.items.filter((x) => x.slotId))
{
if (!item.upd)
{
@ -1151,7 +1142,7 @@ export class ProfileFixerService
if (regxp.test(item.upd.Tag?.Name))
{
this.logger.warning(`Fixed item: ${item._id}s Tag value, removed invalid characters`);
item.upd.Tag.Name = item.upd.Tag.Name.replace(regxp, '');
item.upd.Tag.Name = item.upd.Tag.Name.replace(regxp, "");
}
// Check items with StackObjectsCount (null)
@ -1170,36 +1161,36 @@ export class ProfileFixerService
// Check Head
if (!customizationDb[pmcProfile.Customization.Head])
{
const defaultHead = (playerIsUsec)
? customizationDbArray.find(x => x._name === "DefaultUsecHead")
: customizationDbArray.find(x => x._name === "DefaultBearHead");
const defaultHead = playerIsUsec
? customizationDbArray.find((x) => x._name === "DefaultUsecHead")
: customizationDbArray.find((x) => x._name === "DefaultBearHead");
pmcProfile.Customization.Head = defaultHead._id;
}
//check Body
// check Body
if (!customizationDb[pmcProfile.Customization.Body])
{
const defaultBody = (pmcProfile.Info.Side.toLowerCase() === "usec")
? customizationDbArray.find(x => x._name === "DefaultUsecBody")
: customizationDbArray.find(x => x._name === "DefaultBearBody");
? customizationDbArray.find((x) => x._name === "DefaultUsecBody")
: customizationDbArray.find((x) => x._name === "DefaultBearBody");
pmcProfile.Customization.Body = defaultBody._id;
}
//check Hands
// check Hands
if (!customizationDb[pmcProfile.Customization.Hands])
{
const defaultHands = (pmcProfile.Info.Side.toLowerCase() === "usec")
? customizationDbArray.find(x => x._name === "DefaultUsecHands")
: customizationDbArray.find(x => x._name === "DefaultBearHands");
? customizationDbArray.find((x) => x._name === "DefaultUsecHands")
: customizationDbArray.find((x) => x._name === "DefaultBearHands");
pmcProfile.Customization.Hands = defaultHands._id;
}
//check Hands
// check Hands
if (!customizationDb[pmcProfile.Customization.Feet])
{
const defaultFeet = (pmcProfile.Info.Side.toLowerCase() === "usec")
? customizationDbArray.find(x => x._name === "DefaultUsecFeet")
: customizationDbArray.find(x => x._name === "DefaultBearFeet");
? customizationDbArray.find((x) => x._name === "DefaultUsecFeet")
: customizationDbArray.find((x) => x._name === "DefaultBearFeet");
pmcProfile.Customization.Feet = defaultFeet._id;
}
}
@ -1276,7 +1267,7 @@ export class ProfileFixerService
{
// Not a number, regenerate
// biome-ignore lint/suspicious/noGlobalIsNan: <value can be a valid string, Number.IsNaN() would ignore it>
if (isNaN(fullProfile.characters.pmc.aid) || !fullProfile.info.aid)
if (isNaN(fullProfile.characters.pmc.aid) || !fullProfile.info.aid)
{
fullProfile.characters.pmc.sessionId = <string><unknown>fullProfile.characters.pmc.aid;
fullProfile.characters.pmc.aid = this.hashUtil.generateAccountId();
@ -1371,28 +1362,28 @@ export class ProfileFixerService
}
}
/**
* After removing mods that add quests, the quest panel will break without removing these
* @param pmcProfile Profile to remove dead quests from
*/
/**
* After removing mods that add quests, the quest panel will break without removing these
* @param pmcProfile Profile to remove dead quests from
*/
protected removeOrphanedQuests(pmcProfile: IPmcData): void
{
const quests = this.databaseServer.getTables().templates.quests;
const profileQuests = pmcProfile.Quests;
const repeatableQuests: IRepeatableQuest[] = [];
for (const repeatableQuestType of pmcProfile.RepeatableQuests)
{
repeatableQuests.push(...repeatableQuestType.activeQuests);
}
for (let i = 0; i < profileQuests.length; i++)
{
if (!quests[profileQuests[i].qid] && !repeatableQuests.find(x => x._id == profileQuests[i].qid))
if (!quests[profileQuests[i].qid] && !repeatableQuests.find((x) => x._id == profileQuests[i].qid))
{
profileQuests.splice(i, 1);
this.logger.success("Successfully removed orphaned quest that doesnt exist in our quest data");
}
}
}
}
}
}

View File

@ -22,46 +22,53 @@ export class RagfairCategoriesService
* @param fleaUnlocked Can player see full flea yet (level 15 by default)
* @returns KVP of item tpls + count of offers
*/
public getCategoriesFromOffers(offers: IRagfairOffer[], searchRequestData: ISearchRequestData, fleaUnlocked: boolean): Record<string, number>
public getCategoriesFromOffers(
offers: IRagfairOffer[],
searchRequestData: ISearchRequestData,
fleaUnlocked: boolean,
): Record<string, number>
{
// Get offers valid for search request, then reduce them down to just the counts
return offers.filter(offer =>
return offers.filter((offer) =>
{
const isTraderOffer = offer.user.memberType === MemberCategory.TRADER;
const isTraderOffer = offer.user.memberType === MemberCategory.TRADER;
// Not level 15 and offer is from player, skip
if (!fleaUnlocked && !isTraderOffer)
{
return false;
}
// Not level 15 and offer is from player, skip
if (!fleaUnlocked && !isTraderOffer)
{
return false;
}
// Remove items not for money when `removeBartering` is enabled
if (searchRequestData.removeBartering && (offer.requirements.length > 1 || !this.paymentHelper.isMoneyTpl(offer.requirements[0]._tpl)))
{
return false;
}
// Remove items not for money when `removeBartering` is enabled
if (
searchRequestData.removeBartering
&& (offer.requirements.length > 1 || !this.paymentHelper.isMoneyTpl(offer.requirements[0]._tpl))
)
{
return false;
}
// Remove when filter set to players only + offer is from trader
if (searchRequestData.offerOwnerType === OfferOwnerType.PLAYEROWNERTYPE && isTraderOffer)
{
return false;
}
// Remove when filter set to players only + offer is from trader
if (searchRequestData.offerOwnerType === OfferOwnerType.PLAYEROWNERTYPE && isTraderOffer)
{
return false;
}
// Remove when filter set to traders only + offer is not from trader
if (searchRequestData.offerOwnerType === OfferOwnerType.TRADEROWNERTYPE && !isTraderOffer)
{
return false;
}
// Remove when filter set to traders only + offer is not from trader
if (searchRequestData.offerOwnerType === OfferOwnerType.TRADEROWNERTYPE && !isTraderOffer)
{
return false;
}
// Passed checks, its a valid offer to process
return true;
}).reduce((acc, offer) =>
{
const itemTpl = offer.items[0]._tpl;
// Increment the category or add if doesnt exist
acc[itemTpl] = (acc[itemTpl] || 0) + 1;
// Passed checks, its a valid offer to process
return true;
}).reduce((acc, offer) =>
{
const itemTpl = offer.items[0]._tpl;
// Increment the category or add if doesnt exist
acc[itemTpl] = (acc[itemTpl] || 0) + 1;
return acc;
}, {});
return acc;
}, {});
}
}

View File

@ -243,7 +243,9 @@ export class RagfairOfferService
const offerinProfileIndex = profile.RagfairInfo.offers.findIndex((o) => o._id === playerOffer._id);
if (offerinProfileIndex === -1)
{
this.logger.warning(this.localisationService.getText("ragfair-unable_to_find_offer_to_remove", playerOffer._id));
this.logger.warning(
this.localisationService.getText("ragfair-unable_to_find_offer_to_remove", playerOffer._id),
);
return;
}

View File

@ -401,12 +401,12 @@ export class RagfairPriceService implements OnLoad
*/
protected getWeaponPreset(weapon: Item): { isDefault: boolean; preset: IPreset; }
{
const defaultPreset = this.presetHelper.getDefaultPreset(weapon._tpl)
const defaultPreset = this.presetHelper.getDefaultPreset(weapon._tpl);
if (defaultPreset)
{
return { isDefault: true, preset: defaultPreset };
}
const nonDefaultPresets = this.presetHelper.getPresets(weapon._tpl)
const nonDefaultPresets = this.presetHelper.getPresets(weapon._tpl);
if (nonDefaultPresets.length === 1)
{
this.logger.debug(

View File

@ -43,9 +43,9 @@ export class RagfairTaxService
// It's structured to resemble the client-side code as closely as possible - avoid making any big structure changes if it's not necessary.
* @param item Item being sold on flea
* @param pmcData player profile
* @param requirementsValue
* @param requirementsValue
* @param offerItemCount Number of offers being created
* @param sellInOnePiece
* @param sellInOnePiece
* @returns Tax in roubles
*/
public calculateTax(

View File

@ -7,7 +7,11 @@ import { ILocationBase } from "@spt-aki/models/eft/common/ILocationBase";
import { IGetRaidTimeRequest } from "@spt-aki/models/eft/game/IGetRaidTimeRequest";
import { ExtractChange, IGetRaidTimeResponse } from "@spt-aki/models/eft/game/IGetRaidTimeResponse";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { ILocationConfig, IScavRaidTimeLocationSettings, LootMultiplier } from "@spt-aki/models/spt/config/ILocationConfig";
import {
ILocationConfig,
IScavRaidTimeLocationSettings,
LootMultiplier,
} from "@spt-aki/models/spt/config/ILocationConfig";
import { IRaidChanges } from "@spt-aki/models/spt/location/IRaidChanges";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
@ -39,7 +43,9 @@ export class RaidTimeAdjustmentService
*/
public makeAdjustmentsToMap(raidAdjustments: IRaidChanges, mapBase: ILocationBase): void
{
this.logger.debug(`Adjusting dynamic loot multipliers to ${raidAdjustments.dynamicLootPercent}% and static loot multipliers to ${raidAdjustments.staticLootPercent}% of original`);
this.logger.debug(
`Adjusting dynamic loot multipliers to ${raidAdjustments.dynamicLootPercent}% and static loot multipliers to ${raidAdjustments.staticLootPercent}% of original`,
);
// Change loot multipler values before they're used below
this.adjustLootMultipliers(this.locationConfig.looseLootMultiplier, raidAdjustments.dynamicLootPercent);
@ -48,8 +54,8 @@ export class RaidTimeAdjustmentService
const mapSettings = this.getMapSettings(mapBase.Id);
if (mapSettings.adjustWaves)
{
// Make alterations to bot spawn waves now player is simulated spawning later
this.adjustWaves(mapBase, raidAdjustments)
// Make alterations to bot spawn waves now player is simulated spawning later
this.adjustWaves(mapBase, raidAdjustments);
}
}
@ -75,7 +81,7 @@ export class RaidTimeAdjustmentService
{
// Remove waves that spawned before the player joined
const originalWaveCount = mapBase.waves.length;
mapBase.waves = mapBase.waves.filter(x => x.time_max > raidAdjustments.simulatedRaidStartSeconds);
mapBase.waves = mapBase.waves.filter((x) => x.time_max > raidAdjustments.simulatedRaidStartSeconds);
// Adjust wave min/max times to match new simulated start
for (const wave of mapBase.waves)
@ -85,7 +91,11 @@ export class RaidTimeAdjustmentService
wave.time_max -= Math.max(raidAdjustments.simulatedRaidStartSeconds, 0);
}
this.logger.debug(`Removed ${originalWaveCount - mapBase.waves.length} wave from map due to simulated raid start time of ${raidAdjustments.simulatedRaidStartSeconds / 60} minutes`);
this.logger.debug(
`Removed ${originalWaveCount - mapBase.waves.length} wave from map due to simulated raid start time of ${
raidAdjustments.simulatedRaidStartSeconds / 60
} minutes`,
);
}
/**
@ -106,8 +116,8 @@ export class RaidTimeAdjustmentService
RaidTimeMinutes: baseEscapeTimeMinutes,
ExitChanges: [],
NewSurviveTimeSeconds: null,
OriginalSurvivalTimeSeconds: db.globals.config.exp.match_end.survived_seconds_requirement
}
OriginalSurvivalTimeSeconds: db.globals.config.exp.match_end.survived_seconds_requirement,
};
// Pmc raid, send default
if (request.Side.toLowerCase() === "pmc")
@ -124,15 +134,17 @@ export class RaidTimeAdjustmentService
// Send default
return result;
}
// Get the weighted percent to reduce the raid time by
const chosenRaidReductionPercent = Number.parseInt(this.weightedRandomHelper.getWeightedValue<string>(
mapSettings.reductionPercentWeights,
));
const chosenRaidReductionPercent = Number.parseInt(
this.weightedRandomHelper.getWeightedValue<string>(mapSettings.reductionPercentWeights),
);
const raidTimeRemainingPercent = 100 - chosenRaidReductionPercent;
// How many minutes raid will last
const newRaidTimeMinutes = Math.floor(this.randomUtil.reduceValueByPercent(baseEscapeTimeMinutes, chosenRaidReductionPercent));
const newRaidTimeMinutes = Math.floor(
this.randomUtil.reduceValueByPercent(baseEscapeTimeMinutes, chosenRaidReductionPercent),
);
// Time player spawns into the raid if it was online
const simulatedRaidStartTimeMinutes = baseEscapeTimeMinutes - newRaidTimeMinutes;
@ -140,21 +152,25 @@ export class RaidTimeAdjustmentService
if (mapSettings.reduceLootByPercent)
{
// Store time reduction percent in app context so loot gen can pick it up later
this.applicationContext.addValue(ContextVariableType.RAID_ADJUSTMENTS,
{
dynamicLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minDynamicLootPercent),
staticLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minStaticLootPercent),
simulatedRaidStartSeconds: simulatedRaidStartTimeMinutes * 60
});
this.applicationContext.addValue(ContextVariableType.RAID_ADJUSTMENTS, {
dynamicLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minDynamicLootPercent),
staticLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minStaticLootPercent),
simulatedRaidStartSeconds: simulatedRaidStartTimeMinutes * 60,
});
}
// Update result object with new time
result.RaidTimeMinutes = newRaidTimeMinutes;
this.logger.debug(`Reduced: ${request.Location} raid time by: ${chosenRaidReductionPercent}% to ${newRaidTimeMinutes} minutes`)
this.logger.debug(
`Reduced: ${request.Location} raid time by: ${chosenRaidReductionPercent}% to ${newRaidTimeMinutes} minutes`,
);
// Calculate how long player needs to be in raid to get a `survived` extract status
result.NewSurviveTimeSeconds = Math.max(result.OriginalSurvivalTimeSeconds - ((baseEscapeTimeMinutes - newRaidTimeMinutes) * 60), 0);
result.NewSurviveTimeSeconds = Math.max(
result.OriginalSurvivalTimeSeconds - ((baseEscapeTimeMinutes - newRaidTimeMinutes) * 60),
0,
);
const exitAdjustments = this.getExitAdjustments(mapBase, newRaidTimeMinutes);
if (exitAdjustments)
@ -188,74 +204,73 @@ export class RaidTimeAdjustmentService
* @param newRaidTimeMinutes How long raid is in minutes
* @returns List of exit changes to send to client
*/
protected getExitAdjustments(mapBase: ILocationBase, newRaidTimeMinutes: number): ExtractChange[]
protected getExitAdjustments(mapBase: ILocationBase, newRaidTimeMinutes: number): ExtractChange[]
{
const result = [];
// Adjust train exits only
for (const exit of mapBase.exits)
{
const result = [];
// Adjust train exits only
for (const exit of mapBase.exits)
if (exit.PassageRequirement !== "Train")
{
if (exit.PassageRequirement !== "Train")
{
continue;
}
// Prepare train adjustment object
const exitChange: ExtractChange = {
Name: exit.Name,
MinTime: null,
MaxTime: null,
Chance: null
}
// At what minute we simulate the player joining the raid
const simulatedRaidEntryTimeMinutes = mapBase.EscapeTimeLimit - newRaidTimeMinutes;
// How many seconds have elapsed in the raid when the player joins
const reductionSeconds = simulatedRaidEntryTimeMinutes * 60;
// Delay between the train extract activating and it becoming available to board
//
// Test method for determining this value:
// 1) Set MinTime, MaxTime, and Count for the train extract all to 120
// 2) Load into Reserve or Lighthouse as a PMC (both have the same result)
// 3) Board the train when it arrives
// 4) Check the raid time on the Raid Ended Screen (it should always be the same)
//
// trainArrivalDelaySeconds = [raid time on raid-ended screen] - MaxTime - Count - ExfiltrationTime
// Example: Raid Time = 5:33 = 333 seconds
// trainArrivalDelaySeconds = 333 - 120 - 120 - 5 = 88
//
// I added 2 seconds just to be safe...
//
const trainArrivalDelaySeconds = this.locationConfig.scavRaidTimeSettings.settings.trainArrivalDelayObservedSeconds;
// Determine the earliest possible time in the raid when the train would leave
const earliestPossibleDepartureMinutes = (exit.MinTime + exit.Count + exit.ExfiltrationTime + trainArrivalDelaySeconds) / 60;
// If raid is after last moment train can leave, assume train has already left, disable extract
const mostPossibleTimeRemainingAfterDeparture = mapBase.EscapeTimeLimit - earliestPossibleDepartureMinutes;
if (newRaidTimeMinutes < mostPossibleTimeRemainingAfterDeparture)
{
exitChange.Chance = 0;
this.logger.debug(`Train Exit: ${exit.Name} disabled as new raid time ${newRaidTimeMinutes} minutes is below ${mostPossibleTimeRemainingAfterDeparture} minutes`);
result.push(exitChange);
continue;
}
// Reduce extract arrival times. Negative values seem to make extract turn red in game.
exitChange.MinTime = Math.max(exit.MinTime - reductionSeconds, 0);
exitChange.MaxTime = Math.max(exit.MaxTime - reductionSeconds, 0);
this.logger.debug(`Train appears between: ${exitChange.MinTime} and ${exitChange.MaxTime} seconds raid time`);
result.push(exitChange);
continue;
}
return result.length > 0
? result
: null ;
// Prepare train adjustment object
const exitChange: ExtractChange = { Name: exit.Name, MinTime: null, MaxTime: null, Chance: null };
// At what minute we simulate the player joining the raid
const simulatedRaidEntryTimeMinutes = mapBase.EscapeTimeLimit - newRaidTimeMinutes;
// How many seconds have elapsed in the raid when the player joins
const reductionSeconds = simulatedRaidEntryTimeMinutes * 60;
// Delay between the train extract activating and it becoming available to board
//
// Test method for determining this value:
// 1) Set MinTime, MaxTime, and Count for the train extract all to 120
// 2) Load into Reserve or Lighthouse as a PMC (both have the same result)
// 3) Board the train when it arrives
// 4) Check the raid time on the Raid Ended Screen (it should always be the same)
//
// trainArrivalDelaySeconds = [raid time on raid-ended screen] - MaxTime - Count - ExfiltrationTime
// Example: Raid Time = 5:33 = 333 seconds
// trainArrivalDelaySeconds = 333 - 120 - 120 - 5 = 88
//
// I added 2 seconds just to be safe...
//
const trainArrivalDelaySeconds =
this.locationConfig.scavRaidTimeSettings.settings.trainArrivalDelayObservedSeconds;
// Determine the earliest possible time in the raid when the train would leave
const earliestPossibleDepartureMinutes =
(exit.MinTime + exit.Count + exit.ExfiltrationTime + trainArrivalDelaySeconds) / 60;
// If raid is after last moment train can leave, assume train has already left, disable extract
const mostPossibleTimeRemainingAfterDeparture = mapBase.EscapeTimeLimit - earliestPossibleDepartureMinutes;
if (newRaidTimeMinutes < mostPossibleTimeRemainingAfterDeparture)
{
exitChange.Chance = 0;
this.logger.debug(
`Train Exit: ${exit.Name} disabled as new raid time ${newRaidTimeMinutes} minutes is below ${mostPossibleTimeRemainingAfterDeparture} minutes`,
);
result.push(exitChange);
continue;
}
// Reduce extract arrival times. Negative values seem to make extract turn red in game.
exitChange.MinTime = Math.max(exit.MinTime - reductionSeconds, 0);
exitChange.MaxTime = Math.max(exit.MaxTime - reductionSeconds, 0);
this.logger.debug(
`Train appears between: ${exitChange.MinTime} and ${exitChange.MaxTime} seconds raid time`,
);
result.push(exitChange);
}
return result.length > 0 ? result : null;
}
}

View File

@ -124,7 +124,7 @@ export class RepairService
const options: IProcessBuyTradeRequestData = {
scheme_items: [{
id: "5449016a4bdc2d6f028b456f", // Rouble tpl
count: Math.round(repairCost)
count: Math.round(repairCost),
}],
tid: traderId,
Action: "SptRepair",
@ -214,10 +214,7 @@ export class RepairService
else
{
// Trader repair - Not as accurate as kit, needs data from live
return Math.min(
repairDetails.repairAmount / 10,
this.repairConfig.maxIntellectGainPerRepair.trader,
);
return Math.min(repairDetails.repairAmount / 10, this.repairConfig.maxIntellectGainPerRepair.trader);
}
}
@ -440,7 +437,13 @@ export class RepairService
if (this.shouldBuffItem(repairDetails, pmcData))
{
if (this.itemHelper.isOfBaseclasses(repairDetails.repairedItem._tpl, [BaseClasses.ARMOR, BaseClasses.VEST, BaseClasses.HEADWEAR]))
if (
this.itemHelper.isOfBaseclasses(repairDetails.repairedItem._tpl, [
BaseClasses.ARMOR,
BaseClasses.VEST,
BaseClasses.HEADWEAR,
])
)
{
const armorConfig = this.repairConfig.repairKit.armor;
this.addBuff(armorConfig, repairDetails.repairedItem);
@ -539,7 +542,13 @@ export class RepairService
*/
protected getItemSkillType(itemTemplate: ITemplateItem): SkillTypes
{
if (this.itemHelper.isOfBaseclasses(itemTemplate._id, [BaseClasses.ARMOR, BaseClasses.VEST, BaseClasses.HEADWEAR]))
if (
this.itemHelper.isOfBaseclasses(itemTemplate._id, [
BaseClasses.ARMOR,
BaseClasses.VEST,
BaseClasses.HEADWEAR,
])
)
{
if (itemTemplate._props.ArmorType === "Light")
{

View File

@ -529,7 +529,7 @@ export class SeasonalEventService
{
const mapData: ILocationData = maps[gifterMapSettings.map];
// Dont add gifter to map twice
if (mapData.base.BossLocationSpawn.some(boss => boss.BossName === "gifter"))
if (mapData.base.BossLocationSpawn.some((boss) => boss.BossName === "gifter"))
{
continue;
}

View File

@ -17,4 +17,4 @@ export class TraderServicesService
const traderServices = this.databaseServer.getTables().traders[traderId]?.services;
return traderServices ?? [];
}
}
}

View File

@ -211,7 +211,9 @@ export class CustomItemService
const weapon = this.itemHelper.getItem(weaponTpl);
if (!weapon[0])
{
this.logger.warning(`Unable to add custom weapon ${weaponTpl} to PMCs as it cannot be found in the Item db`);
this.logger.warning(
`Unable to add custom weapon ${weaponTpl} to PMCs as it cannot be found in the Item db`,
);
return;
}

View File

@ -84,7 +84,7 @@ export class DatabaseImporter implements OnLoad
const imageFilePath = `${this.filepath}images/`;
const directories = this.vfs.getDirs(imageFilePath);
this.loadImages(imageFilePath, directories, [
"/files/achievement/",
"/files/achievement/",
"/files/CONTENT/banners/",
"/files/handbook/",
"/files/Hideout/",

View File

@ -51,4 +51,4 @@ export class HashUtil
const max = 1999999;
return (max > min) ? Math.floor(Math.random() * (max - min + 1) + min) : min;
}
}
}

View File

@ -83,11 +83,7 @@ export class HttpResponseUtil
{
if (output.warnings?.length > 0)
{
output.warnings.push({
index: output.warnings?.length - 1,
errmsg: message,
code: errorCode.toString()
})
output.warnings.push({ index: output.warnings?.length - 1, errmsg: message, code: errorCode.toString() });
}
else
{

View File

@ -200,9 +200,7 @@ export class ProbabilityObject<K, V = undefined>
@injectable()
export class RandomUtil
{
constructor(
@inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("WinstonLogger") protected logger: ILogger)
constructor(@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("WinstonLogger") protected logger: ILogger)
{
}
@ -296,15 +294,15 @@ export class RandomUtil
v = Math.random();
}
const w = Math.sqrt(-2.0 * Math.log(u)) * Math.cos((2.0 * Math.PI) * v);
const valueDrawn = mean + w * sigma;
const valueDrawn = mean + w * sigma;
if (valueDrawn < 0)
{
if (attempt > 100)
{
return this.getFloat(0.01, mean * 2);
}
if (attempt > 100)
{
return this.getFloat(0.01, mean * 2);
}
return this.getNormallyDistributedRandomNumber(mean, sigma, attempt++);
return this.getNormallyDistributedRandomNumber(mean, sigma, attempt++);
}
return valueDrawn;
@ -486,7 +484,7 @@ export class RandomUtil
// Roll a number between 0 and 1
const rolledChance = this.getInt(0, maxRoll) / 10000;
return rolledChance <= probabilityChance;
}
}

View File

@ -25,22 +25,19 @@ describe("HealthController", () =>
{
it("Should Heal Players heavy bleed and heal chest to full hp", () =>
{
const maxHealth = 100
const maxHealth = 100;
const pmcData = {
Health: {
BodyParts: {
Chest: {
Health: {
Current: 50, // Has damage
Maximum: maxHealth
Maximum: maxHealth,
},
Effects: {HeavyBleeding: {
Time: 20
}
}
}
}
}
Effects: { HeavyBleeding: { Time: 20 } },
},
},
},
};
const bleedRemovalAndLimbHealRequest = {
Action: "RestoreHealth",
@ -49,62 +46,53 @@ describe("HealthController", () =>
BodyParts: {
Chest: {
Health: 23, // > 0 value means it will heal
Effects: ["HeavyBleeding"] // non-null means it will remove effect from player
}
}
}
Effects: ["HeavyBleeding"], // non-null means it will remove effect from player
},
},
},
};
const sessionId = "12345";
// Mock output generation
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue(
{
warnings: {},
profileChanges: {
"12345": {
health: {}
}
}
});
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue({
warnings: {},
profileChanges: { "12345": { health: {} } },
});
// Mock payment
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue(
{
warnings: {},
profileChanges: {
"12345": {
health: {}
}
}
});
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue({
warnings: {},
profileChanges: { "12345": { health: {} } },
});
const result = healthController.healthTreatment(
pmcData as unknown as IPmcData,
bleedRemovalAndLimbHealRequest as IHealthTreatmentRequestData,
sessionId,
);
const result = healthController.healthTreatment(pmcData as unknown as IPmcData, bleedRemovalAndLimbHealRequest as IHealthTreatmentRequestData, sessionId);
// Has healed chest to full
expect(result.profileChanges[sessionId].health.BodyParts.Chest.Health.Current).equals(maxHealth);
// Has removed Heavy bleed effect from chest
expect(result.profileChanges[sessionId].health.BodyParts.Chest).not.toHaveProperty("Effects");
});
it("Should Heal Players heavy bleed and leave limb health at existing value", () =>
{
const maxHealth = 100
const maxHealth = 100;
const pmcData = {
Health: {
BodyParts: {
Chest: {
Health: {
Current: 50, // Has damage
Maximum: maxHealth
Maximum: maxHealth,
},
Effects: {HeavyBleeding: {
Time: 20
}
}
}
}
}
Effects: { HeavyBleeding: { Time: 20 } },
},
},
},
};
const limbOnlyHealRequest = {
Action: "RestoreHealth",
@ -113,47 +101,41 @@ describe("HealthController", () =>
BodyParts: {
Chest: {
Health: 23, // > 0 value means it will heal limb to full
Effects: null // null means no healing of effects
}
}
}
Effects: null, // null means no healing of effects
},
},
},
};
const sessionId = "12345";
// Mock output generation
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue(
{
warnings: {},
profileChanges: {
"12345": {
health: {}
}
}
});
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue({
warnings: {},
profileChanges: { "12345": { health: {} } },
});
// Mock payment
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue(
{
warnings: {},
profileChanges: {
"12345": {
health: {}
}
}
});
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue({
warnings: {},
profileChanges: { "12345": { health: {} } },
});
const result = healthController.healthTreatment(
pmcData as unknown as IPmcData,
limbOnlyHealRequest as IHealthTreatmentRequestData,
sessionId,
);
const result = healthController.healthTreatment(pmcData as unknown as IPmcData, limbOnlyHealRequest as IHealthTreatmentRequestData, sessionId);
// Has healed chest to full
expect(result.profileChanges[sessionId].health.BodyParts.Chest.Health.Current).equals(maxHealth);
// Has not removed Heavy bleed effect from chest
expect(result.profileChanges[sessionId].health.BodyParts.Chest).toHaveProperty("Effects");
});
it("Should Heal Players heavy bleed and leave limb health at existing value", () =>
{
const maxHealth = 100
const maxHealth = 100;
const currentHealth = 50;
const pmcData = {
Health: {
@ -161,15 +143,12 @@ describe("HealthController", () =>
Chest: {
Health: {
Current: currentHealth, // Has damage
Maximum: maxHealth
Maximum: maxHealth,
},
Effects: {HeavyBleeding: {
Time: 20
}
}
}
}
}
Effects: { HeavyBleeding: { Time: 20 } },
},
},
},
};
const limbOnlyHealRequest = {
Action: "RestoreHealth",
@ -178,40 +157,34 @@ describe("HealthController", () =>
BodyParts: {
Chest: {
Health: 0, // 0 value means it will not heal and damage
Effects: null // null means no healing of effects
}
}
}
Effects: null, // null means no healing of effects
},
},
},
};
const sessionId = "12345";
// Mock output generation
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue(
{
warnings: {},
profileChanges: {
"12345": {
health: {}
}
}
});
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue({
warnings: {},
profileChanges: { "12345": { health: {} } },
});
// Mock payment
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue(
{
warnings: {},
profileChanges: {
"12345": {
health: {}
}
}
});
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue({
warnings: {},
profileChanges: { "12345": { health: {} } },
});
const result = healthController.healthTreatment(
pmcData as unknown as IPmcData,
limbOnlyHealRequest as IHealthTreatmentRequestData,
sessionId,
);
const result = healthController.healthTreatment(pmcData as unknown as IPmcData, limbOnlyHealRequest as IHealthTreatmentRequestData, sessionId);
// Has not healed chest to full
expect(result.profileChanges[sessionId].health.BodyParts.Chest.Health.Current).equals(currentHealth);
// Has not removed Heavy bleed effect from chest
expect(result.profileChanges[sessionId].health.BodyParts.Chest).toHaveProperty("Effects");
});