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 * Handle client/achievement/list
*
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars // 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)); return this.httpResponse.getBody(this.achievementController.getAchievements(sessionID));
} }
/** /**
* Handle client/achievement/statistic * Handle client/achievement/statistic
*
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars // 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)); 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 // eslint-disable-next-line @typescript-eslint/no-unused-vars
public createMagazineTemplate(url: string, request: ISetMagazineRequest, sessionID: string): INullResponseData public createMagazineTemplate(url: string, request: ISetMagazineRequest, sessionID: string): INullResponseData
{ {
this.buildController.createMagazineTemplate(sessionID, request) this.buildController.createMagazineTemplate(sessionID, request);
return this.httpResponse.nullResponse(); return this.httpResponse.nullResponse();
} }

View File

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

View File

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

View File

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

View File

@ -30,7 +30,7 @@ export class InventoryCallbacks
{ {
constructor( constructor(
@inject("InventoryController") protected inventoryController: InventoryController, @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); return this.inventoryController.openRandomLootContainer(pmcData, body, sessionID);
} }
public redeemProfileReward(pmcData: IPmcData, public redeemProfileReward(
pmcData: IPmcData,
body: IRedeemProfileRequestData, body: IRedeemProfileRequestData,
sessionId: string sessionId: string,
): IItemEventRouterResponse ): IItemEventRouterResponse
{ {
return this.inventoryController.redeemProfileReward(pmcData, body, sessionId) return this.inventoryController.redeemProfileReward(pmcData, body, sessionId);
} }
public setFavoriteItem(pmcData: IPmcData, public setFavoriteItem(pmcData: IPmcData, body: ISetFavoriteItems, sessionId: string): IItemEventRouterResponse
body: ISetFavoriteItems,
sessionId: string): IItemEventRouterResponse
{ {
return this.inventoryController.setFavoriteItem(pmcData, body, sessionId); return this.inventoryController.setFavoriteItem(pmcData, body, sessionId);
} }

View File

@ -160,7 +160,11 @@ export class MatchCallbacks
* @returns * @returns
*/ */
// eslint-disable-next-line @typescript-eslint/no-unused-vars // 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)); return this.httpResponse.getBody(this.matchController.getGroupStatus(info));
} }

View File

@ -34,7 +34,11 @@ export class ProfileCallbacks
/** /**
* Handle client/game/profile/create * 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); const id = this.profileController.createProfile(info, sessionID);

View File

@ -69,7 +69,7 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
// Process all offers / expire offers // Process all offers / expire offers
await this.ragfairServer.update(); await this.ragfairServer.update();
return true; return true;
} }
return false; return false;
@ -142,7 +142,11 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
} }
/** Handle client/ragfair/offer/findbyid */ /** 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)); 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 */ /** Logged in users session id */
SESSION_ID = 0, SESSION_ID = 0,
/** Currently acive raid information */ /** Currently acive raid information */

View File

@ -23,7 +23,7 @@ export class AchievementController
*/ */
public getAchievements(sessionID: string): IGetAchievementsResponse 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 public getAchievementStatistics(sessionId: string): ICompletedAchievementsResponse
{ {
const achievements = this.databaseServer.getTables().templates.achievements; const achievements = this.databaseServer.getTables().templates.achievements;
const stats = {} const stats = {};
for (const achievement of achievements) for (const achievement of achievements)
{ {
stats[achievement.id] = 0; 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.generateBotsFirstTime(info, pmcProfile, sessionId);
} }
return this.returnSingleBotFromCache(sessionId, info); return this.returnSingleBotFromCache(sessionId, info);
} }
@ -162,11 +162,17 @@ export class BotController
* @param request Bot generation request object * @param request Bot generation request object
* @param pmcProfile Player profile * @param pmcProfile Player profile
* @param sessionId Session id * @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) for (const condition of request.conditions)
{ {
const botGenerationDetails: BotGenerationDetails = { const botGenerationDetails: BotGenerationDetails = {
@ -180,7 +186,7 @@ export class BotController
botCountToGenerate: this.botConfig.presetBatch[condition.Role], botCountToGenerate: this.botConfig.presetBatch[condition.Role],
botDifficulty: condition.Difficulty, botDifficulty: condition.Difficulty,
isPlayerScav: false, isPlayerScav: false,
allPmcsHaveSameNameAsPlayer: allPmcsHaveSameNameAsPlayer allPmcsHaveSameNameAsPlayer: allPmcsHaveSameNameAsPlayer,
}; };
// Event bots need special actions to occur, set data up for them // 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 // Add eventRole data + reassign role property to be base type
botGenerationDetails.eventRole = condition.Role; botGenerationDetails.eventRole = condition.Role;
botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot( botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot(
botGenerationDetails.eventRole botGenerationDetails.eventRole,
); );
} }
@ -338,9 +344,9 @@ export class BotController
public getBotCap(): number public getBotCap(): number
{ {
const defaultMapCapId = "default"; const defaultMapCapId = "default";
const raidConfig = this.applicationContext const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<
.getLatestValue(ContextVariableType.RAID_CONFIGURATION) IGetRaidConfigurationRequestData
.getValue<IGetRaidConfigurationRequestData>(); >();
if (!raidConfig) if (!raidConfig)
{ {
@ -366,9 +372,10 @@ export class BotController
public getAiBotBrainTypes(): any public getAiBotBrainTypes(): any
{ {
return { return {
pmc: this.pmcConfig.pmcType, pmc: this.pmcConfig.pmcType,
assault: this.botConfig.assaultBrainType, 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 */ /** Handle client/builds/weapon/save */
public saveWeaponBuild( public saveWeaponBuild(sessionId: string, body: IPresetBuildActionRequestData): void
sessionId: string,
body: IPresetBuildActionRequestData,
): void
{ {
const pmcData = this.profileHelper.getPmcProfile(sessionId); const pmcData = this.profileHelper.getPmcProfile(sessionId);
@ -84,12 +81,7 @@ export class BuildController
body.Root = body.Items[0]._id; body.Root = body.Items[0]._id;
// Create new object ready to save into profile userbuilds.weaponBuilds // Create new object ready to save into profile userbuilds.weaponBuilds
const newBuild: IWeaponBuild = { const newBuild: IWeaponBuild = { Id: body.Id, Name: body.Name, Root: body.Root, Items: body.Items };
Id: body.Id,
Name: body.Name,
Root: body.Root,
Items: body.Items,
};
const savedWeaponBuilds = this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds; const savedWeaponBuilds = this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds;
const existingBuild = savedWeaponBuilds.find((x) => x.Id === body.Id); const existingBuild = savedWeaponBuilds.find((x) => x.Id === body.Id);
@ -110,15 +102,13 @@ export class BuildController
} }
/** Handle client/builds/equipment/save event */ /** Handle client/builds/equipment/save event */
public saveEquipmentBuild( public saveEquipmentBuild(sessionID: string, request: IPresetBuildActionRequestData): void
sessionID: string,
request: IPresetBuildActionRequestData,
): void
{ {
const buildType = "equipmentBuilds"; const buildType = "equipmentBuilds";
const pmcData = this.profileHelper.getPmcProfile(sessionID); 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. // Replace duplicate ID's. The first item is the base item.
// Root ID and the base item ID need to match. // Root ID and the base item ID need to match.
@ -214,16 +204,15 @@ export class BuildController
profile.userbuilds.magazineBuilds = []; 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) if (existingArrayId === -1)
{ {
profile.userbuilds.magazineBuilds.push(result); profile.userbuilds.magazineBuilds.push(result);
} }
else 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.checkTraderRepairValuesExist();
this.adjustLocationBotValues() this.adjustLocationBotValues();
// repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in // 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 // 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) if (this.coreConfig.fixes.fixProfileBreakingInventoryItemIssues)
{ {
this.profileFixerService.fixProfileBreakingInventoryItemIssues(pmcProfile) this.profileFixerService.fixProfileBreakingInventoryItemIssues(pmcProfile);
} }
if (pmcProfile.Health) if (pmcProfile.Health)
@ -439,7 +439,10 @@ export class GameController
public getGameConfig(sessionID: string): IGameConfigResponse public getGameConfig(sessionID: string): IGameConfigResponse
{ {
const profile = this.profileHelper.getPmcProfile(sessionID); 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 = { const config: IGameConfigResponse = {
languages: this.databaseServer.getTables().locales.languages, languages: this.databaseServer.getTables().locales.languages,
@ -574,10 +577,8 @@ export class GameController
(sum, curr) => sum + curr.value, (sum, curr) => sum + curr.value,
0, 0,
); );
hydrationRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === BonusType.HYDRATION_REGENERATION).reduce( hydrationRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === BonusType.HYDRATION_REGENERATION)
(sum, curr) => sum + curr.value, .reduce((sum, curr) => sum + curr.value, 0);
0,
);
hpRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === BonusType.HEALTH_REGENERATION).reduce( hpRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === BonusType.HEALTH_REGENERATION).reduce(
(sum, curr) => sum + curr.value, (sum, curr) => sum + curr.value,
0, 0,
@ -822,7 +823,7 @@ export class GameController
dateAdded: Date.now(), dateAdded: Date.now(),
name: modDetails.name, name: modDetails.name,
version: modDetails.version, 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 { PresetHelper } from "@spt-aki/helpers/PresetHelper";
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper"; import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData"; 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 { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { HideoutUpgradeCompleteRequestData } from "@spt-aki/models/eft/hideout/HideoutUpgradeCompleteRequestData"; import { HideoutUpgradeCompleteRequestData } from "@spt-aki/models/eft/hideout/HideoutUpgradeCompleteRequestData";
import { IHandleQTEEventRequestData } from "@spt-aki/models/eft/hideout/IHandleQTEEventRequestData"; import { IHandleQTEEventRequestData } from "@spt-aki/models/eft/hideout/IHandleQTEEventRequestData";
@ -503,9 +509,9 @@ export class HideoutController
itemWithModsToAdd: [itemToReturn], itemWithModsToAdd: [itemToReturn],
foundInRaid: !!itemToReturn.upd.SpawnedInSession, foundInRaid: !!itemToReturn.upd.SpawnedInSession,
callback: null, callback: null,
useSortingTable: false useSortingTable: false,
} };
this.inventoryHelper.addItemToStash(sessionID, request, pmcData, output); this.inventoryHelper.addItemToStash(sessionID, request, pmcData, output);
if (output.warnings && output.warnings.length > 0) if (output.warnings && output.warnings.length > 0)
{ {
@ -787,10 +793,7 @@ export class HideoutController
const preset = this.presetHelper.getDefaultPreset(recipe.endProduct); 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 // 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( const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(preset._items));
null,
this.jsonUtil.clone(preset._items),
);
this.itemHelper.remapRootItemId(presetAndMods); this.itemHelper.remapRootItemId(presetAndMods);
@ -805,9 +808,7 @@ export class HideoutController
const rewardToAdd: Item = { const rewardToAdd: Item = {
_id: this.hashUtil.generate(), _id: this.hashUtil.generate(),
_tpl: recipe.endProduct, _tpl: recipe.endProduct,
upd: { upd: { StackObjectsCount: recipe.count },
StackObjectsCount: recipe.count
}
}; };
// Split item into separate items with acceptable stack sizes // 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) // Add the first reward item to array when not a preset (first preset added above earlier)
if (!rewardIsPreset) if (!rewardIsPreset)
{ {
itemAndChildrenToSendToPlayer.push([{ itemAndChildrenToSendToPlayer.push([{ _id: this.hashUtil.generate(), _tpl: recipe.endProduct }]);
_id: this.hashUtil.generate(),
_tpl: recipe.endProduct
}]);
} }
// Add multiple of item if recipe requests it // Add multiple of item if recipe requests it
@ -846,10 +844,9 @@ export class HideoutController
{ {
for (const reward of itemAndChildrenToSendToPlayer) for (const reward of itemAndChildrenToSendToPlayer)
{ {
if (!reward[0].upd) if (!reward[0].upd)
{ {
reward[0].upd = {} reward[0].upd = {};
} }
reward[0].upd.RecodableComponent = { IsEncoded: true }; reward[0].upd.RecodableComponent = { IsEncoded: true };
@ -916,7 +913,7 @@ export class HideoutController
itemsWithModsToAdd: itemAndChildrenToSendToPlayer, itemsWithModsToAdd: itemAndChildrenToSendToPlayer,
foundInRaid: true, foundInRaid: true,
useSortingTable: false, useSortingTable: false,
callback: null callback: null,
}; };
// Add FiR crafted items(s) to player inventory // Add FiR crafted items(s) to player inventory
@ -946,13 +943,8 @@ export class HideoutController
const intellectAmountToGive = 0.5 * (Math.round(craftingExpAmount / 15)); const intellectAmountToGive = 0.5 * (Math.round(craftingExpAmount / 15));
if (intellectAmountToGive > 0) if (intellectAmountToGive > 0)
{ {
this.profileHelper.addSkillPointsToPlayer( this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.INTELLECT, intellectAmountToGive);
pmcData,
SkillTypes.INTELLECT,
intellectAmountToGive,
);
} }
} }
area.lastRecipe = request.recipeId; area.lastRecipe = request.recipeId;
@ -1042,7 +1034,7 @@ export class HideoutController
itemsWithModsToAdd: scavCaseRewards, itemsWithModsToAdd: scavCaseRewards,
foundInRaid: true, foundInRaid: true,
callback: null, callback: null,
useSortingTable: false useSortingTable: false,
}; };
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output); this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);

View File

@ -348,7 +348,7 @@ export class InraidController
{ {
for (const quest of scavProfile.Quests) 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) if (!pmcQuest)
{ {
this.logger.warning(`No PMC quest found for ID: ${quest.qid}`); 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 // 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 // This is to stop items being duplicated by being returned from both the item delivery, and insurance
const deliveredItemIds = items.map(x => x._id); const deliveredItemIds = items.map((x) => x._id);
pmcData.InsuredItems = pmcData.InsuredItems.filter(x => !deliveredItemIds.includes(x.itemId)); pmcData.InsuredItems = pmcData.InsuredItems.filter((x) => !deliveredItemIds.includes(x.itemId));
// Send the items to the player // Send the items to the player
this.mailSendService.sendLocalisedNpcMessageToPlayer( this.mailSendService.sendLocalisedNpcMessageToPlayer(
sessionId, sessionId,

View File

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

View File

@ -132,7 +132,7 @@ export class LauncherController
return profileId; return profileId;
} }
protected generateProfileId(): string protected generateProfileId(): string
{ {
const timestamp = this.timeUtil.getTimestamp(); const timestamp = this.timeUtil.getTimestamp();
@ -142,8 +142,8 @@ export class LauncherController
protected formatID(timeStamp: number, counter: number): string protected formatID(timeStamp: number, counter: number): string
{ {
const timeStampStr = timeStamp.toString(16).padStart(8, '0'); const timeStampStr = timeStamp.toString(16).padStart(8, "0");
const counterStr = counter.toString(16).padStart(16, '0'); const counterStr = counter.toString(16).padStart(16, "0");
return timeStampStr.toLowerCase() + counterStr.toLowerCase(); 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 // Check for a loot multipler adjustment in app context and apply if one is found
let locationConfigCopy: ILocationConfig; 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) if (raidAdjustments)
{ {
locationConfigCopy = this.jsonUtil.clone(this.locationConfig); // Clone values so they can be used to reset originals later 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, inraid: {} as Inraid,
insurance: [], insurance: [],
traderPurchases: {}, traderPurchases: {},
achievements: {} achievements: {},
}; };
this.profileFixerService.checkForAndFixPmcProfileIssues(profileDetails.characters.pmc); 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 } }]; return [{ _id: this.hashUtil.generate(), Info: { Level: 1, Side: "Bear", Nickname: info.nickname } }];
} }
/** /**
* Handle client/profile/status * Handle client/profile/status
*/ */
public getProfileStatus(sessionId: string): GetProfileStatusResponseData public getProfileStatus(sessionId: string): GetProfileStatusResponseData
@ -399,20 +399,14 @@ export class ProfileController
const account = this.saveServer.getProfile(sessionId).info; const account = this.saveServer.getProfile(sessionId).info;
const response: GetProfileStatusResponseData = { const response: GetProfileStatusResponseData = {
maxPveCountExceeded: false, maxPveCountExceeded: false,
profiles: [{ profiles: [{ profileid: account.scavId, profileToken: null, status: "Free", sid: "", ip: "", port: 0 }, {
profileid: account.scavId,
profileToken: null,
status: "Free",
sid: "",
ip: "",
port: 0,
}, {
profileid: account.id, profileid: account.id,
profileToken: null, profileToken: null,
status: "Free", status: "Free",
sid: "", sid: "",
ip: "", ip: "",
port: 0 }], port: 0,
}],
}; };
return response; return response;
@ -422,7 +416,7 @@ export class ProfileController
{ {
const player = this.profileHelper.getFullProfile(sessionId); const player = this.profileHelper.getFullProfile(sessionId);
const playerPmc = player.characters.pmc; const playerPmc = player.characters.pmc;
// return player for now // return player for now
return { return {
id: playerPmc._id, id: playerPmc._id,
@ -434,34 +428,34 @@ export class ProfileController
memberCategory: playerPmc.Info.MemberCategory, memberCategory: playerPmc.Info.MemberCategory,
bannedState: playerPmc.Info.BannedState, bannedState: playerPmc.Info.BannedState,
bannedUntil: playerPmc.Info.BannedUntil, bannedUntil: playerPmc.Info.BannedUntil,
registrationDate: playerPmc.Info.RegistrationDate registrationDate: playerPmc.Info.RegistrationDate,
}, },
customization: { customization: {
head: playerPmc.Customization.Head, head: playerPmc.Customization.Head,
body: playerPmc.Customization.Body, body: playerPmc.Customization.Body,
feet: playerPmc.Customization.Feet, feet: playerPmc.Customization.Feet,
hands: playerPmc.Customization.Hands hands: playerPmc.Customization.Hands,
}, },
skills: playerPmc.Skills, skills: playerPmc.Skills,
equipment: { equipment: {
// Default inventory tpl // Default inventory tpl
Id: playerPmc.Inventory.items.find(x => x._tpl === "55d7217a4bdc2d86028b456d")._id, Id: playerPmc.Inventory.items.find((x) => x._tpl === "55d7217a4bdc2d86028b456d")._id,
Items: playerPmc.Inventory.items Items: playerPmc.Inventory.items,
}, },
achievements: playerPmc.Achievements, achievements: playerPmc.Achievements,
favoriteItems: playerPmc.Inventory.favoriteItems ?? [], favoriteItems: playerPmc.Inventory.favoriteItems ?? [],
pmcStats: { pmcStats: {
eft: { eft: {
totalInGameTime: playerPmc.Stats.Eft.TotalInGameTime, totalInGameTime: playerPmc.Stats.Eft.TotalInGameTime,
overAllCounters: playerPmc.Stats.Eft.OverallCounters overAllCounters: playerPmc.Stats.Eft.OverallCounters,
} },
}, },
scavStats: { scavStats: {
eft: { eft: {
totalInGameTime: player.characters.scav.Stats.Eft.TotalInGameTime, 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]; const trader = profile.TradersInfo[quest.traderId];
if (!trader) 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; continue;
} }
@ -135,7 +137,9 @@ export class QuestController
for (const conditionToFulfil of questRequirements) for (const conditionToFulfil of questRequirements)
{ {
// If the previous quest isn't in the user profile, it hasn't been completed or started // 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) if (!prerequisiteQuest)
{ {
haveCompletedPreviousQuest = false; haveCompletedPreviousQuest = false;
@ -307,10 +311,10 @@ export class QuestController
): IItemEventRouterResponse ): IItemEventRouterResponse
{ {
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID); const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
// Does quest exist in profile // Does quest exist in profile
// Restarting a failed quest can mean quest exists 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) if (existingQuestStatus)
{ {
// Update existing // Update existing
@ -356,9 +360,10 @@ export class QuestController
); );
// Having accepted new quest, look for newly unlocked quests and inform client of them // Having accepted new quest, look for newly unlocked quests and inform client of them
acceptQuestResponse.profileChanges[sessionID].quests.push(...this.questHelper acceptQuestResponse.profileChanges[sessionID].quests.push(
.getNewlyAccessibleQuestsWhenStartingQuest(acceptedQuest.qid, sessionID)); ...this.questHelper.getNewlyAccessibleQuestsWhenStartingQuest(acceptedQuest.qid, sessionID),
);
return acceptQuestResponse; return acceptQuestResponse;
} }
@ -380,7 +385,11 @@ export class QuestController
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID); const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
// Create and store quest status object inside player profile // 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); pmcData.Quests.push(newRepeatableQuest);
// Look for the generated quest cache in profile.RepeatableQuests // Look for the generated quest cache in profile.RepeatableQuests
@ -431,7 +440,7 @@ export class QuestController
if (!acceptQuestResponse.profileChanges[sessionID].repeatableQuests) if (!acceptQuestResponse.profileChanges[sessionID].repeatableQuests)
{ {
acceptQuestResponse.profileChanges[sessionID].repeatableQuests = [] acceptQuestResponse.profileChanges[sessionID].repeatableQuests = [];
} }
acceptQuestResponse.profileChanges[sessionID].repeatableQuests.push(responseData); 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 // Check if it's a repeatable quest. If so, remove from Quests
for (const currentRepeatable of pmcData.RepeatableQuests) 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) if (repeatableQuest)
{ {
// Need to remove redundant scav quest object as its no longer necessary, is tracked in pmc profile // 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) if (nextQuestWaitCondition)
{ {
// Now + wait time // Now + wait time
const availableAfterTimestamp = this.timeUtil.getTimestamp() const availableAfterTimestamp = this.timeUtil.getTimestamp() + nextQuestWaitCondition.availableAfter;
+ nextQuestWaitCondition.availableAfter;
// Update quest in profile with status of AvailableAfter // Update quest in profile with status of AvailableAfter
const existingQuestInProfile = pmcData.Quests.find((x) => x.qid === quest._id); const existingQuestInProfile = pmcData.Quests.find((x) => x.qid === quest._id);
@ -912,7 +922,8 @@ export class QuestController
id: conditionId, id: conditionId,
sourceId: questId, sourceId: questId,
type: "HandoverItem", type: "HandoverItem",
value: counterValue }; value: counterValue,
};
} }
/** /**

View File

@ -97,7 +97,7 @@ export class RagfairController
const pmcProfile = this.profileHelper.getPmcProfile(sessionID); const pmcProfile = this.profileHelper.getPmcProfile(sessionID);
result.offers = this.getOffersForSearchType(searchRequest, itemsToAdd, traderAssorts, pmcProfile); result.offers = this.getOffersForSearchType(searchRequest, itemsToAdd, traderAssorts, pmcProfile);
if (searchRequest.updateOfferCount) if (searchRequest.updateOfferCount)
{ {
result.categories = this.getSpecificCategories(pmcProfile, searchRequest, result.offers); result.categories = this.getSpecificCategories(pmcProfile, searchRequest, result.offers);
@ -158,7 +158,7 @@ export class RagfairController
public getOfferById(sessionId: string, request: IGetRagfairOfferByIdRequest): IRagfairOffer public getOfferById(sessionId: string, request: IGetRagfairOfferByIdRequest): IRagfairOffer
{ {
const offers = this.ragfairOfferService.getOffers(); 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; return offerToReturn;
} }
@ -199,10 +199,15 @@ export class RagfairController
* @param offers ragfair offers to get categories for * @param offers ragfair offers to get categories for
* @returns record with templates + counts * @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 // 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 = []; let offerPool = [];
if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest)) if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest))
{ {
@ -698,7 +703,9 @@ export class RagfairController
if (playerOfferIndex === -1) if (playerOfferIndex === -1)
{ {
this.logger.error( 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( return this.httpResponse.appendErrorToOutput(
output, output,
@ -735,7 +742,9 @@ export class RagfairController
if (playerOfferIndex === -1) if (playerOfferIndex === -1)
{ {
this.logger.warning( 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( return this.httpResponse.appendErrorToOutput(
output, output,

View File

@ -83,7 +83,7 @@ export class RepeatableQuestController
* *
* @param {string} _info Request from client * @param {string} _info Request from client
* @param {string} sessionID Player's session id * @param {string} sessionID Player's session id
* *
* @returns {array} Array of "repeatableQuestObjects" as descibed above * @returns {array} Array of "repeatableQuestObjects" as descibed above
*/ */
public getClientRepeatableQuests(_info: IEmptyRequestData, sessionID: string): IPmcDataRepeatableQuest[] public getClientRepeatableQuests(_info: IEmptyRequestData, sessionID: string): IPmcDataRepeatableQuest[]
@ -120,7 +120,7 @@ export class RepeatableQuestController
for (const activeQuest of currentRepeatableQuestType.activeQuests) for (const activeQuest of currentRepeatableQuestType.activeQuests)
{ {
// Keep finished quests in list so player can hand in // 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)
{ {
if (quest.status === QuestStatus.AvailableForFinish) if (quest.status === QuestStatus.AvailableForFinish)
@ -136,7 +136,7 @@ export class RepeatableQuestController
this.profileFixerService.removeDanglingConditionCounters(pmcData); this.profileFixerService.removeDanglingConditionCounters(pmcData);
// Remove expired quest from pmc.quest array // 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.inactiveQuests.push(activeQuest);
} }
currentRepeatableQuestType.activeQuests = questsToKeep; 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 { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer";
import { IProcessBaseTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBaseTradeRequestData"; import { IProcessBaseTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBaseTradeRequestData";
import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData"; 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 { IProcessSellTradeRequestData } from "@spt-aki/models/eft/trade/IProcessSellTradeRequestData";
import { ISellScavItemsToFenceRequestData } from "@spt-aki/models/eft/trade/ISellScavItemsToFenceRequestData"; import { ISellScavItemsToFenceRequestData } from "@spt-aki/models/eft/trade/ISellScavItemsToFenceRequestData";
import { BackendErrorCodes } from "@spt-aki/models/enums/BackendErrorCodes"; import { BackendErrorCodes } from "@spt-aki/models/enums/BackendErrorCodes";
@ -146,14 +149,20 @@ export class TradeController
* @param requestOffer request data from client * @param requestOffer request data from client
* @param output Output to send back to 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 // Skip buying items when player doesn't have needed loyalty
if (this.playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer, pmcData)) if (this.playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer, pmcData))
{ {
const errorMessage = `Unable to buy item: ${ const errorMessage = `Unable to buy item: ${
fleaOffer.items[0]._tpl 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.logger.debug(errorMessage);
this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.RAGFAIRUNAVAILABLE); this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.RAGFAIRUNAVAILABLE);
@ -182,7 +191,13 @@ export class TradeController
* @param requestOffer Request data from client * @param requestOffer Request data from client
* @param output Output to send back to 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 = { const buyData: IProcessBuyTradeRequestData = {
Action: "TradingConfirm", 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 // 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) if (output.warnings.length > 0)
{ {
return; return;
@ -231,10 +252,7 @@ export class TradeController
const scavProfile = this.profileHelper.getFullProfile(sessionId)?.characters?.scav; const scavProfile = this.profileHelper.getFullProfile(sessionId)?.characters?.scav;
if (!scavProfile) if (!scavProfile)
{ {
return this.httpResponse.appendErrorToOutput( return this.httpResponse.appendErrorToOutput(output, `Profile ${request.fromOwner.id} has no scav account`);
output,
`Profile ${request.fromOwner.id} has no scav account`,
);
} }
this.sellInventoryToTrader(sessionId, scavProfile, pmcData, Traders.FENCE, output); this.sellInventoryToTrader(sessionId, scavProfile, pmcData, Traders.FENCE, output);
@ -256,7 +274,7 @@ export class TradeController
profileWithItemsToSell: IPmcData, profileWithItemsToSell: IPmcData,
profileThatGetsMoney: IPmcData, profileThatGetsMoney: IPmcData,
trader: Traders, trader: Traders,
output: IItemEventRouterResponse output: IItemEventRouterResponse,
): void ): void
{ {
const handbookPrices = this.ragfairPriceService.getAllStaticPrices(); const handbookPrices = this.ragfairPriceService.getAllStaticPrices();
@ -335,4 +353,4 @@ export class TradeController
return totalPrice; return totalPrice;
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -341,7 +341,6 @@ export class BotLootGenerator
} }
} }
this.addRequiredChildItemsToParent(itemToAddTemplate, itemWithChildrenToAdd, isPmc); this.addRequiredChildItemsToParent(itemToAddTemplate, itemWithChildrenToAdd, isPmc);
// Attempt to add item to container(s) // Attempt to add item to container(s)
@ -359,7 +358,9 @@ export class BotLootGenerator
if (itemAddedResult === ItemAddedResult.NO_CONTAINERS) if (itemAddedResult === ItemAddedResult.NO_CONTAINERS)
{ {
// Bot has no container to put item in, exit // 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; break;
} }
@ -367,7 +368,11 @@ export class BotLootGenerator
if (fitItemIntoContainerAttempts >= 4) if (fitItemIntoContainerAttempts >= 4)
{ {
this.logger.debug( 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; break;
@ -399,7 +404,11 @@ export class BotLootGenerator
* @param itemToAddChildrenTo Item to add children to * @param itemToAddChildrenTo Item to add children to
* @param isPmc Is the item being generated for a pmc (affects money/ammo stack sizes) * @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 // Fill ammo box
if (this.itemHelper.isOfBaseclass(itemToAddTemplate._id, BaseClasses.AMMO_BOX)) if (this.itemHelper.isOfBaseclass(itemToAddTemplate._id, BaseClasses.AMMO_BOX))
@ -477,7 +486,11 @@ export class BotLootGenerator
if (result !== ItemAddedResult.SUCCESS) 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 const randomSize = itemTemplate._props.StackMaxSize === 1
? 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) 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 // 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); this.addCartridgeToChamber(weaponWithModsArray, ammoTpl, chamberSlotNames);
} }
@ -311,7 +311,10 @@ export class BotWeaponGenerator
{ {
// Invalid weapon generated, fallback to preset // Invalid weapon generated, fallback to preset
this.logger.warning( 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 = []; const weaponMods = [];
@ -364,10 +367,12 @@ export class BotWeaponGenerator
} }
// Iterate over required slots in db item, check mod exists for that slot // 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 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) if (!weaponSlotItem)
{ {
this.logger.warning( this.logger.warning(
@ -547,7 +552,10 @@ export class BotWeaponGenerator
{ {
// Shouldn't happen // Shouldn't happen
this.logger.warning( 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) while (!chosenAmmoTpl)
{ {
const possibleAmmo = this.weightedRandomHelper.getWeightedValue<string>(compatibleCartridges); const possibleAmmo = this.weightedRandomHelper.getWeightedValue<string>(compatibleCartridges);
// Weapon has chamber but does not support cartridge // 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) && !weaponTemplate._props.Chambers[0]._props.filters[0].Filter.includes(possibleAmmo)
) )
{ {

View File

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

View File

@ -1,14 +1,14 @@
export interface IFilterPlateModsForSlotByLevelResult export interface IFilterPlateModsForSlotByLevelResult
{ {
result: Result result: Result;
plateModTpls: string[] plateModTpls: string[];
} }
export enum Result
export enum Result { {
UNKNOWN_FAILURE = -1, UNKNOWN_FAILURE = -1,
SUCCESS = 1, SUCCESS = 1,
NO_DEFAULT_FILTER = 2, NO_DEFAULT_FILTER = 2,
NOT_PLATE_HOLDING_SLOT = 3, 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; const staticContainerGroupData: IStaticContainer = db.locations[locationId].statics;
if (!staticContainerGroupData) 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; return result;
} }
@ -248,7 +248,6 @@ export class LocationGenerator
staticContainerCount++; staticContainerCount++;
staticLootItemCount += containerWithLoot.template.Items.length; staticLootItemCount += containerWithLoot.template.Items.length;
} }
} }
@ -430,7 +429,7 @@ export class LocationGenerator
itemCountToAdd, itemCountToAdd,
this.locationConfig.allowDuplicateItemsInStaticContainers, this.locationConfig.allowDuplicateItemsInStaticContainers,
locklist, locklist,
).filter(x => !tplsForced.includes(x)); ).filter((x) => !tplsForced.includes(x));
// Add forced loot to chosen item pool // Add forced loot to chosen item pool
const tplsToAddToContainer = tplsForced.concat(chosenTpls); const tplsToAddToContainer = tplsForced.concat(chosenTpls);
@ -516,7 +515,9 @@ export class LocationGenerator
const countDistribution = staticLootDist[containerTypeId]?.itemcountDistribution; const countDistribution = staticLootDist[containerTypeId]?.itemcountDistribution;
if (!countDistribution) 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; return 0;
} }
@ -603,7 +604,10 @@ export class LocationGenerator
// Draw from random distribution // Draw from random distribution
const desiredSpawnpointCount = Math.round( const desiredSpawnpointCount = Math.round(
this.getLooseLootMultiplerForLocation(locationName) 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 // 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 chosenItem = spawnPoint.template.Items.find((x) => x._id === chosenComposedKey);
const chosenTpl = chosenItem._tpl; const chosenTpl = chosenItem._tpl;
const itemTemplate = this.itemHelper.getItem(chosenTpl)[1]; const itemTemplate = this.itemHelper.getItem(chosenTpl)[1];
// Item array to return // Item array to return
let itemWithMods: Item[] = []; let itemWithMods: Item[] = [];
@ -864,18 +868,19 @@ export class LocationGenerator
this.locationConfig.minFillLooseMagazinePercent / 100, this.locationConfig.minFillLooseMagazinePercent / 100,
); );
} }
itemWithMods.push(...magazineItem); itemWithMods.push(...magazineItem);
} }
else if (this.itemHelper.armorItemCanHoldMods(chosenTpl)) else if (this.itemHelper.armorItemCanHoldMods(chosenTpl))
{ {
itemWithMods.push({ itemWithMods.push({ _id: this.objectId.generate(), _tpl: chosenTpl });
_id: this.objectId.generate(),
_tpl: chosenTpl,
});
if (itemTemplate._props.Slots?.length > 0) 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 else
@ -1004,7 +1009,10 @@ export class LocationGenerator
if (!rootItem) if (!rootItem)
{ {
this.logger.error( 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")); 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 // We make base item above, at start of function, no need to do it here
if (itemTemplate._props.Slots?.length > 0) 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 { return { items: items, width: width, height: height };
items: items,
width: width,
height: height
};
} }
} }

View File

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

View File

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

View File

@ -75,21 +75,18 @@ export class RagfairAssortGenerator
const results: Item[][] = []; const results: Item[][] = [];
/** Get cloned items from db */ /** 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 */ /** Store processed preset tpls so we dont add them when procesing non-preset items */
const processedArmorItems: string[] = []; const processedArmorItems: string[] = [];
const seasonalEventActive = this.seasonalEventService.seasonalEventEnabled(); const seasonalEventActive = this.seasonalEventService.seasonalEventEnabled();
const seasonalItemTplBlacklist = this.seasonalEventService.getInactiveSeasonalEventItems(); const seasonalItemTplBlacklist = this.seasonalEventService.getInactiveSeasonalEventItems();
const presets = this.getPresetsToAdd(); const presets = this.getPresetsToAdd();
for (const preset of presets) for (const preset of presets)
{ {
// Update Ids and clone // Update Ids and clone
const presetAndMods: Item[] = this.itemHelper.replaceIDs( const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(preset._items));
null,
this.jsonUtil.clone(preset._items),
);
this.itemHelper.remapRootItemId(presetAndMods); this.itemHelper.remapRootItemId(presetAndMods);
// Add presets base item tpl to the processed list so its skipped later on when processing items // 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].parentId = "hideout";
presetAndMods[0].slotId = "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) 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 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; return results;
@ -140,8 +137,8 @@ export class RagfairAssortGenerator
protected getPresetsToAdd(): IPreset[] protected getPresetsToAdd(): IPreset[]
{ {
return (this.ragfairConfig.dynamic.showDefaultPresetsOnly) return (this.ragfairConfig.dynamic.showDefaultPresetsOnly)
? Object.values(this.presetHelper.getDefaultPresets()) ? Object.values(this.presetHelper.getDefaultPresets())
: this.presetHelper.getAllPresets() : this.presetHelper.getAllPresets();
} }
/** /**

View File

@ -288,7 +288,8 @@ export class RagfairOfferGenerator
if (this.ragfairServerHelper.isPlayer(userID)) if (this.ragfairServerHelper.isPlayer(userID))
{ {
// Player offer = current time + offerDurationTimeInHour; // 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); 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 // Armor presets can hold plates above the allowed flea level, remove if necessary
if (isPreset && this.ragfairConfig.dynamic.blacklist.enableBsgList) 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 // 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 // Presets get unique id generated during getPresetItems() earlier + would require regenerating all children to match
assortItemWithChildren[0]._id = this.hashUtil.generate(); assortItemWithChildren[0]._id = this.hashUtil.generate();
} }
delete assortItemWithChildren[0].parentId; delete assortItemWithChildren[0].parentId;
delete assortItemWithChildren[0].slotId; delete assortItemWithChildren[0].slotId;
assortSingleOfferProcesses.push(this.createSingleOfferForItem(assortItemWithChildren, isPreset, itemDetails)); assortSingleOfferProcesses.push(
this.createSingleOfferForItem(assortItemWithChildren, isPreset, itemDetails),
);
} }
await Promise.all(assortSingleOfferProcesses); await Promise.all(assortSingleOfferProcesses);
@ -399,7 +405,9 @@ export class RagfairOfferGenerator
return false; 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) if (plateSlots.length === 0)
{ {
// Has no plate slots e.g. "left_side_plate", exit // Has no plate slots e.g. "left_side_plate", exit
@ -409,7 +417,8 @@ export class RagfairOfferGenerator
let removedPlate = false; let removedPlate = false;
for (const plateSlot of plateSlots) 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) if (plateArmorLevel > plateProtectionLimit)
{ {
presetWithChildren.splice(presetWithChildren.indexOf(plateSlot), 1); 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> ): Promise<void>
{ {
// Set stack size to random value // 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 isBarterOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.barter.chancePercent);
const isPackOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.pack.chancePercent) const isPackOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.pack.chancePercent)
&& !isBarterOffer && !isBarterOffer
&& itemWithChildren.length === 1 && 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(); const randomUserId = this.hashUtil.generate();
let barterScheme: IBarterScheme[]; let barterScheme: IBarterScheme[];
@ -614,22 +629,29 @@ export class RagfairOfferGenerator
* @param itemWithMods Item to adjust condition details of * @param itemWithMods Item to adjust condition details of
* @param itemDetails db item details of first item in array * @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 rootItem = itemWithMods[0];
const itemConditionValues = this.ragfairConfig.dynamic.condition[conditionSettingsId]; const itemConditionValues = this.ragfairConfig.dynamic.condition[conditionSettingsId];
const multiplier = this.randomUtil.getFloat( const multiplier = this.randomUtil.getFloat(itemConditionValues.min, itemConditionValues.max);
itemConditionValues.min,
itemConditionValues.max,
);
// Randomise armor + plates + armor related things // Randomise armor + plates + armor related things
if (this.itemHelper.armorItemCanHoldMods(rootItem._tpl) if (
|| this.itemHelper.isOfBaseclasses(rootItem._tpl, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT])) this.itemHelper.armorItemCanHoldMods(rootItem._tpl)
|| this.itemHelper.isOfBaseclasses(rootItem._tpl, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT])
)
{ {
// Chance to not adjust armor // 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; return;
} }
@ -637,18 +659,17 @@ export class RagfairOfferGenerator
this.randomiseArmorDurabilityValues(itemWithMods); this.randomiseArmorDurabilityValues(itemWithMods);
// Add hits to visor // Add hits to visor
const visorMod = itemWithMods.find(item => item.parentId === BaseClasses.ARMORED_EQUIPMENT && item.slotId === "mod_equipment_000"); const visorMod = itemWithMods.find((item) =>
if (this.randomUtil.getChance100(25) item.parentId === BaseClasses.ARMORED_EQUIPMENT && item.slotId === "mod_equipment_000"
&& visorMod) );
if (this.randomUtil.getChance100(25) && visorMod)
{ {
if (!visorMod.upd) if (!visorMod.upd)
{ {
visorMod.upd = {}; visorMod.upd = {};
} }
visorMod.upd.FaceShield = { visorMod.upd.FaceShield = { Hits: this.randomUtil.getInt(1, 3) };
Hits: this.randomUtil.getInt(1,3)
}
} }
return; return;
@ -673,7 +694,8 @@ export class RagfairOfferGenerator
if (rootItem.upd.Key && itemDetails._props.MaximumNumberOfUsage > 1) if (rootItem.upd.Key && itemDetails._props.MaximumNumberOfUsage > 1)
{ {
// randomize key uses // 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; return;
} }
@ -752,22 +774,26 @@ export class RagfairOfferGenerator
// Store mod types durabiltiy multiplier for later use in current/max durability value calculation // Store mod types durabiltiy multiplier for later use in current/max durability value calculation
if (!childMultiplerValues[itemDbDetails._parent]) if (!childMultiplerValues[itemDbDetails._parent])
{ {
childMultiplerValues[itemDbDetails._parent] = this.randomUtil.getFloat( childMultiplerValues[itemDbDetails._parent] =
itemDurabilityConfigDict[itemDbDetails._parent].min, this.randomUtil.getFloat(
itemDurabilityConfigDict[itemDbDetails._parent].max, itemDurabilityConfigDict[itemDbDetails._parent].min,
) / itemDurabilityConfigDict[itemDbDetails._parent].max; itemDurabilityConfigDict[itemDbDetails._parent].max,
) / itemDurabilityConfigDict[itemDbDetails._parent].max;
} }
const modMultipler = childMultiplerValues[itemDbDetails._parent]; const modMultipler = childMultiplerValues[itemDbDetails._parent];
const maxDurability = Math.round( 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( const durability = Math.round(
this.randomUtil.getFloat(maxDurability * this.randomUtil.getFloat(modMultipler, 1), maxDurability), this.randomUtil.getFloat(maxDurability * this.randomUtil.getFloat(modMultipler, 1), maxDurability),
); );
item.upd.Repairable = { item.upd.Repairable = {
Durability: durability || 1, // Never let value become 0 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 * @param multipler What to multiply the resulting price by
* @returns Barter scheme for offer * @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 currency = this.ragfairServerHelper.getDynamicOfferCurrency();
const price = this.ragfairPriceService.getDynamicOfferPriceForOffer(offerWithChildren, currency, isPackOffer) 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 { Exit, ILocationBase } from "@spt-aki/models/eft/common/ILocationBase";
import { TraderInfo } from "@spt-aki/models/eft/common/tables/IBotBase"; import { TraderInfo } from "@spt-aki/models/eft/common/tables/IBotBase";
import { Item } from "@spt-aki/models/eft/common/tables/IItem"; 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 { IRepeatableQuest } from "@spt-aki/models/eft/common/tables/IRepeatableQuests";
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem"; import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses"; import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
@ -292,11 +297,15 @@ export class RepeatableQuestGenerator
// Filter out close range weapons from far distance requirement // Filter out close range weapons from far distance requirement
if (distance > 50) 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 else if (distance < 20)
{ { // Filter out far range weapons from close distance requirement
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter(category => ["MarksmanRifle", "DMR"].includes(category.key)); weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter((category) =>
["MarksmanRifle", "DMR"].includes(category.key)
);
} }
// Pick a weighted weapon category // Pick a weighted weapon category
@ -418,7 +427,7 @@ export class RepeatableQuestGenerator
id: this.objectId.generate(), id: this.objectId.generate(),
dynamicLocale: true, dynamicLocale: true,
target: location, target: location,
conditionType: "Location" conditionType: "Location",
}; };
return propsObject; return propsObject;
@ -477,7 +486,7 @@ export class RepeatableQuestGenerator
if (allowedWeaponCategory?.length > 0) if (allowedWeaponCategory?.length > 0)
{ {
// TODO - fix - does weaponCategories exist? // TODO - fix - does weaponCategories exist?
//killConditionProps.weaponCategories = [allowedWeaponCategory]; // killConditionProps.weaponCategories = [allowedWeaponCategory];
} }
return killConditionProps; return killConditionProps;
@ -568,8 +577,8 @@ export class RepeatableQuestGenerator
} }
// Draw items to ask player to retrieve // Draw items to ask player to retrieve
let isAmmo = 0 let isAmmo = 0;
const randomNumbersUsed = []; const randomNumbersUsed = [];
for (let i = 0; i < distinctItemsToRetrieveCount; i++) for (let i = 0; i < distinctItemsToRetrieveCount; i++)
{ {
let randomNumber = this.randomUtil.randInt(itemSelection.length); let randomNumber = this.randomUtil.randInt(itemSelection.length);
@ -694,7 +703,8 @@ export class RepeatableQuestGenerator
): IRepeatableQuest ): IRepeatableQuest
{ {
const explorationConfig = repeatableConfig.questConfig.Exploration; 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) if (Object.keys(questTypePool.pool.Exploration.locations).length === 0)
{ {
@ -731,7 +741,7 @@ export class RepeatableQuestGenerator
dynamicLocale: true, dynamicLocale: true,
target: locationTarget, target: locationTarget,
}; };
quest.conditions.AvailableForFinish[0].counter.id = this.objectId.generate(); quest.conditions.AvailableForFinish[0].counter.id = this.objectId.generate();
quest.conditions.AvailableForFinish[0].counter.conditions = [exitStatusCondition, locationCondition]; quest.conditions.AvailableForFinish[0].counter.conditions = [exitStatusCondition, locationCondition];
quest.conditions.AvailableForFinish[0].value = numExtracts; 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) // Filter by whitelist, it's also possible that the field "PassageRequirement" does not exist (e.g. Shoreline)
let mapExits = this.getLocationExitsForSide(locationKey, repeatableConfig.side); let mapExits = this.getLocationExitsForSide(locationKey, repeatableConfig.side);
// Exclude scav coop exits when choosing pmc exit // Exclude scav coop exits when choosing pmc exit
if (repeatableConfig.side === "Pmc") 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 // 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) // Exclude exits with a requirement to leave (e.g. car extracts)
const possibleExits = exitPool.filter((exit) => const possibleExits = exitPool.filter((
(!("PassageRequirement" in exit) exit,
|| repeatableConfig.questConfig.Exploration.specificExits.passageRequirementWhitelist.includes( ) => (!("PassageRequirement" in exit)
exit.PassageRequirement || repeatableConfig.questConfig.Exploration.specificExits.passageRequirementWhitelist.includes(
)) exit.PassageRequirement,
))
); );
if (possibleExits.length === 0) 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 else
{ {
@ -792,13 +805,12 @@ export class RepeatableQuestGenerator
protected getLocationExitsForSide(locationKey: string, playerSide: string): Exit[] protected getLocationExitsForSide(locationKey: string, playerSide: string): Exit[]
{ {
const mapBase = this.databaseServer.getTables().locations[locationKey.toLowerCase()].base as ILocationBase; const mapBase = this.databaseServer.getTables().locations[locationKey.toLowerCase()].base as ILocationBase;
const infilPointsOfSameSide = new Set<string>(); const infilPointsOfSameSide = new Set<string>();
for (const spawnPoint of mapBase.SpawnPointParams) for (const spawnPoint of mapBase.SpawnPointParams)
{ {
// Same side, add infil to list // Same side, add infil to list
if (spawnPoint.Sides.includes(playerSide) if (spawnPoint.Sides.includes(playerSide) || spawnPoint.Sides.includes("All"))
|| spawnPoint.Sides.includes("All"))
{ {
// Has specific start location // Has specific start location
if (spawnPoint.Infiltration.length > 0) if (spawnPoint.Infiltration.length > 0)
@ -811,7 +823,9 @@ export class RepeatableQuestGenerator
// use list of allowed infils to figure out side of exits // use list of allowed infils to figure out side of exits
const infilPointsArray = Array.from(infilPointsOfSameSide); 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( protected generatePickupQuest(
@ -838,16 +852,16 @@ export class RepeatableQuestGenerator
findCondition.target = [itemTypeToFetchWithCount.itemType]; findCondition.target = [itemTypeToFetchWithCount.itemType];
findCondition.value = itemCountToFetch; 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"); // const locationCondition = counterCreatorCondition._props.counter.conditions.find(x => x._parent === "Location");
// (locationCondition._props as ILocationConditionProps).target = [...locationTarget]; // (locationCondition._props as ILocationConditionProps).target = [...locationTarget];
const equipmentCondition = counterCreatorCondition.counter.conditions.find((x) => const equipmentCondition = counterCreatorCondition.counter.conditions.find((x) =>
x.conditionType === "Equipment" x.conditionType === "Equipment"
); );
equipmentCondition.equipmentInclusive = [[ equipmentCondition.equipmentInclusive = [[itemTypeToFetchWithCount.itemType]];
itemTypeToFetchWithCount.itemType,
]];
// Add rewards // Add rewards
quest.rewards = this.generateReward(pmcLevel, 1, traderId, repeatableConfig, pickupConfig); quest.rewards = this.generateReward(pmcLevel, 1, traderId, repeatableConfig, pickupConfig);
@ -874,12 +888,7 @@ export class RepeatableQuestGenerator
*/ */
protected generateExplorationExitCondition(exit: Exit): IQuestConditionCounterCondition protected generateExplorationExitCondition(exit: Exit): IQuestConditionCounterCondition
{ {
return { return { conditionType: "ExitName", exitName: exit.Name, id: this.objectId.generate(), dynamicLocale: true };
conditionType: "ExitName",
exitName: exit.Name,
id: this.objectId.generate(),
dynamicLocale: true,
};
} }
/** /**
@ -951,11 +960,7 @@ export class RepeatableQuestGenerator
let roublesBudget = rewardRoubles; let roublesBudget = rewardRoubles;
let rewardItemPool = this.chooseRewardItemsWithinBudget(repeatableConfig, roublesBudget, traderId); let rewardItemPool = this.chooseRewardItemsWithinBudget(repeatableConfig, roublesBudget, traderId);
const rewards: IQuestRewards = { const rewards: IQuestRewards = { Started: [], Success: [], Fail: [] };
Started: [],
Success: [],
Fail: [],
};
let rewardIndex = 0; let rewardIndex = 0;
// Add xp reward // Add xp reward
@ -970,7 +975,11 @@ export class RepeatableQuestGenerator
{ {
// convert to equivalent dollars // convert to equivalent dollars
rewards.Success.push( 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 else
@ -980,14 +989,19 @@ export class RepeatableQuestGenerator
rewardIndex++; rewardIndex++;
const traderWhitelistDetails = repeatableConfig.traderWhitelist.find((x) => x.traderId === traderId); 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 // Add a random default preset weapon as reward
const defaultPresets = Object.values(this.presetHelper.getDefaultPresets()); const defaultPresets = Object.values(this.presetHelper.getDefaultPresets());
const defaultPreset = this.jsonUtil.clone(this.randomUtil.getArrayValue(defaultPresets)); 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 // 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++; rewardIndex++;
} }
@ -1059,7 +1073,8 @@ export class RepeatableQuestGenerator
target: traderId, target: traderId,
value: rewardReputation, value: rewardReputation,
type: QuestRewardType.TRADER_STANDING, type: QuestRewardType.TRADER_STANDING,
index: rewardIndex }; index: rewardIndex,
};
rewards.Success.push(reward); rewards.Success.push(reward);
rewardIndex++; rewardIndex++;
} }
@ -1164,7 +1179,7 @@ export class RepeatableQuestGenerator
if (preset) 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.target = rootItem._id; // Target property and root items id must match
rewardItem.items = this.itemHelper.reparentItemAndChildren(rootItem, preset); rewardItem.items = this.itemHelper.reparentItemAndChildren(rootItem, preset);
} }

View File

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

View File

@ -19,7 +19,7 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
@inject("ItemHelper") protected itemHelper: ItemHelper, @inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("BotWeaponGeneratorHelper") protected botWeaponGeneratorHelper: BotWeaponGeneratorHelper, @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 // Prevent infinite loop by only allowing 5 attempts at fitting a magazine into inventory
if (fitAttempts > 5) 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; 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 // 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") if (magTemplate._props.ReloadMagType === "InternalMagazine")
{ {
const result = this.getRandomExternalMagazineForInternalMagazineGun(inventoryMagGen.getWeaponTemplate()._id, attemptedMagBlacklist); const result = this.getRandomExternalMagazineForInternalMagazineGun(
inventoryMagGen.getWeaponTemplate()._id,
attemptedMagBlacklist,
);
if (!result?._id) 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; break;
} }
@ -138,24 +145,29 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
* @param weaponTpl Weapon to get mag for * @param weaponTpl Weapon to get mag for
* @returns tpl of magazine * @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 // 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) if (!magSlot)
{ {
return null; return null;
} }
// All possible mags that fit into the weapon excluding blacklisted // 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) if (!magazinePool)
{ {
return null; return null;
} }
// Non-internal magazines that fit into the weapon // 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) if (!externalMagazineOnlyPool || externalMagazineOnlyPool?.length === 0)
{ {
return null; return null;

View File

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

View File

@ -186,12 +186,12 @@ export class BotWeaponGeneratorHelper
{ {
// Bot doesnt have any containers we want to add item to // Bot doesnt have any containers we want to add item to
this.logger.debug( this.logger.debug(
`Unable to add item: ${ `Unable to add item: ${itemWithChildren[0]._tpl} to bot as it lacks the following containers: ${
itemWithChildren[0]._tpl equipmentSlots.join(",")
} 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 // 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 // Make sure IDs are unique before adding to array - prevent collisions
const presetToSend = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(preset._items)); 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)) else if (this.itemHelper.isOfBaseclass(checkedItem[1]._id, BaseClasses.AMMO_BOX))
{ {
@ -124,10 +123,7 @@ export class GiveSptCommand implements ISptCommand
const item: Item = { const item: Item = {
_id: this.hashUtil.generate(), _id: this.hashUtil.generate(),
_tpl: checkedItem[1]._id, _tpl: checkedItem[1]._id,
upd: { upd: { StackObjectsCount: +quantity, SpawnedInSession: true },
StackObjectsCount: +quantity,
SpawnedInSession: true
},
}; };
itemsToSend.push(...this.itemHelper.splitStack(item)); itemsToSend.push(...this.itemHelper.splitStack(item));
} }

View File

@ -22,7 +22,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
@inject("RandomUtil") protected randomUtil: RandomUtil, @inject("RandomUtil") protected randomUtil: RandomUtil,
@inject("MailSendService") protected mailSendService: MailSendService, @inject("MailSendService") protected mailSendService: MailSendService,
@inject("GiftService") protected giftService: GiftService, @inject("GiftService") protected giftService: GiftService,
@inject("ConfigServer") protected configServer: ConfigServer @inject("ConfigServer") protected configServer: ConfigServer,
) )
{ {
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE); this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
@ -64,7 +64,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
"Hey! you got the right code!", "Hey! you got the right code!",
"A secret code, how exciting!", "A secret code, how exciting!",
"You found a gift code!", "You found a gift code!",
]) ]),
); );
return; return;
@ -75,7 +75,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
sptFriendUser, 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; return;
@ -91,7 +91,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
"I love you too buddy :3!", "I love you too buddy :3!",
"uwu", "uwu",
`love you too ${sender?.Info?.Nickname}`, `love you too ${sender?.Info?.Nickname}`,
]) ]),
); );
} }
@ -100,7 +100,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
sptFriendUser, 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", "Heyyyyy",
"Hey there", "Hey there",
`Hello ${sender?.Info?.Nickname}`, `Hello ${sender?.Info?.Nickname}`,
]) ]),
); );
} }
@ -134,7 +134,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
"Cool guy, he made EFT!", "Cool guy, he made EFT!",
"Legend", "Legend",
"Remember when he said webel-webel-webel-webel, classic nikita moment", "Remember when he said webel-webel-webel-webel, classic nikita moment",
]) ]),
); );
} }
@ -143,7 +143,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
this.mailSendService.sendUserMessageToPlayer( this.mailSendService.sendUserMessageToPlayer(
sessionId, sessionId,
sptFriendUser, 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 { NotificationSendHelper } from "@spt-aki/helpers/NotificationSendHelper";
import { NotifierHelper } from "@spt-aki/helpers/NotifierHelper"; import { NotifierHelper } from "@spt-aki/helpers/NotifierHelper";
import { Item } from "@spt-aki/models/eft/common/tables/IItem"; import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { import { Dialogue, MessagePreview } from "@spt-aki/models/eft/profile/IAkiProfile";
Dialogue,
MessagePreview,
} from "@spt-aki/models/eft/profile/IAkiProfile";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { SaveServer } from "@spt-aki/servers/SaveServer"; import { SaveServer } from "@spt-aki/servers/SaveServer";

View File

@ -37,7 +37,7 @@ export class HandbookHelper
constructor( constructor(
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @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 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 // 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 let fuelDrainRate = this.databaseServer.getTables().hideout.settings.generatorFuelFlowRate
* this.getTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn); * this.getTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn);
const fuelBonus = pmcData.Bonuses.find((bonus) => bonus.type === BonusType.FUEL_CONSUMPTION); const fuelBonus = pmcData.Bonuses.find((bonus) => bonus.type === BonusType.FUEL_CONSUMPTION);
const fuelBonusPercent = 1.0 - (fuelBonus ? Math.abs(fuelBonus.value) : 0) / 100; const fuelBonusPercent = 1.0 - (fuelBonus ? Math.abs(fuelBonus.value) : 0) / 100;
fuelDrainRate *= fuelBonusPercent; fuelDrainRate *= fuelBonusPercent;
// Hideout management resource consumption bonus: // Hideout management resource consumption bonus:
const hideoutManagementConsumptionBonus = 1.0 - this.getHideoutManagementConsumptionBonus(pmcData); const hideoutManagementConsumptionBonus = 1.0 - this.getHideoutManagementConsumptionBonus(pmcData);
fuelDrainRate *= hideoutManagementConsumptionBonus; fuelDrainRate *= hideoutManagementConsumptionBonus;
@ -452,13 +452,13 @@ export class HideoutHelper
// No fuel left, skip // No fuel left, skip
continue; continue;
} }
// Undefined fuel, fresh fuel item and needs its max fuel amount looked up // Undefined fuel, fresh fuel item and needs its max fuel amount looked up
if (!fuelRemaining) if (!fuelRemaining)
{ {
const fuelItemTemplate = this.itemHelper.getItem(fuelItemInSlot._tpl)[1]; const fuelItemTemplate = this.itemHelper.getItem(fuelItemInSlot._tpl)[1];
pointsConsumed = fuelDrainRate; pointsConsumed = fuelDrainRate;
fuelRemaining = fuelItemTemplate._props.MaxResource - fuelDrainRate; fuelRemaining = fuelItemTemplate._props.MaxResource - fuelDrainRate;
} }
else else
{ {
@ -481,7 +481,9 @@ export class HideoutHelper
{ {
fuelItemInSlot.upd = this.getAreaUpdObject(1, fuelRemaining, pointsConsumed); 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; hasFuelRemaining = true;
break; // Break here to avoid updating all the fuel tanks break; // Break here to avoid updating all the fuel tanks
@ -955,7 +957,7 @@ export class HideoutHelper
pmcData: IPmcData, pmcData: IPmcData,
request: IHideoutTakeProductionRequestData, request: IHideoutTakeProductionRequestData,
sessionId: string, sessionId: string,
output: IItemEventRouterResponse output: IItemEventRouterResponse,
): void ): void
{ {
// Get how many coins were crafted and ready to pick up // Get how many coins were crafted and ready to pick up
@ -973,15 +975,11 @@ export class HideoutHelper
const itemsToAdd: Item[][] = []; const itemsToAdd: Item[][] = [];
for (let index = 0; index < craftedCoinCount; index++) for (let index = 0; index < craftedCoinCount; index++)
{ {
itemsToAdd.push( itemsToAdd.push([{
[{ _id: this.hashUtil.generate(),
_id: this.hashUtil.generate(), _tpl: HideoutHelper.bitcoinTpl,
_tpl: HideoutHelper.bitcoinTpl, upd: { StackObjectsCount: 1 },
upd: { }]);
StackObjectsCount: 1
}
}]
);
} }
// Create request for what we want to add to stash // Create request for what we want to add to stash
@ -989,7 +987,7 @@ export class HideoutHelper
itemsWithModsToAdd: itemsToAdd, itemsWithModsToAdd: itemsToAdd,
foundInRaid: true, foundInRaid: true,
useSortingTable: false, useSortingTable: false,
callback: null callback: null,
}; };
// Add FiR coins to player inventory // Add FiR coins to player inventory
@ -1070,24 +1068,29 @@ export class HideoutHelper
*/ */
public applyPlaceOfFameDogtagBonus(pmcData: IPmcData): void 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 // 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 // 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 // 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 // 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) // Calculate bonus percent (apply hideoutManagement bonus)
const hideoutManagementSkill = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.HIDEOUT_MANAGEMENT); 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 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 // Update bonus value to above calcualted value
combatBonusProfile.value = Number.parseFloat(bonus.toFixed(2)); combatBonusProfile.value = Number.parseFloat(bonus.toFixed(2));
@ -1112,8 +1115,7 @@ export class HideoutHelper
continue; continue;
} }
if (Number.parseInt(dogtag.upd.Dogtag?.AccountId) === pmcData.aid if (Number.parseInt(dogtag.upd.Dogtag?.AccountId) === pmcData.aid)
)
{ {
continue; continue;
} }

View File

@ -45,7 +45,7 @@ export class InRaidHelper
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ProfileFixerService") protected profileFixerService: ProfileFixerService, @inject("ProfileFixerService") protected profileFixerService: ProfileFixerService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RandomUtil") protected randomUtil: RandomUtil @inject("RandomUtil") protected randomUtil: RandomUtil,
) )
{ {
this.lostOnDeathConfig = this.configServer.getConfig(ConfigTypes.LOST_ON_DEATH); this.lostOnDeathConfig = this.configServer.getConfig(ConfigTypes.LOST_ON_DEATH);
@ -68,7 +68,7 @@ export class InRaidHelper
*/ */
public addUpdToMoneyFromRaid(items: Item[]): void 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) if (!item.upd)
{ {
@ -129,9 +129,9 @@ export class InRaidHelper
let pmcStandingForKill = botTypes[victim.Side.toLowerCase()]?.experience?.standingForKill; let pmcStandingForKill = botTypes[victim.Side.toLowerCase()]?.experience?.standingForKill;
const pmcKillProbabilityForScavGain = this.inRaidConfig.pmcKillProbabilityForScavGain; 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; return pmcStandingForKill;
@ -151,7 +151,7 @@ export class InRaidHelper
saveProgressRequest: ISaveProgressRequestData, saveProgressRequest: ISaveProgressRequestData,
sessionID: string, sessionID: string,
): void ): void
{ {
// Remove skill fatigue values // Remove skill fatigue values
this.resetSkillPointsEarnedDuringRaid(saveProgressRequest.profile); this.resetSkillPointsEarnedDuringRaid(saveProgressRequest.profile);
@ -214,19 +214,23 @@ export class InRaidHelper
if (matchingPreRaidCounter.value !== postRaidValue) if (matchingPreRaidCounter.value !== postRaidValue)
{ {
this.logger.error( 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 pmcData Server PMC profile
* @param saveProgressRequest Post-raid request data * @param saveProgressRequest Post-raid request data
* @param sessionId Session id * @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 // Process failed quests then copy everything
this.processAlteredQuests(sessionId, pmcData, pmcData.Quests, saveProgressRequest.profile); this.processAlteredQuests(sessionId, pmcData, pmcData.Quests, saveProgressRequest.profile);
@ -250,13 +254,19 @@ export class InRaidHelper
* @param saveProgressRequest Post-raid request data * @param saveProgressRequest Post-raid request data
* @param sessionId Session id * @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 // 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) 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); this.profileFixerService.checkForAndFixScavProfileIssues(scavData);
@ -305,7 +315,7 @@ export class InRaidHelper
} }
// Already completed/failed before raid, skip // 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", // Daily quests get their status altered in-raid to "AvailableForStart",
// Copy pre-raid status to post raid data // Copy pre-raid status to post raid data
@ -323,7 +333,9 @@ export class InRaidHelper
} }
// Quest with time-gate has unlocked // 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 // Flag as ready to complete
postRaidQuest.status = QuestStatus.AvailableForStart; postRaidQuest.status = QuestStatus.AvailableForStart;
@ -351,7 +363,9 @@ export class InRaidHelper
// Does failed quest have requirement to collect items from raid // Does failed quest have requirement to collect items from raid
const questDbData = this.questHelper.getQuestFromDb(postRaidQuest.qid, pmcData); const questDbData = this.questHelper.getQuestFromDb(postRaidQuest.qid, pmcData);
// AvailableForFinish // AvailableForFinish
const matchingAffFindConditions = questDbData.conditions.AvailableForFinish.filter(condition => condition.conditionType === "FindItem"); const matchingAffFindConditions = questDbData.conditions.AvailableForFinish.filter((condition) =>
condition.conditionType === "FindItem"
);
const itemsToCollect: string[] = []; const itemsToCollect: string[] = [];
if (matchingAffFindConditions) if (matchingAffFindConditions)
{ {
@ -364,21 +378,25 @@ export class InRaidHelper
// Remove quest items from profile as quest has failed and may still be alive // 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 // 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 // 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 // updateProfileBaseStats() has already passed by ref EFT.Stats, all changes applied to postRaid profile also apply to server profile
for (const itemTpl of itemsToCollect) for (const itemTpl of itemsToCollect)
{ {
// Look for sessioncounter and remove it // 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) if (counterIndex > -1)
{ {
postRaidProfile.Stats.Eft.SessionCounters.Items.splice(counterIndex, 1); postRaidProfile.Stats.Eft.SessionCounters.Items.splice(counterIndex, 1);
} }
// Look for quest item and remove it // 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) if (inventoryItemIndex > -1)
{ {
postRaidProfile.Inventory.items.splice(inventoryItemIndex, 1); postRaidProfile.Inventory.items.splice(inventoryItemIndex, 1);
@ -399,13 +417,15 @@ export class InRaidHelper
const dbQuest = this.questHelper.getQuestFromDb(lockedQuest.qid, null); const dbQuest = this.questHelper.getQuestFromDb(lockedQuest.qid, null);
if (!dbQuest) 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; continue;
} }
// Find the time requirement in AvailableForStart array (assuming there is one as quest in locked state === its time-gated) // 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) if (afsRequirement && afsRequirement.availableAfter > 0)
{ {
// Prereq quest has a wait // Prereq quest has a wait

View File

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

View File

@ -107,7 +107,7 @@ export class ItemHelper
*/ */
public armorItemCanHoldMods(itemTpl: string): boolean 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 // 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"]; const softInsertSlotIds = [
if (itemDbDetails[1]._props.Slots.find(slot => softInsertSlotIds.includes(slot._name.toLowerCase()))) "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; return true;
} }
// Also classified as BUILT_IN_INSERTS // Also classified as BUILT_IN_INSERTS
const helmetInsertSlotIds = ["helmet_top", "helmet_back", "helmet_ears"] const helmetInsertSlotIds = ["helmet_top", "helmet_back", "helmet_ears"];
if (itemDbDetails[1]._props.Slots.find(slot => helmetInsertSlotIds.includes(slot._name.toLowerCase()))) if (itemDbDetails[1]._props.Slots.find((slot) => helmetInsertSlotIds.includes(slot._name.toLowerCase())))
{ {
return true; return true;
} }
@ -446,8 +455,7 @@ export class ItemHelper
for (const itemFromAssort of assort) for (const itemFromAssort of assort)
{ {
if (itemFromAssort.parentId === itemIdToFind if (itemFromAssort.parentId === itemIdToFind && !list.find((item) => itemFromAssort._id === item._id))
&& !list.find((item) => itemFromAssort._id === item._id))
{ {
list.push(itemFromAssort); list.push(itemFromAssort);
list = list.concat(this.findAndReturnChildrenByAssort(itemFromAssort._id, assort)); 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 * Turn items like money into separate stacks that adhere to max stack size
* @param itemToSplit Item to split into smaller stacks * @param itemToSplit Item to split into smaller stacks
* @returns * @returns
*/ */
public splitStackIntoSeparateItems(itemToSplit: Item): Item[][] public splitStackIntoSeparateItems(itemToSplit: Item): Item[][]
{ {
@ -604,9 +612,7 @@ export class ItemHelper
{ {
const filterResult = itemsToSearch.filter((item) => const filterResult = itemsToSearch.filter((item) =>
{ {
return by === "tpl" return by === "tpl" ? (item._tpl === barterId) : (item._id === barterId);
? (item._tpl === barterId)
: (item._id === barterId);
}); });
matchingItems.push(...filterResult); matchingItems.push(...filterResult);
@ -978,7 +984,15 @@ export class ItemHelper
const cartridgeCountToAdd = (remainingSpace < maxPerStack) ? remainingSpace : maxPerStack; const cartridgeCountToAdd = (remainingSpace < maxPerStack) ? remainingSpace : maxPerStack;
// Add cartridge item into items array // 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; currentStoredCartridgeCount += cartridgeCountToAdd;
location++; location++;
@ -1068,7 +1082,9 @@ export class ItemHelper
if (!magazineCartridgeMaxCount) 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; return;
} }
@ -1104,10 +1120,11 @@ export class ItemHelper
magazineWithChildCartridges.push( magazineWithChildCartridges.push(
this.createCartridges( this.createCartridges(
magazineWithChildCartridges[0]._id, magazineWithChildCartridges[0]._id,
cartridgeTpl, cartridgeCountToAdd, cartridgeTpl,
cartridgeCountToAdd,
location, location,
magazineWithChildCartridges[0].upd?.SpawnedInSession magazineWithChildCartridges[0].upd?.SpawnedInSession,
) ),
); );
currentStoredCartridgeCount += cartridgeCountToAdd; currentStoredCartridgeCount += cartridgeCountToAdd;
@ -1170,7 +1187,13 @@ export class ItemHelper
* @param foundInRaid OPTIONAL - Are cartridges found in raid (SpawnedInSession) * @param foundInRaid OPTIONAL - Are cartridges found in raid (SpawnedInSession)
* @returns Item * @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 { return {
_id: this.objectId.generate(), _id: this.objectId.generate(),
@ -1178,10 +1201,7 @@ export class ItemHelper
parentId: parentId, parentId: parentId,
slotId: "cartridges", slotId: "cartridges",
location: location, location: location,
upd: { upd: { StackObjectsCount: stackCount, SpawnedInSession: foundInRaid },
StackObjectsCount: stackCount,
SpawnedInSession: foundInRaid
},
}; };
} }
@ -1225,7 +1245,12 @@ export class ItemHelper
* @param requiredOnly Only add required mods * @param requiredOnly Only add required mods
* @returns Item with children * @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 result = itemToAdd;
const incompatibleModTpls: Set<string> = new Set(); 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); const chosenTpl = this.getCompatibleTplFromArray(itemPool, incompatibleModTpls);
if (!chosenTpl) 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; continue;
} }
@ -1264,7 +1291,7 @@ export class ItemHelper
_id: this.hashUtil.generate(), _id: this.hashUtil.generate(),
_tpl: chosenTpl, _tpl: chosenTpl,
parentId: result[0]._id, parentId: result[0]._id,
slotId: slot._name slotId: slot._name,
}; };
result.push(modItemToAdd); result.push(modItemToAdd);
@ -1300,7 +1327,7 @@ export class ItemHelper
if (incompatibleModTpls.has(tpl)) if (incompatibleModTpls.has(tpl))
{ {
// Incompatible tpl was chosen, try again // Incompatible tpl was chosen, try again
count++ count++;
if (count >= possibleTpls.length) if (count >= possibleTpls.length)
{ {
return null; return null;

View File

@ -285,8 +285,8 @@ export class QuestHelper
item.upd.SpawnedInSession = true; item.upd.SpawnedInSession = true;
// Separate base item from mods, fix stacks // 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 ( if (
(item.parentId !== undefined) && (item.parentId === "hideout") // Has parentId of hideout (item.parentId !== undefined) && (item.parentId === "hideout") // Has parentId of hideout
&& (item.upd !== undefined) && (item.upd.StackObjectsCount !== undefined) // Has upd with stackobject count && (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); questReward.items = this.jsonUtil.clone(defaultPreset._items);
// Remap target id to the new presets id // 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; 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]; 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 // 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 // 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) => const questRewards = quest.rewards[QuestStatus[status]].flatMap((reward: IQuestReward) =>
reward.type === "Item" reward.type === "Item" ? this.processReward(reward) : []
? this.processReward(reward)
: []
); );
return questRewards; return questRewards;
@ -654,7 +654,9 @@ export class QuestHelper
public getQuestWithOnlyLevelRequirementStartCondition(quest: IQuest): IQuest public getQuestWithOnlyLevelRequirementStartCondition(quest: IQuest): IQuest
{ {
quest = this.jsonUtil.clone(quest); 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; return quest;
} }
@ -686,7 +688,7 @@ export class QuestHelper
// Create a dialog message for completing the quest. // Create a dialog message for completing the quest.
const quest = this.getQuestFromDb(failRequest.qid, pmcData); 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) if (!questIsRepeatable)
{ {
this.mailSendService.sendLocalisedNpcMessageToPlayer( this.mailSendService.sendLocalisedNpcMessageToPlayer(
@ -1007,9 +1009,7 @@ export class QuestHelper
const questInDb = allQuests.find((x) => x._id === questId); const questInDb = allQuests.find((x) => x._id === questId);
if (!questInDb) if (!questInDb)
{ {
this.logger.debug( this.logger.debug(`Unable to find quest: ${questId} in db, cannot get 'FindItem' condition, skipping`);
`Unable to find quest: ${questId} in db, cannot get 'FindItem' condition, skipping`,
);
continue; continue;
} }

View File

@ -565,7 +565,10 @@ export class RagfairOfferHelper
// Filter out presets when search request has multiple buildItems // Filter out presets when search request has multiple buildItems
// Assuming 1 build item = single item e.g. gun // 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 // Don't include preset offer
return false; return false;
@ -610,7 +613,8 @@ export class RagfairOfferHelper
return false; return false;
} }
if (this.isConditionItem(offerRootItem) if (
this.isConditionItem(offerRootItem)
&& !this.itemQualityInRange(offerRootItem, searchRequest.conditionFrom, searchRequest.conditionTo) && !this.itemQualityInRange(offerRootItem, searchRequest.conditionFrom, searchRequest.conditionTo)
) )
{ {
@ -700,9 +704,10 @@ export class RagfairOfferHelper
*/ */
protected isConditionItem(item: Item): boolean 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 // 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 ? true
: false; : false;
} }

View File

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

View File

@ -160,7 +160,9 @@ export class RagfairServerHelper
MessageType.MESSAGE_WITH_ITEMS, MessageType.MESSAGE_WITH_ITEMS,
RagfairServerHelper.goodsReturnedTemplate, RagfairServerHelper.goodsReturnedTemplate,
returnedItems, 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 * Possible bug, returns all items associated with an items tpl, could be multiple presets from globals.json
* @param item Preset item * @param item Preset item
* @returns * @returns
*/ */
public getPresetItemsByTpl(item: Item): Item[] public getPresetItemsByTpl(item: Item): Item[]
{ {

View File

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

View File

@ -148,7 +148,7 @@ export class TraderAssortHelper
protected resetBuyRestrictionCurrentValue(assortItems: Item[]): void protected resetBuyRestrictionCurrentValue(assortItems: Item[]): void
{ {
// iterate over root items // 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 // no value to adjust
if (!assort.upd.BuyRestrictionCurrent) if (!assort.upd.BuyRestrictionCurrent)

View File

@ -146,10 +146,12 @@ export class PreAkiModLoader implements IModLoader
const modOrder = this.vfs.readFile(this.modOrderPath, { encoding: "utf8" }); const modOrder = this.vfs.readFile(this.modOrderPath, { encoding: "utf8" });
try try
{ {
this.jsonUtil.deserialize<any>(modOrder, this.modOrderPath).order.forEach((mod: string, index: number) => this.jsonUtil.deserialize<any>(modOrder, this.modOrderPath).order.forEach(
{ (mod: string, index: number) =>
this.order[mod] = index; {
}); this.order[mod] = index;
},
);
} }
catch (error) catch (error)
{ {
@ -429,7 +431,7 @@ export class PreAkiModLoader implements IModLoader
public sortModsLoadOrder(): string[] public sortModsLoadOrder(): string[]
{ {
// if loadorder.json exists: load it, otherwise generate load order // 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)) if (this.vfs.exists(loadOrderPath))
{ {
return this.jsonUtil.deserialize(this.vfs.readFile(loadOrderPath), 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 export interface ISetMagazineRequest
{ {
Id: string Id: string;
Name: string Name: string;
Caliber: string Caliber: string;
Items: IMagazineTemplateAmmoItem[] Items: IMagazineTemplateAmmoItem[];
TopCount: number TopCount: number;
BottomCount: number BottomCount: number;
} }

View File

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

View File

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

View File

@ -5,12 +5,12 @@ export interface IPmcData extends IBotBase
export interface IPostRaidPmcData extends IBotBase export interface IPostRaidPmcData extends IBotBase
{ {
Stats: IPostRaidStats Stats: IPostRaidStats;
} }
export interface IPostRaidStats export interface IPostRaidStats
{ {
Eft: IEftStats Eft: IEftStats;
/** Only found in profile we get from client post raid */ /** 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 export interface IAchievement
{ {
id: string id: string;
imageUrl: string imageUrl: string;
assetPath: string assetPath: string;
rewards: IQuestRewards rewards: IQuestRewards;
conditions: IQuestConditionTypes conditions: IQuestConditionTypes;
instantComplete: boolean instantComplete: boolean;
showNotificationsInGame: boolean showNotificationsInGame: boolean;
showProgress: boolean showProgress: boolean;
prefab: string prefab: string;
rarity: string rarity: string;
hidden: boolean hidden: boolean;
showConditions: boolean showConditions: boolean;
progressBarEnabled: boolean progressBarEnabled: boolean;
side: string side: string;
index: number index: number;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,5 +4,5 @@ export interface ISetFavoriteItems extends IInventoryBaseActionRequestData
{ {
Action: "SetFavoriteItems"; Action: "SetFavoriteItems";
items: any[]; 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 export interface IGetGroupStatusResponse
{ {
players: IPlayer[] players: IPlayer[];
maxPveCountExceeded: boolean maxPveCountExceeded: boolean;
} }
export interface IPlayer export interface IPlayer
{ {
aid: string aid: string;
_id: string _id: string;
lookingGroup: boolean lookingGroup: boolean;
IsLeader: boolean IsLeader: boolean;
IsReady: boolean IsReady: boolean;
Info: ICurrentGroupMemberInfo Info: ICurrentGroupMemberInfo;
} }
export interface ICurrentGroupMemberInfo export interface ICurrentGroupMemberInfo
@ -22,4 +22,4 @@ export interface ICurrentGroupMemberInfo
Side: string; Side: string;
Level: string; Level: string;
MemberCategory: MemberCategory; MemberCategory: MemberCategory;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,11 +5,11 @@ export enum BonusType
HEALTH_REGENERATION = "HealthRegeneration", HEALTH_REGENERATION = "HealthRegeneration",
EXPERIENCE_RATE = "ExperienceRate", EXPERIENCE_RATE = "ExperienceRate",
QUEST_MONEY_REWARD = "QuestMoneyReward", QUEST_MONEY_REWARD = "QuestMoneyReward",
SCAV_COOLDOWN_TIMER = "ScavCooldownTimer", SCAV_COOLDOWN_TIMER = "ScavCooldownTimer",
UNLOCK_ITEM_CRAFT = "UnlockItemCraft", UNLOCK_ITEM_CRAFT = "UnlockItemCraft",
UNLOCK_ITEM_PASSIVE_CREATION = "UnlockItemPassiveCreation", UNLOCK_ITEM_PASSIVE_CREATION = "UnlockItemPassiveCreation",
UNLOCK_RANDOM_ITEM_CREATION = "UnlockRandomItemCreation", UNLOCK_RANDOM_ITEM_CREATION = "UnlockRandomItemCreation",
SKILL_LEVELING_BOOST = "SkillLevelingBoost", SKILL_LEVELING_BOOST = "SkillLevelingBoost",
DEBUFF_END_DELAY = "DebuffEndDelay", DEBUFF_END_DELAY = "DebuffEndDelay",
RAGFAIR_COMMISSION = "RagfairCommission", RAGFAIR_COMMISSION = "RagfairCommission",
INSURANCE_RETURN_TIME = "InsuranceReturnTime", INSURANCE_RETURN_TIME = "InsuranceReturnTime",
@ -19,15 +19,15 @@ export enum BonusType
UNLOCK_ITEM_CHARGE = "UnlockItemCharge", UNLOCK_ITEM_CHARGE = "UnlockItemCharge",
RECEIVE_ITEM_BONUS = "ReceiveItemBonus", RECEIVE_ITEM_BONUS = "ReceiveItemBonus",
UNLOCK_UNIQUE_ID = "UnlockUniqueId", UNLOCK_UNIQUE_ID = "UnlockUniqueId",
INCREASE_CANISTER_SLOTS = "IncreaseCanisterSlots", INCREASE_CANISTER_SLOTS = "IncreaseCanisterSlots",
ADDITIONAL_SLOTS = "AdditionalSlots", ADDITIONAL_SLOTS = "AdditionalSlots",
FUEL_CONSUMPTION = "FuelConsumption", FUEL_CONSUMPTION = "FuelConsumption",
REPAIR_WEAPON_BONUS = "RepairWeaponBonus", REPAIR_WEAPON_BONUS = "RepairWeaponBonus",
REPAIR_ARMOR_BONUS = "RepairArmorBonus", REPAIR_ARMOR_BONUS = "RepairArmorBonus",
UNLOCK_WEAPON_REPAIR = "UnlockWeaponRepair", UNLOCK_WEAPON_REPAIR = "UnlockWeaponRepair",
UNLOCK_ARMOR_REPAIR = "UnlockArmorRepair", UNLOCK_ARMOR_REPAIR = "UnlockArmorRepair",
STASH_SIZE = "StashSize", STASH_SIZE = "StashSize",
MAXIMUM_ENERGY_RESERVE = "MaximumEnergyReserve", MAXIMUM_ENERGY_RESERVE = "MaximumEnergyReserve",
TEXT_BONUS = "TextBonus", 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", AIRDROP = "aki-airdrop",
BOT = "aki-bot", BOT = "aki-bot",
PMC = "aki-pmc", PMC = "aki-pmc",
@ -25,5 +26,5 @@ export enum ConfigTypes {
SEASONAL_EVENT = "aki-seasonalevents", SEASONAL_EVENT = "aki-seasonalevents",
LOST_ON_DEATH = "aki-lostondeath", LOST_ON_DEATH = "aki-lostondeath",
GIFTS = "aki-gifts", GIFTS = "aki-gifts",
BTR = "aki-btr" BTR = "aki-btr",
} }

View File

@ -1,4 +1,5 @@
export enum HideoutAreas { export enum HideoutAreas
{
NOTSET = -1, NOTSET = -1,
VENTS = 0, VENTS = 0,
SECURITY = 1, SECURITY = 1,
@ -25,5 +26,5 @@ export enum HideoutAreas {
EMERGENCY_WALL = 22, EMERGENCY_WALL = 22,
GYM = 23, GYM = 23,
WEAPON_STAND = 24, 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, UNKNOWN = -1,
SUCCESS = 1, SUCCESS = 1,
NO_SPACE = 2, NO_SPACE = 2,

View File

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

View File

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

View File

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

View File

@ -5,5 +5,5 @@ export enum TraderServiceType
CULTISTS_AID = "CultistsAid", CULTISTS_AID = "CultistsAid",
BTR_ITEMS_DELIVERY = "BtrItemsDelivery", BTR_ITEMS_DELIVERY = "BtrItemsDelivery",
PLAYER_TAXI = "PlayerTaxi", 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, GYM = 0,
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -103,7 +103,7 @@ export interface EquipmentFilters
/** Chance gun laser is active during the day */ /** Chance gun laser is active during the day */
laserIsActiveChancePercent?: number; laserIsActiveChancePercent?: number;
/** Should plates be filtered by level */ /** Should plates be filtered by level */
filterPlatesByLevel?:boolean filterPlatesByLevel?: boolean;
/** Chance NODS are down/active during the day */ /** Chance NODS are down/active during the day */
nvgIsActiveChanceDayPercent?: number; nvgIsActiveChanceDayPercent?: number;
/** Chance NODS are down/active during the night */ /** Chance NODS are down/active during the night */
@ -121,7 +121,7 @@ export interface EquipmentFilters
weightingAdjustmentsByPlayerLevel?: WeightingAdjustmentDetails[]; weightingAdjustmentsByPlayerLevel?: WeightingAdjustmentDetails[];
/** Should the stock mod be forced to spawn on bot */ /** Should the stock mod be forced to spawn on bot */
forceStock: boolean; forceStock: boolean;
armorPlateWeighting?: IArmorPlateWeights[] armorPlateWeighting?: IArmorPlateWeights[];
} }
export interface ModLimits export interface ModLimits
@ -169,7 +169,6 @@ export interface WeightingAdjustmentDetails
equipment?: IAdjustmentDetails; equipment?: IAdjustmentDetails;
/** Key: clothing slot e.g. feet, value: item tpl + weight */ /** Key: clothing slot e.g. feet, value: item tpl + weight */
clothing?: IAdjustmentDetails; clothing?: IAdjustmentDetails;
} }
export interface 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*/ /** Key: map, value: settings to control how long scav raids are*/
scavRaidTimeSettings: IScavRaidTimeSettings; scavRaidTimeSettings: IScavRaidTimeSettings;
/** Settings to adjust mods for lootable equipment in raid */ /** Settings to adjust mods for lootable equipment in raid */
equipmentLootSettings: IEquipmentLootSettings equipmentLootSettings: IEquipmentLootSettings;
} }
export interface IEquipmentLootSettings export interface IEquipmentLootSettings
{ {
// Percentage chance item will be added to equipment // Percentage chance item will be added to equipment
modSpawnChancePercent: Record<string, number> modSpawnChancePercent: Record<string, number>;
} }
export interface IFixEmptyBotWavesSettings export interface IFixEmptyBotWavesSettings
@ -118,13 +118,13 @@ export interface IContainerRandomistionSettings
export interface IScavRaidTimeSettings export interface IScavRaidTimeSettings
{ {
settings: IScavRaidTimeConfigSettings settings: IScavRaidTimeConfigSettings;
maps: Record<string, IScavRaidTimeLocationSettings> maps: Record<string, IScavRaidTimeLocationSettings>;
} }
export interface IScavRaidTimeConfigSettings export interface IScavRaidTimeConfigSettings
{ {
trainArrivalDelayObservedSeconds: number trainArrivalDelayObservedSeconds: number;
} }
export interface IScavRaidTimeLocationSettings export interface IScavRaidTimeLocationSettings
@ -133,7 +133,7 @@ export interface IScavRaidTimeLocationSettings
reduceLootByPercent: boolean; reduceLootByPercent: boolean;
/** Smallest % of container loot that should be spawned */ /** Smallest % of container loot that should be spawned */
minStaticLootPercent: number; minStaticLootPercent: number;
/** Smallest % of loose loot that should be spawned */ /** Smallest % of loose loot that should be spawned */
minDynamicLootPercent: number; minDynamicLootPercent: number;
/** Chance raid time is reduced */ /** Chance raid time is reduced */
reducedChancePercent: number; reducedChancePercent: number;

View File

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

View File

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

View File

@ -1,9 +1,9 @@
export interface IRaidChanges export interface IRaidChanges
{ {
/** What percentage of dynamic loot should the map contain */ /** What percentage of dynamic loot should the map contain */
dynamicLootPercent: number dynamicLootPercent: number;
/** What percentage of static loot should the map contain */ /** 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 */ /** 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[]; defaultEquipmentPresets: IDefaultEquipmentPreset[];
/** Achievements */ /** Achievements */
achievements: IAchievement[] achievements: IAchievement[];
}; };
traders?: Record<string, ITrader>; traders?: Record<string, ITrader>;

View File

@ -2,7 +2,7 @@ import { TraderServiceType } from "@spt-aki/models/enums/TraderServiceType";
export interface ITraderServiceModel export interface ITraderServiceModel
{ {
serviceType: TraderServiceType, serviceType: TraderServiceType;
itemsToPay?: Record<string, number>[], itemsToPay?: Record<string, number>[];
subServices?: 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 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); 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 => (url: string, info: any, sessionID: string, output: string): any =>
{ {
return this.inraidCallbacks.itemDelivery(url, info, sessionID); 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]); 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); return this.ragfairCategoriesService.getCategoriesFromOffers(offers, searchRequestData, fleaUnlocked);
} }

View File

@ -27,7 +27,7 @@ export class SaveServer
@inject("HashUtil") protected hashUtil: HashUtil, @inject("HashUtil") protected hashUtil: HashUtil,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("WinstonLogger") protected logger: ILogger, @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 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); const fmd5 = this.hashUtil.generateMd5ForData(jsonProfile);
if (typeof (this.saveMd5[sessionID]) !== "string" || this.saveMd5[sessionID] !== fmd5) if (typeof (this.saveMd5[sessionID]) !== "string" || this.saveMd5[sessionID] !== fmd5)
{ {

View File

@ -201,7 +201,13 @@ export class BotEquipmentModPoolService
protected generateGearPool(): void protected generateGearPool(): void
{ {
const gear = Object.values(this.databaseServer.getTables().templates.items).filter((x) => 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"); this.generatePool(gear, "gear");

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -401,12 +401,12 @@ export class RagfairPriceService implements OnLoad
*/ */
protected getWeaponPreset(weapon: Item): { isDefault: boolean; preset: IPreset; } protected getWeaponPreset(weapon: Item): { isDefault: boolean; preset: IPreset; }
{ {
const defaultPreset = this.presetHelper.getDefaultPreset(weapon._tpl) const defaultPreset = this.presetHelper.getDefaultPreset(weapon._tpl);
if (defaultPreset) if (defaultPreset)
{ {
return { isDefault: true, preset: defaultPreset }; return { isDefault: true, preset: defaultPreset };
} }
const nonDefaultPresets = this.presetHelper.getPresets(weapon._tpl) const nonDefaultPresets = this.presetHelper.getPresets(weapon._tpl);
if (nonDefaultPresets.length === 1) if (nonDefaultPresets.length === 1)
{ {
this.logger.debug( 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. // 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 item Item being sold on flea
* @param pmcData player profile * @param pmcData player profile
* @param requirementsValue * @param requirementsValue
* @param offerItemCount Number of offers being created * @param offerItemCount Number of offers being created
* @param sellInOnePiece * @param sellInOnePiece
* @returns Tax in roubles * @returns Tax in roubles
*/ */
public calculateTax( 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 { IGetRaidTimeRequest } from "@spt-aki/models/eft/game/IGetRaidTimeRequest";
import { ExtractChange, IGetRaidTimeResponse } from "@spt-aki/models/eft/game/IGetRaidTimeResponse"; import { ExtractChange, IGetRaidTimeResponse } from "@spt-aki/models/eft/game/IGetRaidTimeResponse";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; 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 { IRaidChanges } from "@spt-aki/models/spt/location/IRaidChanges";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
@ -39,7 +43,9 @@ export class RaidTimeAdjustmentService
*/ */
public makeAdjustmentsToMap(raidAdjustments: IRaidChanges, mapBase: ILocationBase): void 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 // Change loot multipler values before they're used below
this.adjustLootMultipliers(this.locationConfig.looseLootMultiplier, raidAdjustments.dynamicLootPercent); this.adjustLootMultipliers(this.locationConfig.looseLootMultiplier, raidAdjustments.dynamicLootPercent);
@ -48,8 +54,8 @@ export class RaidTimeAdjustmentService
const mapSettings = this.getMapSettings(mapBase.Id); const mapSettings = this.getMapSettings(mapBase.Id);
if (mapSettings.adjustWaves) if (mapSettings.adjustWaves)
{ {
// Make alterations to bot spawn waves now player is simulated spawning later // Make alterations to bot spawn waves now player is simulated spawning later
this.adjustWaves(mapBase, raidAdjustments) this.adjustWaves(mapBase, raidAdjustments);
} }
} }
@ -75,7 +81,7 @@ export class RaidTimeAdjustmentService
{ {
// Remove waves that spawned before the player joined // Remove waves that spawned before the player joined
const originalWaveCount = mapBase.waves.length; 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 // Adjust wave min/max times to match new simulated start
for (const wave of mapBase.waves) for (const wave of mapBase.waves)
@ -85,7 +91,11 @@ export class RaidTimeAdjustmentService
wave.time_max -= Math.max(raidAdjustments.simulatedRaidStartSeconds, 0); 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, RaidTimeMinutes: baseEscapeTimeMinutes,
ExitChanges: [], ExitChanges: [],
NewSurviveTimeSeconds: null, 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 // Pmc raid, send default
if (request.Side.toLowerCase() === "pmc") if (request.Side.toLowerCase() === "pmc")
@ -124,15 +134,17 @@ export class RaidTimeAdjustmentService
// Send default // Send default
return result; return result;
} }
// Get the weighted percent to reduce the raid time by // Get the weighted percent to reduce the raid time by
const chosenRaidReductionPercent = Number.parseInt(this.weightedRandomHelper.getWeightedValue<string>( const chosenRaidReductionPercent = Number.parseInt(
mapSettings.reductionPercentWeights, this.weightedRandomHelper.getWeightedValue<string>(mapSettings.reductionPercentWeights),
)); );
const raidTimeRemainingPercent = 100 - chosenRaidReductionPercent; const raidTimeRemainingPercent = 100 - chosenRaidReductionPercent;
// How many minutes raid will last // 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 // Time player spawns into the raid if it was online
const simulatedRaidStartTimeMinutes = baseEscapeTimeMinutes - newRaidTimeMinutes; const simulatedRaidStartTimeMinutes = baseEscapeTimeMinutes - newRaidTimeMinutes;
@ -140,21 +152,25 @@ export class RaidTimeAdjustmentService
if (mapSettings.reduceLootByPercent) if (mapSettings.reduceLootByPercent)
{ {
// Store time reduction percent in app context so loot gen can pick it up later // Store time reduction percent in app context so loot gen can pick it up later
this.applicationContext.addValue(ContextVariableType.RAID_ADJUSTMENTS, this.applicationContext.addValue(ContextVariableType.RAID_ADJUSTMENTS, {
{ dynamicLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minDynamicLootPercent),
dynamicLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minDynamicLootPercent), staticLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minStaticLootPercent),
staticLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minStaticLootPercent), simulatedRaidStartSeconds: simulatedRaidStartTimeMinutes * 60,
simulatedRaidStartSeconds: simulatedRaidStartTimeMinutes * 60 });
});
} }
// Update result object with new time // Update result object with new time
result.RaidTimeMinutes = newRaidTimeMinutes; 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 // 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); const exitAdjustments = this.getExitAdjustments(mapBase, newRaidTimeMinutes);
if (exitAdjustments) if (exitAdjustments)
@ -188,74 +204,73 @@ export class RaidTimeAdjustmentService
* @param newRaidTimeMinutes How long raid is in minutes * @param newRaidTimeMinutes How long raid is in minutes
* @returns List of exit changes to send to client * @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 = []; if (exit.PassageRequirement !== "Train")
// Adjust train exits only
for (const exit of mapBase.exits)
{ {
if (exit.PassageRequirement !== "Train") continue;
{
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);
} }
return result.length > 0 // Prepare train adjustment object
? result const exitChange: ExtractChange = { Name: exit.Name, MinTime: null, MaxTime: null, Chance: null };
: 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 = { const options: IProcessBuyTradeRequestData = {
scheme_items: [{ scheme_items: [{
id: "5449016a4bdc2d6f028b456f", // Rouble tpl id: "5449016a4bdc2d6f028b456f", // Rouble tpl
count: Math.round(repairCost) count: Math.round(repairCost),
}], }],
tid: traderId, tid: traderId,
Action: "SptRepair", Action: "SptRepair",
@ -214,10 +214,7 @@ export class RepairService
else else
{ {
// Trader repair - Not as accurate as kit, needs data from live // Trader repair - Not as accurate as kit, needs data from live
return Math.min( return Math.min(repairDetails.repairAmount / 10, this.repairConfig.maxIntellectGainPerRepair.trader);
repairDetails.repairAmount / 10,
this.repairConfig.maxIntellectGainPerRepair.trader,
);
} }
} }
@ -440,7 +437,13 @@ export class RepairService
if (this.shouldBuffItem(repairDetails, pmcData)) 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; const armorConfig = this.repairConfig.repairKit.armor;
this.addBuff(armorConfig, repairDetails.repairedItem); this.addBuff(armorConfig, repairDetails.repairedItem);
@ -539,7 +542,13 @@ export class RepairService
*/ */
protected getItemSkillType(itemTemplate: ITemplateItem): SkillTypes 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") if (itemTemplate._props.ArmorType === "Light")
{ {

View File

@ -529,7 +529,7 @@ export class SeasonalEventService
{ {
const mapData: ILocationData = maps[gifterMapSettings.map]; const mapData: ILocationData = maps[gifterMapSettings.map];
// Dont add gifter to map twice // 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; continue;
} }

View File

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

View File

@ -211,7 +211,9 @@ export class CustomItemService
const weapon = this.itemHelper.getItem(weaponTpl); const weapon = this.itemHelper.getItem(weaponTpl);
if (!weapon[0]) 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; return;
} }

View File

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

View File

@ -51,4 +51,4 @@ export class HashUtil
const max = 1999999; const max = 1999999;
return (max > min) ? Math.floor(Math.random() * (max - min + 1) + min) : min; 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) if (output.warnings?.length > 0)
{ {
output.warnings.push({ output.warnings.push({ index: output.warnings?.length - 1, errmsg: message, code: errorCode.toString() });
index: output.warnings?.length - 1,
errmsg: message,
code: errorCode.toString()
})
} }
else else
{ {

View File

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

View File

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