1
mirror of https://github.com/thepeacockproject/Peacock synced 2025-03-27 11:12:44 +01:00
moonysolari 3adb4b1f67
Fix a number of bugs on progression, entitlements, and warning logs ()
* Fix "Contract undefined not found!" warnings

* Implement support for /GetContractOpportunities

* Add OpportunityData to MissionManifestMetadata

* Resolved a ts-expect-error by providing types

* Fix requiem unlockables pistol

* Adjust max player level
2023-01-10 17:29:54 -05:00

1347 lines
32 KiB
TypeScript

/*
* The Peacock Project - a HITMAN server replacement.
* Copyright (C) 2021-2022 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 { ProfileChallengeData, SavedChallenge } from "./challenges"
import { SessionGhostModeDetails } from "../multiplayer/multiplayerService"
import { IContextListener } from "../statemachines/contextListeners"
import { Timer } from "@peacockproject/statemachine-parser"
/**
* 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 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 {
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]: {
context: unknown
state: string
timers: Timer[]
}
}
}
/**
* 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
Image: string
}
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[]
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
}
}
defaultloadout?: {
[location: string]: {
"2"?: string
"3"?: string
"4"?: string
"5"?: string
}
}
entP: string[]
achievements?: unknown
gamepersistentdata: {
__stats?: unknown
PersistentBool: Record<string, unknown>
}
opportunityprogression: {
[opportunityId: RepositoryId]: boolean
}
}
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
Opportunities?: number
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.
*/
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
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
Completed?: boolean
LocationId: string
ParentLocationId: string
CompletionData?: CompletionData
DlcName: string
DlcImage: string
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?: unknown
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
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
GroupDefinition?: ContractGroupDefinition
}
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
DifficultyLevels?: unknown // more investigation required
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 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?: {
ContractIds?: string[]
ContractTypes?: string[]
Locations?: string[]
GameModes?: string[]
}
CrowdChoice?: {
Tag: string
}
}
/**
* Game-facing challenge progression data.
*/
export interface ChallengeProgressionData {
ChallengeId: string
ProfileId: string
Completed: 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
}
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>
}
}