/* * The Peacock Project - a HITMAN server replacement. * Copyright (C) 2021-2023 The Peacock Project Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ import type * as core from "express-serve-static-core" import type { IContractCreationPayload } from "../statemachines/contractCreation" import type { Request } from "express" import { ChallengeContext, ProfileChallengeData, SavedChallenge, } from "./challenges" import { SessionGhostModeDetails } from "../multiplayer/multiplayerService" import { IContextListener } from "../statemachines/contextListeners" /** * A duration or relative point in time expressed in seconds. */ export type Seconds = number /** * The game's major version. */ export type GameVersion = "h1" | "h2" | "h3" | "scpc" /** * The server configuration's target audience. * * Notes: * - pc-prod8 is deprecated, as HITMAN 3 after 3.100.0 no longer uses it. */ export type GameAudience = | "pc-prod8" | "pc-prod7" | "pc-prod6" | "steam-prod_8" | "epic-prod_8" | "xboxone-prod" | "scpc-prod" | "playtest01-prod_8" /** * Data from the JSON Web Token (JWT) authentication scheme. */ export interface JwtData { /** * Usually bearer. */ "auth:method": "bearer" | string /** * Always "user". */ roles: "user" sub: string /** * Profile ID. */ unique_name: string /** * User ID. */ userid: string /** * Either "steam" or "epic" on PC. */ platform: "steam" | "epic" /** * Client/account locale. */ locale: string /** * Client/account region. */ rgn: string /** * External appid. */ pis: string /** * Country, specified by the locale property. */ cntry: string /** * Expires in. */ exp: string /** * Not before. */ nbf: string /** * Issuer (from external provider). */ iss: string /** * The audience. * * @see GameAudience */ aud: GameAudience } /** * A request with a JSON web token (JWT) already parsed. Also contains our custom request properties. */ export interface RequestWithJwt< Query = core.Query, // TODO: Make this `unknown` instead, requires lots of changes elsewhere // eslint-disable-next-line @typescript-eslint/no-explicit-any RequestBody = any, > extends Request< core.ParamsDictionary, // eslint-disable-next-line any, RequestBody, core.Query & Query > { /** * The user's JSON Web Token (JWT) data. */ jwt: JwtData /** * The current Hitman server version. */ serverVersion?: string /** * The game's version. */ gameVersion: GameVersion /** * Used internally to declare if a route should be propagated to its handler or cancelled early. */ shouldCease?: boolean } /** * The status of cameras in a mission. */ export enum PeacockCameraStatus { NotSpotted = "NOT_SPOTTED", Spotted = "SPOTTED", Erased = "ERASED", } /** * The status of security cameras in a mission (event-end). */ export type SecurityCameraStatus = "destroyed" | "spotted" | "erased" /** * A repository ID (really just a UUID v4). */ export type RepositoryId = string /** * Possible mission `Metadata.Type` values. */ export type MissionType = | "mission" | "elusive" | "escalation" | "featured" | "sniper" | "usercreated" | "creation" | "tutorial" | "orbis" | "campaign" | "arcade" | "vsrace" | "evergreen" /** * The data acquired when using the "contract search" functionality. */ export interface ContractSearchResult { Data: { Contracts: { UserCentricContract: UserCentricContract }[] ErrorReason: string HasMore: boolean HasPrevious: boolean Page: number TotalCount: number } } /** * The last kill in a contract session. * * @see ContractSession */ export interface ContractSessionLastKill { timestamp?: Date | number repositoryIds?: RepositoryId[] } /** * A contract session is created every time you start a level, which contains the data of the play session. * Primarily used for scoring, saving, and loading. */ export interface ContractSession { Id: string gameVersion: GameVersion sessionStart: Date | number lastUpdate: Date | number contractId: string userId: string timerStart: Date | number timerEnd: Date | number duration: Date | number crowdNpcKills: number targetKills: Set<RepositoryId> npcKills: Set<RepositoryId> bodiesHidden: Set<RepositoryId> pacifications: Set<RepositoryId> disguisesUsed: Set<RepositoryId> disguisesRuined: Set<RepositoryId> spottedBy: Set<RepositoryId> witnesses: Set<RepositoryId> bodiesFoundBy: Set<RepositoryId> legacyHasBodyBeenFound: boolean killsNoticedBy: Set<RepositoryId> completedObjectives: Set<RepositoryId> failedObjectives: Set<RepositoryId> recording: PeacockCameraStatus lastAccident: number lastKill: ContractSessionLastKill kills: Set<RatingKill> markedTargets: Set<RepositoryId> compat: boolean currentDisguise: string difficulty: number objectiveDefinitions: Map<string, unknown> objectiveStates: Map<string, string> objectiveContexts: Map<string, unknown> /** * Session Ghost Mode details. * * @since v5.0.0 */ ghost?: SessionGhostModeDetails /** * The current state of the challenges. * * @since v5.6.0-dev.1 */ challengeContexts?: { [challengeId: string]: ChallengeContext } /** * Session Evergreen details. * * @since v6.0.0 */ evergreen?: { payout: number scoringScreenEndState: string failed: boolean } } /** * The SaveFile object passed by the client in /ProfileService/UpdateUserSaveFileTable */ export interface SaveFile { // The contract session ID of the save ContractSessionId: string // The unix timestamp at the time of saving TimeStamp: number Value: { // The name of the save slot Name: string // The token of the last event that happened before the save was made LastEventToken: string } } /** * The body sent with the UpdateUserSaveFileTable request from the game after saving. * * @see SaveFile */ export interface UpdateUserSaveFileTableBody { clientSaveFileList: SaveFile[] deletedSaveFileList: SaveFile[] } /** * The Hitman server version in object form. */ export interface ServerVersion { readonly _Major: number readonly _Minor: number readonly _Build: number readonly _Revision: number } /** * An event sent from the game client to the server. */ export interface ClientToServerEvent<EventValue = unknown> { Value: EventValue extends object ? Readonly<EventValue> : EventValue ContractSessionId: string ContractId: string Name: string Timestamp: number } /** * A wrapper for {@link ServerToClientEvent} that also has a timestamp value. * * @see ServerToClientEvent */ export interface S2CEventWithTimestamp<EventValue = unknown> { time: number | string event: ServerToClientEvent<EventValue> } /** * A server to client push message. The message component is encoded JSON. */ export interface PushMessage { time: number | string message: string } /** * A server to client event. */ export interface ServerToClientEvent<EventValue = unknown> { message?: string CreatedAt?: string Token?: string IsReplicated?: boolean Version: ServerVersion CreatedContract?: string | null Id?: string Name?: string UserId?: string ContractId?: string SessionId?: string | null ContractSessionId?: string Timestamp?: number Value?: EventValue Origin?: string | null } export interface MissionStory { CommonRepositoryId: RepositoryId PreviouslyCompleted: boolean IsMainOpportunity: boolean Title: string Summary: string Briefing: string Location: string SubLocation: string Image: string } export interface PlayerProfileView { template: unknown data: { SubLocationData: { ParentLocation: Unlockable Location: Unlockable CompletionData: CompletionData ChallengeCategoryCompletion: ChallengeCategoryCompletion[] ChallengeCompletion: ChallengeCompletion OpportunityStatistics: OpportunityStatistics LocationCompletionPercent: number }[] PlayerProfileXp: { Total: number Level: number Seasons: { Number: number Locations: { LocationId: string Xp: number ActionXp: number LocationProgression?: { Level: number MaxLevel: number } }[] }[] } } } export interface ChallengeCompletion { ChallengesCount: number CompletedChallengesCount: number CompletionPercent?: number } export interface ChallengeCategoryCompletion extends ChallengeCompletion { Name: string } export interface OpportunityStatistics { Count: number Completed: number } export interface ContractHistory { LastPlayedAt?: number Completed?: boolean IsEscalation?: boolean } export interface UserProfile { Id: string LinkedAccounts: { dev?: string epic?: string steam?: string gog?: string xbox?: string stadia?: string } Extensions: { /** * Map of escalation group ID to current level number. */ PeacockEscalations: { [escalationId: string]: number } PeacockFavoriteContracts: string[] PeacockPlayedContracts: { [contractId: string]: ContractHistory } PeacockCompletedEscalations: string[] Saves: { [slot: string]: { Timestamp: number ContractSessionId: string Token: string } } ChallengeProgression: { [id: string]: ProfileChallengeData } /** * Player progression data. */ progression: { /** * Player XP and level data. */ PlayerProfileXP: { ProfileLevel: number /** * The total amount of XP a user has obtained. */ Total: number Sublocations: { Location: string Xp: number ActionXp: number }[] } Locations: { [location: string]: { Xp: number Level: number PreviouslySeenXp: number } } } defaultloadout?: { [location: string]: { "2"?: string "3"?: string "4"?: string "5"?: string } } entP: string[] achievements?: unknown gamepersistentdata: { __stats?: unknown PersistentBool: Record<string, unknown> HitsFilterType: { // "all" / "completed" / "failed" MyHistory: string MyContracts: string MyPlaylist: string } } opportunityprogression: { [opportunityId: RepositoryId]: boolean } CPD: CPDStore } ETag: string | null Gamertag: string DevId: string | null SteamId: string | null StadiaId: string | null EpicId: string | null NintendoId: string | null XboxLiveId: string | null PSNAccountId: string | null PSNOnlineId: string | null } export interface RatingKill { IsHeadshot: boolean KillClass: string KillItemCategory: string KillMethodBroad: string KillMethodStrict: string KillItemRepositoryId: RepositoryId // only used in contract creation? RequiredKillMethodType?: number // TODO: why did we do this?? _RepositoryId?: RepositoryId // !!! use the one above this one - this is only a placeholder and will not actually work RepositoryId?: RepositoryId OutfitRepoId: string } export interface NamespaceEntitlementEpic { namespace: string itemId: string owned: boolean } /** * An unlockable item. */ export interface Unlockable { Id: string DisplayNameLocKey: string GameAsset: string | null Guid: string Type: string Subtype?: string // TODO: is this used? SubType?: string ImageId?: string | null RMTPrice?: number GamePrice?: number IsPurchasable: boolean IsPublished: boolean IsDroppable: boolean Capabilities: unknown[] Qualities?: Record<string, unknown> | null Properties: { RewardHidden?: boolean HowToUnlock?: string AllowUpSync?: boolean Background?: string Icon?: string LockedIcon?: string DlcImage?: string DlcName?: string IsLocked?: boolean Order?: number ProgressionKey?: string Season?: number RequiredResources?: string[] Entitlements?: string[] ParentLocation?: string GameChangers?: unknown[] CreateContractId?: null | string IsFreeDLC?: boolean HideProgression?: boolean ExcludeParentRewards?: boolean Quality?: number | string UpcomingContent?: boolean UpcomingKey?: "UI_MENU_LIVETILE_CONTENT_UPCOMING_HEADLINE" | string LimitedLoadout?: boolean NormalLoadoutUnlock?: | { normal: string pro1: string } | string Unlocks?: string[] Rarity?: string | null // noinspection SpellCheckingInspection LoadoutSlot?: | "carriedweapon" | "concealedweapon" | "disguise" | "gear" | string IsConsumable?: boolean RepositoryId?: RepositoryId OrderIndex?: number Name?: string Description?: string UnlockOrder?: number Location?: string Equip?: string[] GameAssets?: string[] RepositoryAssets?: RepositoryId[] Gameplay?: ItemGameplay AlwaysAdd?: boolean BlacklistedByDefault?: boolean IsContainer?: boolean LoadoutSettings?: { GearSlotsEnabledCount?: number GearSlotsAllowContainers?: boolean ConcealedWeaponSlotEnabled?: boolean } UnlockedByDefault?: boolean DifficultyUnlock?: { pro1?: string } Difficulty?: string /** * Sniper rifle modifier repository IDs. */ Modifiers?: RepositoryId[] | null // noinspection SpellCheckingInspection /** * Inclusion data for an unlockable. The only known use for this is * sniper rifle unlockables for Sniper Assassin mode. * * With the `InclusionData` type added, * I think this line can be `InclusionData: InclusionData`. --Moony */ InclusionData?: { ContractTypes?: MissionType[] | null } | null /** * Item perks - only known use is for Sniper Assassin. */ Perks?: string[] | null } Rarity?: string | null } export interface ItemGameplay { range?: number damage?: number clipsize?: number rateoffire?: number } export interface CompletionData { Level: number MaxLevel: number XP: number PreviouslySeenXp: number Completion: number XpLeft: number Id: string SubLocationId: string HideProgression: boolean IsLocationProgression: boolean Name: string | null } export interface UserCentricContract { Contract: MissionManifest Data: { IsLocked: boolean LockedReason: string LocationLevel: number LocationMaxLevel: number LocationCompletion: number LocationXpLeft: number LocationHideProgression: boolean ElusiveContractState: string LastPlayedAt?: string IsFeatured?: boolean // For favorite contracts PlaylistData?: { IsAdded: boolean // Not sure if this is important AddedTime: string } Completed?: boolean LocationId: string ParentLocationId: string CompletionData?: CompletionData DlcName: string DlcImage: string EscalationCompleted?: boolean EscalationCompletedLevels?: number EscalationTotalLevels?: number InGroup?: string } } export interface TargetCondition { Type: string RepositoryId?: RepositoryId HardCondition?: boolean ObjectiveId?: string KillMethod?: string } /** * Data structure for an objective's HUD template. */ export interface HUDTemplate { display: | string | { $loc: { key: string data: string } } iconType?: number } /** * Data structure for a mission manifest's `Data.VR` bricks property. */ export interface VRQualityDefinition { Quality: string Bricks: string[] } export interface MissionManifestObjective { _comment?: string Id: string Type?: "kill" | "statemachine" | string Scope?: string Primary?: boolean IsHidden?: boolean BriefingText?: string | { $loc: { key: string; data: string | number[] } } LongBriefingText?: | string | { $loc: { key: string; data: string | number[] } } Image?: string BriefingName?: string ShowInHud?: boolean CombinedDisplayInHud?: boolean DisplayAsKillObjective?: boolean Category?: "primary" | "secondary" | "condition" | string ForceShowOnLoadingScreen?: boolean /** * Allow Elusive Target Arcade contracts to be restarted if this objective is already successfully completed. */ AllowEtRestartOnSuccess?: boolean OnInactive?: { IfCompleted?: { State?: string } } Definition?: { display?: { iconType?: number } ContextListeners?: null | Record<string, IContextListener<never>> Scope?: string States?: Record<string, unknown> Constants?: Record<string, unknown> Context?: Record<string, unknown | string[] | string> } Activation?: { $eq?: (string | boolean)[] } OnActive?: { IfInProgress?: { Visible?: boolean State?: "Completed" | "InProgress" | "Failed" | string } IfCompleted?: { Visible?: boolean State?: "Completed" | "InProgress" | "Failed" | string } IfFailed?: { Visible?: boolean State?: "Completed" | "InProgress" | "Failed" | string } } ObjectiveType?: "kill" | "customkill" | "setpiece" | "custom" | string TargetConditions?: TargetCondition[] ExcludeFromScoring?: boolean HUDTemplate?: HUDTemplate SuccessEvent?: { EventName: "Kill" | string EventValues: { RepositoryId: RepositoryId } } FailedEvent?: { EventName: string EventValues?: { RepositoryId?: RepositoryId } } ResetEvent?: null IgnoreIfInactive?: boolean GameChangerName?: string IsPrestigeObjective?: boolean } /** * Data for a group contract. */ export type ContractGroupDefinition = { /** * The contract group type. */ Type: MissionType /** * The contracts in this group, ordered by their position in the group. */ Order: string[] } export interface EscalationInfo { Type?: MissionType InGroup?: string NextContractId?: string GroupData?: { Level: number TotalLevels: number Completed: boolean FirstContractId: string } } export interface MissionManifestMetadata { Id: string Location: string IsPublished?: boolean CreationTimestamp?: string CreatorUserId?: string Title: string Description?: string BriefingVideo?: | string | { Mode: string VideoId: string }[] DebriefingVideo?: string TileImage?: | string | { Mode: string Image: string }[] CodeName_Hint?: string ScenePath: string Type: MissionType Release?: string | object RequiredUnlockable?: string Drops?: string[] Opportunities?: string[] OpportunityData?: MissionStory[] Entitlements: string[] LastUpdate?: string PublicId?: string GroupObjectiveDisplayOrder?: GroupObjectiveDisplayOrderItem[] GameVersion?: string ServerVersion?: string AllowNonTargetKills?: boolean Difficulty?: "pro1" | string CharacterSetup?: { Mode: "singleplayer" | "multiplayer" | string Characters: [ { Name: string Id: string MandatoryLoadout?: string[] }, ] }[] CharacterLoadoutData?: { Id: string Loadout: unknown CompletionData: CompletionData }[] SpawnSelectionType?: "random" | string Gamemodes?: ("versus" | string)[] Enginemodes?: ("singleplayer" | "multiplayer" | string)[] EndConditions?: { PointLimit?: number } Subtype?: string GroupTitle?: string TargetExpiration?: number TargetExpirationReduced?: number TargetLifeTime?: number NonTargetKillPenaltyEnabled?: boolean NoticedTargetStreakPenaltyMax?: number IsFeatured?: boolean // Begin escalation-exclusive properties InGroup?: string NextContractId?: string GroupDefinition?: ContractGroupDefinition GroupData?: { Level: number TotalLevels: number Completed: boolean FirstContractId: string } // End escalation-exclusive properties /** * Useless property. * * @deprecated */ readonly UserData?: unknown | null IsVersus?: boolean IsEvergreenSafehouse?: boolean UseContractProgressionData?: boolean CpdId?: string // Elusive custom property (like official's year) Season?: number } export interface GroupObjectiveDisplayOrderItem { Id: string IsNew?: boolean } export interface GameChanger { Id: string Name: string Description: string TileImage?: string | null Icon?: string | null ObjectivesCategory?: string | null IsHidden?: boolean | null Resource?: string[] | null Objectives?: MissionManifestObjective[] | null LongDescription?: string | null IsPrestigeObjective?: boolean } /** * A mission's manifest is what defines how a specific contract plays. * * @see MissionManifestMetadata * @see MissionManifestObjective */ export interface MissionManifest { Data: { EnableSaving?: boolean Objectives?: MissionManifestObjective[] GameDifficulties?: { Difficulty: "easy" | "normal" | "hard" | string Bricks: string[] }[] GameModesBricks?: unknown[] EngineModesBricks?: unknown[] // noinspection SpellCheckingInspection /** * IOI typo. * * @deprecated */ EngineModessBricks?: null MandatoryLoadout?: unknown[] RecommendedLoadout?: unknown[] Bricks: string[] VR?: VRQualityDefinition[] GameChangers?: string[] GameChangerReferences?: GameChanger[] Entrances?: string[] Stashpoints?: string[] EnableExits?: { $eq?: (string | boolean)[] } DevOnlyBricks?: string[] } Metadata: MissionManifestMetadata readonly UserData?: Record<string, never> | never[] Peacock?: { noAgencyPickupsActive?: boolean noGear?: boolean noCarriedWeapon?: boolean } } /** * A configuration that tells the game where it should connect to. * This config is the first thing that the game asks for when logging in. */ export interface ServerConnectionConfig { Versions: { Name: string GAME_VER: string ISSUER_ID: string SERVER_VER: { Metrics: { MetricsServerHost: string } Authentication: { AuthenticationHost: string } Configuration: { Url: string AgreementUrl: string } Resources: { ResourcesServicePath: string } GlobalAuthentication: { AuthenticationHost: string RequestedAudience: string } } }[] } /** * The format we store our locations data in, which is more concise than the * one used by the game. * * @see GameLocationsData */ export interface PeacockLocationsData { /** * The parent locations. */ parents: Record<string, Unlockable> /** * The sub-locations. */ children: Record<string, Unlockable> } /** * A structure representing the game's LocationsData object. */ export interface GameLocationsData { Data: { HasMore: boolean Page: number Locations: { /** * The contract creation contract. */ Contract: MissionManifest Location: Unlockable SubLocation: Unlockable }[] } } /** * The body sent with the CreateFromParams request from the game during the final phase of contract creation. * * @see IContractCreationPayload */ export interface CreateFromParamsBody { creationData: { Title: string Description: string ContractId: string ContractPublicId: string Targets: IContractCreationPayload[] ContractConditionIds: string[] } } export interface CommonSelectScreenConfig { template: unknown data?: { Unlocked: string[] Contract: MissionManifest OrderedUnlocks: Unlockable[] UserCentric: UserCentricContract } } export type CompiledIoiStatemachine = unknown /** * The `ChallengeProgress` data for a `challengetree` context listener. */ export interface ChallengeProgressCTreeContextListener { total: number completed: string[] | readonly string[] missing: number all: string[] | readonly string[] count: number } /** * The `ChallengeProgress` data for a `challengecount` context listener. */ export interface ChallengeProgressCCountContextListener { total: number count: number } export interface CompiledChallengeTreeCategory { CategoryId: string ChallengesCount: number CompletedChallengesCount: number CompletionData: CompletionData Icon: string Image: string ImageLocked?: string IsLocked: boolean Location: Unlockable Name: string RequiredResources: string[] SwitchData: { Data: { CategoryData: CompiledChallengeTreeCategoryInfo Challenges: CompiledChallengeTreeData[] CompletionData: CompletionData HasNext: boolean HasPrevious: boolean NextCategoryIcon?: string PreviousCategoryIcon?: string } IsLeaf: boolean } } export interface CompiledChallengeTreeCategoryInfo { Name: string Image: string Icon: string ChallengesCount: number CompletedChallengesCount: number } /** * The data for a challenge's `ChallengeProgression` field. Tells the game how * many challenges are completed, how many are left, etc. */ export type ChallengeTreeWaterfallState = | ChallengeProgressCTreeContextListener | ChallengeProgressCCountContextListener | null export interface CompiledChallengeTreeData { CategoryName: string ChallengeProgress?: ChallengeTreeWaterfallState Completed: boolean CompletionData: CompletionData Description: string // A string array of at most one element ("easy", "normal", or "hard"). // If empty, then the challenge should appear in sessions on any difficulty. // If not, then it should only appear in sessions on or above the specified difficulty. DifficultyLevels?: string[] Displayed?: boolean Drops?: Unlockable[] HideProgression: boolean Icon: string Id: string ImageName: string IsLocked: boolean IsPlayable: boolean LocationId: string Name: string ParentLocationId: string Rewards: { MasteryXP?: number } Type?: string UserCentricContract?: UserCentricContract } export interface InclusionData { ContractIds?: string[] ContractTypes?: MissionType[] Locations?: string[] GameModes?: string[] } export interface CompiledChallengeIngameData { Id: string GroupId?: string Name: string Type: "Hit" | string Description?: string ImageName?: string Definition: CompiledIoiStatemachine Tags?: string[] Drops?: string[] LastModified?: string PlayableSince?: string | null PlayableUntil?: string | null Xp: number XpModifier: unknown InclusionData?: InclusionData CrowdChoice?: { Tag: string } } /** * Game-facing challenge progression data. */ export interface ChallengeProgressionData { ChallengeId: string ProfileId: string Completed: boolean Ticked: boolean State: Record<string, unknown> CompletedAt: Date | string | null MustBeSaved: boolean } export interface CompiledChallengeRuntimeData { Challenge: CompiledChallengeIngameData Progression: ChallengeProgressionData } export interface CompiledChallengeRewardData { ChallengeId: string ChallengeName: string ChallengeDescription: string ChallengeImageUrl: string XPGain: number } export type LoadoutSavingMechanism = "PROFILES" | "LEGACY" export type ImageLoadingStrategy = "SAVEASREQUESTED" | "ONLINE" | "OFFLINE" export type Flags = Record< string, { desc: string; default: boolean | string | number } > /** * A "hit" object. */ export interface IHit { Id: string UserCentricContract: UserCentricContract Location: Unlockable SubLocation?: Unlockable ChallengesCompleted: number ChallengesTotal: number LocationLevel: number LocationMaxLevel: number LocationCompletion: number LocationXPLeft: number LocationHideProgression: boolean } /** * A video object. * * @see ICampaignVideo * @see StoryData */ export interface IVideo { VideoTitle: string VideoHeader: string VideoId: string Entitlements: string[] IsLocked: boolean LockedReason?: string VideoType: string VideoImage: string RequiredResources: string[] Data: { DlcName: string DlcImage: string } } /** * A campaign mission item. * * @see IHit */ export type ICampaignMission = { Type: "Mission" Data: IHit } /** * A campaign video item. * * @see IVideo */ export type ICampaignVideo = { Type: "Video" Data: IVideo } export interface RegistryChallenge extends SavedChallenge { /** * Warning: this property is INTERNAL and should NOT BE SPECIFIED by API users. * * @internal */ inGroup?: string } /** * An element for the game's story data. */ export type StoryData = ICampaignMission | ICampaignVideo /** * A campaign object. */ export interface Campaign { Name: string Image: string Type: MissionType | string BackgroundImage?: string | null StoryData: StoryData[] Subgroups?: Campaign[] Properties?: { BackgroundImage?: string | null } } /** * A loadout. */ export interface Loadout { /** * Random ID. * * @since Peacock v5 */ id: string name: string data: { [locationName: string]: { readonly 2?: string readonly 3?: string readonly 4?: string } & { [briefcaseId: string]: string } } } /** * The object for individual game versions' loadout profiles data. * * @see LoadoutFile */ export interface LoadoutsGameVersion { selected: string | null loadouts: Loadout[] } /** * The top-level format for the loadout profiles storage file. */ export interface LoadoutFile { h1: LoadoutsGameVersion h2: LoadoutsGameVersion h3: LoadoutsGameVersion } /** * A function that generates a campaign mission object for use in the campaigns menu. */ export type GenSingleMissionFunc = ( contractId: string, gameVersion: GameVersion, ) => ICampaignMission /** * A function that generates a campaign video object for use in the campaigns menu. */ export type GenSingleVideoFunc = ( videoId: string, gameVersion: GameVersion, ) => ICampaignVideo /** * A "hits category" is used to display lists of contracts in-game. * * @see IHit */ export interface HitsCategoryCategory { Category: string Data: { Type: string Hits: IHit[] Page: number HasMore: boolean } CurrentSubType: string } export interface PlayNextCampaignDetails { CampaignName: string ParentCampaignName?: string } export interface PlayNextGetCampaignsHookReturn { /** * The UUID of the next contract in the campaign. */ nextContractId: string /** * An object containing the campaign's details. */ campaignDetails: PlayNextCampaignDetails /** * An array index for plugins to override play next tiles that Peacock * internally added * * @since v6.3.0 */ overrideIndex?: number } export type SafehouseCategory = { Category: string SubCategories: SafehouseCategory[] IsLeaf: boolean Data: null } export type SniperLoadout = { ID: string InstanceID: string Unlockable: Unlockable[] MainUnlockable: Unlockable } /** * Common type for the `Entrances` and `AgencyPickups` configs. */ export type SceneConfig = Record<string, string[]> /** * Where a state machine's `Context` data should be stored. * * - For `profile`, the challenge's context data should be stored in the user's * profile. This data will be used across hits, instead of being constrained * to a single hit, like `Hit` is. * * - For `hit`, the challenge's context data should be stored with persistent * data for the current contract. This appears to function differently when * this is used in objectives. * * - For `session`, the challenge's context data should be stored on the * contract session. When a mission is restarted, this data is not persisted, * since that creates a new session. */ export type ContextScopedStorageLocation = "profile" | "hit" | "session" /** * Evergreen-related types */ export type CPDStore = Record<string, Record<string, string | number | boolean>> export type ContractProgressionData = Record<string, string | number | boolean> /** SMF's lastDeploy.json */ export interface SMFLastDeploy { loadOrder: string[] lastServerSideStates?: { unlockables?: Unlockable[] contracts?: { [k: string]: MissionManifest } blobs?: Record<string, string> } }