Formatting
Have you been using the recommended VSCode extensions? ;)
This commit is contained in:
parent
8227e06d77
commit
90492f3aa2
@ -20,21 +20,26 @@ export class AchievementCallbacks
|
||||
|
||||
/**
|
||||
* Handle client/achievement/list
|
||||
*
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public getAchievements(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IGetAchievementsResponse>
|
||||
public getAchievements(
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGetAchievementsResponse>
|
||||
{
|
||||
|
||||
return this.httpResponse.getBody(this.achievementController.getAchievements(sessionID));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle client/achievement/statistic
|
||||
*
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public statistic(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<ICompletedAchievementsResponse>
|
||||
public statistic(
|
||||
url: string,
|
||||
info: IEmptyRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<ICompletedAchievementsResponse>
|
||||
{
|
||||
return this.httpResponse.getBody(this.achievementController.getAchievementStatistics(sessionID));
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ export class BuildsCallbacks
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public createMagazineTemplate(url: string, request: ISetMagazineRequest, sessionID: string): INullResponseData
|
||||
{
|
||||
this.buildController.createMagazineTemplate(sessionID, request)
|
||||
this.buildController.createMagazineTemplate(sessionID, request);
|
||||
|
||||
return this.httpResponse.nullResponse();
|
||||
}
|
||||
|
@ -28,10 +28,7 @@ export class CustomizationCallbacks
|
||||
*/
|
||||
public getSuits(url: string, info: IEmptyRequestData, sessionID: string): IGetBodyResponseData<IGetSuitsResponse>
|
||||
{
|
||||
const result: IGetSuitsResponse = {
|
||||
_id: sessionID,
|
||||
suites: this.saveServer.getProfile(sessionID).suits,
|
||||
};
|
||||
const result: IGetSuitsResponse = { _id: sessionID, suites: this.saveServer.getProfile(sessionID).suits };
|
||||
return this.httpResponse.getBody(result);
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ export class InraidCallbacks
|
||||
*/
|
||||
public getTraderServices(url: string, info: IEmptyRequestData, sessionId: string): string
|
||||
{
|
||||
const lastSlashPos = url.lastIndexOf('/');
|
||||
const lastSlashPos = url.lastIndexOf("/");
|
||||
const traderId = url.substring(lastSlashPos + 1);
|
||||
return this.httpResponse.noBody(this.inraidController.getTraderServices(sessionId, traderId));
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ export class InventoryCallbacks
|
||||
{
|
||||
constructor(
|
||||
@inject("InventoryController") protected inventoryController: InventoryController,
|
||||
@inject("QuestController") protected questController: QuestController
|
||||
@inject("QuestController") protected questController: QuestController,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -164,17 +164,16 @@ export class InventoryCallbacks
|
||||
return this.inventoryController.openRandomLootContainer(pmcData, body, sessionID);
|
||||
}
|
||||
|
||||
public redeemProfileReward(pmcData: IPmcData,
|
||||
public redeemProfileReward(
|
||||
pmcData: IPmcData,
|
||||
body: IRedeemProfileRequestData,
|
||||
sessionId: string
|
||||
sessionId: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
return this.inventoryController.redeemProfileReward(pmcData, body, sessionId)
|
||||
return this.inventoryController.redeemProfileReward(pmcData, body, sessionId);
|
||||
}
|
||||
|
||||
public setFavoriteItem(pmcData: IPmcData,
|
||||
body: ISetFavoriteItems,
|
||||
sessionId: string): IItemEventRouterResponse
|
||||
public setFavoriteItem(pmcData: IPmcData, body: ISetFavoriteItems, sessionId: string): IItemEventRouterResponse
|
||||
{
|
||||
return this.inventoryController.setFavoriteItem(pmcData, body, sessionId);
|
||||
}
|
||||
|
@ -160,7 +160,11 @@ export class MatchCallbacks
|
||||
* @returns
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
public getGroupStatus(url: string, info: IGetGroupStatusRequestData, sessionID: string): IGetBodyResponseData<IGetGroupStatusResponse>
|
||||
public getGroupStatus(
|
||||
url: string,
|
||||
info: IGetGroupStatusRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IGetGroupStatusResponse>
|
||||
{
|
||||
return this.httpResponse.getBody(this.matchController.getGroupStatus(info));
|
||||
}
|
||||
|
@ -34,7 +34,11 @@ export class ProfileCallbacks
|
||||
/**
|
||||
* Handle client/game/profile/create
|
||||
*/
|
||||
public createProfile(url: string, info: IProfileCreateRequestData, sessionID: string): IGetBodyResponseData<ICreateProfileResponse>
|
||||
public createProfile(
|
||||
url: string,
|
||||
info: IProfileCreateRequestData,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<ICreateProfileResponse>
|
||||
{
|
||||
const id = this.profileController.createProfile(info, sessionID);
|
||||
|
||||
|
@ -142,7 +142,11 @@ export class RagfairCallbacks implements OnLoad, OnUpdate
|
||||
}
|
||||
|
||||
/** Handle client/ragfair/offer/findbyid */
|
||||
public getFleaOfferById(url: string, request: IGetRagfairOfferByIdRequest, sessionID: string): IGetBodyResponseData<IRagfairOffer>
|
||||
public getFleaOfferById(
|
||||
url: string,
|
||||
request: IGetRagfairOfferByIdRequest,
|
||||
sessionID: string,
|
||||
): IGetBodyResponseData<IRagfairOffer>
|
||||
{
|
||||
return this.httpResponse.getBody(this.ragfairController.getOfferById(sessionID, request));
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
export enum ContextVariableType {
|
||||
export enum ContextVariableType
|
||||
{
|
||||
/** Logged in users session id */
|
||||
SESSION_ID = 0,
|
||||
/** Currently acive raid information */
|
||||
|
@ -34,7 +34,7 @@ export class AchievementController
|
||||
public getAchievementStatistics(sessionId: string): ICompletedAchievementsResponse
|
||||
{
|
||||
const achievements = this.databaseServer.getTables().templates.achievements;
|
||||
const stats = {}
|
||||
const stats = {};
|
||||
|
||||
for (const achievement of achievements)
|
||||
{
|
||||
|
@ -164,9 +164,15 @@ export class BotController
|
||||
* @param sessionId Session id
|
||||
* @returns
|
||||
*/
|
||||
protected generateBotsFirstTime(request: IGenerateBotsRequestData, pmcProfile: IPmcData, sessionId: string): IBotBase[]
|
||||
protected generateBotsFirstTime(
|
||||
request: IGenerateBotsRequestData,
|
||||
pmcProfile: IPmcData,
|
||||
sessionId: string,
|
||||
): IBotBase[]
|
||||
{
|
||||
const allPmcsHaveSameNameAsPlayer = this.randomUtil.getChance100(this.pmcConfig.allPMCsHavePlayerNameWithRandomPrefixChance);
|
||||
const allPmcsHaveSameNameAsPlayer = this.randomUtil.getChance100(
|
||||
this.pmcConfig.allPMCsHavePlayerNameWithRandomPrefixChance,
|
||||
);
|
||||
for (const condition of request.conditions)
|
||||
{
|
||||
const botGenerationDetails: BotGenerationDetails = {
|
||||
@ -180,7 +186,7 @@ export class BotController
|
||||
botCountToGenerate: this.botConfig.presetBatch[condition.Role],
|
||||
botDifficulty: condition.Difficulty,
|
||||
isPlayerScav: false,
|
||||
allPmcsHaveSameNameAsPlayer: allPmcsHaveSameNameAsPlayer
|
||||
allPmcsHaveSameNameAsPlayer: allPmcsHaveSameNameAsPlayer,
|
||||
};
|
||||
|
||||
// Event bots need special actions to occur, set data up for them
|
||||
@ -190,7 +196,7 @@ export class BotController
|
||||
// Add eventRole data + reassign role property to be base type
|
||||
botGenerationDetails.eventRole = condition.Role;
|
||||
botGenerationDetails.role = this.seasonalEventService.getBaseRoleForEventBot(
|
||||
botGenerationDetails.eventRole
|
||||
botGenerationDetails.eventRole,
|
||||
);
|
||||
}
|
||||
|
||||
@ -338,9 +344,9 @@ export class BotController
|
||||
public getBotCap(): number
|
||||
{
|
||||
const defaultMapCapId = "default";
|
||||
const raidConfig = this.applicationContext
|
||||
.getLatestValue(ContextVariableType.RAID_CONFIGURATION)
|
||||
.getValue<IGetRaidConfigurationRequestData>();
|
||||
const raidConfig = this.applicationContext.getLatestValue(ContextVariableType.RAID_CONFIGURATION).getValue<
|
||||
IGetRaidConfigurationRequestData
|
||||
>();
|
||||
|
||||
if (!raidConfig)
|
||||
{
|
||||
@ -369,6 +375,7 @@ export class BotController
|
||||
return {
|
||||
pmc: this.pmcConfig.pmcType,
|
||||
assault: this.botConfig.assaultBrainType,
|
||||
playerScav: this.botConfig.playerScavBrainType};
|
||||
playerScav: this.botConfig.playerScavBrainType,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -71,10 +71,7 @@ export class BuildController
|
||||
}
|
||||
|
||||
/** Handle client/builds/weapon/save */
|
||||
public saveWeaponBuild(
|
||||
sessionId: string,
|
||||
body: IPresetBuildActionRequestData,
|
||||
): void
|
||||
public saveWeaponBuild(sessionId: string, body: IPresetBuildActionRequestData): void
|
||||
{
|
||||
const pmcData = this.profileHelper.getPmcProfile(sessionId);
|
||||
|
||||
@ -84,12 +81,7 @@ export class BuildController
|
||||
body.Root = body.Items[0]._id;
|
||||
|
||||
// Create new object ready to save into profile userbuilds.weaponBuilds
|
||||
const newBuild: IWeaponBuild = {
|
||||
Id: body.Id,
|
||||
Name: body.Name,
|
||||
Root: body.Root,
|
||||
Items: body.Items,
|
||||
};
|
||||
const newBuild: IWeaponBuild = { Id: body.Id, Name: body.Name, Root: body.Root, Items: body.Items };
|
||||
|
||||
const savedWeaponBuilds = this.saveServer.getProfile(sessionId).userbuilds.weaponBuilds;
|
||||
const existingBuild = savedWeaponBuilds.find((x) => x.Id === body.Id);
|
||||
@ -110,15 +102,13 @@ export class BuildController
|
||||
}
|
||||
|
||||
/** Handle client/builds/equipment/save event */
|
||||
public saveEquipmentBuild(
|
||||
sessionID: string,
|
||||
request: IPresetBuildActionRequestData,
|
||||
): void
|
||||
public saveEquipmentBuild(sessionID: string, request: IPresetBuildActionRequestData): void
|
||||
{
|
||||
const buildType = "equipmentBuilds";
|
||||
const pmcData = this.profileHelper.getPmcProfile(sessionID);
|
||||
|
||||
const existingSavedEquipmentBuilds: IEquipmentBuild[] = this.saveServer.getProfile(sessionID).userbuilds[buildType];
|
||||
const existingSavedEquipmentBuilds: IEquipmentBuild[] =
|
||||
this.saveServer.getProfile(sessionID).userbuilds[buildType];
|
||||
|
||||
// Replace duplicate ID's. The first item is the base item.
|
||||
// Root ID and the base item ID need to match.
|
||||
@ -214,16 +204,15 @@ export class BuildController
|
||||
profile.userbuilds.magazineBuilds = [];
|
||||
}
|
||||
|
||||
const existingArrayId = profile.userbuilds.magazineBuilds.findIndex(item => item.Name === request.Name);
|
||||
const existingArrayId = profile.userbuilds.magazineBuilds.findIndex((item) => item.Name === request.Name);
|
||||
|
||||
if (existingArrayId === -1)
|
||||
{
|
||||
|
||||
profile.userbuilds.magazineBuilds.push(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
profile.userbuilds.magazineBuilds.splice(existingArrayId, 1, result)
|
||||
profile.userbuilds.magazineBuilds.splice(existingArrayId, 1, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -131,7 +131,7 @@ export class GameController
|
||||
|
||||
this.checkTraderRepairValuesExist();
|
||||
|
||||
this.adjustLocationBotValues()
|
||||
this.adjustLocationBotValues();
|
||||
|
||||
// repeatableQuests are stored by in profile.Quests due to the responses of the client (e.g. Quests in
|
||||
// offraidData). Since we don't want to clutter the Quests list, we need to remove all completed (failed or
|
||||
@ -151,7 +151,7 @@ export class GameController
|
||||
|
||||
if (this.coreConfig.fixes.fixProfileBreakingInventoryItemIssues)
|
||||
{
|
||||
this.profileFixerService.fixProfileBreakingInventoryItemIssues(pmcProfile)
|
||||
this.profileFixerService.fixProfileBreakingInventoryItemIssues(pmcProfile);
|
||||
}
|
||||
|
||||
if (pmcProfile.Health)
|
||||
@ -439,7 +439,10 @@ export class GameController
|
||||
public getGameConfig(sessionID: string): IGameConfigResponse
|
||||
{
|
||||
const profile = this.profileHelper.getPmcProfile(sessionID);
|
||||
const gameTime = profile.Stats?.Eft.OverallCounters.Items?.find(counter => counter.Key.includes("LifeTime") && counter.Key.includes("Pmc"))?.Value ?? 0;
|
||||
const gameTime =
|
||||
profile.Stats?.Eft.OverallCounters.Items?.find((counter) =>
|
||||
counter.Key.includes("LifeTime") && counter.Key.includes("Pmc")
|
||||
)?.Value ?? 0;
|
||||
|
||||
const config: IGameConfigResponse = {
|
||||
languages: this.databaseServer.getTables().locales.languages,
|
||||
@ -574,10 +577,8 @@ export class GameController
|
||||
(sum, curr) => sum + curr.value,
|
||||
0,
|
||||
);
|
||||
hydrationRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === BonusType.HYDRATION_REGENERATION).reduce(
|
||||
(sum, curr) => sum + curr.value,
|
||||
0,
|
||||
);
|
||||
hydrationRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === BonusType.HYDRATION_REGENERATION)
|
||||
.reduce((sum, curr) => sum + curr.value, 0);
|
||||
hpRegenPerHour += pmcProfile.Bonuses.filter((x) => x.type === BonusType.HEALTH_REGENERATION).reduce(
|
||||
(sum, curr) => sum + curr.value,
|
||||
0,
|
||||
@ -822,7 +823,7 @@ export class GameController
|
||||
dateAdded: Date.now(),
|
||||
name: modDetails.name,
|
||||
version: modDetails.version,
|
||||
url: modDetails.url
|
||||
url: modDetails.url,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,13 @@ import { PaymentHelper } from "@spt-aki/helpers/PaymentHelper";
|
||||
import { PresetHelper } from "@spt-aki/helpers/PresetHelper";
|
||||
import { ProfileHelper } from "@spt-aki/helpers/ProfileHelper";
|
||||
import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
|
||||
import { HideoutArea, ITaskConditionCounter, Product, Production, ScavCase } from "@spt-aki/models/eft/common/tables/IBotBase";
|
||||
import {
|
||||
HideoutArea,
|
||||
ITaskConditionCounter,
|
||||
Product,
|
||||
Production,
|
||||
ScavCase,
|
||||
} from "@spt-aki/models/eft/common/tables/IBotBase";
|
||||
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
||||
import { HideoutUpgradeCompleteRequestData } from "@spt-aki/models/eft/hideout/HideoutUpgradeCompleteRequestData";
|
||||
import { IHandleQTEEventRequestData } from "@spt-aki/models/eft/hideout/IHandleQTEEventRequestData";
|
||||
@ -503,8 +509,8 @@ export class HideoutController
|
||||
itemWithModsToAdd: [itemToReturn],
|
||||
foundInRaid: !!itemToReturn.upd.SpawnedInSession,
|
||||
callback: null,
|
||||
useSortingTable: false
|
||||
}
|
||||
useSortingTable: false,
|
||||
};
|
||||
|
||||
this.inventoryHelper.addItemToStash(sessionID, request, pmcData, output);
|
||||
if (output.warnings && output.warnings.length > 0)
|
||||
@ -787,10 +793,7 @@ export class HideoutController
|
||||
const preset = this.presetHelper.getDefaultPreset(recipe.endProduct);
|
||||
|
||||
// Ensure preset has unique ids and is cloned so we don't alter the preset data stored in memory
|
||||
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
|
||||
null,
|
||||
this.jsonUtil.clone(preset._items),
|
||||
);
|
||||
const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(preset._items));
|
||||
|
||||
this.itemHelper.remapRootItemId(presetAndMods);
|
||||
|
||||
@ -805,9 +808,7 @@ export class HideoutController
|
||||
const rewardToAdd: Item = {
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: recipe.endProduct,
|
||||
upd: {
|
||||
StackObjectsCount: recipe.count
|
||||
}
|
||||
upd: { StackObjectsCount: recipe.count },
|
||||
};
|
||||
|
||||
// Split item into separate items with acceptable stack sizes
|
||||
@ -821,10 +822,7 @@ export class HideoutController
|
||||
// Add the first reward item to array when not a preset (first preset added above earlier)
|
||||
if (!rewardIsPreset)
|
||||
{
|
||||
itemAndChildrenToSendToPlayer.push([{
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: recipe.endProduct
|
||||
}]);
|
||||
itemAndChildrenToSendToPlayer.push([{ _id: this.hashUtil.generate(), _tpl: recipe.endProduct }]);
|
||||
}
|
||||
|
||||
// Add multiple of item if recipe requests it
|
||||
@ -846,10 +844,9 @@ export class HideoutController
|
||||
{
|
||||
for (const reward of itemAndChildrenToSendToPlayer)
|
||||
{
|
||||
|
||||
if (!reward[0].upd)
|
||||
{
|
||||
reward[0].upd = {}
|
||||
reward[0].upd = {};
|
||||
}
|
||||
|
||||
reward[0].upd.RecodableComponent = { IsEncoded: true };
|
||||
@ -916,7 +913,7 @@ export class HideoutController
|
||||
itemsWithModsToAdd: itemAndChildrenToSendToPlayer,
|
||||
foundInRaid: true,
|
||||
useSortingTable: false,
|
||||
callback: null
|
||||
callback: null,
|
||||
};
|
||||
|
||||
// Add FiR crafted items(s) to player inventory
|
||||
@ -946,13 +943,8 @@ export class HideoutController
|
||||
const intellectAmountToGive = 0.5 * (Math.round(craftingExpAmount / 15));
|
||||
if (intellectAmountToGive > 0)
|
||||
{
|
||||
this.profileHelper.addSkillPointsToPlayer(
|
||||
pmcData,
|
||||
SkillTypes.INTELLECT,
|
||||
intellectAmountToGive,
|
||||
);
|
||||
this.profileHelper.addSkillPointsToPlayer(pmcData, SkillTypes.INTELLECT, intellectAmountToGive);
|
||||
}
|
||||
|
||||
}
|
||||
area.lastRecipe = request.recipeId;
|
||||
|
||||
@ -1042,7 +1034,7 @@ export class HideoutController
|
||||
itemsWithModsToAdd: scavCaseRewards,
|
||||
foundInRaid: true,
|
||||
callback: null,
|
||||
useSortingTable: false
|
||||
useSortingTable: false,
|
||||
};
|
||||
|
||||
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);
|
||||
|
@ -348,7 +348,7 @@ export class InraidController
|
||||
{
|
||||
for (const quest of scavProfile.Quests)
|
||||
{
|
||||
const pmcQuest = pmcProfile.Quests.find(x => x.qid === quest.qid);
|
||||
const pmcQuest = pmcProfile.Quests.find((x) => x.qid === quest.qid);
|
||||
if (!pmcQuest)
|
||||
{
|
||||
this.logger.warning(`No PMC quest found for ID: ${quest.qid}`);
|
||||
@ -546,8 +546,8 @@ export class InraidController
|
||||
|
||||
// Remove any items that were returned by the item delivery, but also insured, from the player's insurance list
|
||||
// This is to stop items being duplicated by being returned from both the item delivery, and insurance
|
||||
const deliveredItemIds = items.map(x => x._id);
|
||||
pmcData.InsuredItems = pmcData.InsuredItems.filter(x => !deliveredItemIds.includes(x.itemId));
|
||||
const deliveredItemIds = items.map((x) => x._id);
|
||||
pmcData.InsuredItems = pmcData.InsuredItems.filter((x) => !deliveredItemIds.includes(x.itemId));
|
||||
|
||||
// Send the items to the player
|
||||
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
||||
|
@ -121,8 +121,7 @@ export class InventoryController
|
||||
}
|
||||
|
||||
// Item is moving into or out of place of fame dogtag slot
|
||||
if (moveRequest.to.container.startsWith("dogtag")
|
||||
|| originalLocationSlotId.startsWith("dogtag"))
|
||||
if (moveRequest.to.container.startsWith("dogtag") || originalLocationSlotId.startsWith("dogtag"))
|
||||
{
|
||||
this.hideoutHelper.applyPlaceOfFameDogtagBonus(pmcData);
|
||||
}
|
||||
@ -177,11 +176,7 @@ export class InventoryController
|
||||
|
||||
if (body.fromOwner?.type === "Mail")
|
||||
{
|
||||
this.inventoryHelper.removeItemAndChildrenFromMailRewards(
|
||||
sessionID,
|
||||
body,
|
||||
output,
|
||||
);
|
||||
this.inventoryHelper.removeItemAndChildrenFromMailRewards(sessionID, body, output);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -190,12 +185,7 @@ export class InventoryController
|
||||
? pmcData
|
||||
: this.profileHelper.getFullProfile(sessionID).characters.scav;
|
||||
|
||||
return this.inventoryHelper.removeItem(
|
||||
profileToRemoveItemFrom,
|
||||
body.item,
|
||||
sessionID,
|
||||
output,
|
||||
);
|
||||
return this.inventoryHelper.removeItem(profileToRemoveItemFrom, body.item, sessionID, output);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -527,9 +517,7 @@ export class InventoryController
|
||||
return this.eventOutputHolder.getOutput(sessionID);
|
||||
}
|
||||
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("inventory-unable_to_toggle_item_not_found", body.item),
|
||||
);
|
||||
this.logger.warning(this.localisationService.getText("inventory-unable_to_toggle_item_not_found", body.item));
|
||||
|
||||
return { warnings: [], profileChanges: {} };
|
||||
}
|
||||
@ -685,7 +673,7 @@ export class InventoryController
|
||||
const item = this.databaseServer.getTables().templates.items[itemTpl];
|
||||
if (!item)
|
||||
{
|
||||
this.logger.warning(`Unable to find item with id ${itemTpl}, skipping inspection`)
|
||||
this.logger.warning(`Unable to find item with id ${itemTpl}, skipping inspection`);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -932,7 +920,7 @@ export class InventoryController
|
||||
|
||||
if (containerSettings.foundInRaid)
|
||||
{
|
||||
foundInRaid = containerSettings.foundInRaid
|
||||
foundInRaid = containerSettings.foundInRaid;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -950,8 +938,8 @@ export class InventoryController
|
||||
itemsWithModsToAdd: rewards,
|
||||
foundInRaid: foundInRaid,
|
||||
callback: null,
|
||||
useSortingTable: true
|
||||
}
|
||||
useSortingTable: true,
|
||||
};
|
||||
this.inventoryHelper.addItemsToStash(sessionID, addItemsRequest, pmcData, output);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
@ -964,7 +952,11 @@ export class InventoryController
|
||||
return output;
|
||||
}
|
||||
|
||||
public redeemProfileReward(pmcData: IPmcData, request: IRedeemProfileRequestData, sessionId: string): IItemEventRouterResponse
|
||||
public redeemProfileReward(
|
||||
pmcData: IPmcData,
|
||||
request: IRedeemProfileRequestData,
|
||||
sessionId: string,
|
||||
): IItemEventRouterResponse
|
||||
{
|
||||
const output = this.eventOutputHolder.getOutput(sessionId);
|
||||
|
||||
@ -974,8 +966,8 @@ export class InventoryController
|
||||
// Hard coded to `SYSTEM` for now
|
||||
// TODO: make this dynamic
|
||||
const dialog = fullprofile.dialogues["59e7125688a45068a6249071"];
|
||||
const mail = dialog.messages.find(x => x._id === event.MessageId);
|
||||
const mailEvent = mail.profileChangeEvents.find(x => x._id === event.EventId);
|
||||
const mail = dialog.messages.find((x) => x._id === event.MessageId);
|
||||
const mailEvent = mail.profileChangeEvents.find((x) => x._id === event.EventId);
|
||||
|
||||
switch (mailEvent.Type)
|
||||
{
|
||||
@ -994,15 +986,15 @@ export class InventoryController
|
||||
break;
|
||||
case "SkillPoints":
|
||||
{
|
||||
const profileSkill = pmcData.Skills.Common.find(x => x.Id === mailEvent.entity);
|
||||
const profileSkill = pmcData.Skills.Common.find((x) => x.Id === mailEvent.entity);
|
||||
profileSkill.Progress = mailEvent.value;
|
||||
this.logger.success(`Set profile skill: ${mailEvent.entity} to: ${mailEvent.value}`);
|
||||
break;
|
||||
}
|
||||
case "ExamineAllItems":
|
||||
{
|
||||
const itemsToInspect = this.itemHelper.getItems().filter(x => x._type !== "Node");
|
||||
this.flagItemsAsInspectedAndRewardXp(itemsToInspect.map(x => x._id), pmcData);
|
||||
const itemsToInspect = this.itemHelper.getItems().filter((x) => x._type !== "Node");
|
||||
this.flagItemsAsInspectedAndRewardXp(itemsToInspect.map((x) => x._id), pmcData);
|
||||
this.logger.success(`Flagged ${itemsToInspect.length} items as examined`);
|
||||
break;
|
||||
}
|
||||
@ -1031,7 +1023,7 @@ export class InventoryController
|
||||
for (const itemId of request.items)
|
||||
{
|
||||
// If id already exists in array, we're removing it
|
||||
const indexOfItemAlreadyFavorited = pmcData.Inventory.favoriteItems.findIndex(x => x === itemId);
|
||||
const indexOfItemAlreadyFavorited = pmcData.Inventory.favoriteItems.findIndex((x) => x === itemId);
|
||||
if (indexOfItemAlreadyFavorited > -1)
|
||||
{
|
||||
pmcData.Inventory.favoriteItems.splice(indexOfItemAlreadyFavorited, 1);
|
||||
|
@ -142,8 +142,8 @@ export class LauncherController
|
||||
|
||||
protected formatID(timeStamp: number, counter: number): string
|
||||
{
|
||||
const timeStampStr = timeStamp.toString(16).padStart(8, '0');
|
||||
const counterStr = counter.toString(16).padStart(16, '0');
|
||||
const timeStampStr = timeStamp.toString(16).padStart(8, "0");
|
||||
const counterStr = counter.toString(16).padStart(16, "0");
|
||||
|
||||
return timeStampStr.toLowerCase() + counterStr.toLowerCase();
|
||||
}
|
||||
|
@ -91,7 +91,9 @@ export class LocationController
|
||||
|
||||
// Check for a loot multipler adjustment in app context and apply if one is found
|
||||
let locationConfigCopy: ILocationConfig;
|
||||
const raidAdjustments = this.applicationContext.getLatestValue(ContextVariableType.RAID_ADJUSTMENTS)?.getValue<IRaidChanges>();
|
||||
const raidAdjustments = this.applicationContext.getLatestValue(ContextVariableType.RAID_ADJUSTMENTS)?.getValue<
|
||||
IRaidChanges
|
||||
>();
|
||||
if (raidAdjustments)
|
||||
{
|
||||
locationConfigCopy = this.jsonUtil.clone(this.locationConfig); // Clone values so they can be used to reset originals later
|
||||
|
@ -185,7 +185,7 @@ export class ProfileController
|
||||
inraid: {} as Inraid,
|
||||
insurance: [],
|
||||
traderPurchases: {},
|
||||
achievements: {}
|
||||
achievements: {},
|
||||
};
|
||||
|
||||
this.profileFixerService.checkForAndFixPmcProfileIssues(profileDetails.characters.pmc);
|
||||
@ -399,20 +399,14 @@ export class ProfileController
|
||||
const account = this.saveServer.getProfile(sessionId).info;
|
||||
const response: GetProfileStatusResponseData = {
|
||||
maxPveCountExceeded: false,
|
||||
profiles: [{
|
||||
profileid: account.scavId,
|
||||
profileToken: null,
|
||||
status: "Free",
|
||||
sid: "",
|
||||
ip: "",
|
||||
port: 0,
|
||||
}, {
|
||||
profiles: [{ profileid: account.scavId, profileToken: null, status: "Free", sid: "", ip: "", port: 0 }, {
|
||||
profileid: account.id,
|
||||
profileToken: null,
|
||||
status: "Free",
|
||||
sid: "",
|
||||
ip: "",
|
||||
port: 0 }],
|
||||
port: 0,
|
||||
}],
|
||||
};
|
||||
|
||||
return response;
|
||||
@ -434,34 +428,34 @@ export class ProfileController
|
||||
memberCategory: playerPmc.Info.MemberCategory,
|
||||
bannedState: playerPmc.Info.BannedState,
|
||||
bannedUntil: playerPmc.Info.BannedUntil,
|
||||
registrationDate: playerPmc.Info.RegistrationDate
|
||||
registrationDate: playerPmc.Info.RegistrationDate,
|
||||
},
|
||||
customization: {
|
||||
head: playerPmc.Customization.Head,
|
||||
body: playerPmc.Customization.Body,
|
||||
feet: playerPmc.Customization.Feet,
|
||||
hands: playerPmc.Customization.Hands
|
||||
hands: playerPmc.Customization.Hands,
|
||||
},
|
||||
skills: playerPmc.Skills,
|
||||
equipment: {
|
||||
// Default inventory tpl
|
||||
Id: playerPmc.Inventory.items.find(x => x._tpl === "55d7217a4bdc2d86028b456d")._id,
|
||||
Items: playerPmc.Inventory.items
|
||||
Id: playerPmc.Inventory.items.find((x) => x._tpl === "55d7217a4bdc2d86028b456d")._id,
|
||||
Items: playerPmc.Inventory.items,
|
||||
},
|
||||
achievements: playerPmc.Achievements,
|
||||
favoriteItems: playerPmc.Inventory.favoriteItems ?? [],
|
||||
pmcStats: {
|
||||
eft: {
|
||||
totalInGameTime: playerPmc.Stats.Eft.TotalInGameTime,
|
||||
overAllCounters: playerPmc.Stats.Eft.OverallCounters
|
||||
}
|
||||
overAllCounters: playerPmc.Stats.Eft.OverallCounters,
|
||||
},
|
||||
},
|
||||
scavStats: {
|
||||
eft: {
|
||||
totalInGameTime: player.characters.scav.Stats.Eft.TotalInGameTime,
|
||||
overAllCounters: player.characters.scav.Stats.Eft.OverallCounters
|
||||
}
|
||||
}
|
||||
overAllCounters: player.characters.scav.Stats.Eft.OverallCounters,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -108,7 +108,9 @@ export class QuestController
|
||||
const trader = profile.TradersInfo[quest.traderId];
|
||||
if (!trader)
|
||||
{
|
||||
this.logger.debug(`Unable to show quest: ${quest.QuestName} as its for a trader: ${quest.traderId} that no longer exists.`);
|
||||
this.logger.debug(
|
||||
`Unable to show quest: ${quest.QuestName} as its for a trader: ${quest.traderId} that no longer exists.`,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
@ -135,7 +137,9 @@ export class QuestController
|
||||
for (const conditionToFulfil of questRequirements)
|
||||
{
|
||||
// If the previous quest isn't in the user profile, it hasn't been completed or started
|
||||
const prerequisiteQuest = profile.Quests.find((profileQuest) => conditionToFulfil.target.includes(profileQuest.qid));
|
||||
const prerequisiteQuest = profile.Quests.find((profileQuest) =>
|
||||
conditionToFulfil.target.includes(profileQuest.qid)
|
||||
);
|
||||
if (!prerequisiteQuest)
|
||||
{
|
||||
haveCompletedPreviousQuest = false;
|
||||
@ -310,7 +314,7 @@ export class QuestController
|
||||
|
||||
// Does quest exist in profile
|
||||
// Restarting a failed quest can mean quest exists in profile
|
||||
const existingQuestStatus = pmcData.Quests.find((x) => x.qid === acceptedQuest.qid)
|
||||
const existingQuestStatus = pmcData.Quests.find((x) => x.qid === acceptedQuest.qid);
|
||||
if (existingQuestStatus)
|
||||
{
|
||||
// Update existing
|
||||
@ -356,8 +360,9 @@ export class QuestController
|
||||
);
|
||||
|
||||
// Having accepted new quest, look for newly unlocked quests and inform client of them
|
||||
acceptQuestResponse.profileChanges[sessionID].quests.push(...this.questHelper
|
||||
.getNewlyAccessibleQuestsWhenStartingQuest(acceptedQuest.qid, sessionID));
|
||||
acceptQuestResponse.profileChanges[sessionID].quests.push(
|
||||
...this.questHelper.getNewlyAccessibleQuestsWhenStartingQuest(acceptedQuest.qid, sessionID),
|
||||
);
|
||||
|
||||
return acceptQuestResponse;
|
||||
}
|
||||
@ -380,7 +385,11 @@ export class QuestController
|
||||
const acceptQuestResponse = this.eventOutputHolder.getOutput(sessionID);
|
||||
|
||||
// Create and store quest status object inside player profile
|
||||
const newRepeatableQuest = this.questHelper.getQuestReadyForProfile(pmcData, QuestStatus.Started, acceptedQuest);
|
||||
const newRepeatableQuest = this.questHelper.getQuestReadyForProfile(
|
||||
pmcData,
|
||||
QuestStatus.Started,
|
||||
acceptedQuest,
|
||||
);
|
||||
pmcData.Quests.push(newRepeatableQuest);
|
||||
|
||||
// Look for the generated quest cache in profile.RepeatableQuests
|
||||
@ -431,7 +440,7 @@ export class QuestController
|
||||
|
||||
if (!acceptQuestResponse.profileChanges[sessionID].repeatableQuests)
|
||||
{
|
||||
acceptQuestResponse.profileChanges[sessionID].repeatableQuests = []
|
||||
acceptQuestResponse.profileChanges[sessionID].repeatableQuests = [];
|
||||
}
|
||||
acceptQuestResponse.profileChanges[sessionID].repeatableQuests.push(responseData);
|
||||
|
||||
@ -510,7 +519,9 @@ export class QuestController
|
||||
// Check if it's a repeatable quest. If so, remove from Quests
|
||||
for (const currentRepeatable of pmcData.RepeatableQuests)
|
||||
{
|
||||
const repeatableQuest = currentRepeatable.activeQuests.find((activeRepeatable) => activeRepeatable._id === completedQuestId);
|
||||
const repeatableQuest = currentRepeatable.activeQuests.find((activeRepeatable) =>
|
||||
activeRepeatable._id === completedQuestId
|
||||
);
|
||||
if (repeatableQuest)
|
||||
{
|
||||
// Need to remove redundant scav quest object as its no longer necessary, is tracked in pmc profile
|
||||
@ -633,8 +644,7 @@ export class QuestController
|
||||
if (nextQuestWaitCondition)
|
||||
{
|
||||
// Now + wait time
|
||||
const availableAfterTimestamp = this.timeUtil.getTimestamp()
|
||||
+ nextQuestWaitCondition.availableAfter;
|
||||
const availableAfterTimestamp = this.timeUtil.getTimestamp() + nextQuestWaitCondition.availableAfter;
|
||||
|
||||
// Update quest in profile with status of AvailableAfter
|
||||
const existingQuestInProfile = pmcData.Quests.find((x) => x.qid === quest._id);
|
||||
@ -912,7 +922,8 @@ export class QuestController
|
||||
id: conditionId,
|
||||
sourceId: questId,
|
||||
type: "HandoverItem",
|
||||
value: counterValue };
|
||||
value: counterValue,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -158,7 +158,7 @@ export class RagfairController
|
||||
public getOfferById(sessionId: string, request: IGetRagfairOfferByIdRequest): IRagfairOffer
|
||||
{
|
||||
const offers = this.ragfairOfferService.getOffers();
|
||||
const offerToReturn = offers.find(x => x.intId === request.id);
|
||||
const offerToReturn = offers.find((x) => x.intId === request.id);
|
||||
|
||||
return offerToReturn;
|
||||
}
|
||||
@ -199,10 +199,15 @@ export class RagfairController
|
||||
* @param offers ragfair offers to get categories for
|
||||
* @returns record with templates + counts
|
||||
*/
|
||||
protected getSpecificCategories(pmcProfile: IPmcData, searchRequest: ISearchRequestData, offers: IRagfairOffer[]): Record<string, number>
|
||||
protected getSpecificCategories(
|
||||
pmcProfile: IPmcData,
|
||||
searchRequest: ISearchRequestData,
|
||||
offers: IRagfairOffer[],
|
||||
): Record<string, number>
|
||||
{
|
||||
// Linked/required search categories
|
||||
const playerHasFleaUnlocked = pmcProfile.Info.Level >= this.databaseServer.getTables().globals.config.RagFair.minUserLevel;
|
||||
const playerHasFleaUnlocked =
|
||||
pmcProfile.Info.Level >= this.databaseServer.getTables().globals.config.RagFair.minUserLevel;
|
||||
let offerPool = [];
|
||||
if (this.isLinkedSearch(searchRequest) || this.isRequiredSearch(searchRequest))
|
||||
{
|
||||
@ -698,7 +703,9 @@ export class RagfairController
|
||||
if (playerOfferIndex === -1)
|
||||
{
|
||||
this.logger.error(
|
||||
this.localisationService.getText("ragfair-offer_not_found_in_profile", { offerId: removeRequest.offerId }),
|
||||
this.localisationService.getText("ragfair-offer_not_found_in_profile", {
|
||||
offerId: removeRequest.offerId,
|
||||
}),
|
||||
);
|
||||
return this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
@ -735,7 +742,9 @@ export class RagfairController
|
||||
if (playerOfferIndex === -1)
|
||||
{
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("ragfair-offer_not_found_in_profile", { offerId: extendRequest.offerId }),
|
||||
this.localisationService.getText("ragfair-offer_not_found_in_profile", {
|
||||
offerId: extendRequest.offerId,
|
||||
}),
|
||||
);
|
||||
return this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
|
@ -120,7 +120,7 @@ export class RepeatableQuestController
|
||||
for (const activeQuest of currentRepeatableQuestType.activeQuests)
|
||||
{
|
||||
// Keep finished quests in list so player can hand in
|
||||
const quest = pmcData.Quests.find(quest => quest.qid === activeQuest._id);
|
||||
const quest = pmcData.Quests.find((quest) => quest.qid === activeQuest._id);
|
||||
if (quest)
|
||||
{
|
||||
if (quest.status === QuestStatus.AvailableForFinish)
|
||||
@ -136,7 +136,7 @@ export class RepeatableQuestController
|
||||
this.profileFixerService.removeDanglingConditionCounters(pmcData);
|
||||
|
||||
// Remove expired quest from pmc.quest array
|
||||
pmcData.Quests = pmcData.Quests.filter(quest => quest.qid !== activeQuest._id);
|
||||
pmcData.Quests = pmcData.Quests.filter((quest) => quest.qid !== activeQuest._id);
|
||||
currentRepeatableQuestType.inactiveQuests.push(activeQuest);
|
||||
}
|
||||
currentRepeatableQuestType.activeQuests = questsToKeep;
|
||||
|
@ -11,7 +11,10 @@ import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEve
|
||||
import { IRagfairOffer } from "@spt-aki/models/eft/ragfair/IRagfairOffer";
|
||||
import { IProcessBaseTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBaseTradeRequestData";
|
||||
import { IProcessBuyTradeRequestData } from "@spt-aki/models/eft/trade/IProcessBuyTradeRequestData";
|
||||
import { IOfferRequest, IProcessRagfairTradeRequestData } from "@spt-aki/models/eft/trade/IProcessRagfairTradeRequestData";
|
||||
import {
|
||||
IOfferRequest,
|
||||
IProcessRagfairTradeRequestData,
|
||||
} from "@spt-aki/models/eft/trade/IProcessRagfairTradeRequestData";
|
||||
import { IProcessSellTradeRequestData } from "@spt-aki/models/eft/trade/IProcessSellTradeRequestData";
|
||||
import { ISellScavItemsToFenceRequestData } from "@spt-aki/models/eft/trade/ISellScavItemsToFenceRequestData";
|
||||
import { BackendErrorCodes } from "@spt-aki/models/enums/BackendErrorCodes";
|
||||
@ -146,14 +149,20 @@ export class TradeController
|
||||
* @param requestOffer request data from client
|
||||
* @param output Output to send back to client
|
||||
*/
|
||||
protected buyTraderItemFromRagfair(sessionId: string, pmcData: IPmcData, fleaOffer: IRagfairOffer, requestOffer: IOfferRequest, output: IItemEventRouterResponse): void
|
||||
protected buyTraderItemFromRagfair(
|
||||
sessionId: string,
|
||||
pmcData: IPmcData,
|
||||
fleaOffer: IRagfairOffer,
|
||||
requestOffer: IOfferRequest,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
// Skip buying items when player doesn't have needed loyalty
|
||||
if (this.playerLacksTraderLoyaltyLevelToBuyOffer(fleaOffer, pmcData))
|
||||
{
|
||||
const errorMessage = `Unable to buy item: ${
|
||||
fleaOffer.items[0]._tpl
|
||||
} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`
|
||||
} from trader: ${fleaOffer.user.id} as loyalty level too low, skipping`;
|
||||
this.logger.debug(errorMessage);
|
||||
|
||||
this.httpResponse.appendErrorToOutput(output, errorMessage, BackendErrorCodes.RAGFAIRUNAVAILABLE);
|
||||
@ -182,7 +191,13 @@ export class TradeController
|
||||
* @param requestOffer Request data from client
|
||||
* @param output Output to send back to client
|
||||
*/
|
||||
protected buyPmcItemFromRagfair(sessionId: string, pmcData: IPmcData, fleaOffer: IRagfairOffer, requestOffer: IOfferRequest, output: IItemEventRouterResponse): void
|
||||
protected buyPmcItemFromRagfair(
|
||||
sessionId: string,
|
||||
pmcData: IPmcData,
|
||||
fleaOffer: IRagfairOffer,
|
||||
requestOffer: IOfferRequest,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
const buyData: IProcessBuyTradeRequestData = {
|
||||
Action: "TradingConfirm",
|
||||
@ -198,7 +213,13 @@ export class TradeController
|
||||
};
|
||||
|
||||
// buyItem() must occur prior to removing the offer stack, otherwise item inside offer doesn't exist for confirmTrading() to use
|
||||
this.tradeHelper.buyItem(pmcData, buyData, sessionId, this.ragfairConfig.dynamic.purchasesAreFoundInRaid, output);
|
||||
this.tradeHelper.buyItem(
|
||||
pmcData,
|
||||
buyData,
|
||||
sessionId,
|
||||
this.ragfairConfig.dynamic.purchasesAreFoundInRaid,
|
||||
output,
|
||||
);
|
||||
if (output.warnings.length > 0)
|
||||
{
|
||||
return;
|
||||
@ -231,10 +252,7 @@ export class TradeController
|
||||
const scavProfile = this.profileHelper.getFullProfile(sessionId)?.characters?.scav;
|
||||
if (!scavProfile)
|
||||
{
|
||||
return this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
`Profile ${request.fromOwner.id} has no scav account`,
|
||||
);
|
||||
return this.httpResponse.appendErrorToOutput(output, `Profile ${request.fromOwner.id} has no scav account`);
|
||||
}
|
||||
|
||||
this.sellInventoryToTrader(sessionId, scavProfile, pmcData, Traders.FENCE, output);
|
||||
@ -256,7 +274,7 @@ export class TradeController
|
||||
profileWithItemsToSell: IPmcData,
|
||||
profileThatGetsMoney: IPmcData,
|
||||
trader: Traders,
|
||||
output: IItemEventRouterResponse
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
const handbookPrices = this.ragfairPriceService.getAllStaticPrices();
|
||||
|
@ -29,7 +29,8 @@ export class WeatherController
|
||||
time: "",
|
||||
date: "",
|
||||
weather: null,
|
||||
winterEventEnabled: this.weatherConfig.forceWinterEvent };
|
||||
winterEventEnabled: this.weatherConfig.forceWinterEvent,
|
||||
};
|
||||
|
||||
result = this.weatherGenerator.calculateGameTime(result);
|
||||
result.weather = this.weatherGenerator.generateWeather();
|
||||
|
@ -485,7 +485,9 @@ export class Container
|
||||
depContainer.register<RagfairStaticRouter>("RagfairStaticRouter", { useClass: RagfairStaticRouter });
|
||||
depContainer.register<TraderStaticRouter>("TraderStaticRouter", { useClass: TraderStaticRouter });
|
||||
depContainer.register<WeatherStaticRouter>("WeatherStaticRouter", { useClass: WeatherStaticRouter });
|
||||
depContainer.register<AchievementStaticRouter>("AchievementStaticRouter", { useClass: AchievementStaticRouter });
|
||||
depContainer.register<AchievementStaticRouter>("AchievementStaticRouter", {
|
||||
useClass: AchievementStaticRouter,
|
||||
});
|
||||
depContainer.register<BuildsStaticRouter>("BuildsStaticRouter", { useClass: BuildsStaticRouter });
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,9 @@ export class BotEquipmentModGenerator
|
||||
const compatibleModsPool = settings.modPool[parentTemplate._id];
|
||||
if (!compatibleModsPool)
|
||||
{
|
||||
this.logger.warning(`bot: ${settings.botRole} lacks a mod slot pool for item: ${parentTemplate._id} ${parentTemplate._name}`);
|
||||
this.logger.warning(
|
||||
`bot: ${settings.botRole} lacks a mod slot pool for item: ${parentTemplate._id} ${parentTemplate._name}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Iterate over mod pool and choose mods to add to item
|
||||
@ -94,12 +96,16 @@ export class BotEquipmentModGenerator
|
||||
modSlot: modSlotName,
|
||||
parentId: parentTemplate._id,
|
||||
parentName: parentTemplate._name,
|
||||
botRole: settings.botRole
|
||||
botRole: settings.botRole,
|
||||
}),
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const modSpawnResult = this.shouldModBeSpawned(itemSlotTemplate, modSlotName.toLowerCase(), settings.spawnChances.equipmentMods);
|
||||
const modSpawnResult = this.shouldModBeSpawned(
|
||||
itemSlotTemplate,
|
||||
modSlotName.toLowerCase(),
|
||||
settings.spawnChances.equipmentMods,
|
||||
);
|
||||
if (modSpawnResult === ModSpawn.SKIP && !forceSpawn)
|
||||
{
|
||||
continue;
|
||||
@ -112,19 +118,33 @@ export class BotEquipmentModGenerator
|
||||
}
|
||||
|
||||
let modPoolToChooseFrom = compatibleModsPool[modSlotName];
|
||||
if (settings.botEquipmentConfig.filterPlatesByLevel && this.itemHelper.isRemovablePlateSlot(modSlotName.toLowerCase()))
|
||||
if (
|
||||
settings.botEquipmentConfig.filterPlatesByLevel
|
||||
&& this.itemHelper.isRemovablePlateSlot(modSlotName.toLowerCase())
|
||||
)
|
||||
{
|
||||
const outcome = this.filterPlateModsForSlotByLevel(settings, modSlotName.toLowerCase(), compatibleModsPool[modSlotName], parentTemplate);
|
||||
const outcome = this.filterPlateModsForSlotByLevel(
|
||||
settings,
|
||||
modSlotName.toLowerCase(),
|
||||
compatibleModsPool[modSlotName],
|
||||
parentTemplate,
|
||||
);
|
||||
if ([Result.UNKNOWN_FAILURE, Result.NO_DEFAULT_FILTER].includes(outcome.result))
|
||||
{
|
||||
this.logger.debug(`Plate slot: ${modSlotName} selection for armor: ${parentTemplate._id} failed: ${Result[outcome.result]}, skipping`);
|
||||
this.logger.debug(
|
||||
`Plate slot: ${modSlotName} selection for armor: ${parentTemplate._id} failed: ${
|
||||
Result[outcome.result]
|
||||
}, skipping`,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([Result.LACKS_PLATE_WEIGHTS].includes(outcome.result))
|
||||
{
|
||||
this.logger.warning(`Plate slot: ${modSlotName} lacks weights for armor: ${parentTemplate._id}, unable to adjust plate choice, using existing data`);
|
||||
this.logger.warning(
|
||||
`Plate slot: ${modSlotName} lacks weights for armor: ${parentTemplate._id}, unable to adjust plate choice, using existing data`,
|
||||
);
|
||||
}
|
||||
|
||||
modPoolToChooseFrom = outcome.plateModTpls;
|
||||
@ -133,16 +153,13 @@ export class BotEquipmentModGenerator
|
||||
// Find random mod and check its compatible
|
||||
let modTpl: string;
|
||||
let found = false;
|
||||
const exhaustableModPool = new ExhaustableArray(
|
||||
modPoolToChooseFrom,
|
||||
this.randomUtil,
|
||||
this.jsonUtil,
|
||||
);
|
||||
const exhaustableModPool = new ExhaustableArray(modPoolToChooseFrom, this.randomUtil, this.jsonUtil);
|
||||
while (exhaustableModPool.hasValues())
|
||||
{
|
||||
modTpl = exhaustableModPool.getRandomValue();
|
||||
if (
|
||||
!this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(equipment, modTpl, modSlotName).incompatible
|
||||
!this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(equipment, modTpl, modSlotName)
|
||||
.incompatible
|
||||
)
|
||||
{
|
||||
found = true;
|
||||
@ -178,13 +195,7 @@ export class BotEquipmentModGenerator
|
||||
if (Object.keys(settings.modPool).includes(modTpl))
|
||||
{
|
||||
// Call self recursively with item being checkced item we just added to bot
|
||||
this.generateModsForEquipment(
|
||||
equipment,
|
||||
modId,
|
||||
modTemplate[1],
|
||||
settings,
|
||||
forceSpawn,
|
||||
);
|
||||
this.generateModsForEquipment(equipment, modId, modTemplate[1], settings, forceSpawn);
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,12 +210,14 @@ export class BotEquipmentModGenerator
|
||||
* @param armorItem
|
||||
* @returns Array of plate tpls to choose from
|
||||
*/
|
||||
protected filterPlateModsForSlotByLevel(settings: IGenerateEquipmentProperties, modSlot: string, existingPlateTplPool: string[], armorItem: ITemplateItem): IFilterPlateModsForSlotByLevelResult
|
||||
protected filterPlateModsForSlotByLevel(
|
||||
settings: IGenerateEquipmentProperties,
|
||||
modSlot: string,
|
||||
existingPlateTplPool: string[],
|
||||
armorItem: ITemplateItem,
|
||||
): IFilterPlateModsForSlotByLevelResult
|
||||
{
|
||||
const result: IFilterPlateModsForSlotByLevelResult = {
|
||||
result: Result.UNKNOWN_FAILURE,
|
||||
plateModTpls: null
|
||||
};
|
||||
const result: IFilterPlateModsForSlotByLevelResult = { result: Result.UNKNOWN_FAILURE, plateModTpls: null };
|
||||
|
||||
// Not pmc or not a plate slot, return original mod pool array
|
||||
if (!this.itemHelper.isRemovablePlateSlot(modSlot))
|
||||
@ -216,10 +229,9 @@ export class BotEquipmentModGenerator
|
||||
}
|
||||
|
||||
// Get the front/back/side weights based on bots level
|
||||
const plateSlotWeights = settings
|
||||
.botEquipmentConfig
|
||||
?.armorPlateWeighting
|
||||
?.find(x => settings.botLevel >= x.levelRange.min && settings.botLevel <= x.levelRange.max);
|
||||
const plateSlotWeights = settings.botEquipmentConfig?.armorPlateWeighting?.find((x) =>
|
||||
settings.botLevel >= x.levelRange.min && settings.botLevel <= x.levelRange.max
|
||||
);
|
||||
if (!plateSlotWeights)
|
||||
{
|
||||
// No weights, return original array of plate tpls
|
||||
@ -244,16 +256,18 @@ export class BotEquipmentModGenerator
|
||||
const chosenArmorPlateLevel = this.weightedRandomHelper.getWeightedValue<string>(plateWeights);
|
||||
|
||||
// Convert the array of ids into database items
|
||||
const platesFromDb = existingPlateTplPool.map(x => this.itemHelper.getItem(x)[1]);
|
||||
const platesFromDb = existingPlateTplPool.map((x) => this.itemHelper.getItem(x)[1]);
|
||||
|
||||
// Filter plates to the chosen level based on its armorClass property
|
||||
const filteredPlates = platesFromDb.filter(x => x._props.armorClass === chosenArmorPlateLevel);
|
||||
const filteredPlates = platesFromDb.filter((x) => x._props.armorClass === chosenArmorPlateLevel);
|
||||
if (filteredPlates.length === 0)
|
||||
{
|
||||
this.logger.debug(`Plate filter was too restrictive for armor: ${armorItem._id}, unable to find plates of level: ${chosenArmorPlateLevel}. Using mod items default plate`);
|
||||
this.logger.debug(
|
||||
`Plate filter was too restrictive for armor: ${armorItem._id}, unable to find plates of level: ${chosenArmorPlateLevel}. Using mod items default plate`,
|
||||
);
|
||||
|
||||
const relatedItemDbModSlot = armorItem._props.Slots.find(slot => slot._name.toLowerCase() === modSlot);
|
||||
const defaultPlate = relatedItemDbModSlot._props.filters[0].Plate
|
||||
const relatedItemDbModSlot = armorItem._props.Slots.find((slot) => slot._name.toLowerCase() === modSlot);
|
||||
const defaultPlate = relatedItemDbModSlot._props.filters[0].Plate;
|
||||
if (!defaultPlate)
|
||||
{
|
||||
// No relevant plate found after filtering AND no default plate
|
||||
@ -262,7 +276,9 @@ export class BotEquipmentModGenerator
|
||||
const defaultPreset = this.presetHelper.getDefaultPreset(armorItem._id);
|
||||
if (defaultPreset)
|
||||
{
|
||||
const relatedPresetSlot = defaultPreset._items.find(item => item.slotId?.toLowerCase() === modSlot);
|
||||
const relatedPresetSlot = defaultPreset._items.find((item) =>
|
||||
item.slotId?.toLowerCase() === modSlot
|
||||
);
|
||||
if (relatedPresetSlot)
|
||||
{
|
||||
result.result = Result.SUCCESS;
|
||||
@ -285,7 +301,7 @@ export class BotEquipmentModGenerator
|
||||
|
||||
// Only return the items ids
|
||||
result.result = Result.SUCCESS;
|
||||
result.plateModTpls = filteredPlates.map(x => x._id);
|
||||
result.plateModTpls = filteredPlates.map((x) => x._id);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -333,7 +349,7 @@ export class BotEquipmentModGenerator
|
||||
this.localisationService.getText("bot-unable_to_add_mods_to_weapon_missing_ammo_slot", {
|
||||
weaponName: parentTemplate._name,
|
||||
weaponId: parentTemplate._id,
|
||||
botRole: botRole
|
||||
botRole: botRole,
|
||||
}),
|
||||
);
|
||||
|
||||
@ -385,7 +401,7 @@ export class BotEquipmentModGenerator
|
||||
weapon,
|
||||
ammoTpl,
|
||||
parentTemplate,
|
||||
modSpawnResult
|
||||
modSpawnResult,
|
||||
);
|
||||
|
||||
// Compatible mod not found
|
||||
@ -469,9 +485,7 @@ export class BotEquipmentModGenerator
|
||||
}
|
||||
|
||||
const modId = this.hashUtil.generate();
|
||||
weapon.push(
|
||||
this.createModItem(modId, modToAddTemplate._id, weaponId, modSlot, modToAddTemplate, botRole),
|
||||
);
|
||||
weapon.push(this.createModItem(modId, modToAddTemplate._id, weaponId, modSlot, modToAddTemplate, botRole));
|
||||
|
||||
// I first thought we could use the recursive generateModsForItems as previously for cylinder magazines.
|
||||
// However, the recursion doesn't go over the slots of the parent mod but over the modPool which is given by the bot config
|
||||
@ -699,7 +713,7 @@ export class BotEquipmentModGenerator
|
||||
const slotRequired = itemSlot._required;
|
||||
if (this.getAmmoContainers().includes(modSlot))
|
||||
{
|
||||
return ModSpawn.SPAWN
|
||||
return ModSpawn.SPAWN;
|
||||
}
|
||||
const spawnMod = this.probabilityHelper.rollChance(modSpawnChances[modSlot]);
|
||||
if (!spawnMod && slotRequired)
|
||||
@ -708,9 +722,7 @@ export class BotEquipmentModGenerator
|
||||
return ModSpawn.DEFAULT_MOD;
|
||||
}
|
||||
|
||||
return spawnMod
|
||||
? ModSpawn.SPAWN
|
||||
: ModSpawn.SKIP;
|
||||
return spawnMod ? ModSpawn.SPAWN : ModSpawn.SKIP;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -733,7 +745,7 @@ export class BotEquipmentModGenerator
|
||||
weapon: Item[],
|
||||
ammoTpl: string,
|
||||
parentTemplate: ITemplateItem,
|
||||
modSpawnResult: ModSpawn
|
||||
modSpawnResult: ModSpawn,
|
||||
): [boolean, ITemplateItem]
|
||||
{
|
||||
/** Slot mod will fill */
|
||||
@ -743,17 +755,23 @@ export class BotEquipmentModGenerator
|
||||
// It's ammo, use predefined ammo parameter
|
||||
if (this.getAmmoContainers().includes(modSlot) && modSlot !== "mod_magazine")
|
||||
{
|
||||
return this.itemHelper.getItem(ammoTpl)
|
||||
return this.itemHelper.getItem(ammoTpl);
|
||||
}
|
||||
|
||||
// Ensure there's a pool of mods to pick from
|
||||
let modPool = this.getModPoolForSlot(itemModPool, modSpawnResult, parentTemplate, weaponTemplate, modSlot, botEquipBlacklist, isRandomisableSlot);
|
||||
let modPool = this.getModPoolForSlot(
|
||||
itemModPool,
|
||||
modSpawnResult,
|
||||
parentTemplate,
|
||||
weaponTemplate,
|
||||
modSlot,
|
||||
botEquipBlacklist,
|
||||
isRandomisableSlot,
|
||||
);
|
||||
if (!(modPool || parentSlot._required))
|
||||
{
|
||||
// Nothing in mod pool + item not required
|
||||
this.logger.debug(
|
||||
`Mod pool for slot: ${modSlot} on item: ${parentTemplate._name} was empty, skipping mod`,
|
||||
);
|
||||
this.logger.debug(`Mod pool for slot: ${modSlot} on item: ${parentTemplate._name} was empty, skipping mod`);
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -763,16 +781,18 @@ export class BotEquipmentModGenerator
|
||||
// scope pool has more than one scope
|
||||
if (modPool.length > 1)
|
||||
{
|
||||
modPool = this.filterSightsByWeaponType(
|
||||
weapon[0],
|
||||
modPool,
|
||||
botWeaponSightWhitelist,
|
||||
);
|
||||
modPool = this.filterSightsByWeaponType(weapon[0], modPool, botWeaponSightWhitelist);
|
||||
}
|
||||
}
|
||||
|
||||
// Pick random mod that's compatible
|
||||
const chosenModResult = this.pickWeaponModTplForSlotFromPool(modPool, parentSlot, modSpawnResult, weapon, modSlot);
|
||||
const chosenModResult = this.pickWeaponModTplForSlotFromPool(
|
||||
modPool,
|
||||
parentSlot,
|
||||
modSpawnResult,
|
||||
weapon,
|
||||
modSlot,
|
||||
);
|
||||
if (chosenModResult.slotBlocked && !parentSlot._required)
|
||||
{
|
||||
// Don't bother trying to fit mod, slot is completely blocked
|
||||
@ -821,15 +841,12 @@ export class BotEquipmentModGenerator
|
||||
parentSlot: Slot,
|
||||
modSpawnResult: ModSpawn,
|
||||
weapon: Item[],
|
||||
modSlotname: string): IChooseRandomCompatibleModResult
|
||||
modSlotname: string,
|
||||
): IChooseRandomCompatibleModResult
|
||||
{
|
||||
let chosenTpl: string;
|
||||
const exhaustableModPool = new ExhaustableArray(modPool, this.randomUtil, this.jsonUtil);
|
||||
let chosenModResult: IChooseRandomCompatibleModResult = {
|
||||
incompatible: true,
|
||||
found: false,
|
||||
reason: "unknown",
|
||||
};
|
||||
let chosenModResult: IChooseRandomCompatibleModResult = { incompatible: true, found: false, reason: "unknown" };
|
||||
const modParentFilterList = parentSlot._props.filters[0].Filter;
|
||||
|
||||
// How many times can a mod for the slot be blocked before we stop trying
|
||||
@ -913,13 +930,16 @@ export class BotEquipmentModGenerator
|
||||
weaponTemplate: ITemplateItem,
|
||||
modSlot: string,
|
||||
botEquipBlacklist: EquipmentFilterDetails,
|
||||
isRandomisableSlot: boolean): string[]
|
||||
isRandomisableSlot: boolean,
|
||||
): string[]
|
||||
{
|
||||
// Mod is flagged as being default only, try and find it in globals
|
||||
if (modSpawnResult === ModSpawn.DEFAULT_MOD)
|
||||
{
|
||||
const defaultWeaponPreset = this.presetHelper.getDefaultPreset(weaponTemplate._id)
|
||||
const matchingMod = defaultWeaponPreset._items.find(item => item?.slotId?.toLowerCase() === modSlot.toLowerCase());
|
||||
const defaultWeaponPreset = this.presetHelper.getDefaultPreset(weaponTemplate._id);
|
||||
const matchingMod = defaultWeaponPreset._items.find((item) =>
|
||||
item?.slotId?.toLowerCase() === modSlot.toLowerCase()
|
||||
);
|
||||
|
||||
// Only filter mods down to single default item if it already exists in existing itemModPool, OR the default item has no children
|
||||
// Filtering mod pool to item that wasnt already there can have problems;
|
||||
@ -1053,7 +1073,7 @@ export class BotEquipmentModGenerator
|
||||
slotAddedToTemplate: Slot,
|
||||
modSlot: string,
|
||||
parentTemplate: ITemplateItem,
|
||||
botRole: string
|
||||
botRole: string,
|
||||
): boolean
|
||||
{
|
||||
const modBeingAddedTemplate = modToAdd[1];
|
||||
@ -1083,7 +1103,7 @@ export class BotEquipmentModGenerator
|
||||
itemName: modBeingAddedTemplate._name,
|
||||
modSlot: modSlot,
|
||||
parentItemName: parentTemplate._name,
|
||||
botRole: botRole
|
||||
botRole: botRole,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
@ -110,9 +110,7 @@ export class BotGenerator
|
||||
|
||||
// Get raw json data for bot (Cloned)
|
||||
const botJsonTemplate = this.jsonUtil.clone(
|
||||
this.botHelper.getBotTemplate((botGenerationDetails.isPmc)
|
||||
? bot.Info.Side
|
||||
: botGenerationDetails.role),
|
||||
this.botHelper.getBotTemplate((botGenerationDetails.isPmc) ? bot.Info.Side : botGenerationDetails.role),
|
||||
);
|
||||
|
||||
bot = this.generateBot(sessionId, bot, botJsonTemplate, botGenerationDetails);
|
||||
@ -161,12 +159,7 @@ export class BotGenerator
|
||||
);
|
||||
}
|
||||
|
||||
bot.Info.Nickname = this.generateBotNickname(
|
||||
botJsonTemplate,
|
||||
botGenerationDetails,
|
||||
botRole,
|
||||
sessionId,
|
||||
);
|
||||
bot.Info.Nickname = this.generateBotNickname(botJsonTemplate, botGenerationDetails, botRole, sessionId);
|
||||
|
||||
if (!this.seasonalEventService.christmasEventEnabled())
|
||||
{
|
||||
|
@ -12,7 +12,12 @@ import { Chances, Generation, IBotType, Inventory, Mods } from "@spt-aki/models/
|
||||
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
||||
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
||||
import { EquipmentSlots } from "@spt-aki/models/enums/EquipmentSlots";
|
||||
import { EquipmentFilterDetails, EquipmentFilters, IBotConfig, RandomisationDetails } from "@spt-aki/models/spt/config/IBotConfig";
|
||||
import {
|
||||
EquipmentFilterDetails,
|
||||
EquipmentFilters,
|
||||
IBotConfig,
|
||||
RandomisationDetails,
|
||||
} from "@spt-aki/models/spt/config/IBotConfig";
|
||||
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
||||
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
||||
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
||||
@ -70,13 +75,7 @@ export class BotInventoryGenerator
|
||||
// Generate base inventory with no items
|
||||
const botInventory = this.generateInventoryBase();
|
||||
|
||||
this.generateAndAddEquipmentToBot(
|
||||
templateInventory,
|
||||
wornItemChances,
|
||||
botRole,
|
||||
botInventory,
|
||||
botLevel,
|
||||
);
|
||||
this.generateAndAddEquipmentToBot(templateInventory, wornItemChances, botRole, botInventory, botLevel);
|
||||
|
||||
// Roll weapon spawns (primary/secondary/holster) and generate a weapon for each roll that passed
|
||||
this.generateAndAddWeaponsToBot(
|
||||
@ -91,13 +90,7 @@ export class BotInventoryGenerator
|
||||
);
|
||||
|
||||
// Pick loot and add to bots containers (rig/backpack/pockets/secure)
|
||||
this.botLootGenerator.generateLoot(
|
||||
sessionId,
|
||||
botJsonTemplate,
|
||||
isPmc,
|
||||
botRole,
|
||||
botInventory,
|
||||
botLevel);
|
||||
this.botLootGenerator.generateLoot(sessionId, botJsonTemplate, isPmc, botRole, botInventory, botLevel);
|
||||
|
||||
return botInventory;
|
||||
}
|
||||
@ -138,7 +131,7 @@ export class BotInventoryGenerator
|
||||
sortingTable: sortingTableId,
|
||||
hideoutAreaStashes: {},
|
||||
fastPanel: {},
|
||||
favoriteItems: []
|
||||
favoriteItems: [],
|
||||
};
|
||||
}
|
||||
|
||||
@ -189,7 +182,7 @@ export class BotInventoryGenerator
|
||||
botLevel: botLevel,
|
||||
inventory: botInventory,
|
||||
botEquipmentConfig: botEquipConfig,
|
||||
randomisationDetails: randomistionDetails
|
||||
randomisationDetails: randomistionDetails,
|
||||
});
|
||||
}
|
||||
|
||||
@ -203,7 +196,7 @@ export class BotInventoryGenerator
|
||||
botLevel: botLevel,
|
||||
inventory: botInventory,
|
||||
botEquipmentConfig: botEquipConfig,
|
||||
randomisationDetails: randomistionDetails
|
||||
randomisationDetails: randomistionDetails,
|
||||
});
|
||||
this.generateEquipment({
|
||||
rootEquipmentSlot: EquipmentSlots.HEADWEAR,
|
||||
@ -214,7 +207,7 @@ export class BotInventoryGenerator
|
||||
botLevel: botLevel,
|
||||
inventory: botInventory,
|
||||
botEquipmentConfig: botEquipConfig,
|
||||
randomisationDetails: randomistionDetails
|
||||
randomisationDetails: randomistionDetails,
|
||||
});
|
||||
this.generateEquipment({
|
||||
rootEquipmentSlot: EquipmentSlots.EARPIECE,
|
||||
@ -225,7 +218,7 @@ export class BotInventoryGenerator
|
||||
botLevel: botLevel,
|
||||
inventory: botInventory,
|
||||
botEquipmentConfig: botEquipConfig,
|
||||
randomisationDetails: randomistionDetails
|
||||
randomisationDetails: randomistionDetails,
|
||||
});
|
||||
this.generateEquipment({
|
||||
rootEquipmentSlot: EquipmentSlots.ARMOR_VEST,
|
||||
@ -236,11 +229,11 @@ export class BotInventoryGenerator
|
||||
botLevel: botLevel,
|
||||
inventory: botInventory,
|
||||
botEquipmentConfig: botEquipConfig,
|
||||
randomisationDetails: randomistionDetails
|
||||
randomisationDetails: randomistionDetails,
|
||||
});
|
||||
|
||||
// Bot has no armor vest and flagged to be foreced to wear armored rig in this event
|
||||
const hasArmorVest = botInventory.items.some(item => item.slotId === "ArmorVest")
|
||||
const hasArmorVest = botInventory.items.some((item) => item.slotId === "ArmorVest");
|
||||
if (botEquipConfig.forceOnlyArmoredRigWhenNoArmor && !hasArmorVest)
|
||||
{
|
||||
// Filter rigs down to only those with armor
|
||||
@ -263,7 +256,7 @@ export class BotInventoryGenerator
|
||||
botLevel: botLevel,
|
||||
inventory: botInventory,
|
||||
botEquipmentConfig: botEquipConfig,
|
||||
randomisationDetails: randomistionDetails
|
||||
randomisationDetails: randomistionDetails,
|
||||
});
|
||||
}
|
||||
|
||||
@ -273,15 +266,17 @@ export class BotInventoryGenerator
|
||||
*/
|
||||
protected filterRigsToThoseWithProtection(templateInventory: Inventory): void
|
||||
{
|
||||
const tacVestsWithArmor = Object.entries(templateInventory.equipment.TacticalVest)
|
||||
.reduce((newVestDictionary, [tplKey]) =>
|
||||
const tacVestsWithArmor = Object.entries(templateInventory.equipment.TacticalVest).reduce(
|
||||
(newVestDictionary, [tplKey]) =>
|
||||
{
|
||||
if (this.itemHelper.getItem(tplKey)[1]._props.Slots?.length > 0)
|
||||
{
|
||||
newVestDictionary[tplKey] = templateInventory.equipment.TacticalVest[tplKey];
|
||||
}
|
||||
return newVestDictionary;
|
||||
}, {});
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
templateInventory.equipment.TacticalVest = tacVestsWithArmor;
|
||||
}
|
||||
@ -292,15 +287,17 @@ export class BotInventoryGenerator
|
||||
*/
|
||||
protected filterRigsToThoseWithoutProtection(templateInventory: Inventory): void
|
||||
{
|
||||
const tacVestsWithoutArmor = Object.entries(templateInventory.equipment.TacticalVest)
|
||||
.reduce((newVestDictionary, [tplKey]) =>
|
||||
const tacVestsWithoutArmor = Object.entries(templateInventory.equipment.TacticalVest).reduce(
|
||||
(newVestDictionary, [tplKey]) =>
|
||||
{
|
||||
if (this.itemHelper.getItem(tplKey)[1]._props.Slots?.length === 0)
|
||||
{
|
||||
newVestDictionary[tplKey] = templateInventory.equipment.TacticalVest[tplKey];
|
||||
}
|
||||
return newVestDictionary;
|
||||
}, {});
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
templateInventory.equipment.TacticalVest = tacVestsWithoutArmor;
|
||||
}
|
||||
@ -312,13 +309,18 @@ export class BotInventoryGenerator
|
||||
protected generateEquipment(settings: IGenerateEquipmentProperties): void
|
||||
{
|
||||
const spawnChance =
|
||||
([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]).includes(settings.rootEquipmentSlot)
|
||||
([EquipmentSlots.POCKETS, EquipmentSlots.SECURED_CONTAINER] as string[]).includes(
|
||||
settings.rootEquipmentSlot,
|
||||
)
|
||||
? 100
|
||||
: settings.spawnChances.equipment[settings.rootEquipmentSlot];
|
||||
if (typeof spawnChance === "undefined")
|
||||
{
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("bot-no_spawn_chance_defined_for_equipment_slot", settings.rootEquipmentSlot),
|
||||
this.localisationService.getText(
|
||||
"bot-no_spawn_chance_defined_for_equipment_slot",
|
||||
settings.rootEquipmentSlot,
|
||||
),
|
||||
);
|
||||
|
||||
return;
|
||||
@ -358,7 +360,8 @@ export class BotInventoryGenerator
|
||||
const compatabilityResult = this.botGeneratorHelper.isItemIncompatibleWithCurrentItems(
|
||||
settings.inventory.items,
|
||||
chosenItemTpl,
|
||||
settings.rootEquipmentSlot);
|
||||
settings.rootEquipmentSlot,
|
||||
);
|
||||
if (compatabilityResult.incompatible)
|
||||
{
|
||||
// Tried x different items that failed, stop
|
||||
@ -410,7 +413,7 @@ export class BotInventoryGenerator
|
||||
[item],
|
||||
id,
|
||||
pickedItemDb,
|
||||
settings
|
||||
settings,
|
||||
);
|
||||
settings.inventory.items.push(...items);
|
||||
}
|
||||
@ -560,18 +563,18 @@ export class BotInventoryGenerator
|
||||
export interface IGenerateEquipmentProperties
|
||||
{
|
||||
/** Root Slot being generated */
|
||||
rootEquipmentSlot: string,
|
||||
rootEquipmentSlot: string;
|
||||
/** Equipment pool for root slot being generated */
|
||||
rootEquipmentPool: Record<string, number>,
|
||||
modPool: Mods,
|
||||
rootEquipmentPool: Record<string, number>;
|
||||
modPool: Mods;
|
||||
/** Dictionary of mod items and their chance to spawn for this bot type */
|
||||
spawnChances: Chances,
|
||||
spawnChances: Chances;
|
||||
/** Role being generated for */
|
||||
botRole: string,
|
||||
botRole: string;
|
||||
/** Level of bot being generated */
|
||||
botLevel: number,
|
||||
inventory: PmcInventory,
|
||||
botEquipmentConfig: EquipmentFilters,
|
||||
botLevel: number;
|
||||
inventory: PmcInventory;
|
||||
botEquipmentConfig: EquipmentFilters;
|
||||
/** Settings from bot.json to adjust how item is generated */
|
||||
randomisationDetails: RandomisationDetails
|
||||
randomisationDetails: RandomisationDetails;
|
||||
}
|
||||
|
@ -43,7 +43,8 @@ export class BotLevelGenerator
|
||||
botGenerationDetails.playerLevel,
|
||||
botGenerationDetails.botRelativeLevelDeltaMin,
|
||||
levelDetails,
|
||||
expTable);
|
||||
expTable,
|
||||
);
|
||||
|
||||
// Get random level based on the exp table.
|
||||
let exp = 0;
|
||||
|
@ -341,7 +341,6 @@ export class BotLootGenerator
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.addRequiredChildItemsToParent(itemToAddTemplate, itemWithChildrenToAdd, isPmc);
|
||||
|
||||
// Attempt to add item to container(s)
|
||||
@ -359,7 +358,9 @@ export class BotLootGenerator
|
||||
if (itemAddedResult === ItemAddedResult.NO_CONTAINERS)
|
||||
{
|
||||
// Bot has no container to put item in, exit
|
||||
this.logger.debug(`Unable to add: ${totalItemCount} items to bot as it lacks a container to include them`);
|
||||
this.logger.debug(
|
||||
`Unable to add: ${totalItemCount} items to bot as it lacks a container to include them`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -367,7 +368,11 @@ export class BotLootGenerator
|
||||
if (fitItemIntoContainerAttempts >= 4)
|
||||
{
|
||||
this.logger.debug(
|
||||
`Failed to place item ${i} of ${totalItemCount} items into ${botRole} containers: ${equipmentSlots.join(",")}. Tried ${fitItemIntoContainerAttempts} times, reason: ${ItemAddedResult[itemAddedResult]}, skipping`,
|
||||
`Failed to place item ${i} of ${totalItemCount} items into ${botRole} containers: ${
|
||||
equipmentSlots.join(",")
|
||||
}. Tried ${fitItemIntoContainerAttempts} times, reason: ${
|
||||
ItemAddedResult[itemAddedResult]
|
||||
}, skipping`,
|
||||
);
|
||||
|
||||
break;
|
||||
@ -399,7 +404,11 @@ export class BotLootGenerator
|
||||
* @param itemToAddChildrenTo Item to add children to
|
||||
* @param isPmc Is the item being generated for a pmc (affects money/ammo stack sizes)
|
||||
*/
|
||||
protected addRequiredChildItemsToParent(itemToAddTemplate: ITemplateItem, itemToAddChildrenTo: Item[], isPmc: boolean): void
|
||||
protected addRequiredChildItemsToParent(
|
||||
itemToAddTemplate: ITemplateItem,
|
||||
itemToAddChildrenTo: Item[],
|
||||
isPmc: boolean,
|
||||
): void
|
||||
{
|
||||
// Fill ammo box
|
||||
if (this.itemHelper.isOfBaseclass(itemToAddTemplate._id, BaseClasses.AMMO_BOX))
|
||||
@ -477,7 +486,11 @@ export class BotLootGenerator
|
||||
|
||||
if (result !== ItemAddedResult.SUCCESS)
|
||||
{
|
||||
this.logger.debug(`Failed to add additional weapon ${generatedWeapon.weapon[0]._id} to bot backpack, reason: ${ItemAddedResult[result]}`);
|
||||
this.logger.debug(
|
||||
`Failed to add additional weapon ${generatedWeapon.weapon[0]._id} to bot backpack, reason: ${
|
||||
ItemAddedResult[result]
|
||||
}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -631,7 +644,10 @@ export class BotLootGenerator
|
||||
{
|
||||
const randomSize = itemTemplate._props.StackMaxSize === 1
|
||||
? 1
|
||||
: this.randomUtil.getInt(itemTemplate._props.StackMinRandom, Math.min(itemTemplate._props.StackMaxRandom, 60));
|
||||
: this.randomUtil.getInt(
|
||||
itemTemplate._props.StackMinRandom,
|
||||
Math.min(itemTemplate._props.StackMaxRandom, 60),
|
||||
);
|
||||
|
||||
if (!ammoItem.upd)
|
||||
{
|
||||
|
@ -212,7 +212,7 @@ export class BotWeaponGenerator
|
||||
)
|
||||
{
|
||||
// Guns have variety of possible Chamber ids, patron_in_weapon/patron_in_weapon_000/patron_in_weapon_001
|
||||
const chamberSlotNames = weaponItemTemplate._props.Chambers.map(x => x._name);
|
||||
const chamberSlotNames = weaponItemTemplate._props.Chambers.map((x) => x._name);
|
||||
this.addCartridgeToChamber(weaponWithModsArray, ammoTpl, chamberSlotNames);
|
||||
}
|
||||
|
||||
@ -311,7 +311,10 @@ export class BotWeaponGenerator
|
||||
{
|
||||
// Invalid weapon generated, fallback to preset
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("bot-weapon_generated_incorrect_using_default", `${weaponTpl} ${itemTemplate._name}`),
|
||||
this.localisationService.getText(
|
||||
"bot-weapon_generated_incorrect_using_default",
|
||||
`${weaponTpl} ${itemTemplate._name}`,
|
||||
),
|
||||
);
|
||||
const weaponMods = [];
|
||||
|
||||
@ -364,10 +367,12 @@ export class BotWeaponGenerator
|
||||
}
|
||||
|
||||
// Iterate over required slots in db item, check mod exists for that slot
|
||||
for (const modSlotTemplate of modTemplate._props.Slots.filter(slot => slot._required))
|
||||
for (const modSlotTemplate of modTemplate._props.Slots.filter((slot) => slot._required))
|
||||
{
|
||||
const slotName = modSlotTemplate._name;
|
||||
const weaponSlotItem = weaponItemArray.find((weaponItem) => weaponItem.parentId === mod._id && weaponItem.slotId === slotName);
|
||||
const weaponSlotItem = weaponItemArray.find((weaponItem) =>
|
||||
weaponItem.parentId === mod._id && weaponItem.slotId === slotName
|
||||
);
|
||||
if (!weaponSlotItem)
|
||||
{
|
||||
this.logger.warning(
|
||||
@ -547,7 +552,10 @@ export class BotWeaponGenerator
|
||||
{
|
||||
// Shouldn't happen
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("bot-weapon_missing_magazine_or_chamber", {weaponId: weaponTemplate._id, botRole: botRole}),
|
||||
this.localisationService.getText("bot-weapon_missing_magazine_or_chamber", {
|
||||
weaponId: weaponTemplate._id,
|
||||
botRole: botRole,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@ -596,7 +604,8 @@ export class BotWeaponGenerator
|
||||
const possibleAmmo = this.weightedRandomHelper.getWeightedValue<string>(compatibleCartridges);
|
||||
|
||||
// Weapon has chamber but does not support cartridge
|
||||
if (weaponTemplate._props.Chambers[0]
|
||||
if (
|
||||
weaponTemplate._props.Chambers[0]
|
||||
&& !weaponTemplate._props.Chambers[0]._props.filters[0].Filter.includes(possibleAmmo)
|
||||
)
|
||||
{
|
||||
|
@ -64,7 +64,8 @@ export class FenceBaseAssortGenerator
|
||||
// Item base type blacklisted
|
||||
if (this.traderConfig.fence.blacklist.length > 0)
|
||||
{
|
||||
if (this.traderConfig.fence.blacklist.includes(rootItemDb._id)
|
||||
if (
|
||||
this.traderConfig.fence.blacklist.includes(rootItemDb._id)
|
||||
|| this.itemHelper.isOfBaseclasses(rootItemDb._id, this.traderConfig.fence.blacklist)
|
||||
)
|
||||
{
|
||||
@ -123,10 +124,7 @@ export class FenceBaseAssortGenerator
|
||||
}
|
||||
|
||||
// Construct preset + mods
|
||||
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
|
||||
null,
|
||||
this.jsonUtil.clone(defaultPreset._items),
|
||||
);
|
||||
const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(defaultPreset._items));
|
||||
|
||||
// Find root item and add some properties to it
|
||||
for (let i = 0; i < presetAndMods.length; i++)
|
||||
@ -156,7 +154,10 @@ export class FenceBaseAssortGenerator
|
||||
|
||||
// Multiply weapon+mods rouble price by multipler in config
|
||||
baseFenceAssort.barter_scheme[presetAndMods[0]._id] = [[]];
|
||||
baseFenceAssort.barter_scheme[presetAndMods[0]._id][0][0] = { _tpl: Money.ROUBLES, count: Math.round(price) * this.traderConfig.fence.presetPriceMult };
|
||||
baseFenceAssort.barter_scheme[presetAndMods[0]._id][0][0] = {
|
||||
_tpl: Money.ROUBLES,
|
||||
count: Math.round(price) * this.traderConfig.fence.presetPriceMult,
|
||||
};
|
||||
|
||||
baseFenceAssort.loyal_level_items[presetAndMods[0]._id] = 1;
|
||||
}
|
||||
@ -177,7 +178,7 @@ export class FenceBaseAssortGenerator
|
||||
}
|
||||
|
||||
// Check for and add required soft inserts to armors
|
||||
const requiredSlots = itemDbDetails._props.Slots.filter(slot => slot._required);
|
||||
const requiredSlots = itemDbDetails._props.Slots.filter((slot) => slot._required);
|
||||
const hasRequiredSlots = requiredSlots.length > 0;
|
||||
if (hasRequiredSlots)
|
||||
{
|
||||
@ -199,9 +200,9 @@ export class FenceBaseAssortGenerator
|
||||
upd: {
|
||||
Repairable: {
|
||||
Durability: modItemDbDetails._props.MaxDurability,
|
||||
MaxDurability: modItemDbDetails._props.MaxDurability
|
||||
}
|
||||
}
|
||||
MaxDurability: modItemDbDetails._props.MaxDurability,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
armor.push(mod);
|
||||
@ -209,12 +210,14 @@ export class FenceBaseAssortGenerator
|
||||
}
|
||||
|
||||
// Check for and add plate items
|
||||
const plateSlots = itemDbDetails._props.Slots.filter(slot => this.itemHelper.isRemovablePlateSlot(slot._name));
|
||||
const plateSlots = itemDbDetails._props.Slots.filter((slot) =>
|
||||
this.itemHelper.isRemovablePlateSlot(slot._name)
|
||||
);
|
||||
if (plateSlots.length > 0)
|
||||
{
|
||||
for (const plateSlot of plateSlots)
|
||||
{
|
||||
const plateTpl = plateSlot._props.filters[0].Plate
|
||||
const plateTpl = plateSlot._props.filters[0].Plate;
|
||||
if (!plateTpl)
|
||||
{
|
||||
// Bsg data lacks a default plate, skip adding mod
|
||||
@ -229,9 +232,9 @@ export class FenceBaseAssortGenerator
|
||||
upd: {
|
||||
Repairable: {
|
||||
Durability: modItemDbDetails._props.MaxDurability,
|
||||
MaxDurability: modItemDbDetails._props.MaxDurability
|
||||
}
|
||||
}
|
||||
MaxDurability: modItemDbDetails._props.MaxDurability,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
export interface IFilterPlateModsForSlotByLevelResult
|
||||
{
|
||||
result: Result
|
||||
plateModTpls: string[]
|
||||
result: Result;
|
||||
plateModTpls: string[];
|
||||
}
|
||||
|
||||
|
||||
export enum Result {
|
||||
export enum Result
|
||||
{
|
||||
UNKNOWN_FAILURE = -1,
|
||||
SUCCESS = 1,
|
||||
NO_DEFAULT_FILTER = 2,
|
||||
NOT_PLATE_HOLDING_SLOT = 3,
|
||||
LACKS_PLATE_WEIGHTS = 4
|
||||
LACKS_PLATE_WEIGHTS = 4,
|
||||
}
|
@ -169,7 +169,7 @@ export class LocationGenerator
|
||||
const staticContainerGroupData: IStaticContainer = db.locations[locationId].statics;
|
||||
if (!staticContainerGroupData)
|
||||
{
|
||||
this.logger.warning(`Map: ${locationId} lacks a statics file, skipping container generation.`)
|
||||
this.logger.warning(`Map: ${locationId} lacks a statics file, skipping container generation.`);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -248,7 +248,6 @@ export class LocationGenerator
|
||||
staticContainerCount++;
|
||||
|
||||
staticLootItemCount += containerWithLoot.template.Items.length;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -430,7 +429,7 @@ export class LocationGenerator
|
||||
itemCountToAdd,
|
||||
this.locationConfig.allowDuplicateItemsInStaticContainers,
|
||||
locklist,
|
||||
).filter(x => !tplsForced.includes(x));
|
||||
).filter((x) => !tplsForced.includes(x));
|
||||
|
||||
// Add forced loot to chosen item pool
|
||||
const tplsToAddToContainer = tplsForced.concat(chosenTpls);
|
||||
@ -516,7 +515,9 @@ export class LocationGenerator
|
||||
const countDistribution = staticLootDist[containerTypeId]?.itemcountDistribution;
|
||||
if (!countDistribution)
|
||||
{
|
||||
this.logger.warning(`Unable to acquire count distrubution for container: ${containerTypeId} on: ${locationName}. defaulting to 0`);
|
||||
this.logger.warning(
|
||||
`Unable to acquire count distrubution for container: ${containerTypeId} on: ${locationName}. defaulting to 0`,
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -603,7 +604,10 @@ export class LocationGenerator
|
||||
// Draw from random distribution
|
||||
const desiredSpawnpointCount = Math.round(
|
||||
this.getLooseLootMultiplerForLocation(locationName)
|
||||
* this.randomUtil.getNormallyDistributedRandomNumber(dynamicLootDist.spawnpointCount.mean, dynamicLootDist.spawnpointCount.std),
|
||||
* this.randomUtil.getNormallyDistributedRandomNumber(
|
||||
dynamicLootDist.spawnpointCount.mean,
|
||||
dynamicLootDist.spawnpointCount.std,
|
||||
),
|
||||
);
|
||||
|
||||
// Positions not in forced but have 100% chance to spawn
|
||||
@ -869,13 +873,14 @@ export class LocationGenerator
|
||||
}
|
||||
else if (this.itemHelper.armorItemCanHoldMods(chosenTpl))
|
||||
{
|
||||
itemWithMods.push({
|
||||
_id: this.objectId.generate(),
|
||||
_tpl: chosenTpl,
|
||||
});
|
||||
itemWithMods.push({ _id: this.objectId.generate(), _tpl: chosenTpl });
|
||||
if (itemTemplate._props.Slots?.length > 0)
|
||||
{
|
||||
itemWithMods = this.itemHelper.addChildSlotItems(itemWithMods, itemTemplate, this.locationConfig.equipmentLootSettings.modSpawnChancePercent);
|
||||
itemWithMods = this.itemHelper.addChildSlotItems(
|
||||
itemWithMods,
|
||||
itemTemplate,
|
||||
this.locationConfig.equipmentLootSettings.modSpawnChancePercent,
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1004,7 +1009,10 @@ export class LocationGenerator
|
||||
if (!rootItem)
|
||||
{
|
||||
this.logger.error(
|
||||
this.localisationService.getText("location-missing_root_item", { tpl: chosenTpl, parentId: parentId }),
|
||||
this.localisationService.getText("location-missing_root_item", {
|
||||
tpl: chosenTpl,
|
||||
parentId: parentId,
|
||||
}),
|
||||
);
|
||||
|
||||
throw new Error(this.localisationService.getText("location-critical_error_see_log"));
|
||||
@ -1085,14 +1093,14 @@ export class LocationGenerator
|
||||
// We make base item above, at start of function, no need to do it here
|
||||
if (itemTemplate._props.Slots?.length > 0)
|
||||
{
|
||||
items = this.itemHelper.addChildSlotItems(items, itemTemplate, this.locationConfig.equipmentLootSettings.modSpawnChancePercent);
|
||||
items = this.itemHelper.addChildSlotItems(
|
||||
items,
|
||||
itemTemplate,
|
||||
this.locationConfig.equipmentLootSettings.modSpawnChancePercent,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
items: items,
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
return { items: items, width: width, height: height };
|
||||
}
|
||||
}
|
||||
|
@ -112,30 +112,50 @@ export class LootGenerator
|
||||
const itemBlacklistArray = Array.from(itemBlacklist);
|
||||
|
||||
// Filter default presets to just weapons
|
||||
const randomisedWeaponPresetCount = this.randomUtil.getInt(options.weaponPresetCount.min, options.weaponPresetCount.max);
|
||||
const randomisedWeaponPresetCount = this.randomUtil.getInt(
|
||||
options.weaponPresetCount.min,
|
||||
options.weaponPresetCount.max,
|
||||
);
|
||||
if (randomisedWeaponPresetCount > 0)
|
||||
{
|
||||
const weaponDefaultPresets = globalDefaultPresets.filter(preset => this.itemHelper.isOfBaseclass(preset._encyclopedia, BaseClasses.WEAPON));
|
||||
const weaponDefaultPresets = globalDefaultPresets.filter((preset) =>
|
||||
this.itemHelper.isOfBaseclass(preset._encyclopedia, BaseClasses.WEAPON)
|
||||
);
|
||||
|
||||
for (let index = 0; index < randomisedWeaponPresetCount; index++)
|
||||
{
|
||||
if (!this.findAndAddRandomPresetToLoot(weaponDefaultPresets, itemTypeCounts, itemBlacklistArray, result))
|
||||
if (
|
||||
!this.findAndAddRandomPresetToLoot(weaponDefaultPresets, itemTypeCounts, itemBlacklistArray, result)
|
||||
)
|
||||
{
|
||||
index--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Filter default presets to just armors and then filter again by protection level
|
||||
const randomisedArmorPresetCount = this.randomUtil.getInt(options.armorPresetCount.min, options.armorPresetCount.max);
|
||||
const randomisedArmorPresetCount = this.randomUtil.getInt(
|
||||
options.armorPresetCount.min,
|
||||
options.armorPresetCount.max,
|
||||
);
|
||||
if (randomisedArmorPresetCount > 0)
|
||||
{
|
||||
const armorDefaultPresets = globalDefaultPresets.filter(preset => this.itemHelper.armorItemCanHoldMods(preset._encyclopedia));
|
||||
const levelFilteredArmorPresets = armorDefaultPresets.filter(armor => this.armorIsDesiredProtectionLevel(armor, options));
|
||||
const armorDefaultPresets = globalDefaultPresets.filter((preset) =>
|
||||
this.itemHelper.armorItemCanHoldMods(preset._encyclopedia)
|
||||
);
|
||||
const levelFilteredArmorPresets = armorDefaultPresets.filter((armor) =>
|
||||
this.armorIsDesiredProtectionLevel(armor, options)
|
||||
);
|
||||
for (let index = 0; index < randomisedArmorPresetCount; index++)
|
||||
{
|
||||
if (!this.findAndAddRandomPresetToLoot(levelFilteredArmorPresets, itemTypeCounts, itemBlacklistArray, result))
|
||||
if (
|
||||
!this.findAndAddRandomPresetToLoot(
|
||||
levelFilteredArmorPresets,
|
||||
itemTypeCounts,
|
||||
itemBlacklistArray,
|
||||
result,
|
||||
)
|
||||
)
|
||||
{
|
||||
index--;
|
||||
}
|
||||
@ -153,21 +173,21 @@ export class LootGenerator
|
||||
*/
|
||||
protected armorIsDesiredProtectionLevel(armor: IPreset, options: LootRequest): boolean
|
||||
{
|
||||
const frontPlate = armor._items.find(mod => mod?.slotId?.toLowerCase() === "front_plate");
|
||||
const frontPlate = armor._items.find((mod) => mod?.slotId?.toLowerCase() === "front_plate");
|
||||
if (frontPlate)
|
||||
{
|
||||
const plateDb = this.itemHelper.getItem(frontPlate._tpl);
|
||||
return options.armorLevelWhitelist.includes(Number.parseInt(plateDb[1]._props.armorClass as any));
|
||||
}
|
||||
|
||||
const helmetTop = armor._items.find(mod => mod?.slotId?.toLowerCase() === "helmet_top");
|
||||
const helmetTop = armor._items.find((mod) => mod?.slotId?.toLowerCase() === "helmet_top");
|
||||
if (helmetTop)
|
||||
{
|
||||
const plateDb = this.itemHelper.getItem(helmetTop._tpl);
|
||||
return options.armorLevelWhitelist.includes(Number.parseInt(plateDb[1]._props.armorClass as any));
|
||||
}
|
||||
|
||||
const softArmorFront = armor._items.find(mod => mod?.slotId?.toLowerCase() === "soft_armor_front");
|
||||
const softArmorFront = armor._items.find((mod) => mod?.slotId?.toLowerCase() === "soft_armor_front");
|
||||
if (softArmorFront)
|
||||
{
|
||||
const plateDb = this.itemHelper.getItem(softArmorFront._tpl);
|
||||
@ -369,10 +389,7 @@ export class LootGenerator
|
||||
chosenWeaponPreset = this.randomUtil.getArrayValue(this.presetHelper.getPresets(chosenWeaponTpl));
|
||||
}
|
||||
|
||||
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
|
||||
null,
|
||||
this.jsonUtil.clone(chosenWeaponPreset._items),
|
||||
);
|
||||
const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(chosenWeaponPreset._items));
|
||||
this.itemHelper.remapRootItemId(presetAndMods);
|
||||
|
||||
// Add preset to return object
|
||||
@ -436,12 +453,7 @@ export class LootGenerator
|
||||
for (let index = 0; index < rewardCount; index++)
|
||||
{
|
||||
const chosenAmmoBox = this.randomUtil.getArrayValue(ammoBoxesMatchingCaliber);
|
||||
const ammoBoxItem: Item[] = [
|
||||
{
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: chosenAmmoBox._id
|
||||
}
|
||||
]
|
||||
const ammoBoxItem: Item[] = [{ _id: this.hashUtil.generate(), _tpl: chosenAmmoBox._id }];
|
||||
this.itemHelper.addCartridgesToAmmoBox(ammoBoxItem, chosenAmmoBox);
|
||||
rewards.push(ammoBoxItem);
|
||||
}
|
||||
@ -469,14 +481,9 @@ export class LootGenerator
|
||||
{
|
||||
// Choose a random item from pool
|
||||
const chosenRewardItem = this.randomUtil.getArrayValue(rewardItemPool);
|
||||
const rewardItem: Item[] = [
|
||||
{
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: chosenRewardItem._id
|
||||
}
|
||||
]
|
||||
const rewardItem: Item[] = [{ _id: this.hashUtil.generate(), _tpl: chosenRewardItem._id }];
|
||||
|
||||
rewards.push(rewardItem)
|
||||
rewards.push(rewardItem);
|
||||
}
|
||||
}
|
||||
|
||||
@ -524,12 +531,9 @@ export class LootGenerator
|
||||
for (let index = 0; index < rewardCount; index++)
|
||||
{
|
||||
const chosenItem = this.randomUtil.drawRandomFromList(relatedItems);
|
||||
const item: Item[] = [{
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: chosenItem[0]._id
|
||||
}];
|
||||
const item: Item[] = [{ _id: this.hashUtil.generate(), _tpl: chosenItem[0]._id }];
|
||||
|
||||
modRewards.push(item)
|
||||
modRewards.push(item);
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,13 +556,8 @@ export class LootGenerator
|
||||
const chosenRewardItemTpl = this.weightedRandomHelper.getWeightedValue<string>(
|
||||
rewardContainerDetails.rewardTplPool,
|
||||
);
|
||||
const rewardItem: Item[] = [
|
||||
{
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: chosenRewardItemTpl
|
||||
}
|
||||
]
|
||||
itemsToReturn.push(rewardItem)
|
||||
const rewardItem: Item[] = [{ _id: this.hashUtil.generate(), _tpl: chosenRewardItemTpl }];
|
||||
itemsToReturn.push(rewardItem);
|
||||
}
|
||||
|
||||
return itemsToReturn;
|
||||
|
@ -75,7 +75,7 @@ export class RagfairAssortGenerator
|
||||
const results: Item[][] = [];
|
||||
|
||||
/** Get cloned items from db */
|
||||
const dbItemsClone = this.itemHelper.getItems().filter(item => item._type !== "Node");
|
||||
const dbItemsClone = this.itemHelper.getItems().filter((item) => item._type !== "Node");
|
||||
|
||||
/** Store processed preset tpls so we dont add them when procesing non-preset items */
|
||||
const processedArmorItems: string[] = [];
|
||||
@ -86,10 +86,7 @@ export class RagfairAssortGenerator
|
||||
for (const preset of presets)
|
||||
{
|
||||
// Update Ids and clone
|
||||
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
|
||||
null,
|
||||
this.jsonUtil.clone(preset._items),
|
||||
);
|
||||
const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(preset._items));
|
||||
this.itemHelper.remapRootItemId(presetAndMods);
|
||||
|
||||
// Add presets base item tpl to the processed list so its skipped later on when processing items
|
||||
@ -141,7 +138,7 @@ export class RagfairAssortGenerator
|
||||
{
|
||||
return (this.ragfairConfig.dynamic.showDefaultPresetsOnly)
|
||||
? Object.values(this.presetHelper.getDefaultPresets())
|
||||
: this.presetHelper.getAllPresets()
|
||||
: this.presetHelper.getAllPresets();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -288,7 +288,8 @@ export class RagfairOfferGenerator
|
||||
if (this.ragfairServerHelper.isPlayer(userID))
|
||||
{
|
||||
// Player offer = current time + offerDurationTimeInHour;
|
||||
const offerDurationTimeHours = this.databaseServer.getTables().globals.config.RagFair.offerDurationTimeInHour;
|
||||
const offerDurationTimeHours =
|
||||
this.databaseServer.getTables().globals.config.RagFair.offerDurationTimeInHour;
|
||||
return this.timeUtil.getTimestamp() + Math.round(offerDurationTimeHours * TimeUtil.ONE_HOUR_AS_SECONDS);
|
||||
}
|
||||
|
||||
@ -357,7 +358,10 @@ export class RagfairOfferGenerator
|
||||
// Armor presets can hold plates above the allowed flea level, remove if necessary
|
||||
if (isPreset && this.ragfairConfig.dynamic.blacklist.enableBsgList)
|
||||
{
|
||||
this.removeBannedPlatesFromPreset(assortItemWithChildren, this.ragfairConfig.dynamic.blacklist.armorPlateMaxProtectionLevel);
|
||||
this.removeBannedPlatesFromPreset(
|
||||
assortItemWithChildren,
|
||||
this.ragfairConfig.dynamic.blacklist.armorPlateMaxProtectionLevel,
|
||||
);
|
||||
}
|
||||
|
||||
// Get number of offers to create
|
||||
@ -379,7 +383,9 @@ export class RagfairOfferGenerator
|
||||
delete assortItemWithChildren[0].parentId;
|
||||
delete assortItemWithChildren[0].slotId;
|
||||
|
||||
assortSingleOfferProcesses.push(this.createSingleOfferForItem(assortItemWithChildren, isPreset, itemDetails));
|
||||
assortSingleOfferProcesses.push(
|
||||
this.createSingleOfferForItem(assortItemWithChildren, isPreset, itemDetails),
|
||||
);
|
||||
}
|
||||
|
||||
await Promise.all(assortSingleOfferProcesses);
|
||||
@ -399,7 +405,9 @@ export class RagfairOfferGenerator
|
||||
return false;
|
||||
}
|
||||
|
||||
const plateSlots = presetWithChildren.filter(item => this.itemHelper.getRemovablePlateSlotIds().includes(item.slotId?.toLowerCase()));
|
||||
const plateSlots = presetWithChildren.filter((item) =>
|
||||
this.itemHelper.getRemovablePlateSlotIds().includes(item.slotId?.toLowerCase())
|
||||
);
|
||||
if (plateSlots.length === 0)
|
||||
{
|
||||
// Has no plate slots e.g. "left_side_plate", exit
|
||||
@ -409,7 +417,8 @@ export class RagfairOfferGenerator
|
||||
let removedPlate = false;
|
||||
for (const plateSlot of plateSlots)
|
||||
{
|
||||
const plateArmorLevel = Number.parseInt(<string>this.itemHelper.getItem(plateSlot._tpl)[1]._props.armorClass) ?? 0;
|
||||
const plateArmorLevel =
|
||||
Number.parseInt(<string>this.itemHelper.getItem(plateSlot._tpl)[1]._props.armorClass) ?? 0;
|
||||
if (plateArmorLevel > plateProtectionLimit)
|
||||
{
|
||||
presetWithChildren.splice(presetWithChildren.indexOf(plateSlot), 1);
|
||||
@ -417,7 +426,7 @@ export class RagfairOfferGenerator
|
||||
}
|
||||
}
|
||||
|
||||
return removedPlate
|
||||
return removedPlate;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -434,13 +443,19 @@ export class RagfairOfferGenerator
|
||||
): Promise<void>
|
||||
{
|
||||
// Set stack size to random value
|
||||
itemWithChildren[0].upd.StackObjectsCount = this.ragfairServerHelper.calculateDynamicStackCount(itemWithChildren[0]._tpl, isPreset);
|
||||
itemWithChildren[0].upd.StackObjectsCount = this.ragfairServerHelper.calculateDynamicStackCount(
|
||||
itemWithChildren[0]._tpl,
|
||||
isPreset,
|
||||
);
|
||||
|
||||
const isBarterOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.barter.chancePercent);
|
||||
const isPackOffer = this.randomUtil.getChance100(this.ragfairConfig.dynamic.pack.chancePercent)
|
||||
&& !isBarterOffer
|
||||
&& itemWithChildren.length === 1
|
||||
&& this.itemHelper.isOfBaseclasses(itemWithChildren[0]._tpl, this.ragfairConfig.dynamic.pack.itemTypeWhitelist);
|
||||
&& this.itemHelper.isOfBaseclasses(
|
||||
itemWithChildren[0]._tpl,
|
||||
this.ragfairConfig.dynamic.pack.itemTypeWhitelist,
|
||||
);
|
||||
const randomUserId = this.hashUtil.generate();
|
||||
|
||||
let barterScheme: IBarterScheme[];
|
||||
@ -614,22 +629,29 @@ export class RagfairOfferGenerator
|
||||
* @param itemWithMods Item to adjust condition details of
|
||||
* @param itemDetails db item details of first item in array
|
||||
*/
|
||||
protected randomiseItemCondition(conditionSettingsId: string, itemWithMods: Item[], itemDetails: ITemplateItem): void
|
||||
protected randomiseItemCondition(
|
||||
conditionSettingsId: string,
|
||||
itemWithMods: Item[],
|
||||
itemDetails: ITemplateItem,
|
||||
): void
|
||||
{
|
||||
const rootItem = itemWithMods[0];
|
||||
|
||||
const itemConditionValues = this.ragfairConfig.dynamic.condition[conditionSettingsId];
|
||||
const multiplier = this.randomUtil.getFloat(
|
||||
itemConditionValues.min,
|
||||
itemConditionValues.max,
|
||||
);
|
||||
const multiplier = this.randomUtil.getFloat(itemConditionValues.min, itemConditionValues.max);
|
||||
|
||||
// Randomise armor + plates + armor related things
|
||||
if (this.itemHelper.armorItemCanHoldMods(rootItem._tpl)
|
||||
|| this.itemHelper.isOfBaseclasses(rootItem._tpl, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT]))
|
||||
if (
|
||||
this.itemHelper.armorItemCanHoldMods(rootItem._tpl)
|
||||
|| this.itemHelper.isOfBaseclasses(rootItem._tpl, [BaseClasses.ARMOR_PLATE, BaseClasses.ARMORED_EQUIPMENT])
|
||||
)
|
||||
{
|
||||
// Chance to not adjust armor
|
||||
if (!this.randomUtil.getChance100(this.ragfairConfig.dynamic.condition[BaseClasses.ARMORED_EQUIPMENT].conditionChance * 100))
|
||||
if (
|
||||
!this.randomUtil.getChance100(
|
||||
this.ragfairConfig.dynamic.condition[BaseClasses.ARMORED_EQUIPMENT].conditionChance * 100,
|
||||
)
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -637,18 +659,17 @@ export class RagfairOfferGenerator
|
||||
this.randomiseArmorDurabilityValues(itemWithMods);
|
||||
|
||||
// Add hits to visor
|
||||
const visorMod = itemWithMods.find(item => item.parentId === BaseClasses.ARMORED_EQUIPMENT && item.slotId === "mod_equipment_000");
|
||||
if (this.randomUtil.getChance100(25)
|
||||
&& visorMod)
|
||||
const visorMod = itemWithMods.find((item) =>
|
||||
item.parentId === BaseClasses.ARMORED_EQUIPMENT && item.slotId === "mod_equipment_000"
|
||||
);
|
||||
if (this.randomUtil.getChance100(25) && visorMod)
|
||||
{
|
||||
if (!visorMod.upd)
|
||||
{
|
||||
visorMod.upd = {};
|
||||
}
|
||||
|
||||
visorMod.upd.FaceShield = {
|
||||
Hits: this.randomUtil.getInt(1,3)
|
||||
}
|
||||
visorMod.upd.FaceShield = { Hits: this.randomUtil.getInt(1, 3) };
|
||||
}
|
||||
|
||||
return;
|
||||
@ -673,7 +694,8 @@ export class RagfairOfferGenerator
|
||||
if (rootItem.upd.Key && itemDetails._props.MaximumNumberOfUsage > 1)
|
||||
{
|
||||
// randomize key uses
|
||||
rootItem.upd.Key.NumberOfUsages = Math.round(itemDetails._props.MaximumNumberOfUsage * (1 - multiplier)) || 0;
|
||||
rootItem.upd.Key.NumberOfUsages = Math.round(itemDetails._props.MaximumNumberOfUsage * (1 - multiplier))
|
||||
|| 0;
|
||||
|
||||
return;
|
||||
}
|
||||
@ -752,7 +774,8 @@ export class RagfairOfferGenerator
|
||||
// Store mod types durabiltiy multiplier for later use in current/max durability value calculation
|
||||
if (!childMultiplerValues[itemDbDetails._parent])
|
||||
{
|
||||
childMultiplerValues[itemDbDetails._parent] = this.randomUtil.getFloat(
|
||||
childMultiplerValues[itemDbDetails._parent] =
|
||||
this.randomUtil.getFloat(
|
||||
itemDurabilityConfigDict[itemDbDetails._parent].min,
|
||||
itemDurabilityConfigDict[itemDbDetails._parent].max,
|
||||
) / itemDurabilityConfigDict[itemDbDetails._parent].max;
|
||||
@ -760,14 +783,17 @@ export class RagfairOfferGenerator
|
||||
|
||||
const modMultipler = childMultiplerValues[itemDbDetails._parent];
|
||||
const maxDurability = Math.round(
|
||||
this.randomUtil.getFloat(itemDbDetails._props.MaxDurability * this.randomUtil.getFloat(modMultipler, 1), itemDbDetails._props.MaxDurability),
|
||||
this.randomUtil.getFloat(
|
||||
itemDbDetails._props.MaxDurability * this.randomUtil.getFloat(modMultipler, 1),
|
||||
itemDbDetails._props.MaxDurability,
|
||||
),
|
||||
);
|
||||
const durability = Math.round(
|
||||
this.randomUtil.getFloat(maxDurability * this.randomUtil.getFloat(modMultipler, 1), maxDurability),
|
||||
);
|
||||
item.upd.Repairable = {
|
||||
Durability: durability || 1, // Never let value become 0
|
||||
MaxDurability: maxDurability
|
||||
MaxDurability: maxDurability,
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -896,7 +922,11 @@ export class RagfairOfferGenerator
|
||||
* @param multipler What to multiply the resulting price by
|
||||
* @returns Barter scheme for offer
|
||||
*/
|
||||
protected createCurrencyBarterScheme(offerWithChildren: Item[], isPackOffer: boolean, multipler = 1): IBarterScheme[]
|
||||
protected createCurrencyBarterScheme(
|
||||
offerWithChildren: Item[],
|
||||
isPackOffer: boolean,
|
||||
multipler = 1,
|
||||
): IBarterScheme[]
|
||||
{
|
||||
const currency = this.ragfairServerHelper.getDynamicOfferCurrency();
|
||||
const price = this.ragfairPriceService.getDynamicOfferPriceForOffer(offerWithChildren, currency, isPackOffer)
|
||||
|
@ -9,7 +9,12 @@ import { RepeatableQuestHelper } from "@spt-aki/helpers/RepeatableQuestHelper";
|
||||
import { Exit, ILocationBase } from "@spt-aki/models/eft/common/ILocationBase";
|
||||
import { TraderInfo } from "@spt-aki/models/eft/common/tables/IBotBase";
|
||||
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
||||
import { IQuestCondition, IQuestConditionCounterCondition, IQuestReward, IQuestRewards } from "@spt-aki/models/eft/common/tables/IQuest";
|
||||
import {
|
||||
IQuestCondition,
|
||||
IQuestConditionCounterCondition,
|
||||
IQuestReward,
|
||||
IQuestRewards,
|
||||
} from "@spt-aki/models/eft/common/tables/IQuest";
|
||||
import { IRepeatableQuest } from "@spt-aki/models/eft/common/tables/IRepeatableQuests";
|
||||
import { ITemplateItem } from "@spt-aki/models/eft/common/tables/ITemplateItem";
|
||||
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
|
||||
@ -292,11 +297,15 @@ export class RepeatableQuestGenerator
|
||||
// Filter out close range weapons from far distance requirement
|
||||
if (distance > 50)
|
||||
{
|
||||
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter(category => ["Shotgun", "Pistol"].includes(category.key));
|
||||
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter((category) =>
|
||||
["Shotgun", "Pistol"].includes(category.key)
|
||||
);
|
||||
}
|
||||
else if (distance < 20) // Filter out far range weapons from close distance requirement
|
||||
{
|
||||
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter(category => ["MarksmanRifle", "DMR"].includes(category.key));
|
||||
else if (distance < 20)
|
||||
{ // Filter out far range weapons from close distance requirement
|
||||
weaponCategoryRequirementConfig = weaponCategoryRequirementConfig.filter((category) =>
|
||||
["MarksmanRifle", "DMR"].includes(category.key)
|
||||
);
|
||||
}
|
||||
|
||||
// Pick a weighted weapon category
|
||||
@ -418,7 +427,7 @@ export class RepeatableQuestGenerator
|
||||
id: this.objectId.generate(),
|
||||
dynamicLocale: true,
|
||||
target: location,
|
||||
conditionType: "Location"
|
||||
conditionType: "Location",
|
||||
};
|
||||
|
||||
return propsObject;
|
||||
@ -568,7 +577,7 @@ export class RepeatableQuestGenerator
|
||||
}
|
||||
|
||||
// Draw items to ask player to retrieve
|
||||
let isAmmo = 0
|
||||
let isAmmo = 0;
|
||||
const randomNumbersUsed = [];
|
||||
for (let i = 0; i < distinctItemsToRetrieveCount; i++)
|
||||
{
|
||||
@ -694,7 +703,8 @@ export class RepeatableQuestGenerator
|
||||
): IRepeatableQuest
|
||||
{
|
||||
const explorationConfig = repeatableConfig.questConfig.Exploration;
|
||||
const requiresSpecificExtract = Math.random() < repeatableConfig.questConfig.Exploration.specificExits.probability;
|
||||
const requiresSpecificExtract =
|
||||
Math.random() < repeatableConfig.questConfig.Exploration.specificExits.probability;
|
||||
|
||||
if (Object.keys(questTypePool.pool.Exploration.locations).length === 0)
|
||||
{
|
||||
@ -746,23 +756,26 @@ export class RepeatableQuestGenerator
|
||||
// Exclude scav coop exits when choosing pmc exit
|
||||
if (repeatableConfig.side === "Pmc")
|
||||
{
|
||||
mapExits = mapExits.filter(exit => exit.PassageRequirement !== "ScavCooperation")
|
||||
mapExits = mapExits.filter((exit) => exit.PassageRequirement !== "ScavCooperation");
|
||||
}
|
||||
|
||||
// Only get exits that have a greater than 0% chance to spawn
|
||||
const exitPool = mapExits.filter(exit => exit.Chance > 0);
|
||||
const exitPool = mapExits.filter((exit) => exit.Chance > 0);
|
||||
|
||||
// Exclude exits with a requirement to leave (e.g. car extracts)
|
||||
const possibleExits = exitPool.filter((exit) =>
|
||||
(!("PassageRequirement" in exit)
|
||||
const possibleExits = exitPool.filter((
|
||||
exit,
|
||||
) => (!("PassageRequirement" in exit)
|
||||
|| repeatableConfig.questConfig.Exploration.specificExits.passageRequirementWhitelist.includes(
|
||||
exit.PassageRequirement
|
||||
exit.PassageRequirement,
|
||||
))
|
||||
);
|
||||
|
||||
if (possibleExits.length === 0)
|
||||
{
|
||||
this.logger.error(`Unable to choose specific exit on map: ${locationKey}, Possible exit pool was empty`);
|
||||
this.logger.error(
|
||||
`Unable to choose specific exit on map: ${locationKey}, Possible exit pool was empty`,
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -797,8 +810,7 @@ export class RepeatableQuestGenerator
|
||||
for (const spawnPoint of mapBase.SpawnPointParams)
|
||||
{
|
||||
// Same side, add infil to list
|
||||
if (spawnPoint.Sides.includes(playerSide)
|
||||
|| spawnPoint.Sides.includes("All"))
|
||||
if (spawnPoint.Sides.includes(playerSide) || spawnPoint.Sides.includes("All"))
|
||||
{
|
||||
// Has specific start location
|
||||
if (spawnPoint.Infiltration.length > 0)
|
||||
@ -811,7 +823,9 @@ export class RepeatableQuestGenerator
|
||||
// use list of allowed infils to figure out side of exits
|
||||
const infilPointsArray = Array.from(infilPointsOfSameSide);
|
||||
|
||||
return mapBase.exits.filter(exit => exit.EntryPoints.split(",").some(entryPoint => infilPointsArray.includes(entryPoint)));
|
||||
return mapBase.exits.filter((exit) =>
|
||||
exit.EntryPoints.split(",").some((entryPoint) => infilPointsArray.includes(entryPoint))
|
||||
);
|
||||
}
|
||||
|
||||
protected generatePickupQuest(
|
||||
@ -838,16 +852,16 @@ export class RepeatableQuestGenerator
|
||||
findCondition.target = [itemTypeToFetchWithCount.itemType];
|
||||
findCondition.value = itemCountToFetch;
|
||||
|
||||
const counterCreatorCondition = quest.conditions.AvailableForFinish.find((x) => x.conditionType === "CounterCreator");
|
||||
const counterCreatorCondition = quest.conditions.AvailableForFinish.find((x) =>
|
||||
x.conditionType === "CounterCreator"
|
||||
);
|
||||
// const locationCondition = counterCreatorCondition._props.counter.conditions.find(x => x._parent === "Location");
|
||||
// (locationCondition._props as ILocationConditionProps).target = [...locationTarget];
|
||||
|
||||
const equipmentCondition = counterCreatorCondition.counter.conditions.find((x) =>
|
||||
x.conditionType === "Equipment"
|
||||
);
|
||||
equipmentCondition.equipmentInclusive = [[
|
||||
itemTypeToFetchWithCount.itemType,
|
||||
]];
|
||||
equipmentCondition.equipmentInclusive = [[itemTypeToFetchWithCount.itemType]];
|
||||
|
||||
// Add rewards
|
||||
quest.rewards = this.generateReward(pmcLevel, 1, traderId, repeatableConfig, pickupConfig);
|
||||
@ -874,12 +888,7 @@ export class RepeatableQuestGenerator
|
||||
*/
|
||||
protected generateExplorationExitCondition(exit: Exit): IQuestConditionCounterCondition
|
||||
{
|
||||
return {
|
||||
conditionType: "ExitName",
|
||||
exitName: exit.Name,
|
||||
id: this.objectId.generate(),
|
||||
dynamicLocale: true,
|
||||
};
|
||||
return { conditionType: "ExitName", exitName: exit.Name, id: this.objectId.generate(), dynamicLocale: true };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -951,11 +960,7 @@ export class RepeatableQuestGenerator
|
||||
let roublesBudget = rewardRoubles;
|
||||
let rewardItemPool = this.chooseRewardItemsWithinBudget(repeatableConfig, roublesBudget, traderId);
|
||||
|
||||
const rewards: IQuestRewards = {
|
||||
Started: [],
|
||||
Success: [],
|
||||
Fail: [],
|
||||
};
|
||||
const rewards: IQuestRewards = { Started: [], Success: [], Fail: [] };
|
||||
|
||||
let rewardIndex = 0;
|
||||
// Add xp reward
|
||||
@ -970,7 +975,11 @@ export class RepeatableQuestGenerator
|
||||
{
|
||||
// convert to equivalent dollars
|
||||
rewards.Success.push(
|
||||
this.generateRewardItem(Money.EUROS, this.handbookHelper.fromRUB(rewardRoubles, Money.EUROS), rewardIndex),
|
||||
this.generateRewardItem(
|
||||
Money.EUROS,
|
||||
this.handbookHelper.fromRUB(rewardRoubles, Money.EUROS),
|
||||
rewardIndex,
|
||||
),
|
||||
);
|
||||
}
|
||||
else
|
||||
@ -980,14 +989,19 @@ export class RepeatableQuestGenerator
|
||||
rewardIndex++;
|
||||
|
||||
const traderWhitelistDetails = repeatableConfig.traderWhitelist.find((x) => x.traderId === traderId);
|
||||
if (traderWhitelistDetails.rewardCanBeWeapon && this.randomUtil.getChance100(traderWhitelistDetails.weaponRewardChancePercent))
|
||||
if (
|
||||
traderWhitelistDetails.rewardCanBeWeapon
|
||||
&& this.randomUtil.getChance100(traderWhitelistDetails.weaponRewardChancePercent)
|
||||
)
|
||||
{
|
||||
// Add a random default preset weapon as reward
|
||||
const defaultPresets = Object.values(this.presetHelper.getDefaultPresets());
|
||||
const defaultPreset = this.jsonUtil.clone(this.randomUtil.getArrayValue(defaultPresets));
|
||||
|
||||
// use _encyclopedia as its always the base items _tpl, items[0] isnt guaranteed to be base item
|
||||
rewards.Success.push(this.generateRewardItem(defaultPreset._encyclopedia, 1, rewardIndex, defaultPreset._items));
|
||||
rewards.Success.push(
|
||||
this.generateRewardItem(defaultPreset._encyclopedia, 1, rewardIndex, defaultPreset._items),
|
||||
);
|
||||
rewardIndex++;
|
||||
}
|
||||
|
||||
@ -1059,7 +1073,8 @@ export class RepeatableQuestGenerator
|
||||
target: traderId,
|
||||
value: rewardReputation,
|
||||
type: QuestRewardType.TRADER_STANDING,
|
||||
index: rewardIndex };
|
||||
index: rewardIndex,
|
||||
};
|
||||
rewards.Success.push(reward);
|
||||
rewardIndex++;
|
||||
}
|
||||
@ -1164,7 +1179,7 @@ export class RepeatableQuestGenerator
|
||||
|
||||
if (preset)
|
||||
{
|
||||
const rootItem = preset.find(x => x._tpl === tpl);
|
||||
const rootItem = preset.find((x) => x._tpl === tpl);
|
||||
rewardItem.target = rootItem._id; // Target property and root items id must match
|
||||
rewardItem.items = this.itemHelper.reparentItemAndChildren(rootItem, preset);
|
||||
}
|
||||
|
@ -289,13 +289,7 @@ export class ScavCaseRewardGenerator
|
||||
const result: Item[][] = [];
|
||||
for (const rewardItemDb of rewardItems)
|
||||
{
|
||||
let resultItem: Item[] = [
|
||||
{
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: rewardItemDb._id,
|
||||
upd: undefined
|
||||
}
|
||||
];
|
||||
let resultItem: Item[] = [{ _id: this.hashUtil.generate(), _tpl: rewardItemDb._id, upd: undefined }];
|
||||
const rootItem = resultItem[0];
|
||||
|
||||
if (this.itemHelper.isOfBaseclass(rewardItemDb._id, BaseClasses.AMMO_BOX))
|
||||
@ -303,8 +297,10 @@ export class ScavCaseRewardGenerator
|
||||
this.itemHelper.addCartridgesToAmmoBox(resultItem, rewardItemDb);
|
||||
}
|
||||
// Armor or weapon = use default preset from globals.json
|
||||
else if (this.itemHelper.armorItemCanHoldMods(rewardItemDb._id)
|
||||
|| this.itemHelper.isOfBaseclass(rewardItemDb._id, BaseClasses.WEAPON))
|
||||
else if (
|
||||
this.itemHelper.armorItemCanHoldMods(rewardItemDb._id)
|
||||
|| this.itemHelper.isOfBaseclass(rewardItemDb._id, BaseClasses.WEAPON)
|
||||
)
|
||||
{
|
||||
const preset = this.presetHelper.getDefaultPreset(rewardItemDb._id);
|
||||
if (!preset)
|
||||
@ -315,10 +311,7 @@ export class ScavCaseRewardGenerator
|
||||
}
|
||||
|
||||
// Ensure preset has unique ids and is cloned so we don't alter the preset data stored in memory
|
||||
const presetAndMods: Item[] = this.itemHelper.replaceIDs(
|
||||
null,
|
||||
this.jsonUtil.clone(preset._items),
|
||||
);
|
||||
const presetAndMods: Item[] = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(preset._items));
|
||||
this.itemHelper.remapRootItemId(presetAndMods);
|
||||
|
||||
resultItem = presetAndMods;
|
||||
@ -334,7 +327,7 @@ export class ScavCaseRewardGenerator
|
||||
delete rootItem.upd;
|
||||
}
|
||||
|
||||
result.push(resultItem)
|
||||
result.push(resultItem);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -19,7 +19,7 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
|
||||
@inject("ItemHelper") protected itemHelper: ItemHelper,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("BotWeaponGeneratorHelper") protected botWeaponGeneratorHelper: BotWeaponGeneratorHelper,
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -75,7 +75,9 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
|
||||
// Prevent infinite loop by only allowing 5 attempts at fitting a magazine into inventory
|
||||
if (fitAttempts > 5)
|
||||
{
|
||||
this.logger.debug(`Failed ${fitAttempts} times to add magazine ${magazineTpl} to bot inventory, stopping`);
|
||||
this.logger.debug(
|
||||
`Failed ${fitAttempts} times to add magazine ${magazineTpl} to bot inventory, stopping`,
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -108,10 +110,15 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
|
||||
// Edge case - some weapons (SKS) have an internal magazine as default, choose random non-internal magazine to add to bot instead
|
||||
if (magTemplate._props.ReloadMagType === "InternalMagazine")
|
||||
{
|
||||
const result = this.getRandomExternalMagazineForInternalMagazineGun(inventoryMagGen.getWeaponTemplate()._id, attemptedMagBlacklist);
|
||||
const result = this.getRandomExternalMagazineForInternalMagazineGun(
|
||||
inventoryMagGen.getWeaponTemplate()._id,
|
||||
attemptedMagBlacklist,
|
||||
);
|
||||
if (!result?._id)
|
||||
{
|
||||
this.logger.debug(`Unable to add additional magazine into bot inventory for weapon: ${weapon._name}, attempted: ${fitAttempts} times`);
|
||||
this.logger.debug(
|
||||
`Unable to add additional magazine into bot inventory for weapon: ${weapon._name}, attempted: ${fitAttempts} times`,
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -138,24 +145,29 @@ export class ExternalInventoryMagGen implements IInventoryMagGen
|
||||
* @param weaponTpl Weapon to get mag for
|
||||
* @returns tpl of magazine
|
||||
*/
|
||||
protected getRandomExternalMagazineForInternalMagazineGun(weaponTpl: string, magazineBlacklist: string[]): ITemplateItem
|
||||
protected getRandomExternalMagazineForInternalMagazineGun(
|
||||
weaponTpl: string,
|
||||
magazineBlacklist: string[],
|
||||
): ITemplateItem
|
||||
{
|
||||
// The mag Slot data for the weapon
|
||||
const magSlot = this.itemHelper.getItem(weaponTpl)[1]._props.Slots.find(x => x._name === "mod_magazine");
|
||||
const magSlot = this.itemHelper.getItem(weaponTpl)[1]._props.Slots.find((x) => x._name === "mod_magazine");
|
||||
if (!magSlot)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// All possible mags that fit into the weapon excluding blacklisted
|
||||
const magazinePool = magSlot._props.filters[0].Filter.filter(x => !magazineBlacklist.includes(x)).map((x) => this.itemHelper.getItem(x)[1]);
|
||||
const magazinePool = magSlot._props.filters[0].Filter.filter((x) => !magazineBlacklist.includes(x)).map((x) =>
|
||||
this.itemHelper.getItem(x)[1]
|
||||
);
|
||||
if (!magazinePool)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Non-internal magazines that fit into the weapon
|
||||
const externalMagazineOnlyPool = magazinePool.filter(x => x._props.ReloadMagType !== "InternalMagazine");
|
||||
const externalMagazineOnlyPool = magazinePool.filter((x) => x._props.ReloadMagType !== "InternalMagazine");
|
||||
if (!externalMagazineOnlyPool || externalMagazineOnlyPool?.length === 0)
|
||||
{
|
||||
return null;
|
||||
|
@ -296,11 +296,7 @@ export class BotGeneratorHelper
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
incompatible: true,
|
||||
found: false,
|
||||
reason: `item: ${tplToCheck} does not exist in the database`
|
||||
};
|
||||
return { incompatible: true, found: false, reason: `item: ${tplToCheck} does not exist in the database` };
|
||||
}
|
||||
|
||||
// No props property
|
||||
@ -314,11 +310,7 @@ export class BotGeneratorHelper
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
incompatible: true,
|
||||
found: false,
|
||||
reason: `item: ${tplToCheck} does not have a _props field`
|
||||
};
|
||||
return { incompatible: true, found: false, reason: `item: ${tplToCheck} does not have a _props field` };
|
||||
}
|
||||
|
||||
// Check if any of the current weapon mod templates have the incoming item defined as incompatible
|
||||
@ -346,10 +338,7 @@ export class BotGeneratorHelper
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
incompatible: false,
|
||||
reason: ""
|
||||
};
|
||||
return { incompatible: false, reason: "" };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -398,11 +387,7 @@ export class BotGeneratorHelper
|
||||
}),
|
||||
);
|
||||
|
||||
return {
|
||||
incompatible: true,
|
||||
found: false,
|
||||
reason: `item: ${tplToCheck} does not have a _props field`
|
||||
};
|
||||
return { incompatible: true, found: false, reason: `item: ${tplToCheck} does not have a _props field` };
|
||||
}
|
||||
|
||||
// Does an equipped item have a property that blocks the desired item - check for prop "BlocksX" .e.g BlocksEarpiece / BlocksFaceCover
|
||||
@ -436,7 +421,7 @@ export class BotGeneratorHelper
|
||||
// Does item being checked get blocked/block existing item
|
||||
if (itemToEquip._props.BlocksHeadwear)
|
||||
{
|
||||
const existingHeadwear = itemsEquipped.find(x => x.slotId === "Headwear");
|
||||
const existingHeadwear = itemsEquipped.find((x) => x.slotId === "Headwear");
|
||||
if (existingHeadwear)
|
||||
{
|
||||
return {
|
||||
@ -452,7 +437,7 @@ export class BotGeneratorHelper
|
||||
// Does item being checked get blocked/block existing item
|
||||
if (itemToEquip._props.BlocksFaceCover)
|
||||
{
|
||||
const existingFaceCover = itemsEquipped.find(item => item.slotId === "FaceCover");
|
||||
const existingFaceCover = itemsEquipped.find((item) => item.slotId === "FaceCover");
|
||||
if (existingFaceCover)
|
||||
{
|
||||
return {
|
||||
@ -468,7 +453,7 @@ export class BotGeneratorHelper
|
||||
// Does item being checked get blocked/block existing item
|
||||
if (itemToEquip._props.BlocksEarpiece)
|
||||
{
|
||||
const existingEarpiece = itemsEquipped.find(item => item.slotId === "Earpiece");
|
||||
const existingEarpiece = itemsEquipped.find((item) => item.slotId === "Earpiece");
|
||||
if (existingEarpiece)
|
||||
{
|
||||
return {
|
||||
@ -484,7 +469,7 @@ export class BotGeneratorHelper
|
||||
// Does item being checked get blocked/block existing item
|
||||
if (itemToEquip._props.BlocksArmorVest)
|
||||
{
|
||||
const existingArmorVest = itemsEquipped.find(item => item.slotId === "ArmorVest");
|
||||
const existingArmorVest = itemsEquipped.find((item) => item.slotId === "ArmorVest");
|
||||
if (existingArmorVest)
|
||||
{
|
||||
return {
|
||||
|
@ -186,12 +186,12 @@ export class BotWeaponGeneratorHelper
|
||||
{
|
||||
// Bot doesnt have any containers we want to add item to
|
||||
this.logger.debug(
|
||||
`Unable to add item: ${
|
||||
itemWithChildren[0]._tpl
|
||||
} to bot as it lacks the following containers: ${equipmentSlots.join(",")}`,
|
||||
`Unable to add item: ${itemWithChildren[0]._tpl} to bot as it lacks the following containers: ${
|
||||
equipmentSlots.join(",")
|
||||
}`,
|
||||
);
|
||||
|
||||
return ItemAddedResult.NO_CONTAINERS
|
||||
return ItemAddedResult.NO_CONTAINERS;
|
||||
}
|
||||
|
||||
// No container of desired type found, skip to next container type
|
||||
|
@ -107,7 +107,6 @@ export class GiveSptCommand implements ISptCommand
|
||||
const presetToSend = this.itemHelper.replaceIDs(null, this.jsonUtil.clone(preset._items));
|
||||
itemsToSend.push(...presetToSend);
|
||||
}
|
||||
|
||||
}
|
||||
else if (this.itemHelper.isOfBaseclass(checkedItem[1]._id, BaseClasses.AMMO_BOX))
|
||||
{
|
||||
@ -124,10 +123,7 @@ export class GiveSptCommand implements ISptCommand
|
||||
const item: Item = {
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: checkedItem[1]._id,
|
||||
upd: {
|
||||
StackObjectsCount: +quantity,
|
||||
SpawnedInSession: true
|
||||
},
|
||||
upd: { StackObjectsCount: +quantity, SpawnedInSession: true },
|
||||
};
|
||||
itemsToSend.push(...this.itemHelper.splitStack(item));
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
@inject("MailSendService") protected mailSendService: MailSendService,
|
||||
@inject("GiftService") protected giftService: GiftService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
|
||||
@ -64,7 +64,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
|
||||
"Hey! you got the right code!",
|
||||
"A secret code, how exciting!",
|
||||
"You found a gift code!",
|
||||
])
|
||||
]),
|
||||
);
|
||||
|
||||
return;
|
||||
@ -75,7 +75,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
|
||||
this.mailSendService.sendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
this.randomUtil.getArrayValue(["Looks like you already used that code", "You already have that!!"])
|
||||
this.randomUtil.getArrayValue(["Looks like you already used that code", "You already have that!!"]),
|
||||
);
|
||||
|
||||
return;
|
||||
@ -91,7 +91,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
|
||||
"I love you too buddy :3!",
|
||||
"uwu",
|
||||
`love you too ${sender?.Info?.Nickname}`,
|
||||
])
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
|
||||
this.mailSendService.sendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
this.randomUtil.getArrayValue(["Its me!!", "spt? i've heard of that project"])
|
||||
this.randomUtil.getArrayValue(["Its me!!", "spt? i've heard of that project"]),
|
||||
);
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
|
||||
"Heyyyyy",
|
||||
"Hey there",
|
||||
`Hello ${sender?.Info?.Nickname}`,
|
||||
])
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@ -134,7 +134,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
|
||||
"Cool guy, he made EFT!",
|
||||
"Legend",
|
||||
"Remember when he said webel-webel-webel-webel, classic nikita moment",
|
||||
])
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
@ -143,7 +143,7 @@ export class SptDialogueChatBot implements IDialogueChatBot
|
||||
this.mailSendService.sendUserMessageToPlayer(
|
||||
sessionId,
|
||||
sptFriendUser,
|
||||
this.randomUtil.getArrayValue(["beep boop", "**sad boop**", "probably", "sometimes", "yeah lol"])
|
||||
this.randomUtil.getArrayValue(["beep boop", "**sad boop**", "probably", "sometimes", "yeah lol"]),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,7 @@ import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
|
||||
import { NotificationSendHelper } from "@spt-aki/helpers/NotificationSendHelper";
|
||||
import { NotifierHelper } from "@spt-aki/helpers/NotifierHelper";
|
||||
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
|
||||
import {
|
||||
Dialogue,
|
||||
MessagePreview,
|
||||
} from "@spt-aki/models/eft/profile/IAkiProfile";
|
||||
import { Dialogue, MessagePreview } from "@spt-aki/models/eft/profile/IAkiProfile";
|
||||
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
||||
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
|
||||
import { SaveServer } from "@spt-aki/servers/SaveServer";
|
||||
|
@ -37,7 +37,7 @@ export class HandbookHelper
|
||||
|
||||
constructor(
|
||||
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
|
||||
@inject("JsonUtil") protected jsonUtil: JsonUtil
|
||||
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -168,6 +168,6 @@ export class HandbookHelper
|
||||
|
||||
public getCategoryById(handbookId: string): Category
|
||||
{
|
||||
return this.databaseServer.getTables().templates.handbook.Categories.find(x => x.Id === handbookId);
|
||||
return this.databaseServer.getTables().templates.handbook.Categories.find((x) => x.Id === handbookId);
|
||||
}
|
||||
}
|
||||
|
@ -481,7 +481,9 @@ export class HideoutHelper
|
||||
{
|
||||
fuelItemInSlot.upd = this.getAreaUpdObject(1, fuelRemaining, pointsConsumed);
|
||||
|
||||
this.logger.debug(`$Profile: ${pmcData._id} Generator has: ${fuelRemaining} fuel left in slot ${i + 1}`);
|
||||
this.logger.debug(
|
||||
`$Profile: ${pmcData._id} Generator has: ${fuelRemaining} fuel left in slot ${i + 1}`,
|
||||
);
|
||||
hasFuelRemaining = true;
|
||||
|
||||
break; // Break here to avoid updating all the fuel tanks
|
||||
@ -955,7 +957,7 @@ export class HideoutHelper
|
||||
pmcData: IPmcData,
|
||||
request: IHideoutTakeProductionRequestData,
|
||||
sessionId: string,
|
||||
output: IItemEventRouterResponse
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
// Get how many coins were crafted and ready to pick up
|
||||
@ -973,15 +975,11 @@ export class HideoutHelper
|
||||
const itemsToAdd: Item[][] = [];
|
||||
for (let index = 0; index < craftedCoinCount; index++)
|
||||
{
|
||||
itemsToAdd.push(
|
||||
[{
|
||||
itemsToAdd.push([{
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: HideoutHelper.bitcoinTpl,
|
||||
upd: {
|
||||
StackObjectsCount: 1
|
||||
}
|
||||
}]
|
||||
);
|
||||
upd: { StackObjectsCount: 1 },
|
||||
}]);
|
||||
}
|
||||
|
||||
// Create request for what we want to add to stash
|
||||
@ -989,7 +987,7 @@ export class HideoutHelper
|
||||
itemsWithModsToAdd: itemsToAdd,
|
||||
foundInRaid: true,
|
||||
useSortingTable: false,
|
||||
callback: null
|
||||
callback: null,
|
||||
};
|
||||
|
||||
// Add FiR coins to player inventory
|
||||
@ -1070,24 +1068,29 @@ export class HideoutHelper
|
||||
*/
|
||||
public applyPlaceOfFameDogtagBonus(pmcData: IPmcData): void
|
||||
{
|
||||
const fameAreaProfile = pmcData.Hideout.Areas.find(area => area.type === HideoutAreas.PLACE_OF_FAME);
|
||||
const fameAreaProfile = pmcData.Hideout.Areas.find((area) => area.type === HideoutAreas.PLACE_OF_FAME);
|
||||
|
||||
// Get hideout area 16 bonus array
|
||||
const fameAreaDb = this.databaseServer.getTables().hideout.areas.find((area) => area.type === HideoutAreas.PLACE_OF_FAME);
|
||||
const fameAreaDb = this.databaseServer.getTables().hideout.areas.find((area) =>
|
||||
area.type === HideoutAreas.PLACE_OF_FAME
|
||||
);
|
||||
|
||||
// Get SkillGroupLevelingBoost object
|
||||
const combatBoostBonusDb = fameAreaDb.stages[fameAreaProfile.level].bonuses.find(bonus => bonus.type === "SkillGroupLevelingBoost");
|
||||
const combatBoostBonusDb = fameAreaDb.stages[fameAreaProfile.level].bonuses.find((bonus) =>
|
||||
bonus.type === "SkillGroupLevelingBoost"
|
||||
);
|
||||
|
||||
// Get SkillGroupLevelingBoost object in profile
|
||||
const combatBonusProfile = pmcData.Bonuses.find(bonus => bonus.id === combatBoostBonusDb.id);
|
||||
const combatBonusProfile = pmcData.Bonuses.find((bonus) => bonus.id === combatBoostBonusDb.id);
|
||||
|
||||
// Get all slotted dogtag items
|
||||
const activeDogtags = pmcData.Inventory.items.filter(item => item?.slotId?.startsWith("dogtag"));
|
||||
const activeDogtags = pmcData.Inventory.items.filter((item) => item?.slotId?.startsWith("dogtag"));
|
||||
|
||||
// Calculate bonus percent (apply hideoutManagement bonus)
|
||||
const hideoutManagementSkill = this.profileHelper.getSkillFromProfile(pmcData, SkillTypes.HIDEOUT_MANAGEMENT);
|
||||
const hideoutManagementSkillBonusPercent = 1 + (hideoutManagementSkill.Progress / 10000); // 5100 becomes 0.51, add 1 to it, 1.51
|
||||
const bonus = this.getDogtagCombatSkillBonusPercent(pmcData, activeDogtags) * hideoutManagementSkillBonusPercent;
|
||||
const bonus = this.getDogtagCombatSkillBonusPercent(pmcData, activeDogtags)
|
||||
* hideoutManagementSkillBonusPercent;
|
||||
|
||||
// Update bonus value to above calcualted value
|
||||
combatBonusProfile.value = Number.parseFloat(bonus.toFixed(2));
|
||||
@ -1112,8 +1115,7 @@ export class HideoutHelper
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Number.parseInt(dogtag.upd.Dogtag?.AccountId) === pmcData.aid
|
||||
)
|
||||
if (Number.parseInt(dogtag.upd.Dogtag?.AccountId) === pmcData.aid)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ export class InRaidHelper
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("ProfileFixerService") protected profileFixerService: ProfileFixerService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil
|
||||
@inject("RandomUtil") protected randomUtil: RandomUtil,
|
||||
)
|
||||
{
|
||||
this.lostOnDeathConfig = this.configServer.getConfig(ConfigTypes.LOST_ON_DEATH);
|
||||
@ -68,7 +68,7 @@ export class InRaidHelper
|
||||
*/
|
||||
public addUpdToMoneyFromRaid(items: Item[]): void
|
||||
{
|
||||
for (const item of items.filter(x => this.paymentHelper.isMoneyTpl(x._tpl)))
|
||||
for (const item of items.filter((x) => this.paymentHelper.isMoneyTpl(x._tpl)))
|
||||
{
|
||||
if (!item.upd)
|
||||
{
|
||||
@ -131,7 +131,7 @@ export class InRaidHelper
|
||||
|
||||
if (this.randomUtil.rollForChanceProbability(pmcKillProbabilityForScavGain))
|
||||
{
|
||||
pmcStandingForKill += this.inRaidConfig.scavExtractGain
|
||||
pmcStandingForKill += this.inRaidConfig.scavExtractGain;
|
||||
}
|
||||
|
||||
return pmcStandingForKill;
|
||||
@ -214,7 +214,7 @@ export class InRaidHelper
|
||||
if (matchingPreRaidCounter.value !== postRaidValue)
|
||||
{
|
||||
this.logger.error(
|
||||
`TaskConditionCounters: ${backendCounterKey} value is different post raid, old: ${matchingPreRaidCounter.value} new: ${postRaidValue}`
|
||||
`TaskConditionCounters: ${backendCounterKey} value is different post raid, old: ${matchingPreRaidCounter.value} new: ${postRaidValue}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -226,7 +226,11 @@ export class InRaidHelper
|
||||
* @param saveProgressRequest Post-raid request data
|
||||
* @param sessionId Session id
|
||||
*/
|
||||
public updatePmcProfileDataPostRaid(pmcData: IPmcData, saveProgressRequest: ISaveProgressRequestData, sessionId: string): void
|
||||
public updatePmcProfileDataPostRaid(
|
||||
pmcData: IPmcData,
|
||||
saveProgressRequest: ISaveProgressRequestData,
|
||||
sessionId: string,
|
||||
): void
|
||||
{
|
||||
// Process failed quests then copy everything
|
||||
this.processAlteredQuests(sessionId, pmcData, pmcData.Quests, saveProgressRequest.profile);
|
||||
@ -250,13 +254,19 @@ export class InRaidHelper
|
||||
* @param saveProgressRequest Post-raid request data
|
||||
* @param sessionId Session id
|
||||
*/
|
||||
public updateScavProfileDataPostRaid(scavData: IPmcData, saveProgressRequest: ISaveProgressRequestData, sessionId: string): void
|
||||
public updateScavProfileDataPostRaid(
|
||||
scavData: IPmcData,
|
||||
saveProgressRequest: ISaveProgressRequestData,
|
||||
sessionId: string,
|
||||
): void
|
||||
{
|
||||
// Only copy active quests into scav profile // Progress will later to copied over to PMC profile
|
||||
const existingActiveQuestIds = scavData.Quests?.filter(x => x.status !== QuestStatus.AvailableForStart).map(x => x.qid);
|
||||
const existingActiveQuestIds = scavData.Quests?.filter((x) => x.status !== QuestStatus.AvailableForStart).map((
|
||||
x,
|
||||
) => x.qid);
|
||||
if (existingActiveQuestIds)
|
||||
{
|
||||
scavData.Quests = saveProgressRequest.profile.Quests.filter(x => existingActiveQuestIds.includes(x.qid));
|
||||
scavData.Quests = saveProgressRequest.profile.Quests.filter((x) => existingActiveQuestIds.includes(x.qid));
|
||||
}
|
||||
|
||||
this.profileFixerService.checkForAndFixScavProfileIssues(scavData);
|
||||
@ -323,7 +333,9 @@ export class InRaidHelper
|
||||
}
|
||||
|
||||
// Quest with time-gate has unlocked
|
||||
if (postRaidQuestStatus === "AvailableAfter" && postRaidQuest.availableAfter <= this.timeUtil.getTimestamp())
|
||||
if (
|
||||
postRaidQuestStatus === "AvailableAfter" && postRaidQuest.availableAfter <= this.timeUtil.getTimestamp()
|
||||
)
|
||||
{
|
||||
// Flag as ready to complete
|
||||
postRaidQuest.status = QuestStatus.AvailableForStart;
|
||||
@ -351,7 +363,9 @@ export class InRaidHelper
|
||||
// Does failed quest have requirement to collect items from raid
|
||||
const questDbData = this.questHelper.getQuestFromDb(postRaidQuest.qid, pmcData);
|
||||
// AvailableForFinish
|
||||
const matchingAffFindConditions = questDbData.conditions.AvailableForFinish.filter(condition => condition.conditionType === "FindItem");
|
||||
const matchingAffFindConditions = questDbData.conditions.AvailableForFinish.filter((condition) =>
|
||||
condition.conditionType === "FindItem"
|
||||
);
|
||||
const itemsToCollect: string[] = [];
|
||||
if (matchingAffFindConditions)
|
||||
{
|
||||
@ -364,21 +378,25 @@ export class InRaidHelper
|
||||
|
||||
// Remove quest items from profile as quest has failed and may still be alive
|
||||
// Required as restarting the quest from main menu does not remove value from CarriedQuestItems array
|
||||
postRaidProfile.Stats.Eft.CarriedQuestItems = postRaidProfile.Stats.Eft.CarriedQuestItems.filter(carriedQuestItem => !itemsToCollect.includes(carriedQuestItem))
|
||||
postRaidProfile.Stats.Eft.CarriedQuestItems = postRaidProfile.Stats.Eft.CarriedQuestItems.filter((
|
||||
carriedQuestItem,
|
||||
) => !itemsToCollect.includes(carriedQuestItem));
|
||||
|
||||
// Remove quest item from profile now quest is failed
|
||||
// updateProfileBaseStats() has already passed by ref EFT.Stats, all changes applied to postRaid profile also apply to server profile
|
||||
for (const itemTpl of itemsToCollect)
|
||||
{
|
||||
// Look for sessioncounter and remove it
|
||||
const counterIndex = postRaidProfile.Stats.Eft.SessionCounters.Items.findIndex(x => x.Key.includes(itemTpl) && x.Key.includes("LootItem"));
|
||||
const counterIndex = postRaidProfile.Stats.Eft.SessionCounters.Items.findIndex((x) =>
|
||||
x.Key.includes(itemTpl) && x.Key.includes("LootItem")
|
||||
);
|
||||
if (counterIndex > -1)
|
||||
{
|
||||
postRaidProfile.Stats.Eft.SessionCounters.Items.splice(counterIndex, 1);
|
||||
}
|
||||
|
||||
// Look for quest item and remove it
|
||||
const inventoryItemIndex = postRaidProfile.Inventory.items.findIndex(x => x._tpl === itemTpl);
|
||||
const inventoryItemIndex = postRaidProfile.Inventory.items.findIndex((x) => x._tpl === itemTpl);
|
||||
if (inventoryItemIndex > -1)
|
||||
{
|
||||
postRaidProfile.Inventory.items.splice(inventoryItemIndex, 1);
|
||||
@ -399,13 +417,15 @@ export class InRaidHelper
|
||||
const dbQuest = this.questHelper.getQuestFromDb(lockedQuest.qid, null);
|
||||
if (!dbQuest)
|
||||
{
|
||||
this.logger.warning(`Unable to adjust locked quest: ${lockedQuest.qid} as it wasnt found in db. It may not become available later on`);
|
||||
this.logger.warning(
|
||||
`Unable to adjust locked quest: ${lockedQuest.qid} as it wasnt found in db. It may not become available later on`,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the time requirement in AvailableForStart array (assuming there is one as quest in locked state === its time-gated)
|
||||
const afsRequirement = dbQuest.conditions.AvailableForStart.find(x => x.conditionType === "Quest");
|
||||
const afsRequirement = dbQuest.conditions.AvailableForStart.find((x) => x.conditionType === "Quest");
|
||||
if (afsRequirement && afsRequirement.availableAfter > 0)
|
||||
{
|
||||
// Prereq quest has a wait
|
||||
|
@ -75,16 +75,18 @@ export class InventoryHelper
|
||||
* @param pmcData Player profile
|
||||
* @param output Client response object
|
||||
*/
|
||||
public addItemsToStash(sessionId: string, request: IAddItemsDirectRequest, pmcData: IPmcData, output: IItemEventRouterResponse): void
|
||||
public addItemsToStash(
|
||||
sessionId: string,
|
||||
request: IAddItemsDirectRequest,
|
||||
pmcData: IPmcData,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
// Check all items fit into inventory before adding
|
||||
if (!this.canPlaceItemsInInventory(sessionId, request.itemsWithModsToAdd))
|
||||
{
|
||||
// No space, exit
|
||||
this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
this.localisationService.getText("inventory-no_stash_space"),
|
||||
)
|
||||
this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("inventory-no_stash_space"));
|
||||
|
||||
return;
|
||||
}
|
||||
@ -95,7 +97,7 @@ export class InventoryHelper
|
||||
itemWithModsToAdd: itemToAdd,
|
||||
foundInRaid: request.foundInRaid,
|
||||
useSortingTable: request.useSortingTable,
|
||||
callback: request.callback
|
||||
callback: request.callback,
|
||||
};
|
||||
|
||||
// Add to player inventory
|
||||
@ -114,7 +116,12 @@ export class InventoryHelper
|
||||
* @param pmcData Player profile
|
||||
* @param output Client response object
|
||||
*/
|
||||
public addItemToStash(sessionId: string, request: IAddItemDirectRequest, pmcData: IPmcData, output: IItemEventRouterResponse): void
|
||||
public addItemToStash(
|
||||
sessionId: string,
|
||||
request: IAddItemDirectRequest,
|
||||
pmcData: IPmcData,
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
const itemWithModsToAddClone = this.jsonUtil.clone(request.itemWithModsToAdd);
|
||||
|
||||
@ -167,7 +174,11 @@ export class InventoryHelper
|
||||
output.profileChanges[sessionId].items.new.push(...itemWithModsToAddClone);
|
||||
pmcData.Inventory.items.push(...itemWithModsToAddClone);
|
||||
|
||||
this.logger.debug(`Added ${itemWithModsToAddClone[0].upd?.StackObjectsCount ?? 1} item: ${itemWithModsToAddClone[0]._tpl} with: ${itemWithModsToAddClone.length - 1} mods to inventory`);
|
||||
this.logger.debug(
|
||||
`Added ${itemWithModsToAddClone[0].upd?.StackObjectsCount ?? 1} item: ${
|
||||
itemWithModsToAddClone[0]._tpl
|
||||
} with: ${itemWithModsToAddClone.length - 1} mods to inventory`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -189,7 +200,6 @@ export class InventoryHelper
|
||||
{
|
||||
item.upd.SpawnedInSession = foundInRaid;
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (delete item.upd.SpawnedInSession)
|
||||
@ -222,10 +232,7 @@ export class InventoryHelper
|
||||
}
|
||||
}
|
||||
|
||||
public canPlaceItemsInInventory(
|
||||
sessionId: string,
|
||||
itemsWithChildren: Item[][]
|
||||
): boolean
|
||||
public canPlaceItemsInInventory(sessionId: string, itemsWithChildren: Item[][]): boolean
|
||||
{
|
||||
const pmcData = this.profileHelper.getPmcProfile(sessionId);
|
||||
|
||||
@ -241,10 +248,7 @@ export class InventoryHelper
|
||||
return true;
|
||||
}
|
||||
|
||||
public canPlaceItemInInventory(
|
||||
stashFS2D: number[][],
|
||||
itemWithChildren: Item[]
|
||||
): boolean
|
||||
public canPlaceItemInInventory(stashFS2D: number[][], itemWithChildren: Item[]): boolean
|
||||
{
|
||||
// Get x/y size of item
|
||||
const rootItem = itemWithChildren[0];
|
||||
@ -288,7 +292,8 @@ export class InventoryHelper
|
||||
itemWithChildren: Item[],
|
||||
playerInventory: Inventory,
|
||||
useSortingTable: boolean,
|
||||
output: IItemEventRouterResponse): void
|
||||
output: IItemEventRouterResponse,
|
||||
): void
|
||||
{
|
||||
// Get x/y size of item
|
||||
const rootItem = itemWithChildren[0];
|
||||
@ -317,9 +322,7 @@ export class InventoryHelper
|
||||
}
|
||||
catch (err)
|
||||
{
|
||||
const errorText = (typeof err === "string")
|
||||
? ` -> ${err}`
|
||||
: "";
|
||||
const errorText = (typeof err === "string") ? ` -> ${err}` : "";
|
||||
this.logger.error(this.localisationService.getText("inventory-fill_container_failed", errorText));
|
||||
|
||||
this.httpResponse.appendErrorToOutput(
|
||||
@ -388,12 +391,9 @@ export class InventoryHelper
|
||||
}
|
||||
else
|
||||
{
|
||||
this.httpResponse.appendErrorToOutput(
|
||||
output,
|
||||
this.localisationService.getText("inventory-no_stash_space"),
|
||||
);
|
||||
this.httpResponse.appendErrorToOutput(output, this.localisationService.getText("inventory-no_stash_space"));
|
||||
|
||||
return
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -137,15 +137,24 @@ export class ItemHelper
|
||||
}
|
||||
|
||||
// Check if item has slots that match soft insert name ids
|
||||
const softInsertSlotIds = ["groin", "soft_armor_back", "soft_armor_front", "soft_armor_left", "soft_armor_right", "shoulder_l", "shoulder_r", "collar"];
|
||||
if (itemDbDetails[1]._props.Slots.find(slot => softInsertSlotIds.includes(slot._name.toLowerCase())))
|
||||
const softInsertSlotIds = [
|
||||
"groin",
|
||||
"soft_armor_back",
|
||||
"soft_armor_front",
|
||||
"soft_armor_left",
|
||||
"soft_armor_right",
|
||||
"shoulder_l",
|
||||
"shoulder_r",
|
||||
"collar",
|
||||
];
|
||||
if (itemDbDetails[1]._props.Slots.find((slot) => softInsertSlotIds.includes(slot._name.toLowerCase())))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Also classified as BUILT_IN_INSERTS
|
||||
const helmetInsertSlotIds = ["helmet_top", "helmet_back", "helmet_ears"]
|
||||
if (itemDbDetails[1]._props.Slots.find(slot => helmetInsertSlotIds.includes(slot._name.toLowerCase())))
|
||||
const helmetInsertSlotIds = ["helmet_top", "helmet_back", "helmet_ears"];
|
||||
if (itemDbDetails[1]._props.Slots.find((slot) => helmetInsertSlotIds.includes(slot._name.toLowerCase())))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -446,8 +455,7 @@ export class ItemHelper
|
||||
|
||||
for (const itemFromAssort of assort)
|
||||
{
|
||||
if (itemFromAssort.parentId === itemIdToFind
|
||||
&& !list.find((item) => itemFromAssort._id === item._id))
|
||||
if (itemFromAssort.parentId === itemIdToFind && !list.find((item) => itemFromAssort._id === item._id))
|
||||
{
|
||||
list.push(itemFromAssort);
|
||||
list = list.concat(this.findAndReturnChildrenByAssort(itemFromAssort._id, assort));
|
||||
@ -604,9 +612,7 @@ export class ItemHelper
|
||||
{
|
||||
const filterResult = itemsToSearch.filter((item) =>
|
||||
{
|
||||
return by === "tpl"
|
||||
? (item._tpl === barterId)
|
||||
: (item._id === barterId);
|
||||
return by === "tpl" ? (item._tpl === barterId) : (item._id === barterId);
|
||||
});
|
||||
|
||||
matchingItems.push(...filterResult);
|
||||
@ -978,7 +984,15 @@ export class ItemHelper
|
||||
const cartridgeCountToAdd = (remainingSpace < maxPerStack) ? remainingSpace : maxPerStack;
|
||||
|
||||
// Add cartridge item into items array
|
||||
ammoBox.push(this.createCartridges(ammoBox[0]._id, cartridgeTpl, cartridgeCountToAdd, location, ammoBox[0].upd?.SpawnedInSession));
|
||||
ammoBox.push(
|
||||
this.createCartridges(
|
||||
ammoBox[0]._id,
|
||||
cartridgeTpl,
|
||||
cartridgeCountToAdd,
|
||||
location,
|
||||
ammoBox[0].upd?.SpawnedInSession,
|
||||
),
|
||||
);
|
||||
|
||||
currentStoredCartridgeCount += cartridgeCountToAdd;
|
||||
location++;
|
||||
@ -1068,7 +1082,9 @@ export class ItemHelper
|
||||
|
||||
if (!magazineCartridgeMaxCount)
|
||||
{
|
||||
this.logger.warning(`Magazine: ${magTemplate._id} ${magTemplate._name} lacks a Cartridges array, unable to fill magazine with ammo`);
|
||||
this.logger.warning(
|
||||
`Magazine: ${magTemplate._id} ${magTemplate._name} lacks a Cartridges array, unable to fill magazine with ammo`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -1104,10 +1120,11 @@ export class ItemHelper
|
||||
magazineWithChildCartridges.push(
|
||||
this.createCartridges(
|
||||
magazineWithChildCartridges[0]._id,
|
||||
cartridgeTpl, cartridgeCountToAdd,
|
||||
cartridgeTpl,
|
||||
cartridgeCountToAdd,
|
||||
location,
|
||||
magazineWithChildCartridges[0].upd?.SpawnedInSession
|
||||
)
|
||||
magazineWithChildCartridges[0].upd?.SpawnedInSession,
|
||||
),
|
||||
);
|
||||
|
||||
currentStoredCartridgeCount += cartridgeCountToAdd;
|
||||
@ -1170,7 +1187,13 @@ export class ItemHelper
|
||||
* @param foundInRaid OPTIONAL - Are cartridges found in raid (SpawnedInSession)
|
||||
* @returns Item
|
||||
*/
|
||||
public createCartridges(parentId: string, ammoTpl: string, stackCount: number, location: number, foundInRaid = false): Item
|
||||
public createCartridges(
|
||||
parentId: string,
|
||||
ammoTpl: string,
|
||||
stackCount: number,
|
||||
location: number,
|
||||
foundInRaid = false,
|
||||
): Item
|
||||
{
|
||||
return {
|
||||
_id: this.objectId.generate(),
|
||||
@ -1178,10 +1201,7 @@ export class ItemHelper
|
||||
parentId: parentId,
|
||||
slotId: "cartridges",
|
||||
location: location,
|
||||
upd: {
|
||||
StackObjectsCount: stackCount,
|
||||
SpawnedInSession: foundInRaid
|
||||
},
|
||||
upd: { StackObjectsCount: stackCount, SpawnedInSession: foundInRaid },
|
||||
};
|
||||
}
|
||||
|
||||
@ -1225,7 +1245,12 @@ export class ItemHelper
|
||||
* @param requiredOnly Only add required mods
|
||||
* @returns Item with children
|
||||
*/
|
||||
public addChildSlotItems(itemToAdd: Item[], itemToAddTemplate: ITemplateItem, modSpawnChanceDict: Record<string, number> = null, requiredOnly = false): Item[]
|
||||
public addChildSlotItems(
|
||||
itemToAdd: Item[],
|
||||
itemToAddTemplate: ITemplateItem,
|
||||
modSpawnChanceDict: Record<string, number> = null,
|
||||
requiredOnly = false,
|
||||
): Item[]
|
||||
{
|
||||
const result = itemToAdd;
|
||||
const incompatibleModTpls: Set<string> = new Set();
|
||||
@ -1255,7 +1280,9 @@ export class ItemHelper
|
||||
const chosenTpl = this.getCompatibleTplFromArray(itemPool, incompatibleModTpls);
|
||||
if (!chosenTpl)
|
||||
{
|
||||
this.logger.debug(`Unable to add mod to item: ${itemToAddTemplate._id} ${itemToAddTemplate._name} slot: ${slot._name} as no compatible tpl could be found in pool of ${itemPool.length}, skipping`);
|
||||
this.logger.debug(
|
||||
`Unable to add mod to item: ${itemToAddTemplate._id} ${itemToAddTemplate._name} slot: ${slot._name} as no compatible tpl could be found in pool of ${itemPool.length}, skipping`,
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
@ -1264,7 +1291,7 @@ export class ItemHelper
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: chosenTpl,
|
||||
parentId: result[0]._id,
|
||||
slotId: slot._name
|
||||
slotId: slot._name,
|
||||
};
|
||||
result.push(modItemToAdd);
|
||||
|
||||
@ -1300,7 +1327,7 @@ export class ItemHelper
|
||||
if (incompatibleModTpls.has(tpl))
|
||||
{
|
||||
// Incompatible tpl was chosen, try again
|
||||
count++
|
||||
count++;
|
||||
if (count >= possibleTpls.length)
|
||||
{
|
||||
return null;
|
||||
|
@ -285,8 +285,8 @@ export class QuestHelper
|
||||
item.upd.SpawnedInSession = true;
|
||||
|
||||
// Separate base item from mods, fix stacks
|
||||
if (item._id === questReward.target) // Is base reward item
|
||||
{
|
||||
if (item._id === questReward.target)
|
||||
{ // Is base reward item
|
||||
if (
|
||||
(item.parentId !== undefined) && (item.parentId === "hideout") // Has parentId of hideout
|
||||
&& (item.upd !== undefined) && (item.upd.StackObjectsCount !== undefined) // Has upd with stackobject count
|
||||
@ -350,12 +350,14 @@ export class QuestHelper
|
||||
questReward.items = this.jsonUtil.clone(defaultPreset._items);
|
||||
|
||||
// Remap target id to the new presets id
|
||||
questReward.target = questReward.items.find(item => item._tpl === originalRewardRootItem._tpl)._id;
|
||||
questReward.target = questReward.items.find((item) => item._tpl === originalRewardRootItem._tpl)._id;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.logger.warning(`Unable to find default preset for armor ${originalRewardRootItem._tpl}, adding mods manually`);
|
||||
this.logger.warning(
|
||||
`Unable to find default preset for armor ${originalRewardRootItem._tpl}, adding mods manually`,
|
||||
);
|
||||
const itemDbData = this.itemHelper.getItem(originalRewardRootItem._tpl)[1];
|
||||
|
||||
// Hydrate reward with only 'required' mods - necessary for things like helmets otherwise you end up with nvgs/visors etc
|
||||
@ -372,9 +374,7 @@ export class QuestHelper
|
||||
{
|
||||
// Iterate over all rewards with the desired status, flatten out items that have a type of Item
|
||||
const questRewards = quest.rewards[QuestStatus[status]].flatMap((reward: IQuestReward) =>
|
||||
reward.type === "Item"
|
||||
? this.processReward(reward)
|
||||
: []
|
||||
reward.type === "Item" ? this.processReward(reward) : []
|
||||
);
|
||||
|
||||
return questRewards;
|
||||
@ -654,7 +654,9 @@ export class QuestHelper
|
||||
public getQuestWithOnlyLevelRequirementStartCondition(quest: IQuest): IQuest
|
||||
{
|
||||
quest = this.jsonUtil.clone(quest);
|
||||
quest.conditions.AvailableForStart = quest.conditions.AvailableForStart.filter((q) => q.conditionType === "Level");
|
||||
quest.conditions.AvailableForStart = quest.conditions.AvailableForStart.filter((q) =>
|
||||
q.conditionType === "Level"
|
||||
);
|
||||
|
||||
return quest;
|
||||
}
|
||||
@ -686,7 +688,7 @@ export class QuestHelper
|
||||
// Create a dialog message for completing the quest.
|
||||
const quest = this.getQuestFromDb(failRequest.qid, pmcData);
|
||||
|
||||
const questIsRepeatable = pmcData.RepeatableQuests.some(quest => quest.id === failRequest.qid);
|
||||
const questIsRepeatable = pmcData.RepeatableQuests.some((quest) => quest.id === failRequest.qid);
|
||||
if (!questIsRepeatable)
|
||||
{
|
||||
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
||||
@ -1007,9 +1009,7 @@ export class QuestHelper
|
||||
const questInDb = allQuests.find((x) => x._id === questId);
|
||||
if (!questInDb)
|
||||
{
|
||||
this.logger.debug(
|
||||
`Unable to find quest: ${questId} in db, cannot get 'FindItem' condition, skipping`,
|
||||
);
|
||||
this.logger.debug(`Unable to find quest: ${questId} in db, cannot get 'FindItem' condition, skipping`);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -565,7 +565,10 @@ export class RagfairOfferHelper
|
||||
|
||||
// Filter out presets when search request has multiple buildItems
|
||||
// Assuming 1 build item = single item e.g. gun
|
||||
if (searchRequest.buildCount && this.presetHelper.hasPreset(offerRootItem._tpl) && Object.keys(searchRequest.buildItems).length > 1)
|
||||
if (
|
||||
searchRequest.buildCount && this.presetHelper.hasPreset(offerRootItem._tpl)
|
||||
&& Object.keys(searchRequest.buildItems).length > 1
|
||||
)
|
||||
{
|
||||
// Don't include preset offer
|
||||
return false;
|
||||
@ -610,7 +613,8 @@ export class RagfairOfferHelper
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isConditionItem(offerRootItem)
|
||||
if (
|
||||
this.isConditionItem(offerRootItem)
|
||||
&& !this.itemQualityInRange(offerRootItem, searchRequest.conditionFrom, searchRequest.conditionTo)
|
||||
)
|
||||
{
|
||||
@ -702,7 +706,8 @@ export class RagfairOfferHelper
|
||||
{
|
||||
// thanks typescript, undefined assertion is not returnable since it
|
||||
// tries to return a multitype object
|
||||
return (item.upd.MedKit || item.upd.Repairable || item.upd.Resource || item.upd.FoodDrink || item.upd.Key || item.upd.RepairKit)
|
||||
return (item.upd.MedKit || item.upd.Repairable || item.upd.Resource || item.upd.FoodDrink || item.upd.Key
|
||||
|| item.upd.RepairKit)
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
@ -73,7 +73,10 @@ export class RagfairSellHelper
|
||||
const startTime = this.timeUtil.getTimestamp();
|
||||
|
||||
// Get a time in future to stop simulating sell chances at
|
||||
const endTime = startTime + this.timeUtil.getHoursAsSeconds(this.databaseServer.getTables().globals.config.RagFair.offerDurationTimeInHour);
|
||||
const endTime = startTime
|
||||
+ this.timeUtil.getHoursAsSeconds(
|
||||
this.databaseServer.getTables().globals.config.RagFair.offerDurationTimeInHour,
|
||||
);
|
||||
|
||||
let sellTime = startTime;
|
||||
let remainingCount = itemSellCount;
|
||||
@ -106,7 +109,10 @@ export class RagfairSellHelper
|
||||
const weighting = (100 - sellChancePercent) / 100;
|
||||
let maximumTime = weighting * (this.ragfairConfig.sell.time.max * 60);
|
||||
const minimumTime = this.ragfairConfig.sell.time.min * 60;
|
||||
if (maximumTime < minimumTime) maximumTime = minimumTime + 5;
|
||||
if (maximumTime < minimumTime)
|
||||
{
|
||||
maximumTime = minimumTime + 5;
|
||||
}
|
||||
// Sell time will be random between min/max
|
||||
sellTime += Math.floor(Math.random() * (maximumTime - minimumTime) + minimumTime);
|
||||
|
||||
|
@ -160,7 +160,9 @@ export class RagfairServerHelper
|
||||
MessageType.MESSAGE_WITH_ITEMS,
|
||||
RagfairServerHelper.goodsReturnedTemplate,
|
||||
returnedItems,
|
||||
this.timeUtil.getHoursAsSeconds(this.databaseServer.getTables().globals.config.RagFair.yourOfferDidNotSellMaxStorageTimeInHour),
|
||||
this.timeUtil.getHoursAsSeconds(
|
||||
this.databaseServer.getTables().globals.config.RagFair.yourOfferDidNotSellMaxStorageTimeInHour,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,8 @@ export class TradeHelper
|
||||
@inject("InventoryHelper") protected inventoryHelper: InventoryHelper,
|
||||
@inject("RagfairServer") protected ragfairServer: RagfairServer,
|
||||
@inject("TraderAssortHelper") protected traderAssortHelper: TraderAssortHelper,
|
||||
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService: TraderPurchasePersisterService,
|
||||
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService:
|
||||
TraderPurchasePersisterService,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{
|
||||
@ -71,7 +72,7 @@ export class TradeHelper
|
||||
): void
|
||||
{
|
||||
let offerItems: Item[] = [];
|
||||
let buyCallback: { (buyCount: number) };
|
||||
let buyCallback: { (buyCount: number); };
|
||||
if (buyRequestData.tid.toLocaleLowerCase() === "ragfair")
|
||||
{
|
||||
buyCallback = (buyCount: number) =>
|
||||
@ -86,18 +87,21 @@ export class TradeHelper
|
||||
const assortHasBuyRestrictions = this.itemHelper.hasBuyRestrictions(itemPurchased);
|
||||
if (assortHasBuyRestrictions)
|
||||
{
|
||||
this.checkPurchaseIsWithinTraderItemLimit(sessionID, buyRequestData.tid, itemPurchased, buyRequestData.item_id, buyCount);
|
||||
this.checkPurchaseIsWithinTraderItemLimit(
|
||||
sessionID,
|
||||
buyRequestData.tid,
|
||||
itemPurchased,
|
||||
buyRequestData.item_id,
|
||||
buyCount,
|
||||
);
|
||||
}
|
||||
|
||||
// Decrement trader item count
|
||||
if (this.traderConfig.persistPurchaseDataInProfile && assortHasBuyRestrictions)
|
||||
{
|
||||
const itemPurchaseDat = {
|
||||
items: [{
|
||||
itemId: buyRequestData.item_id,
|
||||
count: buyCount
|
||||
}],
|
||||
traderId: buyRequestData.tid
|
||||
items: [{ itemId: buyRequestData.item_id, count: buyCount }],
|
||||
traderId: buyRequestData.tid,
|
||||
};
|
||||
this.traderHelper.addTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDat);
|
||||
}
|
||||
@ -150,12 +154,17 @@ export class TradeHelper
|
||||
const traderAssorts = this.traderHelper.getTraderAssortsByTraderId(buyRequestData.tid).items;
|
||||
const itemPurchased = traderAssorts.find((x) => x._id === buyRequestData.item_id);
|
||||
|
||||
|
||||
// Ensure purchase does not exceed trader item limit
|
||||
const assortHasBuyRestrictions = this.itemHelper.hasBuyRestrictions(itemPurchased);
|
||||
if (assortHasBuyRestrictions)
|
||||
{
|
||||
this.checkPurchaseIsWithinTraderItemLimit(sessionID, buyRequestData.tid, itemPurchased, buyRequestData.item_id, buyCount);
|
||||
this.checkPurchaseIsWithinTraderItemLimit(
|
||||
sessionID,
|
||||
buyRequestData.tid,
|
||||
itemPurchased,
|
||||
buyRequestData.item_id,
|
||||
buyCount,
|
||||
);
|
||||
}
|
||||
|
||||
// Decrement trader item count
|
||||
@ -164,11 +173,8 @@ export class TradeHelper
|
||||
if (this.traderConfig.persistPurchaseDataInProfile && assortHasBuyRestrictions)
|
||||
{
|
||||
const itemPurchaseDat = {
|
||||
items: [{
|
||||
itemId: buyRequestData.item_id,
|
||||
count: buyCount
|
||||
}],
|
||||
traderId: buyRequestData.tid
|
||||
items: [{ itemId: buyRequestData.item_id, count: buyCount }],
|
||||
traderId: buyRequestData.tid,
|
||||
};
|
||||
this.traderHelper.addTraderPurchasesToPlayerProfile(sessionID, itemPurchaseDat);
|
||||
}
|
||||
@ -209,7 +215,7 @@ export class TradeHelper
|
||||
itemWithModsToAdd: this.itemHelper.reparentItemAndChildren(offerItems[0], offerItems),
|
||||
foundInRaid: foundInRaid,
|
||||
callback: buyCallback,
|
||||
useSortingTable: false
|
||||
useSortingTable: false,
|
||||
};
|
||||
|
||||
// Add item + children to stash
|
||||
@ -299,9 +305,19 @@ export class TradeHelper
|
||||
* @param assortId Id of assort being purchased
|
||||
* @param count How many of the item are being bought
|
||||
*/
|
||||
protected checkPurchaseIsWithinTraderItemLimit(sessionId: string, traderId: string, assortBeingPurchased: Item, assortId: string, count: number): void
|
||||
protected checkPurchaseIsWithinTraderItemLimit(
|
||||
sessionId: string,
|
||||
traderId: string,
|
||||
assortBeingPurchased: Item,
|
||||
assortId: string,
|
||||
count: number,
|
||||
): void
|
||||
{
|
||||
const traderPurchaseData = this.traderPurchasePersisterService.getProfileTraderPurchase(sessionId, traderId, assortBeingPurchased._id);
|
||||
const traderPurchaseData = this.traderPurchasePersisterService.getProfileTraderPurchase(
|
||||
sessionId,
|
||||
traderId,
|
||||
assortBeingPurchased._id,
|
||||
);
|
||||
if ((traderPurchaseData?.count ?? 0 + count) > assortBeingPurchased.upd?.BuyRestrictionMax)
|
||||
{
|
||||
throw new Error(
|
||||
|
@ -148,7 +148,7 @@ export class TraderAssortHelper
|
||||
protected resetBuyRestrictionCurrentValue(assortItems: Item[]): void
|
||||
{
|
||||
// iterate over root items
|
||||
for (const assort of assortItems.filter(item => item.slotId === "hideout"))
|
||||
for (const assort of assortItems.filter((item) => item.slotId === "hideout"))
|
||||
{
|
||||
// no value to adjust
|
||||
if (!assort.upd.BuyRestrictionCurrent)
|
||||
|
@ -146,10 +146,12 @@ export class PreAkiModLoader implements IModLoader
|
||||
const modOrder = this.vfs.readFile(this.modOrderPath, { encoding: "utf8" });
|
||||
try
|
||||
{
|
||||
this.jsonUtil.deserialize<any>(modOrder, this.modOrderPath).order.forEach((mod: string, index: number) =>
|
||||
this.jsonUtil.deserialize<any>(modOrder, this.modOrderPath).order.forEach(
|
||||
(mod: string, index: number) =>
|
||||
{
|
||||
this.order[mod] = index;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
catch (error)
|
||||
{
|
||||
@ -429,7 +431,7 @@ export class PreAkiModLoader implements IModLoader
|
||||
public sortModsLoadOrder(): string[]
|
||||
{
|
||||
// if loadorder.json exists: load it, otherwise generate load order
|
||||
const loadOrderPath = `${this.basepath}loadorder.json`
|
||||
const loadOrderPath = `${this.basepath}loadorder.json`;
|
||||
if (this.vfs.exists(loadOrderPath))
|
||||
{
|
||||
return this.jsonUtil.deserialize(this.vfs.readFile(loadOrderPath), loadOrderPath);
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { IMagazineTemplateAmmoItem } from "../profile/IAkiProfile"
|
||||
import { IMagazineTemplateAmmoItem } from "../profile/IAkiProfile";
|
||||
|
||||
export interface ISetMagazineRequest
|
||||
{
|
||||
Id: string
|
||||
Name: string
|
||||
Caliber: string
|
||||
Items: IMagazineTemplateAmmoItem[]
|
||||
TopCount: number
|
||||
BottomCount: number
|
||||
Id: string;
|
||||
Name: string;
|
||||
Caliber: string;
|
||||
Items: IMagazineTemplateAmmoItem[];
|
||||
TopCount: number;
|
||||
BottomCount: number;
|
||||
}
|
@ -77,9 +77,9 @@ export interface IConfig
|
||||
SkillFatigueReset: number;
|
||||
DiscardLimitsEnabled: boolean;
|
||||
EventSettings: IEventSettings;
|
||||
FavoriteItemsSettings: IFavoriteItemsSettings
|
||||
VaultingSettings: IVaultingSettings
|
||||
BTRSettings: IBTRSettings
|
||||
FavoriteItemsSettings: IFavoriteItemsSettings;
|
||||
VaultingSettings: IVaultingSettings;
|
||||
BTRSettings: IBTRSettings;
|
||||
EventType: string[];
|
||||
WalkSpeed: Ixyz;
|
||||
SprintSpeed: Ixyz;
|
||||
@ -1036,98 +1036,97 @@ export interface IRestrictionsInRaid
|
||||
|
||||
export interface IFavoriteItemsSettings
|
||||
{
|
||||
WeaponStandMaxItemsCount: number
|
||||
PlaceOfFameMaxItemsCount: number
|
||||
WeaponStandMaxItemsCount: number;
|
||||
PlaceOfFameMaxItemsCount: number;
|
||||
}
|
||||
|
||||
export interface IVaultingSettings
|
||||
{
|
||||
IsActive: boolean
|
||||
VaultingInputTime: number
|
||||
GridSettings: IVaultingGridSettings
|
||||
MovesSettings: IVaultingMovesSettings
|
||||
IsActive: boolean;
|
||||
VaultingInputTime: number;
|
||||
GridSettings: IVaultingGridSettings;
|
||||
MovesSettings: IVaultingMovesSettings;
|
||||
}
|
||||
|
||||
export interface IVaultingGridSettings
|
||||
{
|
||||
GridSizeX: number
|
||||
GridSizeY: number
|
||||
GridSizeZ: number
|
||||
SteppingLengthX: number
|
||||
SteppingLengthY: number
|
||||
SteppingLengthZ: number
|
||||
GridOffsetX: number
|
||||
GridOffsetY: number
|
||||
GridOffsetZ: number
|
||||
OffsetFactor: number
|
||||
|
||||
GridSizeX: number;
|
||||
GridSizeY: number;
|
||||
GridSizeZ: number;
|
||||
SteppingLengthX: number;
|
||||
SteppingLengthY: number;
|
||||
SteppingLengthZ: number;
|
||||
GridOffsetX: number;
|
||||
GridOffsetY: number;
|
||||
GridOffsetZ: number;
|
||||
OffsetFactor: number;
|
||||
}
|
||||
|
||||
export interface IVaultingMovesSettings
|
||||
{
|
||||
VaultSettings: IVaultingSubMoveSettings
|
||||
ClimbSettings: IVaultingSubMoveSettings
|
||||
VaultSettings: IVaultingSubMoveSettings;
|
||||
ClimbSettings: IVaultingSubMoveSettings;
|
||||
}
|
||||
|
||||
export interface IVaultingSubMoveSettings
|
||||
{
|
||||
IsActive: boolean
|
||||
MaxWithoutHandHeight: number
|
||||
SpeedRange: Ixyz
|
||||
MoveRestrictions: IMoveRestrictions
|
||||
AutoMoveRestrictions: IMoveRestrictions
|
||||
IsActive: boolean;
|
||||
MaxWithoutHandHeight: number;
|
||||
SpeedRange: Ixyz;
|
||||
MoveRestrictions: IMoveRestrictions;
|
||||
AutoMoveRestrictions: IMoveRestrictions;
|
||||
}
|
||||
|
||||
export interface IMoveRestrictions
|
||||
{
|
||||
IsActive: boolean
|
||||
MinDistantToInteract: number
|
||||
MinHeight: number
|
||||
MaxHeight: number
|
||||
MinLength: number
|
||||
MaxLength: number
|
||||
IsActive: boolean;
|
||||
MinDistantToInteract: number;
|
||||
MinHeight: number;
|
||||
MaxHeight: number;
|
||||
MinLength: number;
|
||||
MaxLength: number;
|
||||
}
|
||||
|
||||
export interface IBTRSettings
|
||||
{
|
||||
LocationsWithBTR: string[]
|
||||
BasePriceTaxi: number
|
||||
AddPriceTaxi: number
|
||||
CleanUpPrice: number
|
||||
DeliveryPrice: number
|
||||
ModDeliveryCost: number
|
||||
BearPriceMod: number
|
||||
UsecPriceMod: number
|
||||
ScavPriceMod: number
|
||||
CoefficientDiscountCharisma: number
|
||||
DeliveryMinPrice: number
|
||||
TaxiMinPrice: number
|
||||
BotCoverMinPrice: number
|
||||
MapsConfigs: Record<string, IBtrMapConfig>
|
||||
DiameterWheel: number
|
||||
HeightWheel: number
|
||||
HeightWheelMaxPosLimit: number
|
||||
HeightWheelMinPosLimit: number
|
||||
SnapToSurfaceWheelsSpeed: number
|
||||
CheckSurfaceForWheelsTimer: number
|
||||
HeightWheelOffset: number
|
||||
LocationsWithBTR: string[];
|
||||
BasePriceTaxi: number;
|
||||
AddPriceTaxi: number;
|
||||
CleanUpPrice: number;
|
||||
DeliveryPrice: number;
|
||||
ModDeliveryCost: number;
|
||||
BearPriceMod: number;
|
||||
UsecPriceMod: number;
|
||||
ScavPriceMod: number;
|
||||
CoefficientDiscountCharisma: number;
|
||||
DeliveryMinPrice: number;
|
||||
TaxiMinPrice: number;
|
||||
BotCoverMinPrice: number;
|
||||
MapsConfigs: Record<string, IBtrMapConfig>;
|
||||
DiameterWheel: number;
|
||||
HeightWheel: number;
|
||||
HeightWheelMaxPosLimit: number;
|
||||
HeightWheelMinPosLimit: number;
|
||||
SnapToSurfaceWheelsSpeed: number;
|
||||
CheckSurfaceForWheelsTimer: number;
|
||||
HeightWheelOffset: number;
|
||||
}
|
||||
|
||||
export interface IBtrMapConfig
|
||||
{
|
||||
mapID: string
|
||||
pathsConfigurations: IBtrMapConfig[]
|
||||
mapID: string;
|
||||
pathsConfigurations: IBtrMapConfig[];
|
||||
}
|
||||
|
||||
export interface IBtrMapConfig
|
||||
{
|
||||
id: string
|
||||
enterPoint: string
|
||||
exitPoint: string
|
||||
pathPoints: string[]
|
||||
once: boolean
|
||||
circle: boolean
|
||||
circleCount: number
|
||||
id: string;
|
||||
enterPoint: string;
|
||||
exitPoint: string;
|
||||
pathPoints: string[];
|
||||
once: boolean;
|
||||
circle: boolean;
|
||||
circleCount: number;
|
||||
}
|
||||
|
||||
export interface ISquadSettings
|
||||
@ -1643,11 +1642,11 @@ export interface IFenceLevel
|
||||
BotHelpChance: number;
|
||||
BotSpreadoutChance: number;
|
||||
BotStopChance: number;
|
||||
PriceModTaxi: number
|
||||
PriceModDelivery: number
|
||||
PriceModCleanUp: number
|
||||
DeliveryGridSize: Ixyz
|
||||
CanInteractWithBtr: boolean
|
||||
PriceModTaxi: number;
|
||||
PriceModDelivery: number;
|
||||
PriceModCleanUp: number;
|
||||
DeliveryGridSize: Ixyz;
|
||||
CanInteractWithBtr: boolean;
|
||||
}
|
||||
|
||||
export interface IInertia
|
||||
@ -1766,13 +1765,14 @@ export interface IAudioGroupPreset
|
||||
|
||||
export interface IEnvironmentSettings
|
||||
{
|
||||
SnowStepsVolumeMultiplier: number
|
||||
SurfaceMultipliers: ISurfaceMultiplier[]
|
||||
SnowStepsVolumeMultiplier: number;
|
||||
SurfaceMultipliers: ISurfaceMultiplier[];
|
||||
}
|
||||
|
||||
export interface ISurfaceMultiplier {
|
||||
SurfaceType: string
|
||||
VolumeMult: number
|
||||
export interface ISurfaceMultiplier
|
||||
{
|
||||
SurfaceType: string;
|
||||
VolumeMult: number;
|
||||
}
|
||||
|
||||
export interface IBotWeaponScattering
|
||||
|
@ -229,7 +229,7 @@ export interface Exit
|
||||
Chance: number;
|
||||
Count: number;
|
||||
EntryPoints: string;
|
||||
EventAvailable: boolean
|
||||
EventAvailable: boolean;
|
||||
ExfiltrationTime: number;
|
||||
ExfiltrationType: string;
|
||||
RequiredSlot?: string;
|
||||
|
@ -5,12 +5,12 @@ export interface IPmcData extends IBotBase
|
||||
|
||||
export interface IPostRaidPmcData extends IBotBase
|
||||
{
|
||||
Stats: IPostRaidStats
|
||||
Stats: IPostRaidStats;
|
||||
}
|
||||
|
||||
export interface IPostRaidStats
|
||||
{
|
||||
Eft: IEftStats
|
||||
Eft: IEftStats;
|
||||
/** Only found in profile we get from client post raid */
|
||||
Arena: IEftStats
|
||||
Arena: IEftStats;
|
||||
}
|
@ -1,20 +1,20 @@
|
||||
import { IQuestConditionTypes, IQuestRewards } from "./IQuest"
|
||||
import { IQuestConditionTypes, IQuestRewards } from "./IQuest";
|
||||
|
||||
export interface IAchievement
|
||||
{
|
||||
id: string
|
||||
imageUrl: string
|
||||
assetPath: string
|
||||
rewards: IQuestRewards
|
||||
conditions: IQuestConditionTypes
|
||||
instantComplete: boolean
|
||||
showNotificationsInGame: boolean
|
||||
showProgress: boolean
|
||||
prefab: string
|
||||
rarity: string
|
||||
hidden: boolean
|
||||
showConditions: boolean
|
||||
progressBarEnabled: boolean
|
||||
side: string
|
||||
index: number
|
||||
id: string;
|
||||
imageUrl: string;
|
||||
assetPath: string;
|
||||
rewards: IQuestRewards;
|
||||
conditions: IQuestConditionTypes;
|
||||
instantComplete: boolean;
|
||||
showNotificationsInGame: boolean;
|
||||
showProgress: boolean;
|
||||
prefab: string;
|
||||
rarity: string;
|
||||
hidden: boolean;
|
||||
showConditions: boolean;
|
||||
progressBarEnabled: boolean;
|
||||
side: string;
|
||||
index: number;
|
||||
}
|
@ -29,7 +29,7 @@ export interface IBotBase
|
||||
UnlockedInfo: IUnlockedInfo;
|
||||
RagfairInfo: RagfairInfo;
|
||||
/** Achievement id and timestamp */
|
||||
Achievements: Record<string, number>
|
||||
Achievements: Record<string, number>;
|
||||
RepeatableQuests: IPmcDataRepeatableQuest[];
|
||||
Bonuses: Bonus[];
|
||||
Notes: Notes;
|
||||
@ -43,11 +43,11 @@ export interface IBotBase
|
||||
|
||||
export interface ITaskConditionCounter
|
||||
{
|
||||
id: string
|
||||
type: string
|
||||
value: number
|
||||
id: string;
|
||||
type: string;
|
||||
value: number;
|
||||
/** Quest id */
|
||||
sourceId: string
|
||||
sourceId: string;
|
||||
}
|
||||
|
||||
export interface IUnlockedInfo
|
||||
@ -165,7 +165,7 @@ export interface Inventory
|
||||
/** Key is hideout area enum numeric as string e.g. "24", value is area _id */
|
||||
hideoutAreaStashes: Record<string, string>;
|
||||
fastPanel: Record<string, string>;
|
||||
favoriteItems: string[]
|
||||
favoriteItems: string[];
|
||||
}
|
||||
|
||||
export interface IBaseJsonSkills
|
||||
|
@ -54,10 +54,10 @@ export interface IQuestCondition
|
||||
{
|
||||
id: string;
|
||||
index?: number;
|
||||
compareMethod?: string
|
||||
compareMethod?: string;
|
||||
dynamicLocale: boolean;
|
||||
visibilityConditions?: VisibilityCondition[];
|
||||
globalQuestCounterId?: string
|
||||
globalQuestCounterId?: string;
|
||||
parentId?: string;
|
||||
target: string[] | string;
|
||||
value?: string | number;
|
||||
@ -75,7 +75,7 @@ export interface IQuestCondition
|
||||
plantTime?: number;
|
||||
zoneId?: string;
|
||||
countInRaid?: boolean;
|
||||
completeInSeconds?: number
|
||||
completeInSeconds?: number;
|
||||
isEncoded?: boolean;
|
||||
conditionType?: string;
|
||||
}
|
||||
@ -89,48 +89,48 @@ export interface IQuestConditionCounter
|
||||
export interface IQuestConditionCounterCondition
|
||||
{
|
||||
id: string;
|
||||
dynamicLocale: boolean
|
||||
dynamicLocale: boolean;
|
||||
target?: string[] | string; // TODO: some objects have an array and some are just strings, thanks bsg very cool
|
||||
completeInSeconds?: number
|
||||
energy?: IValueCompare
|
||||
completeInSeconds?: number;
|
||||
energy?: IValueCompare;
|
||||
exitName?: string;
|
||||
hydration?: IValueCompare
|
||||
time?: IValueCompare
|
||||
hydration?: IValueCompare;
|
||||
time?: IValueCompare;
|
||||
compareMethod?: string;
|
||||
value?: number;
|
||||
weapon?: string[];
|
||||
distance?: ICounterConditionDistance
|
||||
distance?: ICounterConditionDistance;
|
||||
equipmentInclusive?: string[][];
|
||||
weaponModsInclusive?: string[][];
|
||||
weaponModsExclusive?: string[][];
|
||||
enemyEquipmentInclusive?: string[][];
|
||||
enemyEquipmentExclusive?: string[][];
|
||||
weaponCaliber?: string[]
|
||||
savageRole?: string[]
|
||||
weaponCaliber?: string[];
|
||||
savageRole?: string[];
|
||||
status?: string[];
|
||||
bodyPart?: string[];
|
||||
daytime?: IDaytimeCounter;
|
||||
conditionType?: string
|
||||
enemyHealthEffects?: IEnemyHealthEffect[]
|
||||
resetOnSessionEnd?: boolean
|
||||
conditionType?: string;
|
||||
enemyHealthEffects?: IEnemyHealthEffect[];
|
||||
resetOnSessionEnd?: boolean;
|
||||
}
|
||||
|
||||
export interface IEnemyHealthEffect
|
||||
{
|
||||
bodyParts: string[]
|
||||
effects: string[]
|
||||
bodyParts: string[];
|
||||
effects: string[];
|
||||
}
|
||||
|
||||
export interface IValueCompare
|
||||
{
|
||||
compareMethod: string
|
||||
value: number
|
||||
compareMethod: string;
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface ICounterConditionDistance
|
||||
{
|
||||
value: number
|
||||
compareMethod: string
|
||||
value: number;
|
||||
compareMethod: string;
|
||||
}
|
||||
|
||||
export interface IDaytimeCounter
|
||||
@ -142,7 +142,7 @@ export interface IDaytimeCounter
|
||||
export interface VisibilityCondition
|
||||
{
|
||||
id: string;
|
||||
target: string
|
||||
target: string;
|
||||
value?: number;
|
||||
dynamicLocale?: boolean;
|
||||
oneSessionOnly: boolean;
|
||||
|
@ -2,9 +2,9 @@ import { IQuest, IQuestConditionTypes, IQuestRewards } from "./IQuest";
|
||||
|
||||
export interface IRepeatableQuest extends IQuest
|
||||
{
|
||||
changeCost: IChangeCost[]
|
||||
changeCost: IChangeCost[];
|
||||
changeStandingCost: number;
|
||||
sptRepatableGroupName: string
|
||||
sptRepatableGroupName: string;
|
||||
}
|
||||
|
||||
export interface IRepeatableQuestDatabase
|
||||
|
@ -86,7 +86,7 @@ export interface Props
|
||||
EffectiveDistance?: number;
|
||||
Ergonomics?: number;
|
||||
Velocity?: number;
|
||||
WithAnimatorAiming?: boolean
|
||||
WithAnimatorAiming?: boolean;
|
||||
RaidModdable?: boolean;
|
||||
ToolModdable?: boolean;
|
||||
UniqueAnimationModID?: number;
|
||||
@ -188,9 +188,9 @@ export interface Props
|
||||
weapUseType?: string;
|
||||
ammoCaliber?: string;
|
||||
OperatingResource?: number;
|
||||
PostRecoilHorizontalRangeHandRotation?: Ixyz
|
||||
PostRecoilVerticalRangeHandRotation?: Ixyz
|
||||
ProgressRecoilAngleOnStable?: Ixyz
|
||||
PostRecoilHorizontalRangeHandRotation?: Ixyz;
|
||||
PostRecoilVerticalRangeHandRotation?: Ixyz;
|
||||
ProgressRecoilAngleOnStable?: Ixyz;
|
||||
RepairComplexity?: number;
|
||||
durabSpawnMin?: number;
|
||||
durabSpawnMax?: number;
|
||||
@ -198,7 +198,7 @@ export interface Props
|
||||
RecoilForceUp?: number;
|
||||
RecoilForceBack?: number;
|
||||
RecoilAngle?: number;
|
||||
RecoilCamera?: number
|
||||
RecoilCamera?: number;
|
||||
weapFireType?: string[];
|
||||
RecolDispersion?: number;
|
||||
SingleFireRate?: number;
|
||||
@ -206,7 +206,7 @@ export interface Props
|
||||
bFirerate?: number;
|
||||
bEffDist?: number;
|
||||
bHearDist?: number;
|
||||
blockLeftStance?: boolean
|
||||
blockLeftStance?: boolean;
|
||||
isChamberLoad?: boolean;
|
||||
chamberAmmoCount?: number;
|
||||
isBoltCatch?: boolean;
|
||||
@ -216,8 +216,8 @@ export interface Props
|
||||
shotgunDispersion?: number;
|
||||
Chambers?: Slot[];
|
||||
CameraSnap?: number;
|
||||
CameraToWeaponAngleSpeedRange?: Ixyz
|
||||
CameraToWeaponAngleStep?: number
|
||||
CameraToWeaponAngleSpeedRange?: Ixyz;
|
||||
CameraToWeaponAngleStep?: number;
|
||||
ReloadMode?: string;
|
||||
AimPlane?: number;
|
||||
TacticalReloadStiffnes?: Ixyz;
|
||||
@ -225,7 +225,7 @@ export interface Props
|
||||
RecoilCenter?: Ixyz;
|
||||
RotationCenter?: Ixyz;
|
||||
RotationCenterNoStock?: Ixyz;
|
||||
ShotsGroupSettings?: IShotsGroupSettings[]
|
||||
ShotsGroupSettings?: IShotsGroupSettings[];
|
||||
FoldedSlot?: string;
|
||||
CompactHandling?: boolean;
|
||||
MinRepairDegradation?: number;
|
||||
@ -257,11 +257,11 @@ export interface Props
|
||||
AllowOverheat?: boolean;
|
||||
DoubleActionAccuracyPenalty?: number;
|
||||
RecoilPosZMult?: number;
|
||||
RecoilReturnPathDampingHandRotation?: number
|
||||
RecoilReturnPathOffsetHandRotation?: number
|
||||
RecoilReturnSpeedHandRotation?: number
|
||||
RecoilStableAngleIncreaseStep?: number
|
||||
RecoilStableIndexShot?: number
|
||||
RecoilReturnPathDampingHandRotation?: number;
|
||||
RecoilReturnPathOffsetHandRotation?: number;
|
||||
RecoilReturnSpeedHandRotation?: number;
|
||||
RecoilStableAngleIncreaseStep?: number;
|
||||
RecoilStableIndexShot?: number;
|
||||
MinRepairKitDegradation?: number;
|
||||
MaxRepairKitDegradation?: number;
|
||||
BlocksEarpiece?: boolean;
|
||||
@ -560,9 +560,9 @@ export interface IColor
|
||||
|
||||
export interface IShotsGroupSettings
|
||||
{
|
||||
EndShotIndex: number
|
||||
ShotRecoilPositionStrength: Ixyz
|
||||
ShotRecoilRadianRange: Ixyz
|
||||
ShotRecoilRotationStrength: Ixyz
|
||||
StartShotIndex: number
|
||||
EndShotIndex: number;
|
||||
ShotRecoilPositionStrength: Ixyz;
|
||||
ShotRecoilRadianRange: Ixyz;
|
||||
ShotRecoilRotationStrength: Ixyz;
|
||||
StartShotIndex: number;
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
export interface IGetRaidTimeRequest
|
||||
{
|
||||
Side: string,
|
||||
Location: string
|
||||
Side: string;
|
||||
Location: string;
|
||||
}
|
@ -1,15 +1,15 @@
|
||||
export interface IGetRaidTimeResponse
|
||||
{
|
||||
RaidTimeMinutes: number
|
||||
NewSurviveTimeSeconds: number
|
||||
OriginalSurvivalTimeSeconds: number
|
||||
ExitChanges: ExtractChange[]
|
||||
RaidTimeMinutes: number;
|
||||
NewSurviveTimeSeconds: number;
|
||||
OriginalSurvivalTimeSeconds: number;
|
||||
ExitChanges: ExtractChange[];
|
||||
}
|
||||
|
||||
export interface ExtractChange
|
||||
{
|
||||
Name: string
|
||||
MinTime?: number
|
||||
MaxTime?: number
|
||||
Chance?: number
|
||||
Name: string;
|
||||
MinTime?: number;
|
||||
MaxTime?: number;
|
||||
Chance?: number;
|
||||
}
|
@ -17,7 +17,9 @@ export interface IQteData
|
||||
area: HideoutAreas;
|
||||
areaLevel: number;
|
||||
quickTimeEvents: IQuickTimeEvent[];
|
||||
requirements: (IAreaRequirement
|
||||
requirements:
|
||||
(
|
||||
| IAreaRequirement
|
||||
| IItemRequirement
|
||||
| ITraderUnlockRequirement
|
||||
| ITraderLoyaltyRequirement
|
||||
@ -26,18 +28,19 @@ export interface IQteData
|
||||
| IToolRequirement
|
||||
| IQuestRequirement
|
||||
| IHealthRequirement
|
||||
| IBodyPartBuffRequirement)[];
|
||||
| IBodyPartBuffRequirement
|
||||
)[];
|
||||
results: Record<QteEffectType, IQteResult>;
|
||||
}
|
||||
|
||||
export interface IQuickTimeEvent
|
||||
{
|
||||
type: QteType;
|
||||
position: { x: number, y: number };
|
||||
position: { x: number; y: number; };
|
||||
startDelay: number;
|
||||
endDelay: number;
|
||||
speed: number;
|
||||
successRange: { x: number, y: number };
|
||||
successRange: { x: number; y: number; };
|
||||
key: string;
|
||||
}
|
||||
|
||||
@ -71,73 +74,73 @@ export interface ISkillLevelMultiplier
|
||||
|
||||
export interface IAreaRequirement extends IQteRequirement
|
||||
{
|
||||
type: RequirementType.AREA,
|
||||
areaType: HideoutAreas,
|
||||
requiredLevel: number,
|
||||
type: RequirementType.AREA;
|
||||
areaType: HideoutAreas;
|
||||
requiredLevel: number;
|
||||
}
|
||||
|
||||
export interface ITraderUnlockRequirement extends IQteRequirement
|
||||
{
|
||||
type: RequirementType.TRADER_UNLOCK,
|
||||
type: RequirementType.TRADER_UNLOCK;
|
||||
traderId: Traders;
|
||||
}
|
||||
|
||||
export interface ITraderLoyaltyRequirement extends IQteRequirement
|
||||
{
|
||||
type: RequirementType.TRADER_LOYALTY,
|
||||
type: RequirementType.TRADER_LOYALTY;
|
||||
traderId: Traders;
|
||||
loyaltyLevel: number;
|
||||
}
|
||||
|
||||
export interface ISkillRequirement extends IQteRequirement
|
||||
{
|
||||
type: RequirementType.SKILL,
|
||||
type: RequirementType.SKILL;
|
||||
skillName: SkillTypes;
|
||||
skillLevel: number;
|
||||
}
|
||||
|
||||
export interface IResourceRequirement extends IQteRequirement
|
||||
{
|
||||
type: RequirementType.RESOURCE,
|
||||
templateId: string,
|
||||
resource: number,
|
||||
type: RequirementType.RESOURCE;
|
||||
templateId: string;
|
||||
resource: number;
|
||||
}
|
||||
|
||||
export interface IItemRequirement extends IQteRequirement
|
||||
{
|
||||
type: RequirementType.ITEM,
|
||||
templateId: string,
|
||||
count: number,
|
||||
isFunctional: boolean,
|
||||
isEncoded: boolean,
|
||||
type: RequirementType.ITEM;
|
||||
templateId: string;
|
||||
count: number;
|
||||
isFunctional: boolean;
|
||||
isEncoded: boolean;
|
||||
}
|
||||
|
||||
export interface IToolRequirement extends IQteRequirement
|
||||
{
|
||||
type: RequirementType.TOOL,
|
||||
templateId: string,
|
||||
count: number,
|
||||
isFunctional: boolean,
|
||||
isEncoded: boolean,
|
||||
type: RequirementType.TOOL;
|
||||
templateId: string;
|
||||
count: number;
|
||||
isFunctional: boolean;
|
||||
isEncoded: boolean;
|
||||
}
|
||||
|
||||
export interface IQuestRequirement extends IQteRequirement
|
||||
{
|
||||
type: RequirementType.QUEST_COMPLETE,
|
||||
questId: string,
|
||||
type: RequirementType.QUEST_COMPLETE;
|
||||
questId: string;
|
||||
}
|
||||
|
||||
export interface IHealthRequirement extends IQteRequirement
|
||||
{
|
||||
type: RequirementType.HEALTH,
|
||||
energy: number,
|
||||
hydration: number,
|
||||
type: RequirementType.HEALTH;
|
||||
energy: number;
|
||||
hydration: number;
|
||||
}
|
||||
|
||||
export interface IBodyPartBuffRequirement extends IQteRequirement
|
||||
{
|
||||
type: RequirementType.BODY_PART_BUFF,
|
||||
effectName: Effect,
|
||||
bodyPart: BodyPart,
|
||||
excluded: boolean,
|
||||
type: RequirementType.BODY_PART_BUFF;
|
||||
effectName: Effect;
|
||||
bodyPart: BodyPart;
|
||||
excluded: boolean;
|
||||
}
|
||||
|
@ -2,6 +2,6 @@ import { Item } from "../common/tables/IItem";
|
||||
|
||||
export interface IItemDeliveryRequestData
|
||||
{
|
||||
items: Item[],
|
||||
traderId: string
|
||||
items: Item[];
|
||||
traderId: string;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { Item } from "../common/tables/IItem"
|
||||
import { Item } from "../common/tables/IItem";
|
||||
|
||||
export interface IAddItemDirectRequest
|
||||
{
|
||||
|
@ -2,7 +2,7 @@ import { IInventoryBaseActionRequestData } from "@spt-aki/models/eft/inventory/I
|
||||
|
||||
export interface IInventoryUnbindRequestData extends IInventoryBaseActionRequestData
|
||||
{
|
||||
Action: "Unbind"
|
||||
item: string
|
||||
index: number
|
||||
Action: "Unbind";
|
||||
item: string;
|
||||
index: number;
|
||||
}
|
@ -3,11 +3,11 @@ import { IInventoryBaseActionRequestData } from "./IInventoryBaseActionRequestDa
|
||||
export interface IRedeemProfileRequestData extends IInventoryBaseActionRequestData
|
||||
{
|
||||
Action: "RedeemProfileReward";
|
||||
events: IRedeemProfileRequestEvent[]
|
||||
events: IRedeemProfileRequestEvent[];
|
||||
}
|
||||
|
||||
export interface IRedeemProfileRequestEvent
|
||||
{
|
||||
MessageId: string
|
||||
EventId: string
|
||||
MessageId: string;
|
||||
EventId: string;
|
||||
}
|
@ -4,5 +4,5 @@ export interface ISetFavoriteItems extends IInventoryBaseActionRequestData
|
||||
{
|
||||
Action: "SetFavoriteItems";
|
||||
items: any[];
|
||||
timestamp: number
|
||||
timestamp: number;
|
||||
}
|
@ -1,19 +1,19 @@
|
||||
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory"
|
||||
import { MemberCategory } from "@spt-aki/models/enums/MemberCategory";
|
||||
|
||||
export interface IGetGroupStatusResponse
|
||||
{
|
||||
players: IPlayer[]
|
||||
maxPveCountExceeded: boolean
|
||||
players: IPlayer[];
|
||||
maxPveCountExceeded: boolean;
|
||||
}
|
||||
|
||||
export interface IPlayer
|
||||
{
|
||||
aid: string
|
||||
_id: string
|
||||
lookingGroup: boolean
|
||||
IsLeader: boolean
|
||||
IsReady: boolean
|
||||
Info: ICurrentGroupMemberInfo
|
||||
aid: string;
|
||||
_id: string;
|
||||
lookingGroup: boolean;
|
||||
IsLeader: boolean;
|
||||
IsReady: boolean;
|
||||
Info: ICurrentGroupMemberInfo;
|
||||
}
|
||||
|
||||
export interface ICurrentGroupMemberInfo
|
||||
|
@ -7,7 +7,6 @@ import { IProfileChangeEvent } from "@spt-aki/models/spt/dialog/ISendMessageDeta
|
||||
|
||||
export interface IAkiProfile
|
||||
{
|
||||
|
||||
info: Info;
|
||||
characters: Characters;
|
||||
/** Clothing purchases */
|
||||
@ -53,7 +52,7 @@ export interface IUserBuilds
|
||||
{
|
||||
weaponBuilds: IWeaponBuild[];
|
||||
equipmentBuilds: IEquipmentBuild[];
|
||||
magazineBuilds: IMagazineBuild[]
|
||||
magazineBuilds: IMagazineBuild[];
|
||||
}
|
||||
|
||||
export interface IUserBuild
|
||||
@ -64,7 +63,6 @@ export interface IUserBuild
|
||||
|
||||
export interface IWeaponBuild extends IUserBuild
|
||||
{
|
||||
|
||||
Root: string;
|
||||
Items: Item[]; // Same as PMC inventory items
|
||||
}
|
||||
@ -78,25 +76,25 @@ export interface IEquipmentBuild extends IUserBuild
|
||||
|
||||
export interface IMagazineBuild extends IUserBuild
|
||||
{
|
||||
Caliber: string
|
||||
TopCount: number
|
||||
BottomCount: number
|
||||
Items: IMagazineTemplateAmmoItem[]
|
||||
Caliber: string;
|
||||
TopCount: number;
|
||||
BottomCount: number;
|
||||
Items: IMagazineTemplateAmmoItem[];
|
||||
}
|
||||
|
||||
export interface IMagazineTemplateAmmoItem
|
||||
{
|
||||
TemplateId: string
|
||||
Count: number
|
||||
TemplateId: string;
|
||||
Count: number;
|
||||
}
|
||||
|
||||
/** Used by defaultEquipmentPresets.json */
|
||||
export interface IDefaultEquipmentPreset extends IUserBuild
|
||||
{
|
||||
Items: Item[]
|
||||
Root: string
|
||||
BuildType: EquipmentBuildType
|
||||
type: string
|
||||
Items: Item[];
|
||||
Root: string;
|
||||
BuildType: EquipmentBuildType;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export interface Dialogue
|
||||
@ -291,8 +289,8 @@ export interface Insurance
|
||||
traderId: string;
|
||||
maxStorageTime: number;
|
||||
systemData: ISystemData;
|
||||
messageType: MessageType
|
||||
messageTemplateId: string
|
||||
messageType: MessageType;
|
||||
messageTemplateId: string;
|
||||
items: Item[];
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
export interface ICompletedAchievementsResponse
|
||||
{
|
||||
elements: Record<string, number>
|
||||
elements: Record<string, number>;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export interface ICreateProfileResponse
|
||||
{
|
||||
uid: string
|
||||
uid: string;
|
||||
}
|
@ -2,5 +2,5 @@ import { IAchievement } from "../common/tables/IAchievement";
|
||||
|
||||
export interface IGetAchievementsResponse
|
||||
{
|
||||
elements: IAchievement[]
|
||||
elements: IAchievement[];
|
||||
}
|
@ -1,30 +1,29 @@
|
||||
|
||||
import { OverallCounters, Skills } from "@spt-aki/models/eft/common/tables/IBotBase"
|
||||
import { Item } from "../common/tables/IItem"
|
||||
import { OverallCounters, Skills } from "@spt-aki/models/eft/common/tables/IBotBase";
|
||||
import { Item } from "../common/tables/IItem";
|
||||
|
||||
export interface IGetOtherProfileResponse
|
||||
{
|
||||
id: string
|
||||
aid: number
|
||||
info: IOtherProfileInfo
|
||||
customization: IOtherProfileCustomization
|
||||
skills: Skills
|
||||
equipment: IOtherProfileEquipment
|
||||
achievements: Record<string, number>
|
||||
favoriteItems: string[]
|
||||
pmcStats: IOtherProfileStats
|
||||
scavStats: IOtherProfileStats
|
||||
id: string;
|
||||
aid: number;
|
||||
info: IOtherProfileInfo;
|
||||
customization: IOtherProfileCustomization;
|
||||
skills: Skills;
|
||||
equipment: IOtherProfileEquipment;
|
||||
achievements: Record<string, number>;
|
||||
favoriteItems: string[];
|
||||
pmcStats: IOtherProfileStats;
|
||||
scavStats: IOtherProfileStats;
|
||||
}
|
||||
|
||||
export interface IOtherProfileInfo
|
||||
{
|
||||
nickname: string
|
||||
side: string
|
||||
experience: number
|
||||
memberCategory: number
|
||||
bannedState: boolean
|
||||
bannedUntil: number
|
||||
registrationDate: number
|
||||
nickname: string;
|
||||
side: string;
|
||||
experience: number;
|
||||
memberCategory: number;
|
||||
bannedState: boolean;
|
||||
bannedUntil: number;
|
||||
registrationDate: number;
|
||||
}
|
||||
|
||||
export interface IOtherProfileCustomization
|
||||
@ -48,6 +47,6 @@ export interface IOtherProfileStats
|
||||
|
||||
export interface IOtherProfileSubStats
|
||||
{
|
||||
totalInGameTime: number
|
||||
overAllCounters: OverallCounters
|
||||
totalInGameTime: number;
|
||||
overAllCounters: OverallCounters;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
export interface IGetRagfairOfferByIdRequest
|
||||
{
|
||||
id: number
|
||||
id: number;
|
||||
}
|
@ -6,7 +6,7 @@ export interface IWeatherData
|
||||
time: string;
|
||||
date: string;
|
||||
weather?: IWeather;
|
||||
winterEventEnabled: boolean
|
||||
winterEventEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface IWeather
|
||||
|
@ -104,5 +104,5 @@ export enum BaseClasses
|
||||
BUILT_IN_INSERTS = "65649eb40bf0ed77b8044453",
|
||||
ARMOR_PLATE = "644120aa86ffbe10ee032b6f",
|
||||
CULTIST_AMULET = "64b69b0c8f3be32ed22682f8",
|
||||
RADIO_TRANSMITTER = "62e9103049c018f425059f38"
|
||||
RADIO_TRANSMITTER = "62e9103049c018f425059f38",
|
||||
}
|
||||
|
@ -4,5 +4,5 @@ export enum BonusSkillType
|
||||
COMBAT = "Combat",
|
||||
SPECIAL = "Special",
|
||||
PRACTICAL = "Practical",
|
||||
MENTAL = "Mental"
|
||||
MENTAL = "Mental",
|
||||
}
|
@ -29,5 +29,5 @@ export enum BonusType
|
||||
STASH_SIZE = "StashSize",
|
||||
MAXIMUM_ENERGY_RESERVE = "MaximumEnergyReserve",
|
||||
TEXT_BONUS = "TextBonus",
|
||||
SKILL_GROUP_LEVELING_BOOST = "SkillGroupLevelingBoost"
|
||||
SKILL_GROUP_LEVELING_BOOST = "SkillGroupLevelingBoost",
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
export enum ConfigTypes {
|
||||
export enum ConfigTypes
|
||||
{
|
||||
AIRDROP = "aki-airdrop",
|
||||
BOT = "aki-bot",
|
||||
PMC = "aki-pmc",
|
||||
@ -25,5 +26,5 @@ export enum ConfigTypes {
|
||||
SEASONAL_EVENT = "aki-seasonalevents",
|
||||
LOST_ON_DEATH = "aki-lostondeath",
|
||||
GIFTS = "aki-gifts",
|
||||
BTR = "aki-btr"
|
||||
BTR = "aki-btr",
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
export enum HideoutAreas {
|
||||
export enum HideoutAreas
|
||||
{
|
||||
NOTSET = -1,
|
||||
VENTS = 0,
|
||||
SECURITY = 1,
|
||||
@ -25,5 +26,5 @@ export enum HideoutAreas {
|
||||
EMERGENCY_WALL = 22,
|
||||
GYM = 23,
|
||||
WEAPON_STAND = 24,
|
||||
WEAPON_STAND_SECONDARY = 25
|
||||
WEAPON_STAND_SECONDARY = 25,
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
export enum ItemAddedResult {
|
||||
export enum ItemAddedResult
|
||||
{
|
||||
UNKNOWN = -1,
|
||||
SUCCESS = 1,
|
||||
NO_SPACE = 2,
|
||||
|
@ -1,4 +1,5 @@
|
||||
export enum ItemEventActions {
|
||||
export enum ItemEventActions
|
||||
{
|
||||
MOVE = "Move",
|
||||
REMOVE = "Remove",
|
||||
SPLIT = "Split",
|
||||
@ -25,5 +26,5 @@ export enum ItemEventActions {
|
||||
REMOVE_EQUIPMENT_BUILD = "RemoveEquipmentBuild",
|
||||
REDEEM_PROFILE_REWARD = "RedeemProfileReward",
|
||||
SET_FAVORITE_ITEMS = "SetFavoriteItems",
|
||||
QUEST_FAIL = "QuestFail"
|
||||
QUEST_FAIL = "QuestFail",
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
export enum ModSpawn {
|
||||
export enum ModSpawn
|
||||
{
|
||||
DEFAULT_MOD,
|
||||
SPAWN,
|
||||
SKIP
|
||||
SKIP,
|
||||
}
|
@ -5,5 +5,5 @@ export enum SeasonalEventType
|
||||
HALLOWEEN = "Halloween",
|
||||
NEW_YEARS = "NewYears",
|
||||
PROMO = "Promo",
|
||||
SNOW = "Snow"
|
||||
SNOW = "Snow",
|
||||
}
|
||||
|
@ -5,5 +5,5 @@ export enum TraderServiceType
|
||||
CULTISTS_AID = "CultistsAid",
|
||||
BTR_ITEMS_DELIVERY = "BtrItemsDelivery",
|
||||
PLAYER_TAXI = "PlayerTaxi",
|
||||
BTR_BOT_COVER = "BtrBotCover"
|
||||
BTR_BOT_COVER = "BtrBotCover",
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
export enum QteActivityType {
|
||||
export enum QteActivityType
|
||||
{
|
||||
GYM = 0,
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
export enum QteEffectType {
|
||||
export enum QteEffectType
|
||||
{
|
||||
FINISH_EFFECT = "FinishEffect",
|
||||
SINGLE_SUCCESS_EFFECT = "SingleSuccessEffect",
|
||||
SINGLE_FAIL_EFFECT = "SingleFailEffect",
|
||||
|
@ -1,4 +1,5 @@
|
||||
export enum QteResultType {
|
||||
export enum QteResultType
|
||||
{
|
||||
NONE = "None",
|
||||
EXIT = "Exit",
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
export enum QteRewardType {
|
||||
export enum QteRewardType
|
||||
{
|
||||
SKILL = "Skill",
|
||||
HEALTH_EFFECT = "HealthEffect",
|
||||
MUSCLE_PAIN = "MusclePain",
|
||||
|
@ -1,3 +1,4 @@
|
||||
export enum QteType {
|
||||
export enum QteType
|
||||
{
|
||||
SHRINKING_CIRCLE = 0,
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
export enum RequirementType {
|
||||
export enum RequirementType
|
||||
{
|
||||
AREA = "Area",
|
||||
ITEM = "Item",
|
||||
TRADER_UNLOCK = "TraderUnlock",
|
||||
|
@ -9,7 +9,7 @@ export interface IBTRConfig extends IBaseConfig
|
||||
/** How long the cover fire service lasts for */
|
||||
coverFireTime: number;
|
||||
/** How long the BTR waits at every point in its path */
|
||||
pointWaitTime: MinMax,
|
||||
pointWaitTime: MinMax;
|
||||
/** How long after purchasing the taxi service before the BTR leaves */
|
||||
taxiWaitTime: number;
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ export interface EquipmentFilters
|
||||
/** Chance gun laser is active during the day */
|
||||
laserIsActiveChancePercent?: number;
|
||||
/** Should plates be filtered by level */
|
||||
filterPlatesByLevel?:boolean
|
||||
filterPlatesByLevel?: boolean;
|
||||
/** Chance NODS are down/active during the day */
|
||||
nvgIsActiveChanceDayPercent?: number;
|
||||
/** Chance NODS are down/active during the night */
|
||||
@ -121,7 +121,7 @@ export interface EquipmentFilters
|
||||
weightingAdjustmentsByPlayerLevel?: WeightingAdjustmentDetails[];
|
||||
/** Should the stock mod be forced to spawn on bot */
|
||||
forceStock: boolean;
|
||||
armorPlateWeighting?: IArmorPlateWeights[]
|
||||
armorPlateWeighting?: IArmorPlateWeights[];
|
||||
}
|
||||
|
||||
export interface ModLimits
|
||||
@ -169,7 +169,6 @@ export interface WeightingAdjustmentDetails
|
||||
equipment?: IAdjustmentDetails;
|
||||
/** Key: clothing slot e.g. feet, value: item tpl + weight */
|
||||
clothing?: IAdjustmentDetails;
|
||||
|
||||
}
|
||||
|
||||
export interface IAdjustmentDetails
|
||||
|
@ -43,13 +43,13 @@ export interface ILocationConfig extends IBaseConfig
|
||||
/** Key: map, value: settings to control how long scav raids are*/
|
||||
scavRaidTimeSettings: IScavRaidTimeSettings;
|
||||
/** Settings to adjust mods for lootable equipment in raid */
|
||||
equipmentLootSettings: IEquipmentLootSettings
|
||||
equipmentLootSettings: IEquipmentLootSettings;
|
||||
}
|
||||
|
||||
export interface IEquipmentLootSettings
|
||||
{
|
||||
// Percentage chance item will be added to equipment
|
||||
modSpawnChancePercent: Record<string, number>
|
||||
modSpawnChancePercent: Record<string, number>;
|
||||
}
|
||||
|
||||
export interface IFixEmptyBotWavesSettings
|
||||
@ -118,13 +118,13 @@ export interface IContainerRandomistionSettings
|
||||
|
||||
export interface IScavRaidTimeSettings
|
||||
{
|
||||
settings: IScavRaidTimeConfigSettings
|
||||
maps: Record<string, IScavRaidTimeLocationSettings>
|
||||
settings: IScavRaidTimeConfigSettings;
|
||||
maps: Record<string, IScavRaidTimeLocationSettings>;
|
||||
}
|
||||
|
||||
export interface IScavRaidTimeConfigSettings
|
||||
{
|
||||
trainArrivalDelayObservedSeconds: number
|
||||
trainArrivalDelayObservedSeconds: number;
|
||||
}
|
||||
|
||||
export interface IScavRaidTimeLocationSettings
|
||||
|
@ -77,8 +77,8 @@ export interface ITraderWhitelist
|
||||
traderId: string;
|
||||
questTypes: string[];
|
||||
rewardBaseWhitelist: string[];
|
||||
rewardCanBeWeapon: boolean
|
||||
weaponRewardChancePercent: number
|
||||
rewardCanBeWeapon: boolean;
|
||||
weaponRewardChancePercent: number;
|
||||
}
|
||||
|
||||
export interface IRepeatableQuestTypesConfig
|
||||
@ -91,9 +91,9 @@ export interface IRepeatableQuestTypesConfig
|
||||
|
||||
export interface IExploration extends IBaseQuestConfig
|
||||
{
|
||||
maxExtracts: number
|
||||
maxExtractsWithSpecificExit: number
|
||||
specificExits: ISpecificExits
|
||||
maxExtracts: number;
|
||||
maxExtractsWithSpecificExit: number;
|
||||
specificExits: ISpecificExits;
|
||||
}
|
||||
|
||||
export interface ISpecificExits
|
||||
@ -106,7 +106,7 @@ export interface ICompletion extends IBaseQuestConfig
|
||||
{
|
||||
minRequestedAmount: number;
|
||||
maxRequestedAmount: number;
|
||||
uniqueItemCount: number
|
||||
uniqueItemCount: number;
|
||||
minRequestedBulletAmount: number;
|
||||
maxRequestedBulletAmount: number;
|
||||
useWhitelist: boolean;
|
||||
|
@ -33,8 +33,8 @@ export interface ISendMessageDetails
|
||||
|
||||
export interface IProfileChangeEvent
|
||||
{
|
||||
_id: string
|
||||
Type: "TraderSalesSum" | "TraderStanding" | "ProfileLevel" | "SkillPoints" | "ExamineAllItems" | "UnlockTrader"
|
||||
value: number
|
||||
entity?: string
|
||||
_id: string;
|
||||
Type: "TraderSalesSum" | "TraderStanding" | "ProfileLevel" | "SkillPoints" | "ExamineAllItems" | "UnlockTrader";
|
||||
value: number;
|
||||
entity?: string;
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
export interface IRaidChanges
|
||||
{
|
||||
/** What percentage of dynamic loot should the map contain */
|
||||
dynamicLootPercent: number
|
||||
dynamicLootPercent: number;
|
||||
/** What percentage of static loot should the map contain */
|
||||
staticLootPercent: number
|
||||
staticLootPercent: number;
|
||||
/** How many seconds into the raid is the player simulated to spawn in at */
|
||||
simulatedRaidStartSeconds: number
|
||||
simulatedRaidStartSeconds: number;
|
||||
}
|
@ -55,7 +55,7 @@ export interface IDatabaseTables
|
||||
defaultEquipmentPresets: IDefaultEquipmentPreset[];
|
||||
|
||||
/** Achievements */
|
||||
achievements: IAchievement[]
|
||||
achievements: IAchievement[];
|
||||
};
|
||||
traders?: Record<string, ITrader>;
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { TraderServiceType } from "@spt-aki/models/enums/TraderServiceType";
|
||||
|
||||
export interface ITraderServiceModel
|
||||
{
|
||||
serviceType: TraderServiceType,
|
||||
itemsToPay?: Record<string, number>[],
|
||||
subServices?: Record<string, number>[],
|
||||
serviceType: TraderServiceType;
|
||||
itemsToPay?: Record<string, number>[];
|
||||
subServices?: Record<string, number>[];
|
||||
}
|
@ -184,7 +184,7 @@ export class EventOutputHolder
|
||||
}
|
||||
|
||||
// Return null if there's no crafts to send to client to match live behaviour
|
||||
return (Object.keys(productions).length > 0) ? productions : null
|
||||
return (Object.keys(productions).length > 0) ? productions : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -71,7 +71,7 @@ export class GameStaticRouter extends StaticRouter
|
||||
{
|
||||
return this.gameCallbacks.getRaidTime(url, info, sessionID);
|
||||
},
|
||||
)
|
||||
),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ export class InraidStaticRouter extends StaticRouter
|
||||
(url: string, info: any, sessionID: string, output: string): any =>
|
||||
{
|
||||
return this.inraidCallbacks.itemDelivery(url, info, sessionID);
|
||||
}
|
||||
},
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
@ -83,7 +83,11 @@ export class RagfairServer
|
||||
return Object.keys(this.ragfairConfig.traders).filter((x) => this.ragfairConfig.traders[x]);
|
||||
}
|
||||
|
||||
public getAllActiveCategories(fleaUnlocked: boolean, searchRequestData: ISearchRequestData, offers: IRagfairOffer[]): Record<string, number>
|
||||
public getAllActiveCategories(
|
||||
fleaUnlocked: boolean,
|
||||
searchRequestData: ISearchRequestData,
|
||||
offers: IRagfairOffer[],
|
||||
): Record<string, number>
|
||||
{
|
||||
return this.ragfairCategoriesService.getCategoriesFromOffers(offers, searchRequestData, fleaUnlocked);
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ export class SaveServer
|
||||
@inject("HashUtil") protected hashUtil: HashUtil,
|
||||
@inject("LocalisationService") protected localisationService: LocalisationService,
|
||||
@inject("WinstonLogger") protected logger: ILogger,
|
||||
@inject("ConfigServer") protected configServer: ConfigServer
|
||||
@inject("ConfigServer") protected configServer: ConfigServer,
|
||||
)
|
||||
{}
|
||||
|
||||
@ -207,7 +207,10 @@ export class SaveServer
|
||||
}
|
||||
|
||||
const start = performance.now();
|
||||
const jsonProfile = this.jsonUtil.serialize(this.profiles[sessionID], !this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE).features.compressProfile);
|
||||
const jsonProfile = this.jsonUtil.serialize(
|
||||
this.profiles[sessionID],
|
||||
!this.configServer.getConfig<ICoreConfig>(ConfigTypes.CORE).features.compressProfile,
|
||||
);
|
||||
const fmd5 = this.hashUtil.generateMd5ForData(jsonProfile);
|
||||
if (typeof (this.saveMd5[sessionID]) !== "string" || this.saveMd5[sessionID] !== fmd5)
|
||||
{
|
||||
|
@ -201,7 +201,13 @@ export class BotEquipmentModPoolService
|
||||
protected generateGearPool(): void
|
||||
{
|
||||
const gear = Object.values(this.databaseServer.getTables().templates.items).filter((x) =>
|
||||
x._type === "Item" && this.itemHelper.isOfBaseclasses(x._id, [BaseClasses.ARMORED_EQUIPMENT, BaseClasses.VEST, BaseClasses.ARMOR, BaseClasses.HEADWEAR])
|
||||
x._type === "Item"
|
||||
&& this.itemHelper.isOfBaseclasses(x._id, [
|
||||
BaseClasses.ARMORED_EQUIPMENT,
|
||||
BaseClasses.VEST,
|
||||
BaseClasses.ARMOR,
|
||||
BaseClasses.HEADWEAR,
|
||||
])
|
||||
);
|
||||
this.generatePool(gear, "gear");
|
||||
|
||||
|
@ -426,17 +426,21 @@ export class FenceService
|
||||
): void
|
||||
{
|
||||
const priceLimits = this.traderConfig.fence.itemCategoryRoublePriceLimit;
|
||||
const assortRootItems = baseFenceAssort.items.filter(x => x.parentId === "hideout" && !x.upd?.sptPresetId);
|
||||
const assortRootItems = baseFenceAssort.items.filter((x) => x.parentId === "hideout" && !x.upd?.sptPresetId);
|
||||
for (let i = 0; i < assortCount; i++)
|
||||
{
|
||||
const chosenBaseAssortRoot = this.randomUtil.getArrayValue(assortRootItems);
|
||||
if (!chosenBaseAssortRoot)
|
||||
{
|
||||
this.logger.error(this.localisationService.getText("fence-unable_to_find_assort_by_id", chosenBaseAssortRoot._id));
|
||||
this.logger.error(
|
||||
this.localisationService.getText("fence-unable_to_find_assort_by_id", chosenBaseAssortRoot._id),
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
const desiredAssortItemAndChildrenClone = this.jsonUtil.clone(this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, chosenBaseAssortRoot._id));
|
||||
const desiredAssortItemAndChildrenClone = this.jsonUtil.clone(
|
||||
this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, chosenBaseAssortRoot._id),
|
||||
);
|
||||
|
||||
const itemDbDetails = this.itemHelper.getItem(chosenBaseAssortRoot._tpl)[1];
|
||||
const itemLimitCount = itemTypeCounts[itemDbDetails._parent];
|
||||
@ -500,7 +504,12 @@ export class FenceService
|
||||
* @param baseFenceAssort
|
||||
* @param loyaltyLevel Which loyalty level is required to see/buy item
|
||||
*/
|
||||
protected addPresetsToAssort(desiredPresetCount: number, assorts: ITraderAssort, baseFenceAssort: ITraderAssort, loyaltyLevel: number): void
|
||||
protected addPresetsToAssort(
|
||||
desiredPresetCount: number,
|
||||
assorts: ITraderAssort,
|
||||
baseFenceAssort: ITraderAssort,
|
||||
loyaltyLevel: number,
|
||||
): void
|
||||
{
|
||||
let presetsAddedCount = 0;
|
||||
if (desiredPresetCount <= 0)
|
||||
@ -508,12 +517,14 @@ export class FenceService
|
||||
return;
|
||||
}
|
||||
|
||||
const presetRootItems = baseFenceAssort.items.filter(item => item.upd?.sptPresetId);
|
||||
const presetRootItems = baseFenceAssort.items.filter((item) => item.upd?.sptPresetId);
|
||||
while (presetsAddedCount < desiredPresetCount)
|
||||
{
|
||||
const randomPresetRoot = this.randomUtil.getArrayValue(presetRootItems);
|
||||
const rootItemDb = this.itemHelper.getItem(randomPresetRoot._tpl)[1];
|
||||
const presetWithChildrenClone = this.jsonUtil.clone(this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, randomPresetRoot._id));
|
||||
const presetWithChildrenClone = this.jsonUtil.clone(
|
||||
this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, randomPresetRoot._id),
|
||||
);
|
||||
|
||||
// Need to add mods to armors so they dont show as red in the trade screen
|
||||
if (this.itemHelper.itemRequiresSoftInserts(randomPresetRoot._tpl))
|
||||
@ -557,7 +568,7 @@ export class FenceService
|
||||
}
|
||||
|
||||
// Check for and adjust soft insert durability values
|
||||
const requiredSlots = itemDbDetails._props.Slots.filter(slot => slot._required);
|
||||
const requiredSlots = itemDbDetails._props.Slots.filter((slot) => slot._required);
|
||||
const hasRequiredSlots = requiredSlots.length > 0;
|
||||
if (hasRequiredSlots)
|
||||
{
|
||||
@ -566,7 +577,8 @@ export class FenceService
|
||||
const modItemDbDetails = this.itemHelper.getItem(requiredSlot._props.filters[0].Plate)[1];
|
||||
const durabilityValues = this.getRandomisedArmorDurabilityValues(
|
||||
modItemDbDetails,
|
||||
this.traderConfig.fence.armorMaxDurabilityPercentMinMax);
|
||||
this.traderConfig.fence.armorMaxDurabilityPercentMinMax,
|
||||
);
|
||||
const plateTpl = requiredSlot._props.filters[0].Plate; // `Plate` property appears to be the 'default' item for slot
|
||||
if (plateTpl === "")
|
||||
{
|
||||
@ -575,36 +587,41 @@ export class FenceService
|
||||
}
|
||||
|
||||
// Find items mod to apply dura changes to
|
||||
const modItemToAdjust = armor.find(mod => mod.slotId.toLowerCase() === requiredSlot._name.toLowerCase());
|
||||
const modItemToAdjust = armor.find((mod) =>
|
||||
mod.slotId.toLowerCase() === requiredSlot._name.toLowerCase()
|
||||
);
|
||||
if (!modItemToAdjust.upd)
|
||||
{
|
||||
modItemToAdjust.upd = {}
|
||||
modItemToAdjust.upd = {};
|
||||
}
|
||||
|
||||
if (!modItemToAdjust.upd.Repairable)
|
||||
{
|
||||
modItemToAdjust.upd.Repairable = {
|
||||
Durability: modItemDbDetails._props.MaxDurability,
|
||||
MaxDurability: modItemDbDetails._props.MaxDurability
|
||||
MaxDurability: modItemDbDetails._props.MaxDurability,
|
||||
};
|
||||
}
|
||||
modItemToAdjust.upd.Repairable.Durability = durabilityValues.Durability;
|
||||
modItemToAdjust.upd.Repairable.MaxDurability = durabilityValues.MaxDurability;
|
||||
|
||||
// 25% chance to add shots to visor when its below max durability
|
||||
if (this.randomUtil.getChance100(25)
|
||||
&& modItemToAdjust.parentId === BaseClasses.ARMORED_EQUIPMENT && modItemToAdjust.slotId === "mod_equipment_000"
|
||||
&& modItemToAdjust.upd.Repairable.Durability < modItemDbDetails._props.MaxDurability) // Is damaged
|
||||
{
|
||||
modItemToAdjust.upd.FaceShield = {
|
||||
Hits: this.randomUtil.getInt(1,3)
|
||||
}
|
||||
if (
|
||||
this.randomUtil.getChance100(25)
|
||||
&& modItemToAdjust.parentId === BaseClasses.ARMORED_EQUIPMENT
|
||||
&& modItemToAdjust.slotId === "mod_equipment_000"
|
||||
&& modItemToAdjust.upd.Repairable.Durability < modItemDbDetails._props.MaxDurability
|
||||
)
|
||||
{ // Is damaged
|
||||
modItemToAdjust.upd.FaceShield = { Hits: this.randomUtil.getInt(1, 3) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for and adjust plate durability values
|
||||
const plateSlots = itemDbDetails._props.Slots.filter(slot => this.itemHelper.isRemovablePlateSlot(slot._name));
|
||||
const plateSlots = itemDbDetails._props.Slots.filter((slot) =>
|
||||
this.itemHelper.isRemovablePlateSlot(slot._name)
|
||||
);
|
||||
if (plateSlots.length > 0)
|
||||
{
|
||||
for (const plateSlot of plateSlots)
|
||||
@ -615,7 +632,7 @@ export class FenceService
|
||||
continue;
|
||||
}
|
||||
|
||||
const plateTpl = plateSlot._props.filters[0].Plate
|
||||
const plateTpl = plateSlot._props.filters[0].Plate;
|
||||
if (!plateTpl)
|
||||
{
|
||||
// Bsg data lacks a default plate, skip adding mod
|
||||
@ -624,20 +641,21 @@ export class FenceService
|
||||
const modItemDbDetails = this.itemHelper.getItem(plateTpl)[1];
|
||||
const durabilityValues = this.getRandomisedArmorDurabilityValues(
|
||||
modItemDbDetails,
|
||||
this.traderConfig.fence.armorMaxDurabilityPercentMinMax);
|
||||
this.traderConfig.fence.armorMaxDurabilityPercentMinMax,
|
||||
);
|
||||
|
||||
// Find items mod to apply dura changes to
|
||||
const modItemToAdjust = armor.find(mod => mod.slotId.toLowerCase() === plateSlot._name.toLowerCase());
|
||||
const modItemToAdjust = armor.find((mod) => mod.slotId.toLowerCase() === plateSlot._name.toLowerCase());
|
||||
if (!modItemToAdjust.upd)
|
||||
{
|
||||
modItemToAdjust.upd = {}
|
||||
modItemToAdjust.upd = {};
|
||||
}
|
||||
|
||||
if (!modItemToAdjust.upd.Repairable)
|
||||
{
|
||||
modItemToAdjust.upd.Repairable = {
|
||||
Durability: modItemDbDetails._props.MaxDurability,
|
||||
MaxDurability: modItemDbDetails._props.MaxDurability
|
||||
MaxDurability: modItemDbDetails._props.MaxDurability,
|
||||
};
|
||||
}
|
||||
|
||||
@ -755,11 +773,13 @@ export class FenceService
|
||||
if (
|
||||
(itemDetails._parent === BaseClasses.ARMORED_EQUIPMENT
|
||||
|| itemDetails._parent === BaseClasses.FACECOVER
|
||||
|| itemDetails._parent === BaseClasses.ARMOR_PLATE
|
||||
) && itemDetails._props.MaxDurability > 0
|
||||
|| itemDetails._parent === BaseClasses.ARMOR_PLATE) && itemDetails._props.MaxDurability > 0
|
||||
)
|
||||
{
|
||||
const values = this.getRandomisedArmorDurabilityValues(itemDetails, this.traderConfig.fence.armorMaxDurabilityPercentMinMax);
|
||||
const values = this.getRandomisedArmorDurabilityValues(
|
||||
itemDetails,
|
||||
this.traderConfig.fence.armorMaxDurabilityPercentMinMax,
|
||||
);
|
||||
itemToAdjust.upd.Repairable = { Durability: values.Durability, MaxDurability: values.MaxDurability };
|
||||
|
||||
return;
|
||||
@ -816,7 +836,10 @@ export class FenceService
|
||||
* @param maxDurabilityMinMaxPercent Max durabiltiy percent min/max values
|
||||
* @returns Durability + MaxDurability values
|
||||
*/
|
||||
protected getRandomisedArmorDurabilityValues(itemDetails: ITemplateItem, maxDurabilityMinMaxPercent: MinMax): Repairable
|
||||
protected getRandomisedArmorDurabilityValues(
|
||||
itemDetails: ITemplateItem,
|
||||
maxDurabilityMinMaxPercent: MinMax,
|
||||
): Repairable
|
||||
{
|
||||
const duraMin = maxDurabilityMinMaxPercent.min / 100 * itemDetails._props.MaxDurability;
|
||||
const duraMax = maxDurabilityMinMaxPercent.max / 100 * itemDetails._props.MaxDurability;
|
||||
@ -905,11 +928,11 @@ export class FenceService
|
||||
public amendOrRemoveFenceOffer(assortId: string, buyCount: number): void
|
||||
{
|
||||
let isNormalAssort = true;
|
||||
let fenceAssortItem = this.fenceAssort.items.find(item => item._id === assortId);
|
||||
let fenceAssortItem = this.fenceAssort.items.find((item) => item._id === assortId);
|
||||
if (!fenceAssortItem)
|
||||
{
|
||||
// Not in main assorts, check secondary section
|
||||
fenceAssortItem = this.fenceDiscountAssort.items.find(item => item._id === assortId);
|
||||
fenceAssortItem = this.fenceDiscountAssort.items.find((item) => item._id === assortId);
|
||||
if (!fenceAssortItem)
|
||||
{
|
||||
this.logger.error(`Offer with id: ${assortId} not found`);
|
||||
@ -937,7 +960,7 @@ export class FenceService
|
||||
const itemWithChildrenToRemove = this.itemHelper.findAndReturnChildrenAsItems(assorts, assortId);
|
||||
for (const itemToRemove of itemWithChildrenToRemove)
|
||||
{
|
||||
let indexToRemove = assorts.findIndex(item => item._id === itemToRemove._id);
|
||||
let indexToRemove = assorts.findIndex((item) => item._id === itemToRemove._id);
|
||||
|
||||
// No offer found in main assort, check discount items
|
||||
if (indexToRemove === -1)
|
||||
@ -947,7 +970,9 @@ export class FenceService
|
||||
|
||||
if (indexToRemove === -1)
|
||||
{
|
||||
this.logger.warning(`unable to remove fence assort item: ${itemToRemove._id} tpl: ${itemToRemove._tpl}`)
|
||||
this.logger.warning(
|
||||
`unable to remove fence assort item: ${itemToRemove._id} tpl: ${itemToRemove._tpl}`,
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -137,7 +137,8 @@ export class GiftService
|
||||
senderDetails: {
|
||||
_id: this.getSenderId(giftData),
|
||||
aid: 1234567, // TODO - pass proper aid value
|
||||
Info: null },
|
||||
Info: null,
|
||||
},
|
||||
messageText: giftData.messageText,
|
||||
items: giftData.items,
|
||||
itemsMaxStorageLifetimeSeconds: this.timeUtil.getHoursAsSeconds(giftData.collectionTimeHours),
|
||||
|
@ -108,7 +108,7 @@ export class InsuranceService
|
||||
const systemData = {
|
||||
date: this.timeUtil.getDateMailFormat(),
|
||||
time: this.timeUtil.getTimeMailFormat(),
|
||||
location: mapId
|
||||
location: mapId,
|
||||
};
|
||||
// Send "i will go look for your stuff" message from trader to player
|
||||
this.mailSendService.sendLocalisedNpcMessageToPlayer(
|
||||
@ -118,7 +118,7 @@ export class InsuranceService
|
||||
this.randomUtil.getArrayValue(dialogueTemplates.insuranceStart),
|
||||
null,
|
||||
null,
|
||||
systemData
|
||||
systemData,
|
||||
);
|
||||
|
||||
// Store insurance to send to player later in profile
|
||||
@ -247,7 +247,7 @@ export class InsuranceService
|
||||
}
|
||||
|
||||
// Item iterated on could have already been processed previously (as a child of another item)
|
||||
if (equipmentToSendToPlayer.some(item => item.itemsToReturnToPlayer._id === insuredItem.itemId))
|
||||
if (equipmentToSendToPlayer.some((item) => item.itemsToReturnToPlayer._id === insuredItem.itemId))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -256,12 +256,15 @@ export class InsuranceService
|
||||
// Catches both events: player died with item on + player survived but dropped item in raid
|
||||
if (!offRaidGearHash[insuredItem.itemId] || playerDied)
|
||||
{
|
||||
equipmentToSendToPlayer.push(
|
||||
{
|
||||
equipmentToSendToPlayer.push({
|
||||
pmcData: pmcData,
|
||||
itemsToReturnToPlayer: this.getInsuredItemDetails(
|
||||
pmcData,
|
||||
this.itemHelper.findAndReturnChildrenAsItems(Object.values(preRaidGearHash), preRaidItem._id, true),
|
||||
this.itemHelper.findAndReturnChildrenAsItems(
|
||||
Object.values(preRaidGearHash),
|
||||
preRaidItem._id,
|
||||
true,
|
||||
),
|
||||
offraidData.insurance,
|
||||
),
|
||||
traderId: insuredItem.tid,
|
||||
@ -287,13 +290,13 @@ export class InsuranceService
|
||||
protected getInsuredItemDetails(
|
||||
pmcData: IPmcData,
|
||||
preRaidItemWithChildren: Item[],
|
||||
allItemsFromClient: IInsuredItemsData[]
|
||||
allItemsFromClient: IInsuredItemsData[],
|
||||
): Item[]
|
||||
{
|
||||
const itemsToReturn: Item[] = [];
|
||||
for (const preRaidItem of preRaidItemWithChildren)
|
||||
{
|
||||
const isInsured = pmcData.InsuredItems.some(item => item.itemId === preRaidItem._id);
|
||||
const isInsured = pmcData.InsuredItems.some((item) => item.itemId === preRaidItem._id);
|
||||
const itemClientInsuranceData = allItemsFromClient?.find((x) => x.id === preRaidItem._id);
|
||||
const itemIsSoftInsert = this.itemHelper.isOfBaseclass(preRaidItem._tpl, BaseClasses.BUILT_IN_INSERTS);
|
||||
|
||||
@ -437,7 +440,7 @@ export class InsuranceService
|
||||
this.addInsuranceItemToArray(sessionId, traderId, itemsToReturnToPlayer);
|
||||
|
||||
// Remove item from insured items array as its been processed
|
||||
const returnedItemIds = itemsToReturnToPlayer.map(item => item._id);
|
||||
const returnedItemIds = itemsToReturnToPlayer.map((item) => item._id);
|
||||
pmcData.InsuredItems = pmcData.InsuredItems.filter((item) => !returnedItemIds.includes(item.itemId));
|
||||
}
|
||||
|
||||
|
@ -113,9 +113,7 @@ export class LocaleService
|
||||
return platformLocale.language;
|
||||
}
|
||||
|
||||
this.logger.warning(
|
||||
`Unsupported system langauge found: ${localeCode}, falling back to english`,
|
||||
);
|
||||
this.logger.warning(`Unsupported system langauge found: ${localeCode}, falling back to english`);
|
||||
|
||||
return "en";
|
||||
}
|
||||
@ -140,9 +138,7 @@ export class LocaleService
|
||||
const langaugeCode = platformLocale.language.toLowerCase();
|
||||
if (!this.localeConfig.serverSupportedLocales.includes(langaugeCode))
|
||||
{
|
||||
this.logger.warning(
|
||||
`Unsupported system langauge found: ${langaugeCode}, falling back to english`,
|
||||
);
|
||||
this.logger.warning(`Unsupported system langauge found: ${langaugeCode}, falling back to english`);
|
||||
|
||||
return "en";
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ export class LocalisationService
|
||||
@inject("LocaleService") protected localeService: LocaleService,
|
||||
)
|
||||
{
|
||||
|
||||
const localeFileDirectory = path.join(
|
||||
process.cwd(),
|
||||
globalThis.G_RELEASE_CONFIGURATION
|
||||
|
@ -17,7 +17,6 @@ export class MatchLocationService
|
||||
|
||||
public createGroup(sessionID: string, info: ICreateGroupRequestData): any
|
||||
{
|
||||
|
||||
const account = this.saveServer.getProfile(sessionID).info;
|
||||
const groupID = "test";
|
||||
|
||||
@ -31,13 +30,7 @@ export class MatchLocationService
|
||||
isSavage: false,
|
||||
timeShift: "CURR",
|
||||
dt: this.timeUtil.getTimestamp(),
|
||||
players: [{
|
||||
_id: account.id,
|
||||
region: "EUR",
|
||||
ip: "127.0.0.1",
|
||||
savageId: account.scavId,
|
||||
accessKeyId: "",
|
||||
}],
|
||||
players: [{ _id: account.id, region: "EUR", ip: "127.0.0.1", savageId: account.scavId, accessKeyId: "" }],
|
||||
customDataCenter: [],
|
||||
};
|
||||
|
||||
|
@ -222,9 +222,7 @@ export class PaymentService
|
||||
const rootCurrencyReward = {
|
||||
_id: this.hashUtil.generate(),
|
||||
_tpl: currency,
|
||||
upd: {
|
||||
StackObjectsCount: calcAmount
|
||||
}
|
||||
upd: { StackObjectsCount: calcAmount },
|
||||
};
|
||||
const rewards = this.itemHelper.splitStackIntoSeparateItems(rootCurrencyReward);
|
||||
|
||||
@ -234,7 +232,7 @@ export class PaymentService
|
||||
itemsWithModsToAdd: rewards,
|
||||
foundInRaid: false,
|
||||
callback: null,
|
||||
useSortingTable: true
|
||||
useSortingTable: true,
|
||||
};
|
||||
this.inventoryHelper.addItemsToStash(sessionID, addItemToStashRequest, pmcData, output);
|
||||
}
|
||||
|
@ -243,13 +243,8 @@ export class ProfileFixerService
|
||||
if (!stashItem)
|
||||
{
|
||||
// Stand inventory stash item doesnt exist, add it
|
||||
pmcProfile.Inventory.items.push(
|
||||
{
|
||||
_id: hideoutStandAreaDb._id,
|
||||
_tpl: stageCurrentAt.container
|
||||
}
|
||||
)
|
||||
stashItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandAreaDb._id)
|
||||
pmcProfile.Inventory.items.push({ _id: hideoutStandAreaDb._id, _tpl: stageCurrentAt.container });
|
||||
stashItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandAreaDb._id);
|
||||
}
|
||||
|
||||
// `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected
|
||||
@ -266,13 +261,8 @@ export class ProfileFixerService
|
||||
if (!stashSecondaryItem)
|
||||
{
|
||||
// Stand inventory stash item doesnt exist, add it
|
||||
pmcProfile.Inventory.items.push(
|
||||
{
|
||||
_id: hideoutStandSecondaryAreaDb._id,
|
||||
_tpl: stageCurrentAt.container
|
||||
}
|
||||
)
|
||||
stashSecondaryItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandSecondaryAreaDb._id)
|
||||
pmcProfile.Inventory.items.push({ _id: hideoutStandSecondaryAreaDb._id, _tpl: stageCurrentAt.container });
|
||||
stashSecondaryItem = pmcProfile.Inventory.items?.find((x) => x._id === hideoutStandSecondaryAreaDb._id);
|
||||
}
|
||||
|
||||
// `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected
|
||||
@ -330,13 +320,8 @@ export class ProfileFixerService
|
||||
if (!stashItem)
|
||||
{
|
||||
// Stand inventory stash item doesnt exist, add it
|
||||
pmcProfile.Inventory.items.push(
|
||||
{
|
||||
_id: placeOfFameAreaDb._id,
|
||||
_tpl: stageCurrentlyAt.container
|
||||
}
|
||||
)
|
||||
stashItem = pmcProfile.Inventory.items?.find((x) => x._id === placeOfFameAreaDb._id)
|
||||
pmcProfile.Inventory.items.push({ _id: placeOfFameAreaDb._id, _tpl: stageCurrentlyAt.container });
|
||||
stashItem = pmcProfile.Inventory.items?.find((x) => x._id === placeOfFameAreaDb._id);
|
||||
}
|
||||
|
||||
// `hideoutAreaStashes` has value related stash inventory items tpl doesnt match what's expected
|
||||
@ -528,9 +513,15 @@ export class ProfileFixerService
|
||||
// Only check if profile has repeatable quests
|
||||
if (pmcProfile.RepeatableQuests && activeRepeatableQuests.length > 0)
|
||||
{
|
||||
const existsInActiveRepeatableQuests = activeRepeatableQuests.some((quest) => quest._id === taskConditionCounter.sourceId);
|
||||
const existsInQuests = pmcProfile.Quests.some((quest) => quest.qid === taskConditionCounter.sourceId);
|
||||
const isAchievementTracker = achievements.some((quest) => quest.id === taskConditionCounter.sourceId);
|
||||
const existsInActiveRepeatableQuests = activeRepeatableQuests.some((quest) =>
|
||||
quest._id === taskConditionCounter.sourceId
|
||||
);
|
||||
const existsInQuests = pmcProfile.Quests.some((quest) =>
|
||||
quest.qid === taskConditionCounter.sourceId
|
||||
);
|
||||
const isAchievementTracker = achievements.some((quest) =>
|
||||
quest.id === taskConditionCounter.sourceId
|
||||
);
|
||||
|
||||
// If task conditions id is neither in activeQuests, quests or achievements - it's stale and should be cleaned up
|
||||
if (!(existsInActiveRepeatableQuests || existsInQuests || isAchievementTracker))
|
||||
@ -1119,7 +1110,7 @@ export class ProfileFixerService
|
||||
if (itemAJson === itemBJson)
|
||||
{
|
||||
// Both items match, we can safely delete one
|
||||
const indexOfItemToRemove = pmcProfile.Inventory.items.findIndex(x => x._id === key);
|
||||
const indexOfItemToRemove = pmcProfile.Inventory.items.findIndex((x) => x._id === key);
|
||||
pmcProfile.Inventory.items.splice(indexOfItemToRemove, 1);
|
||||
this.logger.warning(`Deleted duplicate item: ${key}`);
|
||||
}
|
||||
@ -1127,10 +1118,10 @@ export class ProfileFixerService
|
||||
{
|
||||
// Items are different, replace ID with unique value
|
||||
// Only replace ID if items have no children, we dont want orphaned children
|
||||
const itemsHaveChildren = pmcProfile.Inventory.items.some(x => x.parentId === key);
|
||||
const itemsHaveChildren = pmcProfile.Inventory.items.some((x) => x.parentId === key);
|
||||
if (!itemsHaveChildren)
|
||||
{
|
||||
const itemToAdjustId = pmcProfile.Inventory.items.find(x => x._id === key);
|
||||
const itemToAdjustId = pmcProfile.Inventory.items.find((x) => x._id === key);
|
||||
itemToAdjustId._id = this.hashUtil.generate();
|
||||
this.logger.warning(`Replace duplicate item Id: ${key} with ${itemToAdjustId._id}`);
|
||||
}
|
||||
@ -1138,7 +1129,7 @@ export class ProfileFixerService
|
||||
}
|
||||
|
||||
// Iterate over all inventory items
|
||||
for (const item of pmcProfile.Inventory.items.filter(x => x.slotId))
|
||||
for (const item of pmcProfile.Inventory.items.filter((x) => x.slotId))
|
||||
{
|
||||
if (!item.upd)
|
||||
{
|
||||
@ -1151,7 +1142,7 @@ export class ProfileFixerService
|
||||
if (regxp.test(item.upd.Tag?.Name))
|
||||
{
|
||||
this.logger.warning(`Fixed item: ${item._id}s Tag value, removed invalid characters`);
|
||||
item.upd.Tag.Name = item.upd.Tag.Name.replace(regxp, '');
|
||||
item.upd.Tag.Name = item.upd.Tag.Name.replace(regxp, "");
|
||||
}
|
||||
|
||||
// Check items with StackObjectsCount (null)
|
||||
@ -1170,9 +1161,9 @@ export class ProfileFixerService
|
||||
// Check Head
|
||||
if (!customizationDb[pmcProfile.Customization.Head])
|
||||
{
|
||||
const defaultHead = (playerIsUsec)
|
||||
? customizationDbArray.find(x => x._name === "DefaultUsecHead")
|
||||
: customizationDbArray.find(x => x._name === "DefaultBearHead");
|
||||
const defaultHead = playerIsUsec
|
||||
? customizationDbArray.find((x) => x._name === "DefaultUsecHead")
|
||||
: customizationDbArray.find((x) => x._name === "DefaultBearHead");
|
||||
pmcProfile.Customization.Head = defaultHead._id;
|
||||
}
|
||||
|
||||
@ -1180,8 +1171,8 @@ export class ProfileFixerService
|
||||
if (!customizationDb[pmcProfile.Customization.Body])
|
||||
{
|
||||
const defaultBody = (pmcProfile.Info.Side.toLowerCase() === "usec")
|
||||
? customizationDbArray.find(x => x._name === "DefaultUsecBody")
|
||||
: customizationDbArray.find(x => x._name === "DefaultBearBody");
|
||||
? customizationDbArray.find((x) => x._name === "DefaultUsecBody")
|
||||
: customizationDbArray.find((x) => x._name === "DefaultBearBody");
|
||||
pmcProfile.Customization.Body = defaultBody._id;
|
||||
}
|
||||
|
||||
@ -1189,8 +1180,8 @@ export class ProfileFixerService
|
||||
if (!customizationDb[pmcProfile.Customization.Hands])
|
||||
{
|
||||
const defaultHands = (pmcProfile.Info.Side.toLowerCase() === "usec")
|
||||
? customizationDbArray.find(x => x._name === "DefaultUsecHands")
|
||||
: customizationDbArray.find(x => x._name === "DefaultBearHands");
|
||||
? customizationDbArray.find((x) => x._name === "DefaultUsecHands")
|
||||
: customizationDbArray.find((x) => x._name === "DefaultBearHands");
|
||||
pmcProfile.Customization.Hands = defaultHands._id;
|
||||
}
|
||||
|
||||
@ -1198,8 +1189,8 @@ export class ProfileFixerService
|
||||
if (!customizationDb[pmcProfile.Customization.Feet])
|
||||
{
|
||||
const defaultFeet = (pmcProfile.Info.Side.toLowerCase() === "usec")
|
||||
? customizationDbArray.find(x => x._name === "DefaultUsecFeet")
|
||||
: customizationDbArray.find(x => x._name === "DefaultBearFeet");
|
||||
? customizationDbArray.find((x) => x._name === "DefaultUsecFeet")
|
||||
: customizationDbArray.find((x) => x._name === "DefaultBearFeet");
|
||||
pmcProfile.Customization.Feet = defaultFeet._id;
|
||||
}
|
||||
}
|
||||
@ -1388,7 +1379,7 @@ export class ProfileFixerService
|
||||
|
||||
for (let i = 0; i < profileQuests.length; i++)
|
||||
{
|
||||
if (!quests[profileQuests[i].qid] && !repeatableQuests.find(x => x._id == profileQuests[i].qid))
|
||||
if (!quests[profileQuests[i].qid] && !repeatableQuests.find((x) => x._id == profileQuests[i].qid))
|
||||
{
|
||||
profileQuests.splice(i, 1);
|
||||
this.logger.success("Successfully removed orphaned quest that doesnt exist in our quest data");
|
||||
|
@ -22,10 +22,14 @@ export class RagfairCategoriesService
|
||||
* @param fleaUnlocked Can player see full flea yet (level 15 by default)
|
||||
* @returns KVP of item tpls + count of offers
|
||||
*/
|
||||
public getCategoriesFromOffers(offers: IRagfairOffer[], searchRequestData: ISearchRequestData, fleaUnlocked: boolean): Record<string, number>
|
||||
public getCategoriesFromOffers(
|
||||
offers: IRagfairOffer[],
|
||||
searchRequestData: ISearchRequestData,
|
||||
fleaUnlocked: boolean,
|
||||
): Record<string, number>
|
||||
{
|
||||
// Get offers valid for search request, then reduce them down to just the counts
|
||||
return offers.filter(offer =>
|
||||
return offers.filter((offer) =>
|
||||
{
|
||||
const isTraderOffer = offer.user.memberType === MemberCategory.TRADER;
|
||||
|
||||
@ -36,7 +40,10 @@ export class RagfairCategoriesService
|
||||
}
|
||||
|
||||
// Remove items not for money when `removeBartering` is enabled
|
||||
if (searchRequestData.removeBartering && (offer.requirements.length > 1 || !this.paymentHelper.isMoneyTpl(offer.requirements[0]._tpl)))
|
||||
if (
|
||||
searchRequestData.removeBartering
|
||||
&& (offer.requirements.length > 1 || !this.paymentHelper.isMoneyTpl(offer.requirements[0]._tpl))
|
||||
)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -243,7 +243,9 @@ export class RagfairOfferService
|
||||
const offerinProfileIndex = profile.RagfairInfo.offers.findIndex((o) => o._id === playerOffer._id);
|
||||
if (offerinProfileIndex === -1)
|
||||
{
|
||||
this.logger.warning(this.localisationService.getText("ragfair-unable_to_find_offer_to_remove", playerOffer._id));
|
||||
this.logger.warning(
|
||||
this.localisationService.getText("ragfair-unable_to_find_offer_to_remove", playerOffer._id),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -401,12 +401,12 @@ export class RagfairPriceService implements OnLoad
|
||||
*/
|
||||
protected getWeaponPreset(weapon: Item): { isDefault: boolean; preset: IPreset; }
|
||||
{
|
||||
const defaultPreset = this.presetHelper.getDefaultPreset(weapon._tpl)
|
||||
const defaultPreset = this.presetHelper.getDefaultPreset(weapon._tpl);
|
||||
if (defaultPreset)
|
||||
{
|
||||
return { isDefault: true, preset: defaultPreset };
|
||||
}
|
||||
const nonDefaultPresets = this.presetHelper.getPresets(weapon._tpl)
|
||||
const nonDefaultPresets = this.presetHelper.getPresets(weapon._tpl);
|
||||
if (nonDefaultPresets.length === 1)
|
||||
{
|
||||
this.logger.debug(
|
||||
|
@ -7,7 +7,11 @@ import { ILocationBase } from "@spt-aki/models/eft/common/ILocationBase";
|
||||
import { IGetRaidTimeRequest } from "@spt-aki/models/eft/game/IGetRaidTimeRequest";
|
||||
import { ExtractChange, IGetRaidTimeResponse } from "@spt-aki/models/eft/game/IGetRaidTimeResponse";
|
||||
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
|
||||
import { ILocationConfig, IScavRaidTimeLocationSettings, LootMultiplier } from "@spt-aki/models/spt/config/ILocationConfig";
|
||||
import {
|
||||
ILocationConfig,
|
||||
IScavRaidTimeLocationSettings,
|
||||
LootMultiplier,
|
||||
} from "@spt-aki/models/spt/config/ILocationConfig";
|
||||
import { IRaidChanges } from "@spt-aki/models/spt/location/IRaidChanges";
|
||||
import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
|
||||
import { ConfigServer } from "@spt-aki/servers/ConfigServer";
|
||||
@ -39,7 +43,9 @@ export class RaidTimeAdjustmentService
|
||||
*/
|
||||
public makeAdjustmentsToMap(raidAdjustments: IRaidChanges, mapBase: ILocationBase): void
|
||||
{
|
||||
this.logger.debug(`Adjusting dynamic loot multipliers to ${raidAdjustments.dynamicLootPercent}% and static loot multipliers to ${raidAdjustments.staticLootPercent}% of original`);
|
||||
this.logger.debug(
|
||||
`Adjusting dynamic loot multipliers to ${raidAdjustments.dynamicLootPercent}% and static loot multipliers to ${raidAdjustments.staticLootPercent}% of original`,
|
||||
);
|
||||
|
||||
// Change loot multipler values before they're used below
|
||||
this.adjustLootMultipliers(this.locationConfig.looseLootMultiplier, raidAdjustments.dynamicLootPercent);
|
||||
@ -49,7 +55,7 @@ export class RaidTimeAdjustmentService
|
||||
if (mapSettings.adjustWaves)
|
||||
{
|
||||
// Make alterations to bot spawn waves now player is simulated spawning later
|
||||
this.adjustWaves(mapBase, raidAdjustments)
|
||||
this.adjustWaves(mapBase, raidAdjustments);
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +81,7 @@ export class RaidTimeAdjustmentService
|
||||
{
|
||||
// Remove waves that spawned before the player joined
|
||||
const originalWaveCount = mapBase.waves.length;
|
||||
mapBase.waves = mapBase.waves.filter(x => x.time_max > raidAdjustments.simulatedRaidStartSeconds);
|
||||
mapBase.waves = mapBase.waves.filter((x) => x.time_max > raidAdjustments.simulatedRaidStartSeconds);
|
||||
|
||||
// Adjust wave min/max times to match new simulated start
|
||||
for (const wave of mapBase.waves)
|
||||
@ -85,7 +91,11 @@ export class RaidTimeAdjustmentService
|
||||
wave.time_max -= Math.max(raidAdjustments.simulatedRaidStartSeconds, 0);
|
||||
}
|
||||
|
||||
this.logger.debug(`Removed ${originalWaveCount - mapBase.waves.length} wave from map due to simulated raid start time of ${raidAdjustments.simulatedRaidStartSeconds / 60} minutes`);
|
||||
this.logger.debug(
|
||||
`Removed ${originalWaveCount - mapBase.waves.length} wave from map due to simulated raid start time of ${
|
||||
raidAdjustments.simulatedRaidStartSeconds / 60
|
||||
} minutes`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,8 +116,8 @@ export class RaidTimeAdjustmentService
|
||||
RaidTimeMinutes: baseEscapeTimeMinutes,
|
||||
ExitChanges: [],
|
||||
NewSurviveTimeSeconds: null,
|
||||
OriginalSurvivalTimeSeconds: db.globals.config.exp.match_end.survived_seconds_requirement
|
||||
}
|
||||
OriginalSurvivalTimeSeconds: db.globals.config.exp.match_end.survived_seconds_requirement,
|
||||
};
|
||||
|
||||
// Pmc raid, send default
|
||||
if (request.Side.toLowerCase() === "pmc")
|
||||
@ -126,13 +136,15 @@ export class RaidTimeAdjustmentService
|
||||
}
|
||||
|
||||
// Get the weighted percent to reduce the raid time by
|
||||
const chosenRaidReductionPercent = Number.parseInt(this.weightedRandomHelper.getWeightedValue<string>(
|
||||
mapSettings.reductionPercentWeights,
|
||||
));
|
||||
const chosenRaidReductionPercent = Number.parseInt(
|
||||
this.weightedRandomHelper.getWeightedValue<string>(mapSettings.reductionPercentWeights),
|
||||
);
|
||||
const raidTimeRemainingPercent = 100 - chosenRaidReductionPercent;
|
||||
|
||||
// How many minutes raid will last
|
||||
const newRaidTimeMinutes = Math.floor(this.randomUtil.reduceValueByPercent(baseEscapeTimeMinutes, chosenRaidReductionPercent));
|
||||
const newRaidTimeMinutes = Math.floor(
|
||||
this.randomUtil.reduceValueByPercent(baseEscapeTimeMinutes, chosenRaidReductionPercent),
|
||||
);
|
||||
|
||||
// Time player spawns into the raid if it was online
|
||||
const simulatedRaidStartTimeMinutes = baseEscapeTimeMinutes - newRaidTimeMinutes;
|
||||
@ -140,21 +152,25 @@ export class RaidTimeAdjustmentService
|
||||
if (mapSettings.reduceLootByPercent)
|
||||
{
|
||||
// Store time reduction percent in app context so loot gen can pick it up later
|
||||
this.applicationContext.addValue(ContextVariableType.RAID_ADJUSTMENTS,
|
||||
{
|
||||
this.applicationContext.addValue(ContextVariableType.RAID_ADJUSTMENTS, {
|
||||
dynamicLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minDynamicLootPercent),
|
||||
staticLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minStaticLootPercent),
|
||||
simulatedRaidStartSeconds: simulatedRaidStartTimeMinutes * 60
|
||||
simulatedRaidStartSeconds: simulatedRaidStartTimeMinutes * 60,
|
||||
});
|
||||
}
|
||||
|
||||
// Update result object with new time
|
||||
result.RaidTimeMinutes = newRaidTimeMinutes;
|
||||
|
||||
this.logger.debug(`Reduced: ${request.Location} raid time by: ${chosenRaidReductionPercent}% to ${newRaidTimeMinutes} minutes`)
|
||||
this.logger.debug(
|
||||
`Reduced: ${request.Location} raid time by: ${chosenRaidReductionPercent}% to ${newRaidTimeMinutes} minutes`,
|
||||
);
|
||||
|
||||
// Calculate how long player needs to be in raid to get a `survived` extract status
|
||||
result.NewSurviveTimeSeconds = Math.max(result.OriginalSurvivalTimeSeconds - ((baseEscapeTimeMinutes - newRaidTimeMinutes) * 60), 0);
|
||||
result.NewSurviveTimeSeconds = Math.max(
|
||||
result.OriginalSurvivalTimeSeconds - ((baseEscapeTimeMinutes - newRaidTimeMinutes) * 60),
|
||||
0,
|
||||
);
|
||||
|
||||
const exitAdjustments = this.getExitAdjustments(mapBase, newRaidTimeMinutes);
|
||||
if (exitAdjustments)
|
||||
@ -200,12 +216,7 @@ 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: null, MaxTime: null, Chance: null };
|
||||
|
||||
// At what minute we simulate the player joining the raid
|
||||
const simulatedRaidEntryTimeMinutes = mapBase.EscapeTimeLimit - newRaidTimeMinutes;
|
||||
@ -227,10 +238,12 @@ export class RaidTimeAdjustmentService
|
||||
//
|
||||
// I added 2 seconds just to be safe...
|
||||
//
|
||||
const trainArrivalDelaySeconds = this.locationConfig.scavRaidTimeSettings.settings.trainArrivalDelayObservedSeconds;
|
||||
const trainArrivalDelaySeconds =
|
||||
this.locationConfig.scavRaidTimeSettings.settings.trainArrivalDelayObservedSeconds;
|
||||
|
||||
// Determine the earliest possible time in the raid when the train would leave
|
||||
const earliestPossibleDepartureMinutes = (exit.MinTime + exit.Count + exit.ExfiltrationTime + trainArrivalDelaySeconds) / 60;
|
||||
const earliestPossibleDepartureMinutes =
|
||||
(exit.MinTime + exit.Count + exit.ExfiltrationTime + trainArrivalDelaySeconds) / 60;
|
||||
|
||||
// If raid is after last moment train can leave, assume train has already left, disable extract
|
||||
const mostPossibleTimeRemainingAfterDeparture = mapBase.EscapeTimeLimit - earliestPossibleDepartureMinutes;
|
||||
@ -238,7 +251,9 @@ export class RaidTimeAdjustmentService
|
||||
{
|
||||
exitChange.Chance = 0;
|
||||
|
||||
this.logger.debug(`Train Exit: ${exit.Name} disabled as new raid time ${newRaidTimeMinutes} minutes is below ${mostPossibleTimeRemainingAfterDeparture} minutes`);
|
||||
this.logger.debug(
|
||||
`Train Exit: ${exit.Name} disabled as new raid time ${newRaidTimeMinutes} minutes is below ${mostPossibleTimeRemainingAfterDeparture} minutes`,
|
||||
);
|
||||
|
||||
result.push(exitChange);
|
||||
|
||||
@ -249,13 +264,13 @@ export class RaidTimeAdjustmentService
|
||||
exitChange.MinTime = Math.max(exit.MinTime - reductionSeconds, 0);
|
||||
exitChange.MaxTime = Math.max(exit.MaxTime - reductionSeconds, 0);
|
||||
|
||||
this.logger.debug(`Train appears between: ${exitChange.MinTime} and ${exitChange.MaxTime} seconds raid time`);
|
||||
this.logger.debug(
|
||||
`Train appears between: ${exitChange.MinTime} and ${exitChange.MaxTime} seconds raid time`,
|
||||
);
|
||||
|
||||
result.push(exitChange);
|
||||
}
|
||||
|
||||
return result.length > 0
|
||||
? result
|
||||
: null ;
|
||||
return result.length > 0 ? result : null;
|
||||
}
|
||||
}
|
||||
|
@ -124,7 +124,7 @@ export class RepairService
|
||||
const options: IProcessBuyTradeRequestData = {
|
||||
scheme_items: [{
|
||||
id: "5449016a4bdc2d6f028b456f", // Rouble tpl
|
||||
count: Math.round(repairCost)
|
||||
count: Math.round(repairCost),
|
||||
}],
|
||||
tid: traderId,
|
||||
Action: "SptRepair",
|
||||
@ -214,10 +214,7 @@ export class RepairService
|
||||
else
|
||||
{
|
||||
// Trader repair - Not as accurate as kit, needs data from live
|
||||
return Math.min(
|
||||
repairDetails.repairAmount / 10,
|
||||
this.repairConfig.maxIntellectGainPerRepair.trader,
|
||||
);
|
||||
return Math.min(repairDetails.repairAmount / 10, this.repairConfig.maxIntellectGainPerRepair.trader);
|
||||
}
|
||||
}
|
||||
|
||||
@ -440,7 +437,13 @@ export class RepairService
|
||||
|
||||
if (this.shouldBuffItem(repairDetails, pmcData))
|
||||
{
|
||||
if (this.itemHelper.isOfBaseclasses(repairDetails.repairedItem._tpl, [BaseClasses.ARMOR, BaseClasses.VEST, BaseClasses.HEADWEAR]))
|
||||
if (
|
||||
this.itemHelper.isOfBaseclasses(repairDetails.repairedItem._tpl, [
|
||||
BaseClasses.ARMOR,
|
||||
BaseClasses.VEST,
|
||||
BaseClasses.HEADWEAR,
|
||||
])
|
||||
)
|
||||
{
|
||||
const armorConfig = this.repairConfig.repairKit.armor;
|
||||
this.addBuff(armorConfig, repairDetails.repairedItem);
|
||||
@ -539,7 +542,13 @@ export class RepairService
|
||||
*/
|
||||
protected getItemSkillType(itemTemplate: ITemplateItem): SkillTypes
|
||||
{
|
||||
if (this.itemHelper.isOfBaseclasses(itemTemplate._id, [BaseClasses.ARMOR, BaseClasses.VEST, BaseClasses.HEADWEAR]))
|
||||
if (
|
||||
this.itemHelper.isOfBaseclasses(itemTemplate._id, [
|
||||
BaseClasses.ARMOR,
|
||||
BaseClasses.VEST,
|
||||
BaseClasses.HEADWEAR,
|
||||
])
|
||||
)
|
||||
{
|
||||
if (itemTemplate._props.ArmorType === "Light")
|
||||
{
|
||||
|
@ -529,7 +529,7 @@ export class SeasonalEventService
|
||||
{
|
||||
const mapData: ILocationData = maps[gifterMapSettings.map];
|
||||
// Dont add gifter to map twice
|
||||
if (mapData.base.BossLocationSpawn.some(boss => boss.BossName === "gifter"))
|
||||
if (mapData.base.BossLocationSpawn.some((boss) => boss.BossName === "gifter"))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -211,7 +211,9 @@ export class CustomItemService
|
||||
const weapon = this.itemHelper.getItem(weaponTpl);
|
||||
if (!weapon[0])
|
||||
{
|
||||
this.logger.warning(`Unable to add custom weapon ${weaponTpl} to PMCs as it cannot be found in the Item db`);
|
||||
this.logger.warning(
|
||||
`Unable to add custom weapon ${weaponTpl} to PMCs as it cannot be found in the Item db`,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -83,11 +83,7 @@ export class HttpResponseUtil
|
||||
{
|
||||
if (output.warnings?.length > 0)
|
||||
{
|
||||
output.warnings.push({
|
||||
index: output.warnings?.length - 1,
|
||||
errmsg: message,
|
||||
code: errorCode.toString()
|
||||
})
|
||||
output.warnings.push({ index: output.warnings?.length - 1, errmsg: message, code: errorCode.toString() });
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -200,9 +200,7 @@ export class ProbabilityObject<K, V = undefined>
|
||||
@injectable()
|
||||
export class RandomUtil
|
||||
{
|
||||
constructor(
|
||||
@inject("JsonUtil") protected jsonUtil: JsonUtil,
|
||||
@inject("WinstonLogger") protected logger: ILogger)
|
||||
constructor(@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("WinstonLogger") protected logger: ILogger)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -25,22 +25,19 @@ describe("HealthController", () =>
|
||||
{
|
||||
it("Should Heal Players heavy bleed and heal chest to full hp", () =>
|
||||
{
|
||||
const maxHealth = 100
|
||||
const maxHealth = 100;
|
||||
const pmcData = {
|
||||
Health: {
|
||||
BodyParts: {
|
||||
Chest: {
|
||||
Health: {
|
||||
Current: 50, // Has damage
|
||||
Maximum: maxHealth
|
||||
Maximum: maxHealth,
|
||||
},
|
||||
Effects: { HeavyBleeding: { Time: 20 } },
|
||||
},
|
||||
},
|
||||
},
|
||||
Effects: {HeavyBleeding: {
|
||||
Time: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const bleedRemovalAndLimbHealRequest = {
|
||||
Action: "RestoreHealth",
|
||||
@ -49,36 +46,30 @@ describe("HealthController", () =>
|
||||
BodyParts: {
|
||||
Chest: {
|
||||
Health: 23, // > 0 value means it will heal
|
||||
Effects: ["HeavyBleeding"] // non-null means it will remove effect from player
|
||||
}
|
||||
}
|
||||
}
|
||||
Effects: ["HeavyBleeding"], // non-null means it will remove effect from player
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const sessionId = "12345";
|
||||
|
||||
// Mock output generation
|
||||
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue(
|
||||
{
|
||||
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue({
|
||||
warnings: {},
|
||||
profileChanges: {
|
||||
"12345": {
|
||||
health: {}
|
||||
}
|
||||
}
|
||||
profileChanges: { "12345": { health: {} } },
|
||||
});
|
||||
|
||||
// Mock payment
|
||||
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue(
|
||||
{
|
||||
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue({
|
||||
warnings: {},
|
||||
profileChanges: {
|
||||
"12345": {
|
||||
health: {}
|
||||
}
|
||||
}
|
||||
profileChanges: { "12345": { health: {} } },
|
||||
});
|
||||
|
||||
const result = healthController.healthTreatment(pmcData as unknown as IPmcData, bleedRemovalAndLimbHealRequest as IHealthTreatmentRequestData, sessionId);
|
||||
const result = healthController.healthTreatment(
|
||||
pmcData as unknown as IPmcData,
|
||||
bleedRemovalAndLimbHealRequest as IHealthTreatmentRequestData,
|
||||
sessionId,
|
||||
);
|
||||
|
||||
// Has healed chest to full
|
||||
expect(result.profileChanges[sessionId].health.BodyParts.Chest.Health.Current).equals(maxHealth);
|
||||
@ -89,22 +80,19 @@ describe("HealthController", () =>
|
||||
|
||||
it("Should Heal Players heavy bleed and leave limb health at existing value", () =>
|
||||
{
|
||||
const maxHealth = 100
|
||||
const maxHealth = 100;
|
||||
const pmcData = {
|
||||
Health: {
|
||||
BodyParts: {
|
||||
Chest: {
|
||||
Health: {
|
||||
Current: 50, // Has damage
|
||||
Maximum: maxHealth
|
||||
Maximum: maxHealth,
|
||||
},
|
||||
Effects: { HeavyBleeding: { Time: 20 } },
|
||||
},
|
||||
},
|
||||
},
|
||||
Effects: {HeavyBleeding: {
|
||||
Time: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const limbOnlyHealRequest = {
|
||||
Action: "RestoreHealth",
|
||||
@ -113,36 +101,30 @@ describe("HealthController", () =>
|
||||
BodyParts: {
|
||||
Chest: {
|
||||
Health: 23, // > 0 value means it will heal limb to full
|
||||
Effects: null // null means no healing of effects
|
||||
}
|
||||
}
|
||||
}
|
||||
Effects: null, // null means no healing of effects
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const sessionId = "12345";
|
||||
|
||||
// Mock output generation
|
||||
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue(
|
||||
{
|
||||
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue({
|
||||
warnings: {},
|
||||
profileChanges: {
|
||||
"12345": {
|
||||
health: {}
|
||||
}
|
||||
}
|
||||
profileChanges: { "12345": { health: {} } },
|
||||
});
|
||||
|
||||
// Mock payment
|
||||
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue(
|
||||
{
|
||||
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue({
|
||||
warnings: {},
|
||||
profileChanges: {
|
||||
"12345": {
|
||||
health: {}
|
||||
}
|
||||
}
|
||||
profileChanges: { "12345": { health: {} } },
|
||||
});
|
||||
|
||||
const result = healthController.healthTreatment(pmcData as unknown as IPmcData, limbOnlyHealRequest as IHealthTreatmentRequestData, sessionId);
|
||||
const result = healthController.healthTreatment(
|
||||
pmcData as unknown as IPmcData,
|
||||
limbOnlyHealRequest as IHealthTreatmentRequestData,
|
||||
sessionId,
|
||||
);
|
||||
|
||||
// Has healed chest to full
|
||||
expect(result.profileChanges[sessionId].health.BodyParts.Chest.Health.Current).equals(maxHealth);
|
||||
@ -153,7 +135,7 @@ describe("HealthController", () =>
|
||||
|
||||
it("Should Heal Players heavy bleed and leave limb health at existing value", () =>
|
||||
{
|
||||
const maxHealth = 100
|
||||
const maxHealth = 100;
|
||||
const currentHealth = 50;
|
||||
const pmcData = {
|
||||
Health: {
|
||||
@ -161,15 +143,12 @@ describe("HealthController", () =>
|
||||
Chest: {
|
||||
Health: {
|
||||
Current: currentHealth, // Has damage
|
||||
Maximum: maxHealth
|
||||
Maximum: maxHealth,
|
||||
},
|
||||
Effects: { HeavyBleeding: { Time: 20 } },
|
||||
},
|
||||
},
|
||||
},
|
||||
Effects: {HeavyBleeding: {
|
||||
Time: 20
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const limbOnlyHealRequest = {
|
||||
Action: "RestoreHealth",
|
||||
@ -178,36 +157,30 @@ describe("HealthController", () =>
|
||||
BodyParts: {
|
||||
Chest: {
|
||||
Health: 0, // 0 value means it will not heal and damage
|
||||
Effects: null // null means no healing of effects
|
||||
}
|
||||
}
|
||||
}
|
||||
Effects: null, // null means no healing of effects
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const sessionId = "12345";
|
||||
|
||||
// Mock output generation
|
||||
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue(
|
||||
{
|
||||
vi.spyOn((healthController as any).eventOutputHolder, "getOutput").mockReturnValue({
|
||||
warnings: {},
|
||||
profileChanges: {
|
||||
"12345": {
|
||||
health: {}
|
||||
}
|
||||
}
|
||||
profileChanges: { "12345": { health: {} } },
|
||||
});
|
||||
|
||||
// Mock payment
|
||||
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue(
|
||||
{
|
||||
vi.spyOn((healthController as any).paymentService, "payMoney").mockReturnValue({
|
||||
warnings: {},
|
||||
profileChanges: {
|
||||
"12345": {
|
||||
health: {}
|
||||
}
|
||||
}
|
||||
profileChanges: { "12345": { health: {} } },
|
||||
});
|
||||
|
||||
const result = healthController.healthTreatment(pmcData as unknown as IPmcData, limbOnlyHealRequest as IHealthTreatmentRequestData, sessionId);
|
||||
const result = healthController.healthTreatment(
|
||||
pmcData as unknown as IPmcData,
|
||||
limbOnlyHealRequest as IHealthTreatmentRequestData,
|
||||
sessionId,
|
||||
);
|
||||
|
||||
// Has not healed chest to full
|
||||
expect(result.profileChanges[sessionId].health.BodyParts.Chest.Health.Current).equals(currentHealth);
|
||||
|
Loading…
Reference in New Issue
Block a user