Null checks first pass (!353)

Co-authored-by: clodan <clodan@clodan.com>
Reviewed-on: https://dev.sp-tarkov.com/SPT/Server/pulls/353
This commit is contained in:
Alex 2024-05-27 16:05:16 +00:00
parent d330ab3715
commit aee391ec1d
54 changed files with 570 additions and 437 deletions

View File

@ -36761,7 +36761,7 @@
"lockedByDefaultOverride": [
"579dc571d53a0658a154fbec"
],
"purchaseAlllClothingByDefaultForTrader":[
"purchaseAllClothingByDefaultForTrader":[
"5ac3b934156ae10c4430e83c"
]
},
@ -37704,7 +37704,7 @@
"lockedByDefaultOverride": [
"579dc571d53a0658a154fbec"
],
"purchaseAlllClothingByDefaultForTrader":[
"purchaseAllClothingByDefaultForTrader":[
"5ac3b934156ae10c4430e83c"
]
},

View File

@ -36,7 +36,7 @@ export class DataCallbacks
*/
public getSettings(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<ISettingsBase>
{
return this.httpResponse.getBody(this.databaseServer.getTables().settings);
return this.httpResponse.getBody(this.databaseServer.getTables().settings!);
}
/**
@ -45,8 +45,8 @@ export class DataCallbacks
*/
public getGlobals(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IGlobals>
{
this.databaseServer.getTables().globals.time = Date.now() / 1000;
return this.httpResponse.getBody(this.databaseServer.getTables().globals);
this.databaseServer.getTables().globals!.time = Date.now() / 1000;
return this.httpResponse.getBody(this.databaseServer.getTables().globals!);
}
/**
@ -55,7 +55,7 @@ export class DataCallbacks
*/
public getTemplateItems(url: string, info: IEmptyRequestData, sessionID: string): string
{
return this.httpResponse.getUnclearedBody(this.databaseServer.getTables().templates.items);
return this.httpResponse.getUnclearedBody(this.databaseServer.getTables().templates!.items);
}
/**
@ -68,7 +68,7 @@ export class DataCallbacks
sessionID: string,
): IGetBodyResponseData<IHandbookBase>
{
return this.httpResponse.getBody(this.databaseServer.getTables().templates.handbook);
return this.httpResponse.getBody(this.databaseServer.getTables().templates!.handbook);
}
/**
@ -81,7 +81,7 @@ export class DataCallbacks
sessionID: string,
): IGetBodyResponseData<Record<string, ICustomizationItem>>
{
return this.httpResponse.getBody(this.databaseServer.getTables().templates.customization);
return this.httpResponse.getBody(this.databaseServer.getTables().templates!.customization);
}
/**
@ -94,7 +94,7 @@ export class DataCallbacks
sessionID: string,
): IGetBodyResponseData<string[]>
{
return this.httpResponse.getBody(this.databaseServer.getTables().templates.character);
return this.httpResponse.getBody(this.databaseServer.getTables().templates!.character);
}
/**
@ -107,7 +107,7 @@ export class DataCallbacks
sessionID: string,
): IGetBodyResponseData<IHideoutSettingsBase>
{
return this.httpResponse.getBody(this.databaseServer.getTables().hideout.settings);
return this.httpResponse.getBody(this.databaseServer.getTables().hideout!.settings);
}
public getHideoutAreas(
@ -116,7 +116,7 @@ export class DataCallbacks
sessionID: string,
): IGetBodyResponseData<IHideoutArea[]>
{
return this.httpResponse.getBody(this.databaseServer.getTables().hideout.areas);
return this.httpResponse.getBody(this.databaseServer.getTables().hideout!.areas);
}
public gethideoutProduction(
@ -125,7 +125,7 @@ export class DataCallbacks
sessionID: string,
): IGetBodyResponseData<IHideoutProduction[]>
{
return this.httpResponse.getBody(this.databaseServer.getTables().hideout.production);
return this.httpResponse.getBody(this.databaseServer.getTables().hideout!.production);
}
public getHideoutScavcase(
@ -134,7 +134,7 @@ export class DataCallbacks
sessionID: string,
): IGetBodyResponseData<IHideoutScavCase[]>
{
return this.httpResponse.getBody(this.databaseServer.getTables().hideout.scavcase);
return this.httpResponse.getBody(this.databaseServer.getTables().hideout!.scavcase);
}
/**
@ -146,7 +146,7 @@ export class DataCallbacks
sessionID: string,
): IGetBodyResponseData<Record<string, string>>
{
return this.httpResponse.getBody(this.databaseServer.getTables().locales.languages);
return this.httpResponse.getBody(this.databaseServer.getTables().locales!.languages);
}
/**
@ -156,13 +156,16 @@ export class DataCallbacks
{
const localeId = url.replace("/client/menu/locale/", "");
const tables = this.databaseServer.getTables();
let result = tables.locales.menu[localeId];
let result = tables.locales?.menu[localeId];
if (result === undefined)
{
result = tables.locales.menu.en;
result = tables.locales?.menu.en;
}
if (result === undefined)
throw new Error(`Unable to determine locale for request with '${localeId}'`);
return this.httpResponse.getBody(result);
}
@ -173,11 +176,11 @@ export class DataCallbacks
{
const localeId = url.replace("/client/locale/", "");
const tables = this.databaseServer.getTables();
let result = tables.locales.global[localeId];
let result = tables.locales?.global[localeId];
if (result === undefined)
{
result = tables.locales.global["en"];
result = tables.locales?.global["en"];
}
return this.httpResponse.getUnclearedBody(result);

View File

@ -95,7 +95,7 @@ export class DialogueCallbacks implements OnUpdate
sessionID: string,
): IGetBodyResponseData<DialogueInfo[]>
{
return this.httpResponse.getBody(this.dialogueController.generateDialogueList(sessionID), 0, null, false);
return this.httpResponse.getBody(this.dialogueController.generateDialogueList(sessionID), 0, undefined, false);
}
/** Handle client/mail/dialog/view */
@ -105,7 +105,12 @@ export class DialogueCallbacks implements OnUpdate
sessionID: string,
): IGetBodyResponseData<IGetMailDialogViewResponseData>
{
return this.httpResponse.getBody(this.dialogueController.generateDialogueView(info, sessionID), 0, null, false);
return this.httpResponse.getBody(
this.dialogueController.generateDialogueView(info, sessionID),
0,
undefined,
false,
);
}
/** Handle client/mail/dialog/info */
@ -154,7 +159,7 @@ export class DialogueCallbacks implements OnUpdate
url: string,
info: IGetAllAttachmentsRequestData,
sessionID: string,
): IGetBodyResponseData<IGetAllAttachmentsResponse>
): IGetBodyResponseData<IGetAllAttachmentsResponse | undefined>
{
return this.httpResponse.getBody(this.dialogueController.getAllAttachments(info.dialogId, sessionID));
}

View File

@ -46,7 +46,7 @@ export class ItemEventCallbacks
for (const warning of warnings)
{
if (!nonCriticalErrorCodes.includes(+warning.code))
if (!nonCriticalErrorCodes.includes(+(warning?.code ?? "0")))
{
return true;
}

View File

@ -94,6 +94,7 @@ export class MatchCallbacks
): IGetBodyResponseData<IGroupCharacter[]>
{
const result = [];
// eslint-disable-next-line strict-null-checks/all
result.push({});
return this.httpResponse.getBody(result);
@ -165,7 +166,7 @@ export class MatchCallbacks
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public getMetrics(url: string, info: any, sessionID: string): IGetBodyResponseData<string>
{
return this.httpResponse.getBody(this.jsonUtil.serialize(this.databaseServer.getTables().match.metrics));
return this.httpResponse.getBody(this.jsonUtil.serialize(this.databaseServer.getTables().match!.metrics));
}
/**

View File

@ -19,27 +19,29 @@ export class ApplicationContext
* const matchInfo = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<IGetRaidConfigurationRequestData>();
* ```
*/
public getLatestValue(type: ContextVariableType): ContextVariable
public getLatestValue(type: ContextVariableType): ContextVariable | undefined
{
if (this.variables.has(type))
{
return this.variables.get(type)?.getTail();
}
return undefined;
}
public getValues(type: ContextVariableType): ContextVariable[]
public getValues(type: ContextVariableType): ContextVariable[] | undefined
{
if (this.variables.has(type))
{
const res: ContextVariable[] = [];
for (const value of this.variables.get(type).values())
for (const value of this.variables.get(type)!.values())
{
res.push(value);
}
return res;
}
return undefined;
}
public addValue(type: ContextVariableType, value: any): void
@ -47,7 +49,7 @@ export class ApplicationContext
let list: LinkedList<ContextVariable>;
if (this.variables.has(type))
{
list = this.variables.get(type);
list = this.variables.get(type)!;
}
else
{

View File

@ -22,7 +22,7 @@ export class AchievementController
*/
public getAchievements(sessionID: string): IGetAchievementsResponse
{
return { elements: this.databaseServer.getTables().templates.achievements };
return { elements: this.databaseServer.getTables().templates!.achievements };
}
/**
@ -32,7 +32,7 @@ export class AchievementController
*/
public getAchievementStatistics(sessionId: string): ICompletedAchievementsResponse
{
const achievements = this.databaseServer.getTables().templates.achievements;
const achievements = this.databaseServer.getTables().templates!.achievements;
const stats = {};
for (const achievement of achievements)

View File

@ -80,7 +80,7 @@ export class BotController
*/
public getBotCoreDifficulty(): IBotCore
{
return this.databaseServer.getTables().bots.core;
return this.databaseServer.getTables().bots!.core;
}
/**
@ -146,7 +146,7 @@ export class BotController
{
const result = {};
const botDb = this.databaseServer.getTables().bots.types;
const botDb = this.databaseServer.getTables().bots!.types;
const botTypes = Object.keys(WildSpawnTypeNumber).filter((v) => Number.isNaN(Number(v)));
for (let botType of botTypes)
{
@ -212,8 +212,12 @@ export class BotController
.getLatestValue(ContextVariableType.RAID_CONFIGURATION)
?.getValue<IGetRaidConfigurationRequestData>();
if (raidSettings === undefined)
{
throw new Error("Raid settings could not be loaded from ApplicationContext");
}
const pmcLevelRangeForMap
= this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings?.location.toLowerCase()];
= this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings.location.toLowerCase()];
const allPmcsHaveSameNameAsPlayer = this.randomUtil.getChance100(
this.pmcConfig.allPMCsHavePlayerNameWithRandomPrefixChance,
@ -369,6 +373,11 @@ export class BotController
const raidSettings = this.applicationContext
.getLatestValue(ContextVariableType.RAID_CONFIGURATION)
?.getValue<IGetRaidConfigurationRequestData>();
if (raidSettings === undefined)
{
throw new Error("Raid settings could not be loaded from ApplicationContext");
}
const pmcLevelRangeForMap
= this.pmcConfig.locationSpecificPmcLevelOverride[raidSettings.location.toLowerCase()];
@ -482,7 +491,7 @@ export class BotController
const defaultMapCapId = "default";
const raidConfig = this.applicationContext
.getLatestValue(ContextVariableType.RAID_CONFIGURATION)
.getValue<IGetRaidConfigurationRequestData>();
?.getValue<IGetRaidConfigurationRequestData>();
if (!raidConfig)
{
@ -497,7 +506,7 @@ export class BotController
this.logger.warning(
this.localisationService.getText(
"bot-no_bot_cap_found_for_location",
raidConfig.location.toLowerCase(),
raidConfig?.location.toLowerCase(),
),
);
botCap = this.botConfig.maxBotCap[defaultMapCapId];

View File

@ -42,7 +42,7 @@ export class BuildController
// Ensure the secure container in the default presets match what the player has equipped
const defaultEquipmentPresetsClone = this.cloner.clone(
this.databaseServer.getTables().templates.defaultEquipmentPresets,
this.databaseServer.getTables().templates!.defaultEquipmentPresets,
);
const playerSecureContainer = profile.characters.pmc.Inventory.items?.find(
(x) => x.slotId === secureContainerSlotId,

View File

@ -38,14 +38,17 @@ export class CustomizationController
public getTraderSuits(traderID: string, sessionID: string): ISuit[]
{
const pmcData: IPmcData = this.profileHelper.getPmcProfile(sessionID);
const templates = this.databaseServer.getTables().templates.customization;
const suits = this.databaseServer.getTables().traders[traderID].suits;
const templates = this.databaseServer.getTables().templates!.customization;
const suits = this.databaseServer.getTables().traders![traderID].suits;
// Get an inner join of clothing from templates.customization and Ragman's suits array
const matchingSuits = suits.filter((x) => x.suiteId in templates);
const matchingSuits = suits?.filter((x) => x.suiteId in templates);
// Return all suits that have a side array containing the players side (usec/bear)
return matchingSuits.filter((x) => templates[x.suiteId]._props.Side.includes(pmcData.Info.Side));
const matchedSuits = matchingSuits?.filter((x) => templates[x.suiteId]._props.Side.includes(pmcData.Info.Side));
if (matchingSuits === undefined)
throw new Error(`Unable to get trader suits for trader ${traderID}`);
return matchedSuits!;
}
/**
@ -61,7 +64,7 @@ export class CustomizationController
for (const suitId of wearClothingRequest.suites)
{
// Find desired clothing item in db
const dbSuit = this.databaseServer.getTables().templates.customization[suitId];
const dbSuit = this.databaseServer.getTables().templates!.customization[suitId];
// Legs
if (dbSuit._parent === this.clothingIds.lowerParentId)
@ -110,7 +113,7 @@ export class CustomizationController
const suitId = traderOffer.suiteId;
if (this.outfitAlreadyPurchased(suitId, sessionId))
{
const suitDetails = db.templates.customization[suitId];
const suitDetails = db.templates!.customization[suitId];
this.logger.error(
this.localisationService.getText("customisation-item_already_purchased", {
itemId: suitDetails._id,
@ -132,7 +135,12 @@ export class CustomizationController
protected getTraderClothingOffer(sessionId: string, offerId: string): ISuit
{
return this.getAllTraderSuits(sessionId).find((x) => x._id === offerId);
const foundSuit = this.getAllTraderSuits(sessionId).find((x) => x._id === offerId);
if (foundSuit === undefined)
{
throw new Error(`Unable to find suit with offer id ${offerId}`);
}
return foundSuit;
}
/**
@ -199,6 +207,11 @@ export class CustomizationController
pmcData.Inventory.items.splice(pmcData.Inventory.items.indexOf(relatedItem), 1);
}
if (!relatedItem.upd || !relatedItem.upd.StackObjectsCount)
{
throw new Error(`Suit with tpl id ${relatedItem._tpl} does not have upd or stack object count properties`);
}
if (relatedItem.upd.StackObjectsCount > clothingItem.count)
{
relatedItem.upd.StackObjectsCount -= clothingItem.count;

View File

@ -37,14 +37,12 @@ export class DialogueController
// if give command is disabled or commando commands are disabled
if (!coreConfigs.features?.chatbotFeatures?.commandoEnabled)
{
const sptCommando = this.dialogueChatBots.find(
(c) => c.getChatBot()._id.toLocaleLowerCase() === "sptcommando",
);
const sptCommando = this.dialogueChatBots.find((c) => c.getChatBot()._id.toLocaleLowerCase() === "sptcommando")!;
this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptCommando), 1);
}
if (!coreConfigs.features?.chatbotFeatures?.sptFriendEnabled)
{
const sptFriend = this.dialogueChatBots.find((c) => c.getChatBot()._id.toLocaleLowerCase() === "sptFriend");
const sptFriend = this.dialogueChatBots.find((c) => c.getChatBot()._id.toLocaleLowerCase() === "sptFriend")!;
this.dialogueChatBots.splice(this.dialogueChatBots.indexOf(sptFriend), 1);
}
}
@ -128,7 +126,7 @@ export class DialogueController
* @param sessionID Player id
* @returns IUserDialogInfo array
*/
public getDialogueUsers(dialog: Dialogue, messageType: MessageType, sessionID: string): IUserDialogInfo[]
public getDialogueUsers(dialog: Dialogue, messageType: MessageType, sessionID: string): IUserDialogInfo[] | undefined
{
const profile = this.saveServer.getProfile(sessionID);
@ -214,7 +212,11 @@ export class DialogueController
const chatBot = this.dialogueChatBots.find((cb) => cb.getChatBot()._id === request.dialogId);
if (chatBot)
{
profile.dialogues[request.dialogId].Users.push(chatBot.getChatBot());
if (!profile.dialogues[request.dialogId].Users)
{
profile.dialogues[request.dialogId].Users = [];
}
profile.dialogues[request.dialogId].Users!.push(chatBot.getChatBot());
}
}
}
@ -228,7 +230,7 @@ export class DialogueController
* @param dialogUsers The participants of the mail
* @returns IUserDialogInfo array
*/
protected getProfilesForMail(fullProfile: ISptProfile, dialogUsers: IUserDialogInfo[]): IUserDialogInfo[]
protected getProfilesForMail(fullProfile: ISptProfile, dialogUsers?: IUserDialogInfo[]): IUserDialogInfo[]
{
const result: IUserDialogInfo[] = [];
if (dialogUsers)
@ -283,7 +285,7 @@ export class DialogueController
*/
protected messagesHaveUncollectedRewards(messages: Message[]): boolean
{
return messages.some((message) => message.items?.data?.length > 0);
return messages.some((message) => (message.items?.data?.length ?? 0) > 0);
}
/**
@ -350,7 +352,7 @@ export class DialogueController
* @param sessionId Session id
* @returns IGetAllAttachmentsResponse
*/
public getAllAttachments(dialogueId: string, sessionId: string): IGetAllAttachmentsResponse
public getAllAttachments(dialogueId: string, sessionId: string): IGetAllAttachmentsResponse | undefined
{
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId);
const dialog = dialogs[dialogueId];
@ -358,7 +360,7 @@ export class DialogueController
{
this.logger.error(this.localisationService.getText("dialogue-unable_to_find_in_profile", { sessionId: sessionId, dialogueId: dialogueId }));
return;
return undefined;
}
// Removes corner 'new messages' tag
@ -397,7 +399,7 @@ export class DialogueController
{
const timeNow = this.timeUtil.getTimestamp();
const dialogs = this.dialogueHelper.getDialogsForProfile(sessionId);
return dialogs[dialogueId].messages.filter((message) => timeNow < message.dt + message.maxStorageTime);
return dialogs[dialogueId].messages.filter((message) => timeNow < message.dt + (message.maxStorageTime ?? 0));
}
/**
@ -407,7 +409,7 @@ export class DialogueController
*/
protected getMessagesWithAttachments(messages: Message[]): Message[]
{
return messages.filter((message) => message.items?.data?.length > 0);
return messages.filter((message) => (message.items?.data?.length ?? 0) > 0);
}
/**
@ -452,7 +454,7 @@ export class DialogueController
*/
protected messageHasExpired(message: Message): boolean
{
return this.timeUtil.getTimestamp() > message.dt + message.maxStorageTime;
return this.timeUtil.getTimestamp() > message.dt + (message.maxStorageTime ?? 0);
}
/** Handle client/friend/request/send */

View File

@ -264,7 +264,7 @@ export class GameController
return;
}
for (const craft of this.databaseServer.getTables().hideout.production)
for (const craft of this.databaseServer.getTables().hideout!.production)
{
// Only adjust crafts ABOVE the override
if (craft.productionTime > craftTimeOverrideSeconds)
@ -282,7 +282,7 @@ export class GameController
return;
}
for (const area of this.databaseServer.getTables().hideout.areas)
for (const area of this.databaseServer.getTables().hideout!.areas)
{
for (const stageKey of Object.keys(area.stages))
{
@ -298,7 +298,7 @@ export class GameController
protected adjustLocationBotValues(): void
{
const mapsDb = this.databaseServer.getTables().locations;
const mapsDb = this.databaseServer.getTables().locations!;
for (const locationKey in this.botConfig.maxBotCap)
{
@ -322,12 +322,12 @@ export class GameController
{
for (const traderKey in this.databaseServer.getTables().traders)
{
const trader = this.databaseServer.getTables().traders[traderKey];
const trader = this.databaseServer.getTables().traders![traderKey];
if (!trader?.base?.repair)
{
this.logger.warning(this.localisationService.getText("trader-missing_repair_property_using_default",
{ traderId: trader.base._id, nickname: trader.base.nickname }));
trader.base.repair = this.cloner.clone(this.databaseServer.getTables().traders.ragfair.base.repair);
trader.base.repair = this.cloner.clone(this.databaseServer.getTables().traders!.ragfair.base.repair);
return;
}
@ -337,9 +337,9 @@ export class GameController
this.logger.warning(this.localisationService.getText("trader-missing_repair_quality_property_using_default",
{ traderId: trader.base._id, nickname: trader.base.nickname }));
trader.base.repair.quality = this.cloner.clone(
this.databaseServer.getTables().traders.ragfair.base.repair.quality,
this.databaseServer.getTables().traders!.ragfair.base.repair.quality,
);
trader.base.repair.quality = this.databaseServer.getTables().traders.ragfair.base.repair.quality;
trader.base.repair.quality = this.databaseServer.getTables().traders!.ragfair.base.repair.quality;
}
}
}
@ -356,7 +356,7 @@ export class GameController
continue;
}
const mapLooseLoot: ILooseLoot = this.databaseServer.getTables().locations[mapId]?.looseLoot;
const mapLooseLoot: ILooseLoot = this.databaseServer.getTables().locations![mapId]?.looseLoot;
if (!mapLooseLoot)
{
this.logger.warning(this.localisationService.getText("location-map_has_no_loose_loot_data", mapId));
@ -391,7 +391,7 @@ export class GameController
const adjustments = this.lootConfig.looseLootSpawnPointAdjustments;
for (const mapId in adjustments)
{
const mapLooseLootData: ILooseLoot = this.databaseServer.getTables().locations[mapId]?.looseLoot;
const mapLooseLootData: ILooseLoot = this.databaseServer.getTables().locations![mapId]?.looseLoot;
if (!mapLooseLootData)
{
this.logger.warning(this.localisationService.getText("location-map_has_no_loose_loot_data", mapId));
@ -418,7 +418,7 @@ export class GameController
/** Apply custom limits on bot types as defined in configs/location.json/botTypeLimits */
protected adjustMapBotLimits(): void
{
const mapsDb = this.databaseServer.getTables().locations;
const mapsDb = this.databaseServer.getTables().locations!;
if (!this.locationConfig.botTypeLimits)
{
return;
@ -470,7 +470,7 @@ export class GameController
)?.Value ?? 0;
const config: IGameConfigResponse = {
languages: this.databaseServer.getTables().locales.languages,
languages: this.databaseServer.getTables().locales!.languages,
ndaFree: false,
reportAvailable: false,
twitchEventMember: false,
@ -552,7 +552,7 @@ export class GameController
*/
protected fixShotgunDispersions(): void
{
const itemDb = this.databaseServer.getTables().templates.items;
const itemDb = this.databaseServer.getTables().templates!.items;
// Saiga 12ga
// Toz 106
@ -582,7 +582,7 @@ export class GameController
protected flagAllItemsInDbAsSellableOnFlea(): void
{
const dbItems = Object.values(this.databaseServer.getTables().templates.items);
const dbItems = Object.values(this.databaseServer.getTables().templates!.items);
for (const item of dbItems)
{
if (item._type === "Item" && !item._props?.CanSellOnRagfair)
@ -613,12 +613,12 @@ export class GameController
// Set new values, whatever is smallest
energyRegenPerHour += pmcProfile.Bonuses.filter(
(bonus) => bonus.type === BonusType.ENERGY_REGENERATION,
).reduce((sum, curr) => sum + curr.value, 0);
).reduce((sum, curr) => sum + (curr.value ?? 0), 0);
hydrationRegenPerHour += pmcProfile.Bonuses.filter(
(bonus) => bonus.type === BonusType.HYDRATION_REGENERATION,
).reduce((sum, curr) => sum + curr.value, 0);
).reduce((sum, curr) => sum + (curr.value ?? 0), 0);
hpRegenPerHour += pmcProfile.Bonuses.filter((bonus) => bonus.type === BonusType.HEALTH_REGENERATION).reduce(
(sum, curr) => sum + curr.value,
(sum, curr) => sum + (curr.value ?? 0),
0,
);
@ -703,7 +703,7 @@ export class GameController
}
// Loop over all of the locations waves and look for waves with identical min and max slots
const location: ILocation = this.databaseServer.getTables().locations[locationKey];
const location: ILocation = this.databaseServer.getTables().locations![locationKey];
if (!location.base)
{
this.logger.warning(
@ -730,7 +730,7 @@ export class GameController
*/
protected fixRoguesSpawningInstantlyOnLighthouse(): void
{
const lighthouse = this.databaseServer.getTables().locations.lighthouse.base;
const lighthouse = this.databaseServer.getTables().locations!.lighthouse!.base;
for (const wave of lighthouse.BossLocationSpawn)
{
// Find Rogues that spawn instantly
@ -778,7 +778,7 @@ export class GameController
}
// Iterate over all maps
const location: ILocation = this.databaseServer.getTables().locations[locationKey];
const location: ILocation = this.databaseServer.getTables().locations![locationKey];
for (const wave of location.base.waves)
{
// Wave has size that makes it candidate for splitting
@ -883,8 +883,8 @@ export class GameController
protected validateQuestAssortUnlocksExist(): void
{
const db = this.databaseServer.getTables();
const traders = db.traders;
const quests = db.templates.quests;
const traders = db.traders!;
const quests = db.templates!.quests;
for (const traderId of Object.values(Traders))
{
const traderData = traders[traderId];
@ -896,9 +896,9 @@ export class GameController
// Merge started/success/fail quest assorts into one dictionary
const mergedQuestAssorts = {
...traderData.questassort.started,
...traderData.questassort.success,
...traderData.questassort.fail,
...traderData.questassort?.started,
...traderData.questassort?.success,
...traderData.questassort?.fail,
};
// Loop over all assorts for trader
@ -929,7 +929,7 @@ export class GameController
const playerName = pmcProfile.Info.Nickname;
if (playerName)
{
const bots = this.databaseServer.getTables().bots.types;
const bots = this.databaseServer.getTables().bots!.types;
if (bots.bear)
{
@ -962,9 +962,9 @@ export class GameController
protected removePraporTestMessage(): void
{
// Iterate over all languages (e.g. "en", "fr")
for (const localeKey in this.databaseServer.getTables().locales.global)
for (const localeKey in this.databaseServer.getTables().locales!.global)
{
this.databaseServer.getTables().locales.global[localeKey]["61687e2c3e526901fa76baf9"] = "";
this.databaseServer.getTables().locales!.global[localeKey]["61687e2c3e526901fa76baf9"] = "";
}
}
@ -973,7 +973,7 @@ export class GameController
*/
protected adjustLabsRaiderSpawnRate(): void
{
const labsBase = this.databaseServer.getTables().locations.laboratory.base;
const labsBase = this.databaseServer.getTables().locations!.laboratory!.base;
const nonTriggerLabsBossSpawns = labsBase.BossLocationSpawn.filter(
(x) => x.TriggerId === "" && x.TriggerName === "",
);

View File

@ -7,6 +7,7 @@ import { BanType } from "@spt/models/eft/common/tables/IBotBase";
import { Item } from "@spt/models/eft/common/tables/IItem";
import { ProfileTraderTemplate } from "@spt/models/eft/common/tables/IProfileTemplate";
import { ITraderAssort, ITraderBase, LoyaltyLevel } from "@spt/models/eft/common/tables/ITrader";
import { ISptProfile } from "@spt/models/eft/profile/ISptProfile";
import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { Money } from "@spt/models/enums/Money";
import { Traders } from "@spt/models/enums/Traders";
@ -15,7 +16,6 @@ import { ILogger } from "@spt/models/spt/utils/ILogger";
import { ConfigServer } from "@spt/servers/ConfigServer";
import { DatabaseServer } from "@spt/servers/DatabaseServer";
import { FenceService } from "@spt/services/FenceService";
import { ISptProfile } from "@spt/models/eft/profile/ISptProfile";
import { LocalisationService } from "@spt/services/LocalisationService";
import { PlayerService } from "@spt/services/PlayerService";
import { RandomUtil } from "@spt/utils/RandomUtil";
@ -26,9 +26,9 @@ export class TraderHelper
{
protected traderConfig: ITraderConfig;
/** Dictionary of item tpl and the highest trader sell rouble price */
protected highestTraderPriceItems: Record<string, number> = null;
protected highestTraderPriceItems?: Record<string, number> = undefined;
/** Dictionary of item tpl and the highest trader buy back rouble price */
protected highestTraderBuyPriceItems: Record<string, number> = null;
protected highestTraderBuyPriceItems?: Record<string, number> = undefined;
constructor(
@inject("WinstonLogger") protected logger: ILogger,
@ -54,7 +54,7 @@ export class TraderHelper
* @param sessionID Players id
* @returns Trader base
*/
public getTrader(traderID: string, sessionID: string): ITraderBase
public getTrader(traderID: string, sessionID: string): ITraderBase | undefined
{
const pmcData = this.profileHelper.getPmcProfile(sessionID);
if (!pmcData)
@ -88,7 +88,7 @@ export class TraderHelper
{
return traderId === Traders.FENCE
? this.fenceService.getRawFenceAssorts()
: this.databaseServer.getTables().traders[traderId].assort;
: this.databaseServer.getTables().traders![traderId].assort!;
}
/**
@ -97,14 +97,14 @@ export class TraderHelper
* @param assortId Id of assort to find
* @returns Item object
*/
public getTraderAssortItemByAssortId(traderId: string, assortId: string): Item
public getTraderAssortItemByAssortId(traderId: string, assortId: string): Item | undefined
{
const traderAssorts = this.getTraderAssortsByTraderId(traderId);
if (!traderAssorts)
{
this.logger.debug(`No assorts on trader: ${traderId} found`);
return null;
return undefined;
}
// Find specific assort in traders data
@ -113,7 +113,7 @@ export class TraderHelper
{
this.logger.debug(`No assort ${assortId} on trader: ${traderId} found`);
return null;
return undefined;
}
return purchasedAssort;
@ -131,7 +131,7 @@ export class TraderHelper
const fullProfile = this.profileHelper.getFullProfile(sessionID);
const pmcData = fullProfile.characters.pmc;
const rawProfileTemplate: ProfileTraderTemplate
= db.templates.profiles[fullProfile.info.edition][pmcData.Info.Side.toLowerCase()]
= db.templates!.profiles[fullProfile.info.edition][pmcData.Info.Side.toLowerCase()]
.trader;
pmcData.TradersInfo[traderID] = {
@ -139,8 +139,8 @@ export class TraderHelper
loyaltyLevel: rawProfileTemplate.initialLoyaltyLevel[traderID] ?? 1,
salesSum: rawProfileTemplate.initialSalesSum,
standing: this.getStartingStanding(traderID, rawProfileTemplate),
nextResupply: db.traders[traderID].base.nextResupply,
unlocked: db.traders[traderID].base.unlockedByDefault,
nextResupply: db.traders![traderID].base.nextResupply,
unlocked: db.traders![traderID].base.unlockedByDefault,
};
// Check if trader should be locked by default
@ -149,19 +149,18 @@ export class TraderHelper
pmcData.TradersInfo[traderID].unlocked = false;
}
if (rawProfileTemplate.purchaseAlllClothingByDefaultForTrader?.includes(traderID))
{
//Get traders clothing
const clothing = this.databaseServer.getTables().traders[traderID].suits;
// Force suit ids into profile
this.addSuitsToProfile(fullProfile, clothing.map(x => x.suiteId));
}
if (rawProfileTemplate.fleaBlockedDays > 0)
if (rawProfileTemplate.purchaseAllClothingByDefaultForTrader?.includes(traderID))
{
const newBanDateTime = this.timeUtil.getTimeStampFromNowDays(rawProfileTemplate.fleaBlockedDays);
// Get traders clothing
const clothing = this.databaseServer.getTables().traders![traderID].suits!;
// Force suit ids into profile
this.addSuitsToProfile(fullProfile, clothing.map((x) => x.suiteId));
}
if ((rawProfileTemplate.fleaBlockedDays ?? 0) > 0)
{
const newBanDateTime = this.timeUtil.getTimeStampFromNowDays(rawProfileTemplate.fleaBlockedDays!);
const existingBan = pmcData.Info.Bans.find((ban) => ban.banType === BanType.RAGFAIR);
if (existingBan)
{
@ -278,7 +277,7 @@ export class TraderHelper
public validateTraderStandingsAndPlayerLevelForProfile(sessionId: string): void
{
const profile = this.profileHelper.getPmcProfile(sessionId);
const traders = Object.keys(this.databaseServer.getTables().traders);
const traders = Object.keys(this.databaseServer.getTables().traders!);
for (const trader of traders)
{
this.lvlUp(trader, profile);
@ -293,7 +292,7 @@ export class TraderHelper
*/
public lvlUp(traderID: string, pmcData: IPmcData): void
{
const loyaltyLevels = this.databaseServer.getTables().traders[traderID].base.loyaltyLevels;
const loyaltyLevels = this.databaseServer.getTables().traders![traderID].base.loyaltyLevels;
// Level up player
pmcData.Info.Level = this.playerService.calculateLevel(pmcData);
@ -332,7 +331,7 @@ export class TraderHelper
public getNextUpdateTimestamp(traderID: string): number
{
const time = this.timeUtil.getTimestamp();
const updateSeconds = this.getTraderUpdateSeconds(traderID);
const updateSeconds = this.getTraderUpdateSeconds(traderID) ?? 0;
return time + updateSeconds;
}
@ -341,7 +340,7 @@ export class TraderHelper
* @param traderId Trader to look up
* @returns Time in seconds
*/
public getTraderUpdateSeconds(traderId: string): number
public getTraderUpdateSeconds(traderId: string): number | undefined
{
const traderDetails = this.traderConfig.updateTime.find((x) => x.traderId === traderId);
if (!traderDetails || traderDetails.seconds.min === undefined || traderDetails.seconds.max === undefined)
@ -360,6 +359,7 @@ export class TraderHelper
seconds: { min: this.traderConfig.updateTimeDefault, max: this.traderConfig.updateTimeDefault },
},
);
return undefined;
}
else
{
@ -369,7 +369,7 @@ export class TraderHelper
public getLoyaltyLevel(traderID: string, pmcData: IPmcData): LoyaltyLevel
{
const trader = this.databaseServer.getTables().traders[traderID].base;
const trader = this.databaseServer.getTables().traders![traderID].base;
let loyaltyLevel = pmcData.TradersInfo[traderID].loyaltyLevel;
if (!loyaltyLevel || loyaltyLevel < 1)
@ -426,14 +426,14 @@ export class TraderHelper
if (
profile.traderPurchases[traderId][purchasedItem.itemId].count + purchasedItem.count
> itemPurchased.upd.BuyRestrictionMax
> itemPurchased.upd!.BuyRestrictionMax!
)
{
throw new Error(
this.localisationService.getText("trader-unable_to_purchase_item_limit_reached",
{
traderId: traderId,
limit: itemPurchased.upd.BuyRestrictionMax,
limit: itemPurchased.upd!.BuyRestrictionMax,
}),
);
}
@ -470,7 +470,7 @@ export class TraderHelper
}
// Get assorts for trader, skip trader if no assorts found
const traderAssorts = this.databaseServer.getTables().traders[Traders[traderName]].assort;
const traderAssorts = this.databaseServer.getTables().traders![Traders[traderName]].assort;
if (!traderAssorts)
{
continue;
@ -522,7 +522,7 @@ export class TraderHelper
for (const traderName in Traders)
{
// Get trader and check buy category allows tpl
const traderBase = this.databaseServer.getTables().traders[Traders[traderName]]?.base;
const traderBase = this.databaseServer.getTables().traders![Traders[traderName]]?.base;
if (traderBase && this.itemHelper.isOfBaseclasses(tpl, traderBase.items_buy.category))
{
// Get loyalty level details player has achieved with this trader
@ -557,7 +557,7 @@ export class TraderHelper
* @param traderId Traders id
* @returns Traders key
*/
public getTraderById(traderId: string): Traders
public getTraderById(traderId: string): Traders | undefined
{
const keys = Object.keys(Traders).filter((x) => Traders[x] === traderId);
@ -565,7 +565,7 @@ export class TraderHelper
{
this.logger.error(this.localisationService.getText("trader-unable_to_find_trader_in_enum", traderId));
return null;
return undefined;
}
return keys[0] as Traders;

View File

@ -202,7 +202,7 @@ export interface Mastering extends IBaseSkill
export interface Stats
{
Eft: IEftStats
Eft?: IEftStats
}
export interface IEftStats

View File

@ -45,5 +45,5 @@ export interface ProfileTraderTemplate
/** What traders default to being locked on profile creation */
lockedByDefaultOverride?: string[]
/** What traders should have their clothing unlocked/purchased on creation */
purchaseAlllClothingByDefaultForTrader?: string[]
purchaseAllClothingByDefaultForTrader?: string[]
}

View File

@ -1,7 +1,7 @@
export interface IGetRaidTimeResponse
{
RaidTimeMinutes: number
NewSurviveTimeSeconds: number
NewSurviveTimeSeconds?: number
OriginalSurvivalTimeSeconds: number
ExitChanges: ExtractChange[]
}

View File

@ -6,7 +6,7 @@ export interface IAddItemsDirectRequest
itemsWithModsToAdd: Item[][]
foundInRaid: boolean
/** Runs after EACH item with children is added */
callback: (buyCount: number) => void
callback?: (buyCount: number) => void
/** Should sorting table be used when no space found in stash */
useSortingTable: boolean
}

View File

@ -112,7 +112,7 @@ export interface IUserDialogInfo
{
_id: string
aid: number
Info: IUserDialogDetails
Info?: IUserDialogDetails
}
export interface IUserDialogDetails

View File

@ -110,7 +110,7 @@ export class GiftService
{
this.mailSendService.sendLocalisedNpcMessageToPlayer(
playerId,
giftData.trader,
giftData.trader!,
MessageType.MESSAGE_WITH_ITEMS,
giftData.localeTextId,
giftData.items,
@ -121,7 +121,7 @@ export class GiftService
{
this.mailSendService.sendDirectNpcMessageToPlayer(
playerId,
giftData.trader,
giftData.trader!,
MessageType.MESSAGE_WITH_ITEMS,
giftData.messageText,
giftData.items,
@ -135,11 +135,11 @@ export class GiftService
// Trader / ragfair
const details: ISendMessageDetails = {
recipientId: playerId,
sender: this.getMessageType(giftData),
sender: this.getMessageType(giftData)!,
senderDetails: {
_id: this.getSenderId(giftData),
_id: this.getSenderId(giftData)!,
aid: 1234567, // TODO - pass proper aid value
Info: null,
Info: undefined,
},
messageText: giftData.messageText,
items: giftData.items,
@ -164,11 +164,11 @@ export class GiftService
* @param giftData Gift to send player
* @returns trader/user/system id
*/
protected getSenderId(giftData: Gift): string
protected getSenderId(giftData: Gift): string | undefined
{
if (giftData.sender === GiftSenderType.TRADER)
{
return Traders[giftData.trader];
return Traders[giftData.trader!];
}
if (giftData.sender === GiftSenderType.USER)
@ -182,7 +182,7 @@ export class GiftService
* @param giftData Gift to send player
* @returns MessageType enum value
*/
protected getMessageType(giftData: Gift): MessageType
protected getMessageType(giftData: Gift): MessageType | undefined
{
switch (giftData.sender)
{

View File

@ -109,28 +109,42 @@ export class InsuranceService
for (const traderId in this.getInsurance(sessionID))
{
const traderBase = this.traderHelper.getTrader(traderId, sessionID);
if (!traderBase)
{
throw new Error(`The trader id ${traderId} was not found!`);
}
let insuranceReturnTimestamp = this.getInsuranceReturnTimestamp(pmcData, traderBase);
if (markOfTheUnheardOnPlayer)
{
insuranceReturnTimestamp *= this.databaseServer.getTables()
.globals.config.Insurance.CoefOfHavingMarkOfUnknown;
.globals!.config.Insurance.CoefOfHavingMarkOfUnknown;
}
const dialogueTemplates = this.databaseServer.getTables().traders![traderId].dialogue;
if (!dialogueTemplates)
{
throw new Error(`The trader id ${traderId} does not have dialogues for insurance`);
}
const dialogueTemplates = this.databaseServer.getTables().traders[traderId].dialogue;
const systemData = {
date: this.timeUtil.getDateMailFormat(),
time: this.timeUtil.getTimeMailFormat(),
location: mapId,
};
const traderEnum = this.traderHelper.getTraderById(traderId);
if (!traderEnum)
{
throw new Error(`The trader id ${traderId} is missing from Traders enum`);
}
// Send "i will go look for your stuff" message from trader to player
this.mailSendService.sendLocalisedNpcMessageToPlayer(
sessionID,
this.traderHelper.getTraderById(traderId),
traderEnum,
MessageType.NPC_TRADER,
this.randomUtil.getArrayValue(dialogueTemplates.insuranceStart),
null,
this.randomUtil.getArrayValue(dialogueTemplates?.insuranceStart),
undefined,
this.timeUtil.getHoursAsSeconds(
this.databaseServer.getTables().globals.config.Insurance.MaxStorageTimeInHour,
this.databaseServer.getTables().globals!.config.Insurance.MaxStorageTimeInHour,
),
systemData,
);
@ -192,7 +206,7 @@ export class InsuranceService
const insuranceReturnTimeBonus = pmcData.Bonuses.find((b) => b.type === BonusType.INSURANCE_RETURN_TIME);
const insuranceReturnTimeBonusPercent
= 1.0 - (insuranceReturnTimeBonus ? Math.abs(insuranceReturnTimeBonus.value) : 0) / 100;
= 1.0 - (insuranceReturnTimeBonus ? Math.abs(insuranceReturnTimeBonus!.value ?? 0) : 0) / 100;
const traderMinReturnAsSeconds = trader.insurance.min_return_hour * TimeUtil.ONE_HOUR_AS_SECONDS;
const traderMaxReturnAsSeconds = trader.insurance.max_return_hour * TimeUtil.ONE_HOUR_AS_SECONDS;
@ -238,10 +252,10 @@ export class InsuranceService
continue;
}
const preRaidItem = preRaidGearMap.get(insuredItem.itemId);
const preRaidItem = preRaidGearMap.get(insuredItem.itemId)!;
// Skip slots we should never return as they're never lost on death
if (this.insuranceConfig.blacklistedEquipment.includes(preRaidItem.slotId))
if (this.insuranceConfig.blacklistedEquipment.includes(preRaidItem.slotId!))
{
continue;
}
@ -261,7 +275,9 @@ export class InsuranceService
// Now that we have the equipment parent item, we can check to see if that item is located in an equipment
// slot that is flagged as lost on death. If it is, then the itemShouldBeLostOnDeath.
const itemShouldBeLostOnDeath = this.lostOnDeathConfig.equipment[equipmentParentItem?.slotId] ?? true;
const itemShouldBeLostOnDeath = equipmentParentItem?.slotId
? this.lostOnDeathConfig.equipment[equipmentParentItem?.slotId] ?? true
: true;
// Was the item found in the player inventory post-raid?
const itemOnPlayerPostRaid = offRaidGearMap.has(insuredItem.itemId);
@ -270,12 +286,18 @@ export class InsuranceService
// Catches both events: player died with item on + player survived but dropped item in raid
if (!itemOnPlayerPostRaid || (playerDied && itemShouldBeLostOnDeath))
{
const inventoryInsuredItem = offraidData.insurance
?.find((insuranceItem) => insuranceItem.id === insuredItem.itemId);
if (!inventoryInsuredItem)
{
throw new Error(`Inventory insured item id ${insuredItem.itemId} was not found`);
}
equipmentPkg.push({
pmcData: pmcData,
itemToReturnToPlayer: this.getInsuredItemDetails(
pmcData,
preRaidItem,
offraidData.insurance?.find((insuranceItem) => insuranceItem.id === insuredItem.itemId),
inventoryInsuredItem,
),
traderId: insuredItem.tid,
sessionID: sessionID,
@ -291,21 +313,31 @@ export class InsuranceService
.filter(
(item) =>
item.parentId === preRaidItem._id
&& this.itemHelper.getSoftInsertSlotIds().includes(item.slotId.toLowerCase()),
&& this.itemHelper.getSoftInsertSlotIds().includes(item.slotId!.toLowerCase()),
)
.map((x) => x._id);
// Add all items found above to return data
for (const softInsertChildModId of softInsertChildIds)
{
const preRaidInventoryItem = preRaidGear.find((item) => item._id === softInsertChildModId);
if (!preRaidInventoryItem)
{
throw new Error(`Preraid inventory item ${softInsertChildModId} was not found`);
}
const inventoryInsuredItem = offraidData.insurance?.find(
(insuranceItem) => insuranceItem.id === softInsertChildModId,
);
if (!inventoryInsuredItem)
{
throw new Error(`Inventory insured item ${softInsertChildModId} was not found`);
}
equipmentPkg.push({
pmcData: pmcData,
itemToReturnToPlayer: this.getInsuredItemDetails(
pmcData,
preRaidGear.find((item) => item._id === softInsertChildModId),
offraidData.insurance?.find(
(insuranceItem) => insuranceItem.id === softInsertChildModId,
),
preRaidInventoryItem,
inventoryInsuredItem,
),
traderId: insuredItem.tid,
sessionID: sessionID,
@ -381,7 +413,7 @@ export class InsuranceService
}
// Remove found in raid status when upd exists + SpawnedInSession value exists
if ("SpawnedInSession" in itemToReturnClone.upd)
if ("SpawnedInSession" in itemToReturnClone.upd!)
{
itemToReturnClone.upd.SpawnedInSession = false;
}
@ -390,17 +422,17 @@ export class InsuranceService
if (insuredItemFromClient?.durability)
{
// Item didnt have Repairable object pre-raid, add it
if (!itemToReturnClone.upd.Repairable)
if (!itemToReturnClone.upd?.Repairable)
{
itemToReturnClone.upd.Repairable = {
itemToReturnClone.upd!.Repairable = {
Durability: insuredItemFromClient.durability,
MaxDurability: insuredItemFromClient.maxDurability,
MaxDurability: insuredItemFromClient.maxDurability!,
};
}
else
{
itemToReturnClone.upd.Repairable.Durability = insuredItemFromClient.durability;
itemToReturnClone.upd.Repairable.MaxDurability = insuredItemFromClient.maxDurability;
itemToReturnClone.upd.Repairable.MaxDurability = insuredItemFromClient.maxDurability!;
}
}
@ -408,9 +440,9 @@ export class InsuranceService
if (insuredItemFromClient?.hits)
{
// Item didnt have faceshield object pre-raid, add it
if (!itemToReturnClone.upd.FaceShield)
if (!itemToReturnClone.upd?.FaceShield)
{
itemToReturnClone.upd.FaceShield = { Hits: insuredItemFromClient.hits };
itemToReturnClone.upd!.FaceShield = { Hits: insuredItemFromClient.hits };
}
else
{
@ -431,7 +463,7 @@ export class InsuranceService
const pocketSlots = ["pocket1", "pocket2", "pocket3", "pocket4"];
// Some pockets can lose items with player death, some don't
if (!("slotId" in itemToReturn) || pocketSlots.includes(itemToReturn.slotId))
if (!("slotId" in itemToReturn) || pocketSlots.includes(itemToReturn.slotId!))
{
itemToReturn.slotId = "hideout";
}

View File

@ -30,7 +30,7 @@ export class ItemBaseClassService
// Clear existing cache
this.itemBaseClassesCache = {};
this.items = this.databaseServer.getTables().templates.items;
this.items = this.databaseServer.getTables().templates!.items;
if (!this.items)
{
this.logger.warning(this.localisationService.getText("baseclass-missing_db_no_cache"));

View File

@ -28,7 +28,7 @@ export class LocaleService
*/
public getLocaleDb(): Record<string, string>
{
const desiredLocale = this.databaseServer.getTables().locales.global[this.getDesiredGameLocale()];
const desiredLocale = this.databaseServer.getTables().locales!.global[this.getDesiredGameLocale()];
if (desiredLocale)
{
return desiredLocale;
@ -38,7 +38,7 @@ export class LocaleService
`Unable to find desired locale file using locale: ${this.getDesiredGameLocale()} from config/locale.json, falling back to 'en'`,
);
return this.databaseServer.getTables().locales.global.en;
return this.databaseServer.getTables().locales!.global.en;
}
/**
@ -135,19 +135,19 @@ export class LocaleService
}
const baseNameCode = platformLocale.baseName?.toLocaleLowerCase();
if (baseNameCode && this.databaseServer.getTables().locales.global[baseNameCode])
if (baseNameCode && this.databaseServer.getTables().locales!.global[baseNameCode])
{
return baseNameCode;
}
const languageCode = platformLocale.language?.toLowerCase();
if (languageCode && this.databaseServer.getTables().locales.global[languageCode])
if (languageCode && this.databaseServer.getTables().locales!.global[languageCode])
{
return languageCode;
}
const regionCode = platformLocale.region?.toLocaleLowerCase();
if (regionCode && this.databaseServer.getTables().locales.global[regionCode])
if (regionCode && this.databaseServer.getTables().locales!.global[regionCode])
{
return regionCode;
}

View File

@ -44,9 +44,9 @@ export class LocalisationService
* @param args optional arguments
* @returns Localised string
*/
public getText(key: string, args = undefined): string
public getText(key: string, args?: any): string
{
return this.i18n.__(key.toLowerCase(), args);
return args ? this.i18n.__(key.toLowerCase(), args) : this.i18n.__(key.toLowerCase());
}
/**
@ -55,7 +55,7 @@ export class LocalisationService
*/
public getKeys(): string[]
{
return Object.keys(this.databaseServer.getTables().locales.server.en);
return Object.keys(this.databaseServer.getTables().locales!.server.en);
}
/**
@ -65,7 +65,7 @@ export class LocalisationService
*/
public getRandomTextThatMatchesPartialKey(partialKey: string): string
{
const filteredKeys = Object.keys(this.databaseServer.getTables().locales.server.en).filter((x) =>
const filteredKeys = Object.keys(this.databaseServer.getTables().locales!.server.en).filter((x) =>
x.startsWith(partialKey),
);
const chosenKey = this.randomUtil.getArrayValue(filteredKeys);

View File

@ -5,7 +5,7 @@ import { NotificationSendHelper } from "@spt/helpers/NotificationSendHelper";
import { NotifierHelper } from "@spt/helpers/NotifierHelper";
import { TraderHelper } from "@spt/helpers/TraderHelper";
import { Item } from "@spt/models/eft/common/tables/IItem";
import { Dialogue, IUserDialogInfo, Message, MessageItems } from "@spt/models/eft/profile/ISptProfile";
import { Dialogue, ISystemData, IUserDialogInfo, Message, MessageContentRagfair, MessageItems } from "@spt/models/eft/profile/ISptProfile";
import { BaseClasses } from "@spt/models/enums/BaseClasses";
import { MessageType } from "@spt/models/enums/MessageType";
import { Traders } from "@spt/models/enums/Traders";
@ -52,9 +52,9 @@ export class MailSendService
messageType: MessageType,
message: string,
items: Item[] = [],
maxStorageTimeSeconds = null,
systemData = null,
ragfair = null,
maxStorageTimeSeconds?: number,
systemData?: ISystemData,
ragfair?: MessageContentRagfair,
): void
{
if (!trader)
@ -112,9 +112,9 @@ export class MailSendService
messageType: MessageType,
messageLocaleId: string,
items: Item[] = [],
maxStorageTimeSeconds = null,
systemData = null,
ragfair = null,
maxStorageTimeSeconds?: number,
systemData?: ISystemData,
ragfair?: MessageContentRagfair,
): void
{
if (!trader)
@ -242,7 +242,7 @@ export class MailSendService
senderDetails: IUserDialogInfo,
message: string,
items: Item[] = [],
maxStorageTimeSeconds = null,
maxStorageTimeSeconds?: number,
): void
{
const details: ISendMessageDetails = {
@ -283,7 +283,7 @@ export class MailSendService
const itemsToSendToPlayer = this.processItemsBeforeAddingToMail(senderDialog.type, messageDetails);
// If there's items to send to player, flag dialog as containing attachments
if (itemsToSendToPlayer.data?.length > 0)
if ((itemsToSendToPlayer.data?.length ?? 0) > 0)
{
senderDialog.attachmentsNew += 1;
}
@ -392,11 +392,11 @@ export class MailSendService
*/
protected addRewardItemsToMessage(
message: Message,
itemsToSendToPlayer: MessageItems,
maxStorageTimeSeconds: number,
itemsToSendToPlayer: MessageItems | undefined,
maxStorageTimeSeconds: number | undefined,
): void
{
if (itemsToSendToPlayer?.data?.length > 0)
if ((itemsToSendToPlayer?.data?.length ?? 0) > 0)
{
message.items = itemsToSendToPlayer;
message.hasRewards = true;
@ -416,13 +416,13 @@ export class MailSendService
messageDetails: ISendMessageDetails,
): MessageItems
{
const db = this.databaseServer.getTables().templates.items;
const db = this.databaseServer.getTables().templates!.items;
let itemsToSendToPlayer: MessageItems = {};
if (messageDetails.items?.length > 0)
if ((messageDetails.items?.length ?? 0) > 0)
{
// Find base item that should be the 'primary' + have its parent id be used as the dialogs 'stash' value
const parentItem = this.getBaseItemFromRewards(messageDetails.items);
const parentItem = this.getBaseItemFromRewards(messageDetails.items!);
if (!parentItem)
{
this.localisationService.getText("mailsend-missing_parent", {
@ -442,7 +442,7 @@ export class MailSendService
itemsToSendToPlayer = { stash: parentItem.parentId, data: [] };
// Ensure Ids are unique and cont collide with items in player inventory later
messageDetails.items = this.itemHelper.replaceIDs(messageDetails.items);
messageDetails.items = this.itemHelper.replaceIDs(messageDetails.items!);
for (const reward of messageDetails.items)
{
@ -476,7 +476,7 @@ export class MailSendService
this.itemHelper.addCartridgesToAmmoBox(boxAndCartridges, itemTemplate);
// Push box + cartridge children into array
itemsToSendToPlayer.data.push(...boxAndCartridges);
itemsToSendToPlayer.data!.push(...boxAndCartridges);
}
else
{
@ -486,12 +486,12 @@ export class MailSendService
}
// Item is sanitised and ready to be pushed into holding array
itemsToSendToPlayer.data.push(reward);
itemsToSendToPlayer.data!.push(reward);
}
}
// Remove empty data property if no rewards
if (itemsToSendToPlayer.data.length === 0)
if (itemsToSendToPlayer.data!.length === 0)
{
delete itemsToSendToPlayer.data;
}
@ -514,7 +514,7 @@ export class MailSendService
}
// Find first item with slotId that indicates its a 'base' item
let item = items.find((x) => ["hideout", "main"].includes(x.slotId));
let item = items.find((x) => ["hideout", "main"].includes(x.slotId ?? ""));
if (item)
{
return item;
@ -542,7 +542,10 @@ export class MailSendService
{
const dialogsInProfile = this.dialogueHelper.getDialogsForProfile(messageDetails.recipientId);
const senderId = this.getMessageSenderIdByType(messageDetails);
if (!senderId)
{
throw new Error(`Unable to find sender id for details ${messageDetails.sender}`);
}
// Does dialog exist
let senderDialog = dialogsInProfile[senderId];
if (!senderDialog)
@ -568,7 +571,7 @@ export class MailSendService
* @param messageDetails
* @returns gets an id of the individual sending it
*/
protected getMessageSenderIdByType(messageDetails: ISendMessageDetails): string
protected getMessageSenderIdByType(messageDetails: ISendMessageDetails): string | undefined
{
if (messageDetails.sender === MessageType.SYSTEM_MESSAGE)
{
@ -577,7 +580,9 @@ export class MailSendService
if (messageDetails.sender === MessageType.NPC_TRADER || messageDetails.dialogType === MessageType.NPC_TRADER)
{
return this.traderHelper.getValidTraderIdByEnumValue(messageDetails.trader);
return messageDetails.trader
? this.traderHelper.getValidTraderIdByEnumValue(messageDetails.trader)
: undefined;
}
if (messageDetails.sender === MessageType.USER_MESSAGE)

View File

@ -32,7 +32,7 @@ export class ModCompilerService
public async compileMod(modName: string, modPath: string, modTypeScriptFiles: string[]): Promise<void>
{
// Concatenate TS files into one string
let tsFileContents: string;
let tsFileContents = "";
let fileExists = true; // does every js file exist (been compiled before)
for (const file of modTypeScriptFiles)
{

View File

@ -49,7 +49,7 @@ export class OpenZoneService
*/
public applyZoneChangesToAllMaps(): void
{
const dbLocations = this.databaseServer.getTables().locations;
const dbLocations = this.databaseServer.getTables().locations!;
for (const mapKey in this.locationConfig.openZones)
{
if (!dbLocations[mapKey])

View File

@ -182,7 +182,7 @@ export class PaymentService
const trader = this.traderHelper.getTrader(request.tid, sessionID);
const currency = this.paymentHelper.getCurrency(trader.currency);
let calcAmount = this.handbookHelper.fromRUB(this.handbookHelper.inRUB(amountToSend, currency), currency);
const currencyMaxStackSize = this.databaseServer.getTables().templates.items[currency]._props.StackMaxSize;
const currencyMaxStackSize = this.databaseServer.getTables().templates!.items[currency]._props.StackMaxSize!;
let skipSendingMoneyToStash = false;
for (const item of pmcData.Inventory.items)
@ -200,18 +200,18 @@ export class PaymentService
}
// Found currency item
if (item.upd.StackObjectsCount < currencyMaxStackSize)
if (item.upd!.StackObjectsCount! < currencyMaxStackSize)
{
if (item.upd.StackObjectsCount + calcAmount > currencyMaxStackSize)
if (item.upd!.StackObjectsCount! + calcAmount > currencyMaxStackSize)
{
// calculate difference
calcAmount -= currencyMaxStackSize - item.upd.StackObjectsCount;
item.upd.StackObjectsCount = currencyMaxStackSize;
calcAmount -= currencyMaxStackSize - item.upd!.StackObjectsCount!;
item.upd!.StackObjectsCount! = currencyMaxStackSize;
}
else
{
skipSendingMoneyToStash = true;
item.upd.StackObjectsCount = item.upd.StackObjectsCount + calcAmount;
item.upd!.StackObjectsCount! = item.upd!.StackObjectsCount! + calcAmount;
}
// Inform client of change to items StackObjectsCount
@ -239,7 +239,7 @@ export class PaymentService
const addItemToStashRequest: IAddItemsDirectRequest = {
itemsWithModsToAdd: rewards,
foundInRaid: false,
callback: null,
callback: undefined,
useSortingTable: true,
};
this.inventoryHelper.addItemsToStash(sessionID, addItemToStashRequest, pmcData, output);
@ -274,7 +274,7 @@ export class PaymentService
pmcData.Inventory.stash,
);
const amountAvailable = moneyItemsInInventory.reduce(
(accumulator, item) => accumulator + item.upd.StackObjectsCount,
(accumulator, item) => accumulator + item.upd!.StackObjectsCount!,
0,
);
@ -299,7 +299,7 @@ export class PaymentService
let leftToPay = amountToPay;
for (const profileMoneyItem of moneyItemsInInventory)
{
const itemAmount = profileMoneyItem.upd.StackObjectsCount;
const itemAmount = profileMoneyItem.upd!.StackObjectsCount!;
if (leftToPay >= itemAmount)
{
leftToPay -= itemAmount;
@ -307,7 +307,7 @@ export class PaymentService
}
else
{
profileMoneyItem.upd.StackObjectsCount -= leftToPay;
profileMoneyItem.upd!.StackObjectsCount! -= leftToPay;
leftToPay = 0;
output.profileChanges[sessionID].items.change.push(profileMoneyItem);
}
@ -394,7 +394,7 @@ export class PaymentService
* @param playerStashId Players stash id
* @returns true if its in inventory
*/
protected isInStash(itemId: string, inventoryItems: Item[], playerStashId: string): boolean
protected isInStash(itemId: string | undefined, inventoryItems: Item[], playerStashId: string): boolean
{
const itemParent = inventoryItems.find((x) => x._id === itemId);

View File

@ -25,7 +25,7 @@ export class PlayerService
{
let accExp = 0;
for (const [level, { exp }] of this.databaseServer.getTables().globals.config.exp.level.exp_table.entries())
for (const [level, { exp }] of this.databaseServer.getTables().globals!.config.exp.level.exp_table.entries())
{
accExp += exp;

View File

@ -51,12 +51,15 @@ export class PmcChatResponseService
const victimDetails = this.getVictimDetails(victim);
const message = this.chooseMessage(true, pmcData);
this.notificationSendHelper.sendMessageToPlayer(
sessionId,
victimDetails,
message,
MessageType.USER_MESSAGE,
);
if (message)
{
this.notificationSendHelper.sendMessageToPlayer(
sessionId,
victimDetails,
message,
MessageType.USER_MESSAGE,
);
}
}
}
@ -120,7 +123,7 @@ export class PmcChatResponseService
* @param pmcData Player profile
* @returns Message from PMC to player
*/
protected chooseMessage(isVictim: boolean, pmcData: IPmcData): string
protected chooseMessage(isVictim: boolean, pmcData: IPmcData): string | undefined
{
// Positive/negative etc
const responseType = this.chooseResponseType(isVictim);

View File

@ -86,27 +86,26 @@ export class ProfileFixerService
this.reorderHideoutAreasWithResouceInputs(pmcProfile);
if (
pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.GENERATOR).slots.length
< 6
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots.Generator
.Slots
if (pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.GENERATOR)!.slots.length
< 6
+ this.databaseServer.getTables().globals!.config.SkillsSettings.HideoutManagement.EliteSlots.Generator
.Slots
)
{
this.logger.debug("Updating generator area slots to a size of 6 + hideout management skill");
this.addEmptyObjectsToHideoutAreaSlots(
HideoutAreas.GENERATOR,
6
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
+ this.databaseServer.getTables().globals!.config.SkillsSettings.HideoutManagement.EliteSlots
.Generator.Slots,
pmcProfile,
);
}
if (
pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WATER_COLLECTOR).slots.length
pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.WATER_COLLECTOR)!.slots.length
< 1
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
+ this.databaseServer.getTables().globals!.config.SkillsSettings.HideoutManagement.EliteSlots
.WaterCollector.Slots
)
{
@ -114,16 +113,16 @@ export class ProfileFixerService
this.addEmptyObjectsToHideoutAreaSlots(
HideoutAreas.WATER_COLLECTOR,
1
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
+ this.databaseServer.getTables().globals!.config.SkillsSettings.HideoutManagement.EliteSlots
.WaterCollector.Slots,
pmcProfile,
);
}
if (
pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.AIR_FILTERING).slots.length
pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.AIR_FILTERING)!.slots.length
< 3
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
+ this.databaseServer.getTables().globals!.config.SkillsSettings.HideoutManagement.EliteSlots
.AirFilteringUnit.Slots
)
{
@ -131,7 +130,7 @@ export class ProfileFixerService
this.addEmptyObjectsToHideoutAreaSlots(
HideoutAreas.AIR_FILTERING,
3
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
+ this.databaseServer.getTables().globals!.config.SkillsSettings.HideoutManagement.EliteSlots
.AirFilteringUnit.Slots,
pmcProfile,
);
@ -139,9 +138,9 @@ export class ProfileFixerService
// BTC Farm doesnt have extra slots for hideout management, but we still check for modded stuff!!
if (
pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.BITCOIN_FARM).slots.length
pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.BITCOIN_FARM)!.slots.length
< 50
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
+ this.databaseServer.getTables().globals!.config.SkillsSettings.HideoutManagement.EliteSlots
.BitcoinFarm.Slots
)
{
@ -149,7 +148,7 @@ export class ProfileFixerService
this.addEmptyObjectsToHideoutAreaSlots(
HideoutAreas.BITCOIN_FARM,
50
+ this.databaseServer.getTables().globals.config.SkillsSettings.HideoutManagement.EliteSlots
+ this.databaseServer.getTables().globals!.config.SkillsSettings.HideoutManagement.EliteSlots
.BitcoinFarm.Slots,
pmcProfile,
);
@ -180,8 +179,8 @@ export class ProfileFixerService
}
const db = this.databaseServer.getTables();
const hideoutStandAreaDb = db.hideout.areas.find((x) => x.type === HideoutAreas.WEAPON_STAND);
const hideoutStandSecondaryAreaDb = db.hideout.areas.find((x) => x.parentArea === hideoutStandAreaDb._id);
const hideoutStandAreaDb = db.hideout!.areas.find((x) => x.type === HideoutAreas.WEAPON_STAND)!;
const hideoutStandSecondaryAreaDb = db.hideout!.areas.find((x) => x.parentArea === hideoutStandAreaDb._id)!;
const stageCurrentAt = hideoutStandAreaDb.stages[weaponStandArea.level];
const hideoutStandStashId = pmcProfile.Inventory.hideoutAreaStashes[HideoutAreas.WEAPON_STAND];
const hideoutSecondaryStashId = pmcProfile.Inventory.hideoutAreaStashes[HideoutAreas.WEAPON_STAND_SECONDARY];
@ -198,14 +197,14 @@ export class ProfileFixerService
const gunStandStashItem = pmcProfile.Inventory.items.find((x) => x._id === hideoutStandAreaDb._id);
if (gunStandStashItem)
{
gunStandStashItem._tpl = stageCurrentAt.container;
gunStandStashItem._tpl = stageCurrentAt.container!;
this.logger.debug(
`Updated existing gun stand inventory stash: ${gunStandStashItem._id} tpl to ${stageCurrentAt.container}`,
);
}
else
{
pmcProfile.Inventory.items.push({ _id: hideoutStandAreaDb._id, _tpl: stageCurrentAt.container });
pmcProfile.Inventory.items.push({ _id: hideoutStandAreaDb._id, _tpl: stageCurrentAt.container! });
this.logger.debug(
`Added missing gun stand inventory stash: ${hideoutStandAreaDb._id} tpl to ${stageCurrentAt.container}`,
);
@ -214,10 +213,10 @@ export class ProfileFixerService
// Add secondary stash item to profile
const gunStandStashSecondaryItem = pmcProfile.Inventory.items.find(
(x) => x._id === hideoutStandSecondaryAreaDb._id,
);
)!;
if (gunStandStashItem)
{
gunStandStashSecondaryItem._tpl = stageCurrentAt.container;
gunStandStashSecondaryItem._tpl = stageCurrentAt.container!;
this.logger.debug(
`Updated gun stand existing inventory secondary stash: ${gunStandStashSecondaryItem._id} tpl to ${stageCurrentAt.container}`,
);
@ -226,7 +225,7 @@ export class ProfileFixerService
{
pmcProfile.Inventory.items.push({
_id: hideoutStandSecondaryAreaDb._id,
_tpl: stageCurrentAt.container,
_tpl: stageCurrentAt.container!,
});
this.logger.debug(
`Added missing gun stand inventory secondary stash: ${hideoutStandSecondaryAreaDb._id} tpl to ${stageCurrentAt.container}`,
@ -240,25 +239,25 @@ export class ProfileFixerService
if (!stashItem)
{
// Stand inventory stash item doesnt exist, add it
pmcProfile.Inventory.items.push({ _id: hideoutStandAreaDb._id, _tpl: stageCurrentAt.container });
pmcProfile.Inventory.items.push({ _id: hideoutStandAreaDb._id, _tpl: stageCurrentAt.container! });
stashItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandAreaDb._id);
}
// `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected
if (hideoutStandStashId && stashItem._tpl !== stageCurrentAt.container)
if (hideoutStandStashId && stashItem!._tpl !== stageCurrentAt.container)
{
this.logger.debug(
`primary Stash tpl was: ${stashItem._tpl}, but should be ${stageCurrentAt.container}, updating`,
`primary Stash tpl was: ${stashItem!._tpl}, but should be ${stageCurrentAt.container}, updating`,
);
// The id inside the profile does not match what the hideout db value is, out of sync, adjust
stashItem._tpl = stageCurrentAt.container;
stashItem!._tpl = stageCurrentAt.container!;
}
let stashSecondaryItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandSecondaryAreaDb._id);
if (!stashSecondaryItem)
{
// Stand inventory stash item doesnt exist, add it
pmcProfile.Inventory.items.push({ _id: hideoutStandSecondaryAreaDb._id, _tpl: stageCurrentAt.container });
pmcProfile.Inventory.items.push({ _id: hideoutStandSecondaryAreaDb._id, _tpl: stageCurrentAt.container! });
stashSecondaryItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandSecondaryAreaDb._id);
}
@ -266,10 +265,10 @@ export class ProfileFixerService
if (hideoutSecondaryStashId && stashSecondaryItem?._tpl !== stageCurrentAt.container)
{
this.logger.debug(
`Secondary stash tpl was: ${stashSecondaryItem._tpl}, but should be ${stageCurrentAt.container}, updating`,
`Secondary stash tpl was: ${stashSecondaryItem?._tpl}, but should be ${stageCurrentAt.container}, updating`,
);
// The id inside the profile does not match what the hideout db value is, out of sync, adjust
stashSecondaryItem._tpl = stageCurrentAt.container;
stashSecondaryItem!._tpl = stageCurrentAt.container!;
}
}
@ -283,7 +282,7 @@ export class ProfileFixerService
}
const db = this.databaseServer.getTables();
const placeOfFameAreaDb = db.hideout.areas.find((area) => area.type === HideoutAreas.PLACE_OF_FAME);
const placeOfFameAreaDb = db.hideout!.areas.find((area) => area.type === HideoutAreas.PLACE_OF_FAME);
if (!placeOfFameAreaDb)
{
return;
@ -301,14 +300,14 @@ export class ProfileFixerService
const placeOfFameStashItem = pmcProfile.Inventory.items.find((x) => x._id === placeOfFameAreaDb._id);
if (placeOfFameStashItem)
{
placeOfFameStashItem._tpl = stageCurrentlyAt.container;
placeOfFameStashItem._tpl = stageCurrentlyAt.container!;
this.logger.debug(
`Updated existing place of fame inventory stash: ${placeOfFameStashItem._id} tpl to ${stageCurrentlyAt.container}`,
);
}
else
{
pmcProfile.Inventory.items.push({ _id: placeOfFameAreaDb._id, _tpl: stageCurrentlyAt.container });
pmcProfile.Inventory.items.push({ _id: placeOfFameAreaDb._id, _tpl: stageCurrentlyAt.container! });
this.logger.debug(
`Added missing place of fame inventory stash: ${placeOfFameAreaDb._id} tpl to ${stageCurrentlyAt.container}`,
);
@ -321,18 +320,18 @@ export class ProfileFixerService
if (!stashItem)
{
// Stand inventory stash item doesnt exist, add it
pmcProfile.Inventory.items.push({ _id: placeOfFameAreaDb._id, _tpl: stageCurrentlyAt.container });
pmcProfile.Inventory.items.push({ _id: placeOfFameAreaDb._id, _tpl: stageCurrentlyAt.container! });
stashItem = pmcProfile.Inventory.items?.find((x) => x._id === placeOfFameAreaDb._id);
}
// `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected
if (placeOfFameStashId && stashItem._tpl !== stageCurrentlyAt.container)
if (placeOfFameStashId && stashItem!._tpl !== stageCurrentlyAt.container)
{
this.logger.debug(
`primary Stash tpl was: ${stashItem._tpl}, but should be ${stageCurrentlyAt.container}, updating`,
`primary Stash tpl was: ${stashItem?._tpl}, but should be ${stageCurrentlyAt.container}, updating`,
);
// The id inside the profile does not match what the hideout db value is, out of sync, adjust
stashItem._tpl = stageCurrentlyAt.container;
stashItem!._tpl = stageCurrentlyAt.container!;
}
}
@ -465,7 +464,7 @@ export class ProfileFixerService
{
const taskConditionKeysToRemove: string[] = [];
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
for (const [key, taskConditionCounter] of Object.entries(pmcProfile.TaskConditionCounters))
@ -501,7 +500,7 @@ export class ProfileFixerService
protected getActiveRepeatableQuests(repeatableQuests: IPmcDataRepeatableQuest[]): IRepeatableQuest[]
{
let activeQuests = [];
let activeQuests: IRepeatableQuest[] = [];
for (const repeatableQuest of repeatableQuests)
{
if (repeatableQuest.activeQuests.length > 0)
@ -648,17 +647,17 @@ export class ProfileFixerService
*/
protected addMissingWallImprovements(pmcProfile: IPmcData): void
{
const profileWallArea = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.EMERGENCY_WALL);
const profileWallArea = pmcProfile.Hideout.Areas.find((x) => x.type === HideoutAreas.EMERGENCY_WALL)!;
const wallDb = this.databaseServer
.getTables()
.hideout.areas.find((x) => x.type === HideoutAreas.EMERGENCY_WALL);
.hideout!.areas.find((x) => x.type === HideoutAreas.EMERGENCY_WALL);
if (profileWallArea.level > 0)
{
for (let i = 0; i < profileWallArea.level; i++)
{
// Get wall stage from db
const wallStageDb = wallDb.stages[i];
const wallStageDb = wallDb!.stages[i];
if (wallStageDb.improvements.length === 0)
{
// No improvements, skip
@ -705,7 +704,7 @@ export class ProfileFixerService
// Have an item property and it has at least one item in it
// Or
// Have no item property
area.slots = area.slots.filter((x) => ("item" in x && x.item?.length > 0) || !("item" in x));
area.slots = area.slots.filter((x) => ("item" in x && (x.item?.length ?? 0) > 0) || !("item" in x));
}
}
@ -727,7 +726,7 @@ export class ProfileFixerService
const area = pmcProfile.Hideout.Areas.find((area) => area.type === areaId);
if (!area)
{
this.logger.debug(`unable to sort: ${area.type} (${areaId}) slots, no area found`);
this.logger.debug(`unable to sort: ${area!.type} (${areaId}) slots, no area found`);
continue;
}
@ -756,7 +755,7 @@ export class ProfileFixerService
): void
{
const area = pmcProfile.Hideout.Areas.find((x) => x.type === areaType);
area.slots = this.addObjectsToArray(emptyItemCount, area.slots);
area!.slots = this.addObjectsToArray(emptyItemCount, area!.slots);
}
protected addObjectsToArray(count: number, slots: HideoutSlot[]): HideoutSlot[]
@ -780,7 +779,7 @@ export class ProfileFixerService
{
const profileHideoutAreas = pmcProfile.Hideout.Areas;
const profileBonuses = pmcProfile.Bonuses;
const dbHideoutAreas = this.databaseServer.getTables().hideout.areas;
const dbHideoutAreas = this.databaseServer.getTables().hideout!.areas;
for (const area of profileHideoutAreas)
{
@ -841,7 +840,7 @@ export class ProfileFixerService
* @param bonus bonus to find
* @returns matching bonus
*/
protected getBonusFromProfile(profileBonuses: Bonus[], bonus: StageBonus): Bonus
protected getBonusFromProfile(profileBonuses: Bonus[], bonus: StageBonus): Bonus | undefined
{
// match by id first, used by "TextBonus" bonuses
if (bonus.id)
@ -871,13 +870,13 @@ export class ProfileFixerService
*/
public checkForOrphanedModdedItems(sessionId: string, fullProfile: ISptProfile): void
{
const itemsDb = this.databaseServer.getTables().templates.items;
const itemsDb = this.databaseServer.getTables().templates!.items;
const pmcProfile = fullProfile.characters.pmc;
// Get items placed in root of stash
// TODO: extend to other areas / sub items
const inventoryItemsToCheck = pmcProfile.Inventory.items.filter((item) =>
["hideout", "main"].includes(item.slotId),
["hideout", "main"].includes(item.slotId ?? ""),
);
if (inventoryItemsToCheck)
{
@ -973,7 +972,7 @@ export class ProfileFixerService
}
}
const clothing = this.databaseServer.getTables().templates.customization;
const clothing = this.databaseServer.getTables().templates!.customization;
for (const [_, suitId] of Object.entries(fullProfile.suits))
{
if (!clothing[suitId])
@ -1008,11 +1007,11 @@ export class ProfileFixerService
continue;
}
for (const successReward of activeQuest.rewards.Success)
for (const successReward of activeQuest.rewards.Success!)
{
if (successReward.type === "Item")
{
for (const rewardItem of successReward.items)
for (const rewardItem of successReward.items!)
{
if (!itemsDb[rewardItem._tpl])
{
@ -1164,7 +1163,7 @@ export class ProfileFixerService
const itemsHaveChildren = pmcProfile.Inventory.items.some((x) => x.parentId === key);
if (!itemsHaveChildren)
{
const itemToAdjustId = pmcProfile.Inventory.items.find((x) => x._id === key);
const itemToAdjustId = pmcProfile.Inventory.items.find((x) => x._id === key)!;
itemToAdjustId._id = this.hashUtil.generate();
this.logger.warning(`Replace duplicate item Id: ${key} with ${itemToAdjustId._id}`);
}
@ -1182,7 +1181,7 @@ export class ProfileFixerService
// Check items with a tag that contains non alphanumeric characters
const regxp = /([/w"\\'])/g;
if (regxp.test(item.upd.Tag?.Name))
if (item.upd.Tag?.Name && regxp.test(item.upd.Tag?.Name))
{
this.logger.warning(`Fixed item: ${item._id}s Tag value, removed invalid characters`);
item.upd.Tag.Name = item.upd.Tag.Name.replace(regxp, "");
@ -1197,8 +1196,8 @@ export class ProfileFixerService
}
// Iterate over clothing
const customizationDb = this.databaseServer.getTables().templates.customization;
const customizationDbArray = Object.values(this.databaseServer.getTables().templates.customization);
const customizationDb = this.databaseServer.getTables().templates!.customization;
const customizationDbArray = Object.values(this.databaseServer.getTables().templates!.customization);
const playerIsUsec = pmcProfile.Info.Side.toLowerCase() === "usec";
// Check Head
@ -1207,7 +1206,7 @@ export class ProfileFixerService
const defaultHead = playerIsUsec
? customizationDbArray.find((x) => x._name === "DefaultUsecHead")
: customizationDbArray.find((x) => x._name === "DefaultBearHead");
pmcProfile.Customization.Head = defaultHead._id;
pmcProfile.Customization.Head = defaultHead!._id;
}
// check Body
@ -1217,7 +1216,7 @@ export class ProfileFixerService
= pmcProfile.Info.Side.toLowerCase() === "usec"
? customizationDbArray.find((x) => x._name === "DefaultUsecBody")
: customizationDbArray.find((x) => x._name === "DefaultBearBody");
pmcProfile.Customization.Body = defaultBody._id;
pmcProfile.Customization.Body = defaultBody!._id;
}
// check Hands
@ -1227,7 +1226,7 @@ export class ProfileFixerService
= pmcProfile.Info.Side.toLowerCase() === "usec"
? customizationDbArray.find((x) => x._name === "DefaultUsecHands")
: customizationDbArray.find((x) => x._name === "DefaultBearHands");
pmcProfile.Customization.Hands = defaultHands._id;
pmcProfile.Customization.Hands = defaultHands!._id;
}
// check Hands
@ -1237,7 +1236,7 @@ export class ProfileFixerService
= pmcProfile.Info.Side.toLowerCase() === "usec"
? customizationDbArray.find((x) => x._name === "DefaultUsecFeet")
: customizationDbArray.find((x) => x._name === "DefaultBearFeet");
pmcProfile.Customization.Feet = defaultFeet._id;
pmcProfile.Customization.Feet = defaultFeet!._id;
}
}
@ -1266,7 +1265,7 @@ export class ProfileFixerService
return;
}
const profileTemplates = this.databaseServer.getTables().templates.profiles[fullProfile.info.edition];
const profileTemplates = this.databaseServer.getTables().templates?.profiles[fullProfile.info.edition];
if (!profileTemplates)
{
return;
@ -1342,7 +1341,7 @@ export class ProfileFixerService
const statsCopy = this.cloner.clone(fullProfile.characters.pmc.Stats);
// Clear stats object
fullProfile.characters.pmc.Stats = { Eft: null };
delete fullProfile.characters.pmc.Stats.Eft;
fullProfile.characters.pmc.Stats.Eft = <any>(<unknown>statsCopy);
}
@ -1364,7 +1363,7 @@ export class ProfileFixerService
}
// Bonus lacks id, find matching hideout area / stage / bonus
for (const area of this.databaseServer.getTables().hideout.areas)
for (const area of this.databaseServer.getTables().hideout!.areas)
{
// TODO: skip if no stages
for (const stageIndex in area.stages)
@ -1429,7 +1428,7 @@ export class ProfileFixerService
*/
protected removeOrphanedQuests(pmcProfile: IPmcData): void
{
const quests = this.databaseServer.getTables().templates.quests;
const quests = this.databaseServer.getTables().templates!.quests;
const profileQuests = pmcProfile.Quests;
const repeatableQuests: IRepeatableQuest[] = [];

View File

@ -25,14 +25,14 @@ export class ProfileSnapshotService
* @param sessionID key
* @returns A player profile object
*/
public getProfileSnapshot(sessionID: string): ISptProfile
public getProfileSnapshot(sessionID: string): ISptProfile | undefined
{
if (this.storedProfileSnapshots[sessionID])
{
return this.storedProfileSnapshots[sessionID];
}
return null;
return undefined;
}
/**

View File

@ -55,7 +55,7 @@ export class RagfairLinkedItemService
return linkedItems[id];
};
for (const item of Object.values(this.databaseServer.getTables().templates.items))
for (const item of Object.values(this.databaseServer.getTables().templates!.items))
{
const itemLinkedSet = getLinkedItems(item._id);
@ -94,7 +94,7 @@ export class RagfairLinkedItemService
applyLinkedItems: (items: string[]) => void,
): void
{
const cylinderMod = cylinder._props.Slots.find((x) => x._name === "mod_magazine");
const cylinderMod = cylinder._props.Slots?.find((x) => x._name === "mod_magazine");
if (cylinderMod)
{
// Get the first cylinder filter tpl
@ -122,7 +122,7 @@ export class RagfairLinkedItemService
return [];
}
const filters = [];
const filters: string[] = [];
for (const sub of item._props[slot])
{
if (!("_props" in sub && "filters" in sub._props))

View File

@ -54,12 +54,12 @@ export class RagfairOfferService
return this.ragfairOfferHandler.getOffers();
}
public getOfferByOfferId(offerId: string): IRagfairOffer
public getOfferByOfferId(offerId: string): IRagfairOffer | undefined
{
return this.ragfairOfferHandler.getOfferById(offerId);
}
public getOffersOfType(templateId: string): IRagfairOffer[]
public getOffersOfType(templateId: string): IRagfairOffer[] | undefined
{
return this.ragfairOfferHandler.getOffersByTemplate(templateId);
}
@ -145,10 +145,13 @@ export class RagfairOfferService
public removeOfferStack(offerId: string, amount: number): void
{
const offer = this.ragfairOfferHandler.getOfferById(offerId);
offer.items[0].upd.StackObjectsCount -= amount;
if (offer.items[0].upd.StackObjectsCount <= 0)
if (offer)
{
this.processStaleOffer(offer);
offer.items[0].upd!.StackObjectsCount! -= amount;
if (offer.items[0].upd!.StackObjectsCount! <= 0)
{
this.processStaleOffer(offer);
}
}
}
@ -164,7 +167,7 @@ export class RagfairOfferService
*/
public traderOffersNeedRefreshing(traderID: string): boolean
{
const trader = this.databaseServer.getTables().traders[traderID];
const trader = this.databaseServer.getTables().traders![traderID];
if (!trader || !trader.base)
{
this.logger.error(this.localisationService.getText("ragfair-trader_missing_base_file", traderID));
@ -259,15 +262,15 @@ export class RagfairOfferService
}
// Reduce player ragfair rep
profile.RagfairInfo.rating -= this.databaseServer.getTables().globals.config.RagFair.ratingDecreaseCount;
profile.RagfairInfo.rating -= this.databaseServer.getTables().globals!.config.RagFair.ratingDecreaseCount;
profile.RagfairInfo.isRatingGrowing = false;
const firstOfferItem = playerOffer.items[0];
if (firstOfferItem.upd.StackObjectsCount > firstOfferItem.upd.OriginalStackObjectsCount)
if (firstOfferItem.upd!.StackObjectsCount! > firstOfferItem.upd!.OriginalStackObjectsCount!)
{
playerOffer.items[0].upd.StackObjectsCount = firstOfferItem.upd.OriginalStackObjectsCount;
playerOffer.items[0].upd!.StackObjectsCount = firstOfferItem.upd!.OriginalStackObjectsCount;
}
delete playerOffer.items[0].upd.OriginalStackObjectsCount;
delete playerOffer.items[0].upd!.OriginalStackObjectsCount;
// Remove player offer from flea
this.ragfairOfferHandler.removeOffer(playerOffer);

View File

@ -64,7 +64,7 @@ export class RagfairPriceService implements OnLoad
*/
public refreshStaticPrices(): void
{
for (const item of Object.values(this.databaseServer.getTables().templates.items).filter(
for (const item of Object.values(this.databaseServer.getTables().templates!.items).filter(
(x) => x._type === "Item",
))
{
@ -77,7 +77,7 @@ export class RagfairPriceService implements OnLoad
*/
public refreshDynamicPrices(): void
{
const pricesTable = this.databaseServer.getTables().templates.prices;
const pricesTable = this.databaseServer.getTables().templates!.prices;
this.prices.dynamic = { ...this.prices.dynamic, ...pricesTable };
}
@ -138,7 +138,7 @@ export class RagfairPriceService implements OnLoad
// If the price doesn't exist in the cache yet, try to find it
if (!this.prices.dynamic[itemTpl])
{
this.prices.dynamic[itemTpl] = this.databaseServer.getTables().templates.prices[itemTpl];
this.prices.dynamic[itemTpl] = this.databaseServer.getTables().templates!.prices[itemTpl];
}
return this.prices.dynamic[itemTpl];
@ -328,7 +328,7 @@ export class RagfairPriceService implements OnLoad
if (unreasonableModifier.enabled)
{
price = this.adjustUnreasonablePrice(
this.databaseServer.getTables().templates.handbook.Items,
this.databaseServer.getTables().templates!.handbook.Items,
unreasonableModifier,
itemTemplateId,
price,
@ -338,7 +338,7 @@ export class RagfairPriceService implements OnLoad
}
// Vary the price based on the type of offer.
const range = this.getOfferTypeRangeValues(isPreset, isPackOffer);
const range = this.getOfferTypeRangeValues(isPreset, isPackOffer ?? false);
price = this.randomiseOfferPrice(price, range);
// Convert to different currency if required.

View File

@ -69,9 +69,9 @@ export class RagfairTaxService
const itemWorth = this.calculateItemWorth(item, itemTemplate, offerItemCount, pmcData);
const requirementsPrice = requirementsValue * (sellInOnePiece ? 1 : offerItemCount);
const itemTaxMult = this.databaseServer.getTables().globals.config.RagFair.communityItemTax / 100.0;
const itemTaxMult = this.databaseServer.getTables().globals!.config.RagFair.communityItemTax / 100.0;
const requirementTaxMult
= this.databaseServer.getTables().globals.config.RagFair.communityRequirementTax / 100.0;
= this.databaseServer.getTables().globals!.config.RagFair.communityRequirementTax / 100.0;
let itemPriceMult = Math.log10(itemWorth / requirementsPrice);
let requirementPriceMult = Math.log10(requirementsPrice / itemWorth);
@ -89,7 +89,7 @@ export class RagfairTaxService
requirementPriceMult = 4 ** requirementPriceMult;
const hideoutFleaTaxDiscountBonus = pmcData.Bonuses.find((b) => b.type === BonusType.RAGFAIR_COMMISSION);
const taxDiscountPercent = hideoutFleaTaxDiscountBonus ? Math.abs(hideoutFleaTaxDiscountBonus.value) : 0;
const taxDiscountPercent = hideoutFleaTaxDiscountBonus ? Math.abs(hideoutFleaTaxDiscountBonus!.value ?? 0) : 0;
const tax
= itemWorth * itemTaxMult * itemPriceMult + requirementsPrice * requirementTaxMult * requirementPriceMult;
@ -98,10 +98,10 @@ export class RagfairTaxService
? itemTemplate._props.RagFairCommissionModifier
: 1;
if (item.upd.Buff)
{
// TODO: enhance tax calc with client implementation from GClass1932/CalculateTaxPrice()
}
// if (item.upd.Buff)
// {
// TODO: enhance tax calc with client implementation from GClass1932/CalculateTaxPrice()
// }
const taxValue = Math.round(discountedTax * itemComissionMult);
this.logger.debug(`Tax Calculated to be: ${taxValue}`);
@ -138,7 +138,7 @@ export class RagfairTaxService
worth += this.calculateItemWorth(
child,
this.itemHelper.getItem(child._tpl)[1],
child.upd.StackObjectsCount,
child.upd!.StackObjectsCount!,
pmcData,
false,
);
@ -146,46 +146,46 @@ export class RagfairTaxService
}
}
if ("Dogtag" in item.upd)
if ("Dogtag" in item.upd!)
{
worth *= item.upd.Dogtag.Level;
worth *= item.upd!.Dogtag!.Level;
}
if ("Key" in item.upd && itemTemplate._props.MaximumNumberOfUsage > 0)
if ("Key" in item.upd! && (itemTemplate._props.MaximumNumberOfUsage ?? 0) > 0)
{
worth
= (worth / itemTemplate._props.MaximumNumberOfUsage)
* (itemTemplate._props.MaximumNumberOfUsage - item.upd.Key.NumberOfUsages);
= (worth / itemTemplate._props.MaximumNumberOfUsage!)
* (itemTemplate._props.MaximumNumberOfUsage! - item.upd!.Key!.NumberOfUsages);
}
if ("Resource" in item.upd && itemTemplate._props.MaxResource > 0)
if ("Resource" in item.upd! && itemTemplate._props.MaxResource! > 0)
{
worth = worth * 0.1 + ((worth * 0.9) / itemTemplate._props.MaxResource) * item.upd.Resource.Value;
worth = worth * 0.1 + ((worth * 0.9) / itemTemplate._props.MaxResource!) * item.upd.Resource!.Value;
}
if ("SideEffect" in item.upd && itemTemplate._props.MaxResource > 0)
if ("SideEffect" in item.upd! && itemTemplate._props.MaxResource! > 0)
{
worth = worth * 0.1 + ((worth * 0.9) / itemTemplate._props.MaxResource) * item.upd.SideEffect.Value;
worth = worth * 0.1 + ((worth * 0.9) / itemTemplate._props.MaxResource!) * item.upd.SideEffect!.Value;
}
if ("MedKit" in item.upd && itemTemplate._props.MaxHpResource > 0)
if ("MedKit" in item.upd! && itemTemplate._props.MaxHpResource! > 0)
{
worth = (worth / itemTemplate._props.MaxHpResource) * item.upd.MedKit.HpResource;
worth = (worth / itemTemplate._props.MaxHpResource!) * item.upd.MedKit!.HpResource;
}
if ("FoodDrink" in item.upd && itemTemplate._props.MaxResource > 0)
if ("FoodDrink" in item.upd! && itemTemplate._props.MaxResource! > 0)
{
worth = (worth / itemTemplate._props.MaxResource) * item.upd.FoodDrink.HpPercent;
worth = (worth / itemTemplate._props.MaxResource!) * item.upd.FoodDrink!.HpPercent;
}
if ("Repairable" in item.upd && <number > itemTemplate._props.armorClass > 0)
if ("Repairable" in item.upd! && <number > itemTemplate._props.armorClass > 0)
{
const num2 = 0.01 * 0.0 ** item.upd.Repairable.MaxDurability;
const num2 = 0.01 * 0.0 ** item.upd.Repairable!.MaxDurability;
worth
= worth * (item.upd.Repairable.MaxDurability / itemTemplate._props.Durability - num2)
= worth * (item.upd.Repairable!.MaxDurability / itemTemplate._props.Durability! - num2)
- Math.floor(
itemTemplate._props.RepairCost
* (item.upd.Repairable.MaxDurability - item.upd.Repairable.Durability),
itemTemplate._props.RepairCost!
* (item.upd.Repairable!.MaxDurability - item.upd.Repairable!.Durability),
);
}

View File

@ -107,15 +107,15 @@ export class RaidTimeAdjustmentService
{
const db = this.databaseServer.getTables();
const mapBase: ILocationBase = db.locations[request.Location.toLowerCase()].base;
const mapBase: ILocationBase = db.locations![request.Location.toLowerCase()].base;
const baseEscapeTimeMinutes = mapBase.EscapeTimeLimit;
// Prep result object to return
const result: IGetRaidTimeResponse = {
RaidTimeMinutes: baseEscapeTimeMinutes,
ExitChanges: [],
NewSurviveTimeSeconds: null,
OriginalSurvivalTimeSeconds: db.globals.config.exp.match_end.survived_seconds_requirement,
NewSurviveTimeSeconds: undefined,
OriginalSurvivalTimeSeconds: db.globals!.config.exp.match_end.survived_seconds_requirement,
};
// Pmc raid, send default
@ -203,9 +203,9 @@ export class RaidTimeAdjustmentService
* @param newRaidTimeMinutes How long raid is in minutes
* @returns List of exit changes to send to client
*/
protected getExitAdjustments(mapBase: ILocationBase, newRaidTimeMinutes: number): ExtractChange[]
protected getExitAdjustments(mapBase: ILocationBase, newRaidTimeMinutes: number): ExtractChange[] | undefined
{
const result = [];
const result: ExtractChange[] = [];
// Adjust train exits only
for (const exit of mapBase.exits)
{
@ -215,7 +215,12 @@ export class RaidTimeAdjustmentService
}
// Prepare train adjustment object
const exitChange: ExtractChange = { Name: exit.Name, MinTime: null, MaxTime: null, Chance: null };
const exitChange: ExtractChange = {
Name: exit.Name,
MinTime: undefined,
MaxTime: undefined,
Chance: undefined,
};
// At what minute we simulate the player joining the raid
const simulatedRaidEntryTimeMinutes = mapBase.EscapeTimeLimit - newRaidTimeMinutes;
@ -270,6 +275,6 @@ export class RaidTimeAdjustmentService
result.push(exitChange);
}
return result.length > 0 ? result : null;
return result.length > 0 ? result : undefined;
}
}

View File

@ -61,17 +61,21 @@ export class RepairService
): RepairDetails
{
const itemToRepair = pmcData.Inventory.items.find((x) => x._id === repairItemDetails._id);
if (itemToRepair === undefined)
if (!itemToRepair)
{
throw new Error(`Item ${repairItemDetails._id} not found in profile inventory, unable to repair`);
}
const priceCoef = this.traderHelper.getLoyaltyLevel(traderId, pmcData).repair_price_coef;
const traderRepairDetails = this.traderHelper.getTrader(traderId, sessionID).repair;
const traderRepairDetails = this.traderHelper.getTrader(traderId, sessionID)?.repair;
if (!traderRepairDetails)
{
throw new Error(`Trader details for ${traderId} was not found`);
}
const repairQualityMultiplier = Number(traderRepairDetails.quality);
const repairRate = priceCoef <= 0 ? 1 : priceCoef / 100 + 1;
const itemToRepairDetails = this.databaseServer.getTables().templates.items[itemToRepair._tpl];
const itemToRepairDetails = this.databaseServer.getTables().templates!.items[itemToRepair._tpl];
const repairItemIsArmor = !!itemToRepairDetails._props.ArmorMaterial;
this.repairHelper.updateItemDurability(
@ -85,7 +89,11 @@ export class RepairService
);
// get repair price
const itemRepairCost = this.databaseServer.getTables().templates.items[itemToRepair._tpl]._props.RepairCost;
const itemRepairCost = this.databaseServer.getTables().templates!.items[itemToRepair._tpl]._props.RepairCost;
if (!itemRepairCost)
{
throw new Error(`Item with tpl ${itemToRepair._tpl} has no repair cost`);
}
const repairCost = Math.round(
itemRepairCost * repairItemDetails.count * repairRate * this.repairConfig.priceMultiplier,
);
@ -186,6 +194,10 @@ export class RepairService
const isHeavyArmor = itemDetails[1]._props.ArmorType === "Heavy";
const vestSkillToLevel = isHeavyArmor ? SkillTypes.HEAVY_VESTS : SkillTypes.LIGHT_VESTS;
if (!repairDetails.repairPoints)
{
throw new Error(`Repair for ${repairDetails.repairedItem._tpl} has no repair points`);
}
const pointsToAddToVestSkill
= repairDetails.repairPoints * this.repairConfig.armorKitSkillPointGainPerRepairPointMultiplier;
@ -215,6 +227,10 @@ export class RepairService
: this.repairConfig.repairKitIntellectGainMultiplier.armor;
// Limit gain to a max value defined in config.maxIntellectGainPerRepair
if (!repairDetails.repairPoints)
{
throw new Error(`Repair for ${repairDetails.repairedItem._tpl} has no repair points`);
}
return Math.min(
repairDetails.repairPoints * intRepairMultiplier,
this.repairConfig.maxIntellectGainPerRepair.kit,
@ -284,7 +300,7 @@ export class RepairService
throw new Error(`Item ${itemToRepairId} not found, unable to repair`);
}
const itemsDb = this.databaseServer.getTables().templates.items;
const itemsDb = this.databaseServer.getTables().templates!.items;
const itemToRepairDetails = itemsDb[itemToRepair._tpl];
const repairItemIsArmor = !!itemToRepairDetails._props.ArmorMaterial;
const repairAmount = repairKits[0].count / this.getKitDivisor(itemToRepairDetails, repairItemIsArmor, pmcData);
@ -307,13 +323,17 @@ export class RepairService
for (const repairKit of repairKits)
{
const repairKitInInventory = pmcData.Inventory.items.find((x) => x._id === repairKit._id);
if (!repairKitInInventory)
{
throw new Error(`Repair kit with id ${repairKit._id} was not found in the inventory`);
}
const repairKitDetails = itemsDb[repairKitInInventory._tpl];
const repairKitReductionAmount = repairKit.count;
this.addMaxResourceToKitIfMissing(repairKitDetails, repairKitInInventory);
// reduce usages on repairkit used
repairKitInInventory.upd.RepairKit.Resource -= repairKitReductionAmount;
repairKitInInventory.upd!.RepairKit!.Resource -= repairKitReductionAmount;
output.profileChanges[sessionId].items.change.push(repairKitInInventory);
}
@ -336,7 +356,7 @@ export class RepairService
*/
protected getKitDivisor(itemToRepairDetails: ITemplateItem, isArmor: boolean, pmcData: IPmcData): number
{
const globals = this.databaseServer.getTables().globals;
const globals = this.databaseServer.getTables().globals!;
const globalRepairSettings = globals.config.RepairSettings;
const intellectRepairPointsPerLevel = globals.config.SkillsSettings.Intellect.RepairPointsCostReduction;
@ -376,10 +396,10 @@ export class RepairService
{
const bonusesMatched = pmcData?.Bonuses?.filter((b) => b.type === skillBonus);
let value = 1;
if (bonusesMatched != null)
if (bonusesMatched)
{
const sumedPercentage = bonusesMatched.map((b) => b.value).reduce((v1, v2) => v1 + v2, 0);
value = 1 + sumedPercentage / 100;
const summedPercentage = bonusesMatched.map((b) => b.value ?? 0).reduce((v1, v2) => v1 + v2, 0);
value = 1 + summedPercentage / 100;
}
return value;
@ -415,7 +435,7 @@ export class RepairService
*/
protected addMaxResourceToKitIfMissing(repairKitDetails: ITemplateItem, repairKitInInventory: Item): void
{
const maxRepairAmount = repairKitDetails._props.MaxRepairResource;
const maxRepairAmount = repairKitDetails._props.MaxRepairResource!;
if (!repairKitInInventory.upd)
{
this.logger.debug(`Repair kit: ${repairKitInInventory._id} in inventory lacks upd object, adding`);
@ -478,13 +498,13 @@ export class RepairService
const bonusThresholdPercents = itemConfig[bonusRarity][bonusType].activeDurabilityPercentMinMax;
const bonusThresholdPercent = this.randomUtil.getInt(bonusThresholdPercents.min, bonusThresholdPercents.max);
item.upd.Buff = {
item.upd!.Buff = {
rarity: bonusRarity,
buffType: bonusType,
value: bonusValue,
thresholdDurability: this.randomUtil.getPercentOfValue(
bonusThresholdPercent,
item.upd.Repairable.Durability,
item.upd!.Repairable!.Durability,
),
};
}
@ -498,7 +518,7 @@ export class RepairService
*/
protected shouldBuffItem(repairDetails: RepairDetails, pmcData: IPmcData): boolean
{
const globals = this.databaseServer.getTables().globals;
const globals = this.databaseServer.getTables().globals!;
const hasTemplate = this.itemHelper.getItem(repairDetails.repairedItem._tpl);
if (!hasTemplate[0])
@ -542,7 +562,11 @@ export class RepairService
(this.profileHelper.getSkillFromProfile(pmcData, itemSkillType)?.Progress ?? 0) / 100,
);
const durabilityToRestorePercent = repairDetails.repairPoints / template._props.MaxDurability;
if (!repairDetails.repairPoints)
{
throw new Error(`Repair for ${repairDetails.repairedItem._tpl} has no repair points`);
}
const durabilityToRestorePercent = repairDetails.repairPoints / template._props.MaxDurability!;
const durabilityMultiplier = this.getDurabilityMultiplier(
receivedDurabilityMaxPercent,
durabilityToRestorePercent,

View File

@ -27,8 +27,8 @@ export class SeasonalEventService
protected httpConfig: IHttpConfig;
protected weatherConfig: IWeatherConfig;
protected halloweenEventActive: boolean = undefined;
protected christmasEventActive: boolean = undefined;
protected halloweenEventActive?: boolean = undefined;
protected christmasEventActive?: boolean = undefined;
/** All events active at this point in time */
protected currentlyActiveEvents: SeasonalEventType[] = [];
@ -128,7 +128,7 @@ export class SeasonalEventService
*/
public getInactiveSeasonalEventItems(): string[]
{
const items = [];
const items: string[] = [];
if (!this.christmasEventEnabled())
{
items.push(...this.christmasEventItems);
@ -157,7 +157,7 @@ export class SeasonalEventService
*/
public christmasEventEnabled(): boolean
{
return this.christmasEventActive;
return this.christmasEventActive ?? false;
}
/**
@ -166,7 +166,7 @@ export class SeasonalEventService
*/
public halloweenEventEnabled(): boolean
{
return this.halloweenEventActive;
return this.halloweenEventActive ?? false;
}
/**
@ -222,7 +222,7 @@ export class SeasonalEventService
{
if (this.currentlyActiveEvents)
{
const globalConfig = this.databaseServer.getTables().globals.config;
const globalConfig = this.databaseServer.getTables().globals!.config;
for (const event of this.currentlyActiveEvents)
{
this.updateGlobalEvents(sessionId, globalConfig, event);
@ -405,12 +405,12 @@ export class SeasonalEventService
protected adjustZryachiyMeleeChance(): void
{
this.databaseServer.getTables().bots.types.bosszryachiy.chances.equipment.Scabbard = 100;
this.databaseServer.getTables().bots!.types.bosszryachiy.chances.equipment.Scabbard = 100;
}
protected enableHalloweenSummonEvent(): void
{
this.databaseServer.getTables().globals.config.EventSettings.EventActive = true;
this.databaseServer.getTables().globals!.config.EventSettings.EventActive = true;
}
protected addEventBossesToMaps(eventType: SeasonalEventType): void
@ -434,10 +434,10 @@ export class SeasonalEventService
for (const boss of bossesToAdd)
{
const mapBosses: BossLocationSpawn[]
= this.databaseServer.getTables().locations[mapKey].base.BossLocationSpawn;
= this.databaseServer.getTables().locations![mapKey].base.BossLocationSpawn;
if (!mapBosses.find((x) => x.BossName === boss.BossName))
{
this.databaseServer.getTables().locations[mapKey].base.BossLocationSpawn.push(...bossesToAdd);
this.databaseServer.getTables().locations![mapKey].base.BossLocationSpawn.push(...bossesToAdd);
}
}
}
@ -486,7 +486,7 @@ export class SeasonalEventService
*/
protected addLootItemsToGifterDropItemsList(): void
{
const gifterBot = this.databaseServer.getTables().bots.types.gifter;
const gifterBot = this.databaseServer.getTables().bots!.types.gifter;
for (const difficulty in gifterBot.difficulty)
{
gifterBot.difficulty[difficulty].Patrol.ITEMS_TO_DROP = Object.keys(
@ -512,7 +512,7 @@ export class SeasonalEventService
// Iterate over bots with changes to apply
for (const bot in botGearChanges)
{
const botToUpdate = this.databaseServer.getTables().bots.types[bot.toLowerCase()];
const botToUpdate = this.databaseServer.getTables().bots!.types[bot.toLowerCase()];
if (!botToUpdate)
{
this.logger.warning(this.localisationService.getText("gameevent-bot_not_found", bot));
@ -541,7 +541,7 @@ export class SeasonalEventService
protected addPumpkinsToScavBackpacks(): void
{
this.databaseServer.getTables().bots.types.assault.inventory.items.Backpack["634959225289190e5e773b3b"] = 400;
this.databaseServer.getTables().bots!.types.assault.inventory.items.Backpack["634959225289190e5e773b3b"] = 400;
}
/**
@ -572,7 +572,7 @@ export class SeasonalEventService
protected addGifterBotToMaps(): void
{
const gifterSettings = this.seasonalEventConfig.gifterSettings;
const maps = this.databaseServer.getTables().locations;
const maps = this.databaseServer.getTables().locations!;
for (const gifterMapSettings of gifterSettings)
{
const mapData: ILocation = maps[gifterMapSettings.map];

View File

@ -35,13 +35,13 @@ export class TraderPurchasePersisterService
* @param traderId Trader to loop up purchases for
* @returns Dict of assort id and count purchased
*/
public getProfileTraderPurchases(sessionId: string, traderId: string): Record<string, TraderPurchaseData>
public getProfileTraderPurchases(sessionId: string, traderId: string): Record<string, TraderPurchaseData> | undefined
{
const profile = this.profileHelper.getFullProfile(sessionId);
if (!profile.traderPurchases)
{
return null;
return undefined;
}
return profile.traderPurchases[traderId];
@ -54,19 +54,20 @@ export class TraderPurchasePersisterService
* @param assortId Id of assort to get data for
* @returns TraderPurchaseData
*/
public getProfileTraderPurchase(sessionId: string, traderId: string, assortId: string): TraderPurchaseData
public getProfileTraderPurchase(sessionId: string, traderId: string, assortId: string):
TraderPurchaseData | undefined
{
const profile = this.profileHelper.getFullProfile(sessionId);
if (!profile.traderPurchases)
{
return null;
return undefined;
}
const traderPurchases = profile.traderPurchases[traderId];
if (!traderPurchases)
{
return null;
return undefined;
}
return traderPurchases[assortId];

View File

@ -1,6 +1,7 @@
import { inject, injectable } from "tsyringe";
import { ProfileHelper } from "@spt/helpers/ProfileHelper";
import { QuestStatus } from "@spt/models/enums/QuestStatus";
import { TraderServiceType } from "@spt/models/enums/TraderServiceType";
import { ITraderServiceModel } from "@spt/models/spt/services/ITraderServiceModel";
import { ILogger } from "@spt/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt/servers/DatabaseServer";
@ -20,14 +21,14 @@ export class TraderServicesService
public getTraderServices(sessionId: string, traderId: string): ITraderServiceModel[]
{
const pmcData = this.profileHelper.getPmcProfile(sessionId);
let traderServices = this.cloner.clone(this.databaseServer.getTables().traders[traderId]?.services);
let traderServices = this.cloner.clone(this.databaseServer.getTables().traders![traderId]?.services);
if (!traderServices)
{
return [];
}
// Filter out any service the user doesn't meet the conditions for
const servicesToDelete = [];
const servicesToDelete: TraderServiceType[] = [];
for (const service of traderServices)
{
if (service.requirements?.standings)

View File

@ -27,6 +27,6 @@ export class AsyncQueue implements IAsyncQueue
}
// When the command is ready, execute it
return this.commandsQueue.shift().cmd();
return this.commandsQueue.shift()!.cmd();
}
}

View File

@ -13,7 +13,7 @@ export class HttpFileUtil
{
const pathSlic = filePath.split("/");
const type
= this.httpServerHelper.getMimeText(pathSlic[pathSlic.length - 1].split(".").at(-1))
= this.httpServerHelper.getMimeText(pathSlic[pathSlic.length - 1].split(".").at(-1) ?? "")
|| this.httpServerHelper.getMimeText("txt");
const fileStream = fs.createReadStream(filePath);

View File

@ -42,14 +42,14 @@ export class HttpResponseUtil
* @param errmsg
* @returns
*/
public getBody<T>(data: T, err = 0, errmsg = null, sanitize = true): IGetBodyResponseData<T>
public getBody<T>(data: T, err: number = 0, errmsg?: string, sanitize = true): IGetBodyResponseData<T>
{
return sanitize
? this.clearString(this.getUnclearedBody(data, err, errmsg))
: (this.getUnclearedBody(data, err, errmsg) as any);
}
public getUnclearedBody(data: any, err = 0, errmsg = null): string
public getUnclearedBody(data: any, err: number = 0, errmsg?: string): string
{
return this.jsonUtil.serialize({ err: err, errmsg: errmsg, data: data });
}
@ -61,7 +61,7 @@ export class HttpResponseUtil
public nullResponse(): INullResponseData
{
return this.clearString(this.getUnclearedBody(null, 0, null));
return this.clearString(this.getUnclearedBody(null, 0, undefined));
}
public emptyArrayResponse(): IGetBodyResponseData<any[]>

View File

@ -57,7 +57,7 @@ export class ImporterUtil
}
// set all loadRecursive to be executed asynchronously
const resEntries = Object.entries(result);
const resEntries = Object.entries(result!);
const resResolved = await Promise.all(resEntries.map((ent) => ent[1]));
for (let resIdx = 0; resIdx < resResolved.length; resIdx++)
{
@ -136,6 +136,8 @@ export class ImporterUtil
while (directoriesToRead.length !== 0)
{
const directory = directoriesToRead.dequeue();
if (!directory)
continue;
filesToProcess.enqueueAll(this.vfs.getFiles(directory).map((f) => new VisitNode(`${directory}/`, f)));
directoriesToRead.enqueueAll(this.vfs.getDirs(directory).map((d) => `${directory}/${d}`));
}
@ -143,6 +145,8 @@ export class ImporterUtil
while (filesToProcess.length !== 0)
{
const fileNode = filesToProcess.dequeue();
if (!fileNode)
continue;
if (this.vfs.getFileExtension(fileNode.fileName) === "json")
{
const filePathAndName = `${fileNode.filePath}${fileNode.fileName}`;

View File

@ -10,7 +10,7 @@ import { VFS } from "@spt/utils/VFS";
@injectable()
export class JsonUtil
{
protected fileHashes = null;
protected fileHashes?: Map<string, string> = undefined;
protected jsonCacheExists = false;
protected jsonCachePath = "./user/cache/jsonCache.json";
@ -60,7 +60,7 @@ export class JsonUtil
* @param options Stringify options or a replacer.
* @returns The string converted from the JavaScript value
*/
public serializeJsonC(data: any, filename?: string | null, options?: IStringifyOptions | Reviver): string
public serializeJsonC(data: any, filename?: string | null, options?: IStringifyOptions | Reviver): string | undefined
{
try
{
@ -74,7 +74,7 @@ export class JsonUtil
}
}
public serializeJson5(data: any, filename?: string | null, prettify = false): string
public serializeJson5(data: any, filename?: string | null, prettify = false): string | undefined
{
try
{
@ -99,7 +99,7 @@ export class JsonUtil
* @param filename Name of file being deserialized
* @returns object
*/
public deserialize<T>(jsonString: string, filename = ""): T
public deserialize<T>(jsonString: string, filename = ""): T | undefined
{
try
{
@ -120,7 +120,7 @@ export class JsonUtil
* @param options Parsing options
* @returns object
*/
public deserializeJsonC<T>(jsonString: string, filename = "", options?: IParseOptions): T
public deserializeJsonC<T>(jsonString: string, filename = "", options?: IParseOptions): T | undefined
{
try
{
@ -134,7 +134,7 @@ export class JsonUtil
}
}
public deserializeJson5<T>(jsonString: string, filename = ""): T
public deserializeJson5<T>(jsonString: string, filename = ""): T | undefined
{
try
{
@ -148,7 +148,7 @@ export class JsonUtil
}
}
public async deserializeWithCacheCheckAsync<T>(jsonString: string, filePath: string): Promise<T>
public async deserializeWithCacheCheckAsync<T>(jsonString: string, filePath: string): Promise<T | undefined>
{
return new Promise((resolve) =>
{
@ -162,7 +162,7 @@ export class JsonUtil
* @param filePath Path to json file being processed
* @returns Object
*/
public deserializeWithCacheCheck<T>(jsonString: string, filePath: string): T
public deserializeWithCacheCheck<T>(jsonString: string, filePath: string): T | undefined
{
this.ensureJsonCacheExists(this.jsonCachePath);
this.hydrateJsonCache(this.jsonCachePath);
@ -170,6 +170,10 @@ export class JsonUtil
// Generate hash of string
const generatedHash = this.hashUtil.generateSha1ForData(jsonString);
if (!this.fileHashes)
{
throw new Error("Unable to deserialize with Cache, file hashes have not been hydrated yet");
}
// Get hash of file and check if missing or hash mismatch
let savedHash = this.fileHashes[filePath];
if (!savedHash || savedHash !== generatedHash)

View File

@ -22,7 +22,7 @@ export class MathUtil
*/
public arrayCumsum(values: number[]): number[]
{
const cumsumArray = [];
const cumsumArray: number[] = [];
let sum = 0;
for (let i = 0; i < values.length; i++)
{
@ -85,7 +85,7 @@ export class MathUtil
* @param {array} y support points in y (of same length as x)
* @return {number} y(xp)
*/
public interp1(xp: number, x: number[], y: number[]): number
public interp1(xp: number, x: number[], y: number[]): number | undefined
{
if (xp > x[x.length - 1])
{
@ -104,5 +104,6 @@ export class MathUtil
return y[i] + ((xp - x[i]) * (y[i + 1] - y[i])) / (x[i + 1] - x[i]);
}
}
return undefined;
}
}

View File

@ -17,29 +17,29 @@ export class RagfairOfferHolder
this.offersByTrader = new Map();
}
public getOfferById(id: string): IRagfairOffer
public getOfferById(id: string): IRagfairOffer | undefined
{
if (this.offersById.has(id))
{
return this.offersById.get(id);
return this.offersById.get(id)!;
}
return undefined;
}
public getOffersByTemplate(templateId: string): Array<IRagfairOffer>
public getOffersByTemplate(templateId: string): Array<IRagfairOffer> | undefined
{
if (this.offersByTemplate.has(templateId))
{
return [...this.offersByTemplate.get(templateId).values()];
return [...this.offersByTemplate.get(templateId)!.values()];
}
return undefined;
}
public getOffersByTrader(traderId: string): Array<IRagfairOffer>
public getOffersByTrader(traderId: string): Array<IRagfairOffer> | undefined
{
if (this.offersByTrader.has(traderId))
{
return [...this.offersByTrader.get(traderId).values()];
return [...this.offersByTrader.get(traderId)!.values()];
}
return undefined;
}
@ -70,7 +70,7 @@ export class RagfairOfferHolder
// for this template, just dont add in more
if (
!(this.ragfairServerHelper.isTrader(trader) || this.ragfairServerHelper.isPlayer(trader))
&& this.getOffersByTemplate(itemTpl)?.length >= this.maxOffersPerTemplate
&& (this.getOffersByTemplate(itemTpl)?.length ?? 0) >= this.maxOffersPerTemplate
)
{
return;
@ -89,17 +89,23 @@ export class RagfairOfferHolder
if (this.offersById.has(offer._id))
{
this.offersById.delete(offer._id);
const traderOffers = this.offersByTrader.get(offer.user.id);
traderOffers.delete(offer._id);
// This was causing a memory leak, we need to make sure that we remove
// the user ID from the cached offers after they dont have anything else
// on the flea placed. We regenerate the ID for the NPC users, making it
// continously grow otherwise
if (traderOffers.size === 0)
if (this.offersByTrader.has(offer.user.id))
{
this.offersByTrader.delete(offer.user.id);
this.offersByTrader.get(offer.user.id)!.delete(offer._id);
// This was causing a memory leak, we need to make sure that we remove
// the user ID from the cached offers after they dont have anything else
// on the flea placed. We regenerate the ID for the NPC users, making it
// continously grow otherwise
if (this.offersByTrader.get(offer.user.id)!.size === 0)
{
this.offersByTrader.delete(offer.user.id);
}
}
if (this.offersByTemplate.has(offer.items[0]._tpl))
{
this.offersByTemplate.get(offer.items[0]._tpl)!.delete(offer._id);
}
this.offersByTemplate.get(offer.items[0]._tpl).delete(offer._id);
}
}
@ -115,7 +121,7 @@ export class RagfairOfferHolder
{
if (this.offersByTrader.has(traderId))
{
this.removeOffers([...this.offersByTrader.get(traderId).values()]);
this.removeOffers([...this.offersByTrader.get(traderId)!.values()]);
}
}
@ -132,7 +138,7 @@ export class RagfairOfferHolder
{
if (this.offersByTemplate.has(template))
{
this.offersByTemplate.get(template).set(offer._id, offer);
this.offersByTemplate.get(template)!.set(offer._id, offer);
}
else
{
@ -146,7 +152,7 @@ export class RagfairOfferHolder
{
if (this.offersByTrader.has(trader))
{
this.offersByTrader.get(trader).set(offer._id, offer);
this.offersByTrader.get(trader)!.set(offer._id, offer);
}
else
{
@ -158,6 +164,6 @@ export class RagfairOfferHolder
protected isStale(offer: IRagfairOffer, time: number): boolean
{
return offer.endTime < time || offer.items[0].upd.StackObjectsCount < 1;
return offer.endTime < time || (offer.items[0].upd?.StackObjectsCount ?? 0) < 1;
}
}

View File

@ -81,7 +81,7 @@ export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityO
* @param {string} key The key of the element whose data shall be retrieved
* @returns {object} The data object
*/
data(key: K): V
data(key: K): V | undefined
{
return this.filter((r) => r.key === key)[0]?.data;
}
@ -151,11 +151,11 @@ export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityO
acc.keyArray.push(x.key);
return acc;
},
{ probArray: [], keyArray: [] },
{ probArray: new Array<number>(), keyArray: new Array<K>() },
);
let probCumsum = this.cumulativeProbability(probArray);
const drawnKeys = [];
const drawnKeys: K[] = [];
for (let i = 0; i < count; i++)
{
const rand = Math.random();
@ -193,14 +193,14 @@ export class ProbabilityObject<K, V = undefined>
{
key: K;
relativeProbability: number;
data: V;
data?: V;
/**
* Constructor for the ProbabilityObject
* @param {string} key The key of the element
* @param {number} relativeProbability The relative probability of this element
* @param {any} data Optional data attached to the element
*/
constructor(key: K, relativeProbability: number, data: V = null)
constructor(key: K, relativeProbability: number, data?: V)
{
this.key = key;
this.relativeProbability = relativeProbability;
@ -354,7 +354,7 @@ export class RandomUtil
list = this.cloner.clone(originalList);
}
const results = [];
const results: T[] = [];
for (let i = 0; i < count; i++)
{
const randomIndex = this.randInt(list.length);

View File

@ -14,7 +14,11 @@ export class VFS
{
accessFilePromisify: (path: fs.PathLike, mode?: number) => Promise<void>;
copyFilePromisify: (src: fs.PathLike, dst: fs.PathLike, flags?: number) => Promise<void>;
mkdirPromisify: (path: fs.PathLike, options: fs.MakeDirectoryOptions & { recursive: true }) => Promise<string>;
mkdirPromisify: (
path: fs.PathLike,
options: fs.MakeDirectoryOptions & { recursive: true }
) => Promise<string | undefined>;
readFilePromisify: (path: fs.PathLike) => Promise<Buffer>;
writeFilePromisify: (path: fs.PathLike, data: string, options?: any) => Promise<void>;
readdirPromisify: (
@ -91,7 +95,7 @@ export class VFS
await this.asyncQueue.waitFor(command);
}
public copyDir(filepath: string, target: string, fileExtensions: string | string[] = undefined): void
public copyDir(filepath: string, target: string, fileExtensions?: string | string[]): void
{
const files = this.getFiles(filepath);
const dirs = this.getDirs(filepath);
@ -109,7 +113,7 @@ export class VFS
for (const file of files)
{
// copy all if fileExtension is not set, copy only those with fileExtension if set
if (!fileExtensions || fileExtensions.includes(file.split(".").pop()))
if (!fileExtensions || fileExtensions.includes(file.split(".").pop() ?? ""))
{
this.copyFile(path.join(filepath, file), path.join(target, file));
}
@ -134,7 +138,7 @@ export class VFS
for (const file of files)
{
// copy all if fileExtension is not set, copy only those with fileExtension if set
if (!fileExtensions || fileExtensions.includes(file.split(".").pop()))
if (!fileExtensions || fileExtensions.includes(file.split(".").pop() ?? ""))
{
await this.copyAsync(path.join(filepath, file), path.join(target, file));
}
@ -279,7 +283,7 @@ export class VFS
const files = this.getFiles(filepath);
const dirs = this.getDirs(filepath);
const promises = [];
const promises: Promise<void>[] = [];
for (const dir of dirs)
{
@ -288,7 +292,7 @@ export class VFS
for (const file of files)
{
promises.push(this.removeFile(path.join(filepath, file)));
promises.push(this.removeFileAsync(path.join(filepath, file)));
}
await Promise.all(promises);
@ -320,7 +324,7 @@ export class VFS
unlockSync(filepath);
}
public getFileExtension(filepath: string): string
public getFileExtension(filepath: string): string | undefined
{
return filepath.split(".").pop();
}

View File

@ -68,7 +68,7 @@ export class Watermark
@inject("WinstonLogger") protected logger: ILogger,
@inject("ConfigServer") protected configServer: ConfigServer,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("WatermarkLocale") protected watermarkLocale?: WatermarkLocale,
@inject("WatermarkLocale") protected watermarkLocale: WatermarkLocale,
)
{
this.sptConfig = this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE);
@ -95,11 +95,14 @@ export class Watermark
this.text = this.text.concat([...modding]);
}
if (this.sptConfig.customWatermarkLocaleKeys?.length > 0)
if (this.sptConfig.customWatermarkLocaleKeys)
{
for (const key of this.sptConfig.customWatermarkLocaleKeys)
if (this.sptConfig.customWatermarkLocaleKeys.length > 0)
{
this.text.push(...["", this.localisationService.getText(key)]);
for (const key of this.sptConfig.customWatermarkLocaleKeys)
{
this.text.push(...["", this.localisationService.getText(key)]);
}
}
}
@ -159,7 +162,7 @@ export class Watermark
/** Draw the watermark */
protected draw(): void
{
const result = [];
const result: string[] = [];
// Calculate size, add 10% for spacing to the right
const longestLength

View File

@ -66,15 +66,18 @@ export class LinkedList<T>
let ref = this.head;
for (let i = 0; i <= idx; ++i)
{
ref = ref.next;
ref = ref?.next;
}
const node = new LinkedListNode(value);
this.length++;
node.next = ref;
node.prev = ref.prev;
ref.prev = node;
node.prev = ref?.prev;
if (ref)
{
ref.prev = node;
}
if (node.prev)
{
@ -104,7 +107,7 @@ export class LinkedList<T>
/**
* Returns the first element's value.
*/
public getHead(): T
public getHead(): T | undefined
{
return this.head?.value;
}
@ -112,7 +115,7 @@ export class LinkedList<T>
/**
* Finds the element from the list at the given index and returns it's value.
*/
public get(idx: number): T
public get(idx: number): T | undefined
{
if (idx < 0 || idx >= this.length)
{
@ -141,7 +144,7 @@ export class LinkedList<T>
/**
* Returns the last element's value.
*/
public getTail(): T
public getTail(): T | undefined
{
return this.tail?.value;
}
@ -149,7 +152,7 @@ export class LinkedList<T>
/**
* Finds and removes the first element from a list that has a value equal to the given value, returns it's value if it successfully removed it.
*/
public remove(value: T): T
public remove(value: T): T | undefined
{
let ref = this.head;
for (let i = 0; ref && i < this.length; ++i)
@ -170,7 +173,7 @@ export class LinkedList<T>
if (this.length === 0)
{
const out = this.head.value;
const out = this.head?.value;
this.head = this.tail = undefined;
return out;
}
@ -202,7 +205,7 @@ export class LinkedList<T>
/**
* Removes the first element from the list and returns it's value. If the list is empty, undefined is returned and the list is not modified.
*/
public shift(): T
public shift(): T | undefined
{
if (!this.head)
{
@ -227,7 +230,7 @@ export class LinkedList<T>
/**
* Removes the element from the list at the given index and returns it's value.
*/
public removeAt(idx: number): T
public removeAt(idx: number): T | undefined
{
if (idx < 0 || idx >= this.length)
{
@ -249,25 +252,25 @@ export class LinkedList<T>
for (let i = 0; i < idx; ++i)
{
ref = ref.next;
ref = ref?.next;
}
if (ref.prev)
if (ref?.prev)
{
ref.prev.next = ref.next;
}
if (ref.next)
if (ref?.next)
{
ref.next.prev = ref.prev;
}
return ref.value;
return ref?.value;
}
/**
* Removes the last element from the list and returns it's value. If the list is empty, undefined is returned and the list is not modified.
*/
public pop(): T
public pop(): T | undefined
{
if (!this.tail)
{
@ -292,13 +295,13 @@ export class LinkedList<T>
/**
* Returns an iterable of index, value pairs for every entry in the list.
*/
public *entries(): IterableIterator<[number, T]>
public *entries(): IterableIterator<[number, T | undefined]>
{
let node = this.head;
for (let i = 0; i < this.length; ++i)
{
yield [i, node.value];
node = node.next;
yield [i, node?.value];
node = node?.next;
}
}

View File

@ -36,7 +36,7 @@ export class Queue<T>
/**
* Removes the first element from the queue and returns it's value. If the queue is empty, undefined is returned and the queue is not modified.
*/
public dequeue(): T
public dequeue(): T | undefined
{
return this.list.shift();
}
@ -44,7 +44,7 @@ export class Queue<T>
/**
* Returns the first element's value.
*/
public peek(): T
public peek(): T | undefined
{
return this.list.getHead();
}

View File

@ -94,7 +94,7 @@ export abstract class AbstractWinstonLogger implements ILogger
process.on("uncaughtException", (error) =>
{
this.error(`${error.name}: ${error.message}`);
this.error(error.stack);
this.error(error.stack ?? "No stack");
});
}
}
@ -184,7 +184,7 @@ export abstract class AbstractWinstonLogger implements ILogger
public async success(data: string | Record<string, unknown>): Promise<void>
{
const command: ICommand = { uuid: crypto.randomUUID(), cmd: async () => await this.logger.succ(data) };
const command: ICommand = { uuid: crypto.randomUUID(), cmd: async () => await this.logger.succ!(data) };
await this.asyncQueue.waitFor(command);
}

View File

@ -1,5 +1,5 @@
{
"extends": "./tsconfig.base.json",
"extends": "./tsconfig.json",
"compilerOptions": {
"strictNullChecks": true,
}