Merge branch 'master' into dev/patcher-core
This commit is contained in:
commit
4768eeaa44
|
@ -8,4 +8,4 @@ webui/dist
|
|||
*.plugin.js
|
||||
*Plugin.js
|
||||
packaging/livesplit-node-client/build
|
||||
webstorm.config.js
|
||||
tests/testData/scripts
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<!-- Thanks for contributing to Peacock! Here's a bit of a template to help make sure everything relevant is covered. -->
|
||||
|
||||
## Scope
|
||||
|
||||
<!-- List any relevant changes you have made here. Be sure to link any issues fixed, or that are relevant to these changes. -->
|
||||
|
||||
## Test Plan
|
||||
|
||||
<!-- List how you have verified these changes work as intended. -->
|
||||
|
||||
## Checklist
|
||||
|
||||
<!--
|
||||
Just a few reminders to make sure everything is perfect. You can place an "X" in the boxes to tick them off.
|
||||
If you have not completed one of the steps below, you can create the pull request as a draft, and then check off the items as you go.
|
||||
When you have completed the checklist, press the "Ready for review" button.
|
||||
-->
|
||||
|
||||
- [ ] I have run Prettier to reformat any changed files
|
||||
- [ ] I have verified my changes work
|
|
@ -0,0 +1,87 @@
|
|||
name: Update Localisation Mod
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ["v*"]
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
update-mod:
|
||||
name: Update Mod
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Peacock
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.PEACOCKBOT_TOKEN }}
|
||||
path: "./Peacock"
|
||||
|
||||
- name: Checkout Peacock Strings
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.PEACOCKBOT_TOKEN }}
|
||||
repository: thepeacockproject/peacock-strings
|
||||
path: "./PeacockStrings"
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: "./Peacock/.nvmrc"
|
||||
cache: "yarn"
|
||||
cache-dependency-path: "./Peacock/yarn.lock"
|
||||
|
||||
- name: Install Packages
|
||||
run: |
|
||||
cd Peacock
|
||||
yarn install --immutable
|
||||
|
||||
#- name: Download ResourceLib
|
||||
# uses: robinraju/release-downloader@v1.7
|
||||
# with:
|
||||
# repository: "OrfeasZ/ZHMTools"
|
||||
# latest: true
|
||||
# fileName: "ResourceLib-win-x64.zip"
|
||||
# out-file-path: "Peacock/resources"
|
||||
|
||||
- name: Download RPKG-CLI
|
||||
id: rpkgcli
|
||||
uses: robinraju/release-downloader@v1.10
|
||||
with:
|
||||
repository: "glacier-modding/RPKG-Tool"
|
||||
latest: true
|
||||
fileName: "rpkg_*-cli.zip"
|
||||
out-file-path: "Peacock/resources"
|
||||
|
||||
- name: Download HMLanguageTools
|
||||
id: hmlt
|
||||
uses: robinraju/release-downloader@v1.10
|
||||
with:
|
||||
repository: "AnthonyFuller/TonyTools"
|
||||
latest: true
|
||||
fileName: "TonyTools.zip"
|
||||
out-file-path: "Peacock/resources"
|
||||
|
||||
- name: Unzip dependencies
|
||||
run: |
|
||||
cd Peacock/resources
|
||||
7z x ${{ fromJson(steps.rpkgcli.outputs.downloaded_files)[0] }}
|
||||
7z x ${{ fromJson(steps.hmlt.outputs.downloaded_files)[0] }}
|
||||
|
||||
- name: Rebuild Locale Packages
|
||||
run: |
|
||||
cd Peacock
|
||||
yarn rebuild-locale
|
||||
|
||||
- name: Copy peacockstrings.locr.json
|
||||
run: |
|
||||
copy ./Peacock/resources/peacockstrings.locr.json ./PeacockStrings/content/chunk0/peacockstrings.locr.json
|
||||
|
||||
- name: Push updated Peacock LOCR
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
cwd: "./PeacockStrings"
|
||||
add: content/chunk0/peacockstrings.locr.json
|
||||
author_name: PeacockBot
|
||||
author_email: admin@thepeacockproject.org
|
||||
message: "enhancement: update strings"
|
|
@ -2,7 +2,7 @@ name: Localisation
|
|||
|
||||
on:
|
||||
push:
|
||||
branches: ["v*"]
|
||||
branches: ["master"]
|
||||
paths: ["resources/locale.json", ".github/workflows/locale.yml"]
|
||||
workflow_dispatch:
|
||||
|
||||
|
@ -18,13 +18,6 @@ jobs:
|
|||
token: ${{ secrets.PEACOCKBOT_TOKEN }}
|
||||
path: "./Peacock"
|
||||
|
||||
- name: Checkout Peacock Strings
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.PEACOCKBOT_TOKEN }}
|
||||
repository: thepeacockproject/peacock-strings
|
||||
path: "./PeacockStrings"
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
|
@ -47,7 +40,7 @@ jobs:
|
|||
|
||||
- name: Download RPKG-CLI
|
||||
id: rpkgcli
|
||||
uses: robinraju/release-downloader@v1.9
|
||||
uses: robinraju/release-downloader@v1.10
|
||||
with:
|
||||
repository: "glacier-modding/RPKG-Tool"
|
||||
latest: true
|
||||
|
@ -56,7 +49,7 @@ jobs:
|
|||
|
||||
- name: Download HMLanguageTools
|
||||
id: hmlt
|
||||
uses: robinraju/release-downloader@v1.9
|
||||
uses: robinraju/release-downloader@v1.10
|
||||
with:
|
||||
repository: "AnthonyFuller/TonyTools"
|
||||
latest: true
|
||||
|
@ -84,16 +77,3 @@ jobs:
|
|||
author_name: PeacockBot
|
||||
author_email: admin@thepeacockproject.org
|
||||
message: "[skip ci] Update locale packages"
|
||||
|
||||
- name: Copy peacockstrings.locr.json
|
||||
run: |
|
||||
copy ./Peacock/resources/peacockstrings.locr.json ./PeacockStrings/content/chunk0/peacockstrings.locr.json
|
||||
|
||||
- name: Push updated Peacock LOCR
|
||||
uses: EndBug/add-and-commit@v9
|
||||
with:
|
||||
cwd: "./PeacockStrings"
|
||||
add: content/chunk0/peacockstrings.locr.json
|
||||
author_name: PeacockBot
|
||||
author_email: admin@thepeacockproject.org
|
||||
message: "enhancement: update strings"
|
||||
|
|
|
@ -20,6 +20,7 @@ resources/**/*.*
|
|||
!resources/locale.json
|
||||
!resources/dynamic_resources_*/*.meta
|
||||
!resources/dynamic_resources_*.rpkg
|
||||
!resources/dynamic_resources_*/*.JSON
|
||||
!resources/rebuildLocale.cjs
|
||||
|
||||
components/contracts.json
|
||||
|
@ -59,3 +60,4 @@ DEBUG_PROFILE.zip
|
|||
|
||||
packaging/add_itemsize/*
|
||||
!packaging/add_itemsize/add_itemsize.js
|
||||
/.idea/AIAssistantCustomInstructionsStorage.xml
|
||||
|
|
|
@ -3,3 +3,6 @@
|
|||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# GitHub Copilot persisted chat sessions
|
||||
/copilot/chatSessions
|
||||
/AIAssistantCustomInstructionsStorage.xml
|
||||
|
|
|
@ -31,8 +31,12 @@
|
|||
<excludeFolder url="file://$MODULE_DIR$/resources/challenges" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/patcher/.idea/.idea.HitmanPatcher/.idea/shelf" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/images" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/logs" />
|
||||
<excludePattern pattern="chunk*.js" />
|
||||
<excludePattern pattern="components/contracts.json,*.tsbuildinfo,offlineassets.zip,chunk0.js.map" />
|
||||
<excludePattern pattern="components/contracts.json" />
|
||||
<excludePattern pattern="*.tsbuildinfo" />
|
||||
<excludePattern pattern="offlineassets.zip" />
|
||||
<excludePattern pattern="chunk0.js.map" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GithubSharedProjectSettings">
|
||||
<option name="branchProtectionPatterns">
|
||||
<list>
|
||||
<option value="main" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
|
|
|
@ -4,6 +4,18 @@
|
|||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"name": "Dev Tools (run-dev tools)",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"env": {
|
||||
"NODE_TLS_REJECT_UNAUTHORIZED": "0"
|
||||
},
|
||||
"runtimeExecutable": "yarn",
|
||||
"runtimeArgs": ["run-dev", "tools"],
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
{
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": false
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"npm.packageManager": "yarn",
|
||||
"eslint.packageManager": "yarn",
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"eslint.format.enable": true,
|
||||
"search.exclude": {
|
||||
|
@ -14,5 +13,8 @@
|
|||
"yarn.lock": true,
|
||||
"**/.yarn": true
|
||||
},
|
||||
"omnisharp.useModernNet": false
|
||||
"omnisharp.useModernNet": false,
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -14,4 +14,4 @@ plugins:
|
|||
- path: .yarn/plugins/@yarnpkg/plugin-outdated.cjs
|
||||
spec: "https://mskelton.dev/yarn-outdated/v2"
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.1.0.cjs
|
||||
yarnPath: .yarn/releases/yarn-4.1.1.cjs
|
||||
|
|
|
@ -29,7 +29,7 @@ import { controller } from "../controller"
|
|||
import { json as jsonMiddleware } from "body-parser"
|
||||
import { uuidRegex } from "../utils"
|
||||
import { compileRuntimeChallenge } from "../candle/challengeHelpers"
|
||||
import { LegacyGetProgressionBody } from "../types/gameSchemas"
|
||||
import { GetChallengeProgressionBody } from "../types/gameSchemas"
|
||||
|
||||
const legacyProfileRouter = Router()
|
||||
|
||||
|
@ -95,7 +95,7 @@ legacyProfileRouter.post(
|
|||
"/ChallengesService/GetProgression",
|
||||
jsonMiddleware(),
|
||||
// @ts-expect-error Has jwt props.
|
||||
(req: RequestWithJwt<never, LegacyGetProgressionBody>, res) => {
|
||||
(req: RequestWithJwt<never, GetChallengeProgressionBody>, res) => {
|
||||
if (!Array.isArray(req.body.challengeids)) {
|
||||
res.status(400).send("invalid body")
|
||||
return
|
||||
|
|
|
@ -79,6 +79,14 @@ type GroupIndexedChallengeLists = {
|
|||
[groupId: string]: RegistryChallenge[]
|
||||
}
|
||||
|
||||
export type ChallengePack = {
|
||||
Name: string
|
||||
Description: string
|
||||
GameVersions: GameVersion[]
|
||||
Image: string
|
||||
Icon: string
|
||||
}
|
||||
|
||||
/**
|
||||
* A base class providing challenge registration support.
|
||||
*/
|
||||
|
@ -99,7 +107,7 @@ export abstract class ChallengeRegistry {
|
|||
/**
|
||||
* @Key1 Game version.
|
||||
* @Key2 The parent location Id.
|
||||
* @Key3 The group Id.
|
||||
* @Key3 The group's categoryId.
|
||||
* @Value A `SavedChallengeGroup` object.
|
||||
*/
|
||||
protected groups: Record<
|
||||
|
@ -145,6 +153,39 @@ export abstract class ChallengeRegistry {
|
|||
|
||||
protected constructor(protected readonly controller: Controller) {}
|
||||
|
||||
public challengePacks: Map<string, ChallengePack> = new Map([
|
||||
[
|
||||
"cheesecake-pack",
|
||||
{
|
||||
Name: "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_CHEESECAKE",
|
||||
Description: "",
|
||||
GameVersions: ["h3"],
|
||||
Image: "images/challenges/categories/packcheesecake/tile.jpg",
|
||||
Icon: "challenge_category_feats",
|
||||
},
|
||||
],
|
||||
[
|
||||
"argentum-pack",
|
||||
{
|
||||
Name: "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGENTUM",
|
||||
Description: "",
|
||||
GameVersions: ["h3"],
|
||||
Image: "images/challenges/categories/packargentum/tile.jpg",
|
||||
Icon: "challenge_category_feats",
|
||||
},
|
||||
],
|
||||
[
|
||||
"argon-pack",
|
||||
{
|
||||
Name: "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGON",
|
||||
Description: "",
|
||||
GameVersions: ["h3"],
|
||||
Image: "images/challenges/categories/packargon/tile.jpg",
|
||||
Icon: "challenge_category_feats",
|
||||
},
|
||||
],
|
||||
])
|
||||
|
||||
registerChallenge(
|
||||
challenge: RegistryChallenge,
|
||||
groupId: string,
|
||||
|
@ -192,6 +233,23 @@ export abstract class ChallengeRegistry {
|
|||
return this.challenges[gameVersion].get(challengeId)
|
||||
}
|
||||
|
||||
getChallengeIds(gameVersion: GameVersion): string[] {
|
||||
return Array.from(this.challenges[gameVersion].keys())
|
||||
}
|
||||
|
||||
removeChallenge(challengeId: string, gameVersion: GameVersion): boolean {
|
||||
const challenge = this.challenges[gameVersion].get(challengeId)
|
||||
if (!challenge) return false
|
||||
|
||||
return (
|
||||
this.challenges[gameVersion].delete(challengeId) &&
|
||||
this.groupContents[gameVersion]
|
||||
.get(challenge.ParentLocationId)!
|
||||
.get(challenge.inGroup!)!
|
||||
.delete(challengeId)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method retrieves all the unlockables associated with the challenges for a given game version.
|
||||
* It iterates over all the challenges for the specified game version and for each challenge, it checks if there are any unlockables (Drops).
|
||||
|
@ -286,6 +344,17 @@ export abstract class ChallengeRegistry {
|
|||
return mainGroup
|
||||
}
|
||||
|
||||
public getChallengesForGroup(
|
||||
groupId: string,
|
||||
gameVersion: GameVersion,
|
||||
): GroupIndexedChallengeLists {
|
||||
return {
|
||||
[groupId]: Array.from(this.challenges[gameVersion].values()).filter(
|
||||
(value) => value.inGroup === groupId,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
public getGroupContentByIdLoc(
|
||||
groupId: string,
|
||||
location: string,
|
||||
|
@ -513,7 +582,7 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
}
|
||||
|
||||
/**
|
||||
* This is a helper function for @see getGroupedChallengeLists. It is not expected to be used elsewhere.
|
||||
* This is a helper function for {@link getGroupedChallengeLists}. It is not expected to be used elsewhere.
|
||||
*
|
||||
* Filter all challenges in a parent location using a given filter, sort them into groups,
|
||||
* and write them into the `challenges` array provided.
|
||||
|
@ -587,6 +656,13 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
): GroupIndexedChallengeLists {
|
||||
let challenges: [string, RegistryChallenge[]][] = []
|
||||
|
||||
// Challenge packs ignore the filter
|
||||
if (this.challengePacks.has(location)) {
|
||||
challenges.push([
|
||||
location,
|
||||
this.getChallengesForGroup(location, gameVersion)[location],
|
||||
])
|
||||
} else {
|
||||
this.getGroupedChallengesByLoc(
|
||||
filter,
|
||||
location,
|
||||
|
@ -594,7 +670,10 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
gameVersion,
|
||||
)
|
||||
|
||||
if (filter.type === ChallengeFilterType.Contract && filter.isFeatured) {
|
||||
if (
|
||||
filter.type === ChallengeFilterType.Contract &&
|
||||
filter.isFeatured
|
||||
) {
|
||||
this.getGroupedChallengesByLoc(
|
||||
filter,
|
||||
"GLOBAL_FEATURED_CHALLENGES",
|
||||
|
@ -619,6 +698,7 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
gameVersion,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// remove empty groups
|
||||
challenges = challenges.filter(
|
||||
|
@ -989,6 +1069,7 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
forContract,
|
||||
userId,
|
||||
gameVersion,
|
||||
false,
|
||||
subLocation,
|
||||
)
|
||||
}
|
||||
|
@ -999,7 +1080,8 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
gameVersion: GameVersion,
|
||||
compiler: Compiler,
|
||||
): CompiledChallengeTreeData[] {
|
||||
return challenges.map((challengeData) => {
|
||||
return challenges
|
||||
.map((challengeData) => {
|
||||
return compiler(
|
||||
challengeData,
|
||||
this.getPersistentChallengeProgression(
|
||||
|
@ -1011,6 +1093,7 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
userId,
|
||||
)
|
||||
})
|
||||
.sort((a, b) => a.OrderIndex - b.OrderIndex)
|
||||
}
|
||||
|
||||
private getChallengeDependencyData(
|
||||
|
@ -1103,43 +1186,27 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
forLocation,
|
||||
userId,
|
||||
gameVersion,
|
||||
locationData,
|
||||
true,
|
||||
locationData,
|
||||
)
|
||||
}
|
||||
|
||||
getChallengeDataForLocation(
|
||||
locationId: string,
|
||||
getChallengeDataForCategory(
|
||||
categoryId: string | null,
|
||||
location: Unlockable | undefined,
|
||||
gameVersion: GameVersion,
|
||||
userId: string,
|
||||
): CompiledChallengeTreeCategory[] {
|
||||
const locationsData = getVersionedConfig<PeacockLocationsData>(
|
||||
"LocationsData",
|
||||
gameVersion,
|
||||
false,
|
||||
)
|
||||
|
||||
const locationData = locationsData.children[locationId]
|
||||
|
||||
if (!locationData) {
|
||||
log(
|
||||
LogLevel.WARN,
|
||||
`Failed to get location data in CSERV [${locationId}]`,
|
||||
)
|
||||
return []
|
||||
}
|
||||
|
||||
const forLocation = this.getChallengesForLocation(
|
||||
locationId,
|
||||
gameVersion,
|
||||
)
|
||||
const challenges = location
|
||||
? this.getChallengesForLocation(location.Id, gameVersion)
|
||||
: this.getChallengesForGroup(categoryId!, gameVersion)
|
||||
|
||||
return this.reBatchIntoSwitchedData(
|
||||
forLocation,
|
||||
challenges,
|
||||
userId,
|
||||
gameVersion,
|
||||
locationData,
|
||||
true,
|
||||
location,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1148,33 +1215,35 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
* @param challengeLists The challenge lists to use.
|
||||
* @param userId The id of the user.
|
||||
* @param gameVersion The current game version.
|
||||
* @param location A location as an `Unlockable`. Might be a parent location or a sublocation, depending on `isDestination`.
|
||||
* @param isDestination Will also get escalation challenges if set to true.
|
||||
* @param location A location as an `Unlockable`. If challengeLists contains all challenges of a challenge pack, this parameter should be "undefined". Otherwise, it might be a parent location or a sublocation, depending on `isDestination`.
|
||||
* @returns An array of `CompiledChallengeTreeCategory` objects.
|
||||
*/
|
||||
private reBatchIntoSwitchedData(
|
||||
challengeLists: GroupIndexedChallengeLists,
|
||||
userId: string,
|
||||
gameVersion: GameVersion,
|
||||
location: Unlockable,
|
||||
isDestination = false,
|
||||
isDestination: boolean,
|
||||
location?: Unlockable,
|
||||
): CompiledChallengeTreeCategory[] {
|
||||
const entries = Object.entries(challengeLists)
|
||||
const compiler = isDestination
|
||||
? this.compileRegistryDestinationChallengeData.bind(this)
|
||||
: this.compileRegistryChallengeTreeData.bind(this)
|
||||
|
||||
const completion = generateCompletionData(
|
||||
location?.Id,
|
||||
userId,
|
||||
gameVersion,
|
||||
)
|
||||
const completion = location
|
||||
? generateCompletionData(location?.Id, userId, gameVersion)
|
||||
: {}
|
||||
|
||||
return entries
|
||||
const groups = entries
|
||||
.map(([groupId, challenges], index) => {
|
||||
if (challenges.length === 0) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const groupData = this.getGroupByIdLoc(
|
||||
groupId,
|
||||
location.Properties.ParentLocation ?? location.Id,
|
||||
challenges[0].ParentLocationId,
|
||||
gameVersion,
|
||||
)
|
||||
|
||||
|
@ -1191,16 +1260,20 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
),
|
||||
)
|
||||
|
||||
const lastGroup = this.getGroupByIdLoc(
|
||||
const lastGroup = location
|
||||
? this.getGroupByIdLoc(
|
||||
Object.keys(challengeLists)[index - 1],
|
||||
location.Properties.ParentLocation ?? location.Id,
|
||||
gameVersion,
|
||||
)
|
||||
const nextGroup = this.getGroupByIdLoc(
|
||||
: undefined
|
||||
const nextGroup = location
|
||||
? this.getGroupByIdLoc(
|
||||
Object.keys(challengeLists)[index + 1],
|
||||
location.Properties.ParentLocation ?? location.Id,
|
||||
gameVersion,
|
||||
)
|
||||
: undefined
|
||||
|
||||
return {
|
||||
Name: groupData.Name,
|
||||
|
@ -1214,9 +1287,11 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
).length,
|
||||
CompletionData: completion,
|
||||
Location: location,
|
||||
IsLocked: location.Properties.IsLocked || false,
|
||||
ImageLocked: location.Properties.LockedIcon || "",
|
||||
RequiredResources: location.Properties.RequiredResources!,
|
||||
IsLocked: location?.Properties.IsLocked || false,
|
||||
ImageLocked: location?.Properties.LockedIcon || "",
|
||||
RequiredResources:
|
||||
location?.Properties.RequiredResources || [],
|
||||
OrderIndex: groupData.OrderIndex ?? 10000,
|
||||
SwitchData: {
|
||||
Data: {
|
||||
Challenges: this.mapSwitchChallenges(
|
||||
|
@ -1253,6 +1328,10 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
}
|
||||
})
|
||||
.filter(Boolean) as CompiledChallengeTreeCategory[]
|
||||
|
||||
return groups.sort((a, b) => {
|
||||
return a.OrderIndex - b.OrderIndex
|
||||
})
|
||||
}
|
||||
|
||||
compileRegistryChallengeTreeData(
|
||||
|
@ -1297,6 +1376,7 @@ export class ChallengeService extends ChallengeRegistry {
|
|||
userId,
|
||||
gameVersion,
|
||||
),
|
||||
OrderIndex: challenge.OrderIndex ?? 10000,
|
||||
DifficultyLevels: challenge.DifficultyLevels ?? [],
|
||||
// Only include CompletionData if ParentLocationId is not an empty string
|
||||
...(challenge.ParentLocationId !== "" && {
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
getSubLocationByName,
|
||||
} from "../contracts/dataGen"
|
||||
import { log, LogLevel } from "../loggingInterop"
|
||||
import { getVersionedConfig } from "../configSwizzleManager"
|
||||
import { getConfig } from "../configSwizzleManager"
|
||||
import { getUserData } from "../databaseHandler"
|
||||
import {
|
||||
LocationMasteryData,
|
||||
|
@ -265,12 +265,9 @@ export class MasteryService {
|
|||
|
||||
// TODO: Refactor this into the new inventory system?
|
||||
const name = isSniper
|
||||
? getVersionedConfig<Unlockable[]>(
|
||||
"SniperUnlockables",
|
||||
gameVersion,
|
||||
false,
|
||||
).find((unlockable) => unlockable.Id === subPackageId)?.Properties
|
||||
.Name
|
||||
? getConfig<Unlockable[]>("SniperUnlockables", false).find(
|
||||
(unlockable) => unlockable.Id === subPackageId,
|
||||
)?.Properties.Name
|
||||
: undefined
|
||||
|
||||
return {
|
||||
|
|
|
@ -187,29 +187,25 @@ export class ProgressionService {
|
|||
if (masteryData) {
|
||||
const previousLevel = locationData.Level
|
||||
|
||||
let newLocationXp = xpRequiredForLevel(maxLevel)
|
||||
|
||||
if (isEvergreenContract) {
|
||||
newLocationXp = xpRequiredForEvergreenLevel(maxLevel)
|
||||
} else if (sniperUnlockable) {
|
||||
newLocationXp = xpRequiredForSniperLevel(maxLevel)
|
||||
}
|
||||
|
||||
locationData.Xp = clampValue(
|
||||
locationData.Xp + masteryXp + actionXp,
|
||||
0,
|
||||
newLocationXp,
|
||||
isEvergreenContract
|
||||
? xpRequiredForEvergreenLevel(maxLevel)
|
||||
: sniperUnlockable
|
||||
? xpRequiredForSniperLevel(maxLevel)
|
||||
: xpRequiredForLevel(maxLevel),
|
||||
)
|
||||
|
||||
let newLocationLevel = levelForXp(newLocationXp)
|
||||
|
||||
if (isEvergreenContract) {
|
||||
newLocationLevel = evergreenLevelForXp(newLocationXp)
|
||||
} else if (sniperUnlockable) {
|
||||
newLocationLevel = sniperLevelForXp(newLocationXp)
|
||||
}
|
||||
|
||||
locationData.Level = clampValue(newLocationLevel, 1, maxLevel)
|
||||
locationData.Level = clampValue(
|
||||
isEvergreenContract
|
||||
? evergreenLevelForXp(locationData.Xp)
|
||||
: sniperUnlockable
|
||||
? sniperLevelForXp(locationData.Xp)
|
||||
: levelForXp(locationData.Xp),
|
||||
1,
|
||||
maxLevel,
|
||||
)
|
||||
|
||||
// If mastery level has gone up, check if there are available drop rewards and award them
|
||||
if (locationData.Level > previousLevel) {
|
||||
|
@ -244,22 +240,14 @@ export class ProgressionService {
|
|||
// Update the SubLocation data
|
||||
const profileData = userProfile.Extensions.progression.PlayerProfileXP
|
||||
|
||||
let foundSubLocation = profileData.Sublocations.find(
|
||||
(e) => e.Location === parentLocationId,
|
||||
)
|
||||
|
||||
if (!foundSubLocation) {
|
||||
foundSubLocation = {
|
||||
Location: parentLocationId,
|
||||
profileData.Sublocations[contract.Metadata.Location] ??= {
|
||||
Xp: 0,
|
||||
ActionXp: 0,
|
||||
}
|
||||
|
||||
profileData.Sublocations.push(foundSubLocation)
|
||||
}
|
||||
|
||||
foundSubLocation.Xp += masteryXp
|
||||
foundSubLocation.ActionXp += actionXp
|
||||
profileData.Sublocations[contract.Metadata.Location].Xp += masteryXp
|
||||
profileData.Sublocations[contract.Metadata.Location].ActionXp +=
|
||||
actionXp
|
||||
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
/*
|
||||
* The Peacock Project - a HITMAN server replacement.
|
||||
* Copyright (C) 2021-2024 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/>.
|
||||
*/
|
||||
|
||||
// load as soon as possible to prevent dependency issues
|
||||
import "./generatedPeacockRequireTable"
|
||||
|
||||
// load flags as soon as possible
|
||||
import { getFlag, loadFlags } from "./flags"
|
||||
|
||||
loadFlags()
|
||||
|
||||
import { program } from "commander"
|
||||
import { toolsMenu } from "./tools"
|
||||
import { readFileSync, writeFileSync } from "fs"
|
||||
import { pack, unpack } from "msgpackr"
|
||||
import { log, LogLevel } from "./loggingInterop"
|
||||
import { startServer } from "./index"
|
||||
import { PEACOCKVERSTRING } from "./utils"
|
||||
import * as process from "node:process"
|
||||
|
||||
program.description(
|
||||
"The Peacock Project is a HITMAN™ World of Assassination Trilogy server replacement.",
|
||||
)
|
||||
|
||||
program.option("-v, --version", "print the version number and exit")
|
||||
program.option(
|
||||
"--hmr",
|
||||
"enable experimental hot reloading of contracts",
|
||||
getFlag("experimentalHMR") as boolean,
|
||||
)
|
||||
program.option(
|
||||
"--plugin-dev-host",
|
||||
"activate plugin development features - requires plugin dev workspace setup",
|
||||
getFlag("developmentPluginDevHost") as boolean,
|
||||
)
|
||||
program.action(
|
||||
(options: { hmr: boolean; pluginDevHost: boolean; version: boolean }) => {
|
||||
if (options.version) {
|
||||
console.log(`Peacock ${PEACOCKVERSTRING}`)
|
||||
return process.exit()
|
||||
}
|
||||
|
||||
return startServer(options)
|
||||
},
|
||||
)
|
||||
|
||||
program.command("tools").description("open the tools UI").action(toolsMenu)
|
||||
|
||||
// noinspection RequiredAttributes
|
||||
program
|
||||
.command("pack")
|
||||
.argument("<input>", "input file to pack")
|
||||
.option("-o, --output <path>", "where to output the packed file to", "")
|
||||
.description("packs an input file into a Challenge Resource Package")
|
||||
.action((input, options: { output: string }) => {
|
||||
const outputPath =
|
||||
options.output || input.replace(/\.[^/\\.]+$/, ".crp")
|
||||
|
||||
writeFileSync(
|
||||
outputPath,
|
||||
pack(JSON.parse(readFileSync(input).toString())),
|
||||
)
|
||||
|
||||
log(LogLevel.INFO, `Packed "${input}" to "${outputPath}" successfully.`)
|
||||
})
|
||||
|
||||
// noinspection RequiredAttributes
|
||||
program
|
||||
.command("unpack")
|
||||
.argument("<input>", "input file to unpack")
|
||||
.option("-o, --output <path>", "where to output the unpacked file to", "")
|
||||
.description("unpacks a Challenge Resource Package")
|
||||
.action((input, options: { output: string }) => {
|
||||
const outputPath =
|
||||
options.output || input.replace(/\.[^/\\.]+$/, ".json")
|
||||
|
||||
writeFileSync(outputPath, JSON.stringify(unpack(readFileSync(input))))
|
||||
|
||||
log(
|
||||
LogLevel.INFO,
|
||||
`Unpacked "${input}" to "${outputPath}" successfully.`,
|
||||
)
|
||||
})
|
||||
|
||||
program.parse(process.argv)
|
|
@ -108,7 +108,7 @@ import MultiplayerPresets from "../static/MultiplayerPresets.json"
|
|||
import LobbySlimTemplate from "../static/LobbySlimTemplate.json"
|
||||
import MasteryDataForLocationTemplate from "../static/MasteryDataForLocationTemplate.json"
|
||||
import LegacyMasteryLocationTemplate from "../static/LegacyMasteryLocationTemplate.json"
|
||||
import DefaultCpdConfig from "../static/DefaultCpdConfig.json"
|
||||
import DefaultCpdConfigs from "../static/DefaultCpdConfigs.json"
|
||||
import EvergreenGameChangerProperties from "../static/EvergreenGameChangerProperties.json"
|
||||
import AreaMap from "../static/AreaMap.json"
|
||||
import ArcadePageTemplate from "../static/ArcadePageTemplate.json"
|
||||
|
@ -217,7 +217,7 @@ const configs = {
|
|||
LobbySlimTemplate,
|
||||
MasteryDataForLocationTemplate,
|
||||
LegacyMasteryLocationTemplate,
|
||||
DefaultCpdConfig,
|
||||
DefaultCpdConfigs,
|
||||
EvergreenGameChangerProperties,
|
||||
AreaMap,
|
||||
ArcadePageTemplate,
|
||||
|
|
|
@ -49,8 +49,8 @@ import {
|
|||
import { log, LogLevel } from "../loggingInterop"
|
||||
import { randomUUID } from "crypto"
|
||||
import {
|
||||
createObjectivesForTarget,
|
||||
createTimeLimit,
|
||||
TargetCreator,
|
||||
} from "../statemachines/contractCreation"
|
||||
import { createSniperLoadouts, SniperCharacter } from "../menus/sniper"
|
||||
import { GetForPlay2Body } from "../types/gameSchemas"
|
||||
|
@ -277,13 +277,13 @@ contractRoutingRouter.post(
|
|||
return
|
||||
}
|
||||
|
||||
req.body.creationData.Targets.forEach((target) => {
|
||||
for (const target of req.body.creationData.Targets) {
|
||||
if (!target.Selected) {
|
||||
return
|
||||
continue
|
||||
}
|
||||
|
||||
objectives.push(...new TargetCreator(target).build())
|
||||
})
|
||||
objectives.push(...createObjectivesForTarget(target))
|
||||
}
|
||||
|
||||
req.body.creationData.ContractConditionIds.forEach(
|
||||
(contractConditionId) => {
|
||||
|
|
|
@ -833,85 +833,3 @@ export function complications(timeString: string) {
|
|||
targetsOnly,
|
||||
]
|
||||
}
|
||||
|
||||
/*
|
||||
const PISTOL_ELIM = {
|
||||
$any: {
|
||||
"?": {
|
||||
$or: [
|
||||
{
|
||||
$eq: ["$.#", "pistol"],
|
||||
},
|
||||
{
|
||||
$eq: ["$.#", "close_combat_pistol_elimination"],
|
||||
},
|
||||
],
|
||||
},
|
||||
in: ["$Value.KillMethodBroad", "$Value.KillMethodStrict"],
|
||||
},
|
||||
}
|
||||
|
||||
export class StateMachineGenerationContext {
|
||||
public createKillMethodStateMachine(): void {
|
||||
this.weaponTargetStateMachine = {
|
||||
_comment: `Eliminate ${this.targetId} using weapon`,
|
||||
Type: "statemachine",
|
||||
Id: this.weaponTargetStateMachineId,
|
||||
Category: "secondary",
|
||||
Definition: {
|
||||
Scope: "Hit",
|
||||
Context: {
|
||||
Targets: [this.targetId],
|
||||
},
|
||||
States: {
|
||||
Start: {
|
||||
Kill: [
|
||||
{
|
||||
Condition: {
|
||||
$and: [
|
||||
{
|
||||
$eq: [
|
||||
"$Value.RepositoryId",
|
||||
this.targetId,
|
||||
],
|
||||
},
|
||||
{
|
||||
$eq: [
|
||||
"$Value.KillMethodStrict",
|
||||
this.genContextParams
|
||||
.KillMethodStrict,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
Transition: "Success",
|
||||
},
|
||||
{
|
||||
Condition: {
|
||||
$eq: ["$Value.RepositoryId", this.targetId],
|
||||
},
|
||||
Transition: "Failure",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
private kmConditions(killMethodBroad) {
|
||||
switch (killMethodBroad) {
|
||||
case "pistol":
|
||||
return PISTOL_ELIM
|
||||
default:
|
||||
// weapon
|
||||
return {
|
||||
$eq: [
|
||||
"$Value.KillMethodStrict",
|
||||
this.genContextParams.KillMethodStrict,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
export const orderedETAs = [
|
||||
"797e204a-ef3d-463b-a386-57df0fe29b8f",
|
||||
"de9788cc-b9c4-47fc-b5df-86451cd82c43",
|
||||
"ff5b4e53-49ea-4d85-b94e-d3c8b3fc7ab3",
|
||||
"80cf04de-8e0b-4f38-b094-600753e2ac24",
|
||||
"85a67f31-75ce-40f5-a281-7765791f58ca",
|
||||
|
|
|
@ -21,7 +21,7 @@ import {
|
|||
ContractHistory,
|
||||
GameVersion,
|
||||
HitsCategoryCategory,
|
||||
IHit,
|
||||
Hit,
|
||||
} from "../types/types"
|
||||
import {
|
||||
contractIdToHitObject,
|
||||
|
@ -242,9 +242,9 @@ export class HitsCategoryService {
|
|||
for (const id of escalations) {
|
||||
const contract = controller.resolveContract(id)
|
||||
|
||||
if (!contract) {
|
||||
continue
|
||||
}
|
||||
if (!contract) continue
|
||||
|
||||
if (contract.Metadata.Season === -1) continue
|
||||
|
||||
const isPeacock = contract.Metadata.Season === 0
|
||||
const season = isPeacock
|
||||
|
@ -326,6 +326,17 @@ export class HitsCategoryService {
|
|||
hit.UserCentricContract.Data.PlaylistData.IsAdded =
|
||||
favorites.includes(hit.Id)
|
||||
}
|
||||
|
||||
// Save the contract
|
||||
if (
|
||||
!controller.contracts.has(
|
||||
hit.UserCentricContract.Contract.Metadata.Id,
|
||||
)
|
||||
) {
|
||||
await controller.commitNewContract(
|
||||
hit.UserCentricContract.Contract,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return resp.data.data
|
||||
|
@ -525,7 +536,7 @@ export class HitsCategoryService {
|
|||
|
||||
const hitObjectList = hits
|
||||
.map((id) => contractIdToHitObject(id, gameVersion, userId))
|
||||
.filter(Boolean) as IHit[]
|
||||
.filter(Boolean) as Hit[]
|
||||
|
||||
if (!this.paginationExempt.includes(category)) {
|
||||
const paginated = paginate(hitObjectList, this.hitsPerPage)
|
||||
|
|
|
@ -139,8 +139,12 @@ export const missionsInLocations = {
|
|||
"85a2b618-2e3c-444f-931c-b89d566e45f7",
|
||||
"88451dd9-4b57-441e-9eab-e20b9879bafa",
|
||||
],
|
||||
LOCATION_HOKKAIDO_MAMUSHI: [],
|
||||
LOCATION_HOKKAIDO_FLU: [],
|
||||
LOCATION_NEWZEALAND: ["3efc73f9-33f0-4af6-9508-7208e6851394"],
|
||||
LOCATION_NEWZEALAND: [
|
||||
"e1e86206-d3f0-a819-e477-3d80e55e8a40",
|
||||
"3efc73f9-33f0-4af6-9508-7208e6851394",
|
||||
],
|
||||
LOCATION_MIAMI: [
|
||||
"719ee044-4b05-4bd9-b2bb-75029f6d2a35",
|
||||
"5284cb9f-9bdd-4b00-99c3-0b5939b01818",
|
||||
|
@ -287,6 +291,7 @@ export const missionsInLocations = {
|
|||
"dd68d30f-3900-415f-bb17-84681a2cd4fc",
|
||||
"9cbdb972-95df-4e0a-be77-7937ec6f2fb0",
|
||||
],
|
||||
LOCATION_MIAMI: ["de9788cc-b9c4-47fc-b5df-86451cd82c43"],
|
||||
},
|
||||
sniper: {
|
||||
LOCATION_AUSTRIA: ["ff9f46cf-00bd-4c12-b887-eac491c3a96d"],
|
||||
|
|
|
@ -18,19 +18,6 @@
|
|||
|
||||
import { ContractSession } from "../types/types"
|
||||
|
||||
/**
|
||||
* Changes a set to an array.
|
||||
*
|
||||
* @param set The set.
|
||||
*/
|
||||
export function normalizeSet<T>(set: Set<T>): T[] {
|
||||
const l: T[] = []
|
||||
|
||||
set.forEach((i) => l.push(i))
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
const SESSION_SET_PROPS: (keyof ContractSession)[] = [
|
||||
"targetKills",
|
||||
"npcKills",
|
||||
|
@ -76,7 +63,7 @@ export function serializeSession(session: ContractSession): unknown {
|
|||
|
||||
if (session[key as K] instanceof Set) {
|
||||
// @ts-expect-error Type mismatch.
|
||||
o[key] = normalizeSet(session[key])
|
||||
o[key] = Array.from(session[key])
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -109,7 +96,7 @@ export function deserializeSession(
|
|||
}
|
||||
|
||||
for (const map of SESSION_MAP_PROPS) {
|
||||
if (Object.hasOwn(session, map)) {
|
||||
if (session[map]) {
|
||||
// @ts-expect-error Type mismatch.
|
||||
session[map] = new Map(session[map])
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import type {
|
|||
GameVersion,
|
||||
GenSingleMissionFunc,
|
||||
GenSingleVideoFunc,
|
||||
IHit,
|
||||
Hit,
|
||||
MissionManifest,
|
||||
PeacockLocationsData,
|
||||
PlayNextGetCampaignsHookReturn,
|
||||
|
@ -359,6 +359,7 @@ export class Controller {
|
|||
PlayNextGetCampaignsHookReturn | undefined
|
||||
>
|
||||
onMissionEnd: SyncHook<[/** session */ ContractSession]>
|
||||
onEscalationReset: SyncHook<[/** groupId */ string]>
|
||||
}
|
||||
public configManager: typeof configManagerType = {
|
||||
getConfig,
|
||||
|
@ -402,9 +403,49 @@ export class Controller {
|
|||
getSearchResults: new AsyncSeriesHook(),
|
||||
getNextCampaignMission: new SyncBailHook(),
|
||||
onMissionEnd: new SyncHook(),
|
||||
onEscalationReset: new SyncHook(),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* You should use {@link smf.modIsInstalled} instead!
|
||||
*
|
||||
* Returns whether a mod is UNAVAILABLE.
|
||||
*
|
||||
* @param modId The mod's ID.
|
||||
* @returns If the mod is unavailable. You should probably abort initialization if true is returned. Also returns true if the `overrideFrameworkChecks` flag is set.
|
||||
* @deprecated since v5.5.0, use `!controller.smf.modIsInstalled`
|
||||
*/
|
||||
public addClientSideModDependency(modId: string): boolean {
|
||||
log(
|
||||
LogLevel.WARN,
|
||||
"controller.addClientSideModDependency is deprecated, use !controller.smf.modIsInstalled instead!",
|
||||
"plugins",
|
||||
)
|
||||
return (
|
||||
getFlag("overrideFrameworkChecks") === true ||
|
||||
!this.smf.modIsInstalled(modId)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* You should use {@link smf.modIsInstalled} instead!
|
||||
*
|
||||
* Returns whether a mod is available and installed.
|
||||
*
|
||||
* @param modId The mod's ID.
|
||||
* @returns If the mod is available (or the `overrideFrameworkChecks` flag is set). You should probably abort initialisation if false is returned.
|
||||
* @deprecated since v7.0.0, use `controller.smf.modIsInstalled`
|
||||
*/
|
||||
public modIsInstalled(modId: string): boolean {
|
||||
log(
|
||||
LogLevel.WARN,
|
||||
"controller.modIsInstalled is deprecated, use controller.smf.modIsInstalled instead!",
|
||||
"plugins",
|
||||
)
|
||||
return this.smf.modIsInstalled(modId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the service and loads in all contracts.
|
||||
*
|
||||
|
@ -434,6 +475,16 @@ export class Controller {
|
|||
this._getETALocations()
|
||||
this.index()
|
||||
|
||||
try {
|
||||
await this._loadResources()
|
||||
|
||||
this.hooks.challengesLoaded.call()
|
||||
this.hooks.masteryDataLoaded.call()
|
||||
} catch (e) {
|
||||
log(LogLevel.ERROR, `Fatal error with challenge bootstrap`, "boot")
|
||||
log(LogLevel.ERROR, e)
|
||||
}
|
||||
|
||||
const deployPath = SMFSupport.modFrameworkDataPath
|
||||
|
||||
if (typeof deployPath === "string") {
|
||||
|
@ -447,16 +498,6 @@ export class Controller {
|
|||
}
|
||||
|
||||
this.hooks.serverStart.call()
|
||||
|
||||
try {
|
||||
await this._loadResources()
|
||||
|
||||
this.hooks.challengesLoaded.call()
|
||||
this.hooks.masteryDataLoaded.call()
|
||||
} catch (e) {
|
||||
log(LogLevel.ERROR, `Fatal error with challenge bootstrap`, "boot")
|
||||
log(LogLevel.ERROR, e)
|
||||
}
|
||||
}
|
||||
|
||||
private _getETALocations(): void {
|
||||
|
@ -730,9 +771,7 @@ export class Controller {
|
|||
)
|
||||
await this.commitNewContract(contractData)
|
||||
|
||||
if (PEACOCK_DEV) {
|
||||
log(LogLevel.DEBUG, `Saved contract to contracts/${pubId}.json`)
|
||||
}
|
||||
|
||||
return contractData
|
||||
}
|
||||
|
@ -825,7 +864,6 @@ export class Controller {
|
|||
/**
|
||||
* Get all global challenges and register a simplified version of them.
|
||||
* @param gameVersion A GameVersion object representing the version of the game.
|
||||
*
|
||||
*/
|
||||
private registerGlobalChallenges(gameVersion: GameVersion) {
|
||||
const regGlobalChallenges: RegistryChallenge[] = getVersionedConfig<
|
||||
|
@ -856,7 +894,7 @@ export class Controller {
|
|||
],
|
||||
meta: {
|
||||
Location: "GLOBAL",
|
||||
GameVersion: gameVersion,
|
||||
GameVersions: [gameVersion],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@ -914,11 +952,12 @@ export class Controller {
|
|||
}
|
||||
|
||||
private _handleChallengeResources(data: ChallengePackage): void {
|
||||
for (const version of data.meta.GameVersions) {
|
||||
for (const group of data.groups) {
|
||||
this.challengeService.registerGroup(
|
||||
group,
|
||||
data.meta.Location,
|
||||
data.meta.GameVersion,
|
||||
version,
|
||||
)
|
||||
|
||||
for (const challenge of group.Challenges) {
|
||||
|
@ -926,11 +965,12 @@ export class Controller {
|
|||
challenge,
|
||||
group.CategoryId,
|
||||
data.meta.Location,
|
||||
data.meta.GameVersion,
|
||||
version,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _handleMasteryResources(data: MasteryPackage): void {
|
||||
this.masteryService.registerMasteryData(data)
|
||||
|
@ -1035,6 +1075,7 @@ export class Controller {
|
|||
module: { exports: {} },
|
||||
exports: {},
|
||||
process,
|
||||
fetch,
|
||||
require: createPeacockRequire(pluginName),
|
||||
})
|
||||
|
||||
|
@ -1175,7 +1216,7 @@ export function contractIdToHitObject(
|
|||
contractId: string,
|
||||
gameVersion: GameVersion,
|
||||
userId: string,
|
||||
): IHit | undefined {
|
||||
): Hit | undefined {
|
||||
const contract = controller.resolveContract(contractId)
|
||||
|
||||
if (!contract) {
|
||||
|
|
|
@ -16,56 +16,115 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { readFile, writeFile } from "atomically"
|
||||
import { join } from "path"
|
||||
import type { ContractSession, GameVersion, UserProfile } from "./types/types"
|
||||
import { serializeSession, deserializeSession } from "./contracts/sessions"
|
||||
import { deserializeSession, serializeSession } from "./contracts/sessions"
|
||||
import { castUserProfile } from "./utils"
|
||||
import { log, LogLevel } from "./loggingInterop"
|
||||
import { unlink, readdir } from "fs/promises"
|
||||
import { mkdir, readdir, readFile, unlink, writeFile } from "fs/promises"
|
||||
import type * as nodeFs from "node:fs/promises"
|
||||
import { existsSync } from "fs"
|
||||
|
||||
// unlink, mkdir, readdir from node:fs/promises
|
||||
type NodeUnlinkMkdirReaddir = Pick<
|
||||
typeof nodeFs,
|
||||
"unlink" | "mkdir" | "readdir" | "writeFile" | "readFile"
|
||||
>
|
||||
|
||||
// custom exists function because node doesn't have an async version of existsSync
|
||||
type ExistsPromise = {
|
||||
exists: (path: string) => Promise<boolean>
|
||||
}
|
||||
|
||||
/**
|
||||
* Container for functions that handle file read/writes,
|
||||
* which could otherwise break if writing partial data.
|
||||
* The fs implementation that this system uses.
|
||||
*/
|
||||
export type DataStorageFs = NodeUnlinkMkdirReaddir & ExistsPromise
|
||||
|
||||
/**
|
||||
* Handles the dispatching of user data in a way that avoids FS operations unless absolutely needed.
|
||||
*/
|
||||
class AsyncUserDataGuard {
|
||||
private readonly userData: Record<string, UserProfile> = {}
|
||||
private readonly dirtyProfiles: Set<string> = new Set()
|
||||
/** @internal */
|
||||
readonly userData: Map<string, UserProfile> = new Map()
|
||||
/** @internal */
|
||||
readonly dirtyProfiles: Set<string> = new Set()
|
||||
/**
|
||||
* Internal list of background tasks that have been scheduled.
|
||||
* The key is the versioned user ID.
|
||||
* @internal
|
||||
*/
|
||||
readonly tasks: Map<string, NodeJS.Timeout> = new Map()
|
||||
|
||||
getProfile(id: string): UserProfile {
|
||||
return this.userData[id]
|
||||
/** If true, none of the background tasks will attempt to write. */
|
||||
#paused = false
|
||||
|
||||
/**
|
||||
* Get the fs implementation to use for file read/writes.
|
||||
* Mainly for test purposes, but could also be used by plugins to make it use a real database.
|
||||
*/
|
||||
getFs(): DataStorageFs {
|
||||
return {
|
||||
writeFile,
|
||||
readFile,
|
||||
unlink,
|
||||
mkdir,
|
||||
readdir,
|
||||
exists(path) {
|
||||
// node's fs doesn't have a promise version of exists
|
||||
return Promise.resolve(existsSync(path))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a loaded user's profile.
|
||||
* @param id The target user ID.
|
||||
* @returns The profile, or undefined if they're not loaded.
|
||||
* Profiles are loaded when they perform the auth handshake via the game.
|
||||
*/
|
||||
getProfile(id: string): UserProfile | undefined {
|
||||
return this.userData.get(id)
|
||||
}
|
||||
|
||||
addLoadedProfile(id: string, profile: UserProfile): void {
|
||||
if (!this.userData[id]) {
|
||||
setInterval(() => {
|
||||
if (!this.dirtyProfiles.has(id)) {
|
||||
if (!this.userData.has(id) && !this.tasks.has(id)) {
|
||||
const interval = setInterval(() => {
|
||||
if (!this.dirtyProfiles.has(id) || this.#paused) {
|
||||
return
|
||||
}
|
||||
|
||||
this.save(id)
|
||||
}, 3000)
|
||||
|
||||
this.tasks.set(id, interval.unref())
|
||||
}
|
||||
|
||||
this.userData.set(id, profile)
|
||||
// just in case
|
||||
this.dirtyProfiles.delete(id)
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves any modifications to a given profile, called by a background scheduled task.
|
||||
* @param id The user ID.
|
||||
*/
|
||||
save(id: string) {
|
||||
this.dirtyProfiles.delete(id)
|
||||
|
||||
this.write(id)
|
||||
.then(() => undefined)
|
||||
.catch((e) => {
|
||||
log(
|
||||
LogLevel.ERROR,
|
||||
`Failed to write user profile ${id}: ${e}`,
|
||||
)
|
||||
log(LogLevel.ERROR, `Failed to write user profile ${id}: ${e}`)
|
||||
})
|
||||
}, 3000).unref()
|
||||
}
|
||||
|
||||
this.userData[id] = profile
|
||||
// just in case
|
||||
this.dirtyProfiles.delete(id)
|
||||
}
|
||||
|
||||
markDirty(id: string): void {
|
||||
this.dirtyProfiles.add(id)
|
||||
}
|
||||
|
||||
private async write(versionedId: string): Promise<void> {
|
||||
/** @internal */
|
||||
async write(versionedId: string): Promise<void> {
|
||||
let path
|
||||
|
||||
const [id, gameVersion] = versionedId.split(".")
|
||||
|
@ -76,24 +135,58 @@ class AsyncUserDataGuard {
|
|||
path = join("userdata", "users", `${id}.json`)
|
||||
}
|
||||
|
||||
await writeFile(path, JSON.stringify(this.getProfile(versionedId)))
|
||||
await this.getFs().writeFile(
|
||||
path,
|
||||
JSON.stringify(this.getProfile(versionedId)),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately write all loaded profiles to the disk, even if no changes are pending.
|
||||
*/
|
||||
async forceFlush() {
|
||||
const taskKeys = this.tasks.keys()
|
||||
this.#paused = true
|
||||
|
||||
for (const id of taskKeys) {
|
||||
this.dirtyProfiles.delete(id)
|
||||
await this.write(id)
|
||||
}
|
||||
|
||||
this.#paused = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Unload all profiles without saving.
|
||||
*/
|
||||
unloadAll() {
|
||||
for (const id of this.tasks.keys()) {
|
||||
clearInterval(this.tasks.get(id))
|
||||
this.dirtyProfiles.delete(id)
|
||||
this.userData.delete(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const asyncGuard = new AsyncUserDataGuard()
|
||||
/**
|
||||
* If you are touching this, you better know what you're doing.
|
||||
*/
|
||||
export const asyncGuard = new AsyncUserDataGuard()
|
||||
|
||||
/**
|
||||
* Gets a user's profile data.
|
||||
*
|
||||
* @param userId The user's ID.
|
||||
* @param gameVersion The game's version.
|
||||
* @returns The user's profile
|
||||
* @returns The user's profile, OR UNDEFINED if not loaded.
|
||||
*/
|
||||
export function getUserData(
|
||||
userId: string,
|
||||
gameVersion: GameVersion,
|
||||
): UserProfile {
|
||||
const data = asyncGuard.getProfile(`${userId}.${gameVersion}`)
|
||||
// TODO: consumers could have undefined returned - this function needs undefined
|
||||
// as part of it's signature, but that requires a lot of changes.
|
||||
const data = asyncGuard.getProfile(`${userId}.${gameVersion}`)!
|
||||
|
||||
// NOTE: ProfileLevel always starts at 1
|
||||
if (data?.Extensions?.progression?.PlayerProfileXP?.ProfileLevel === 0) {
|
||||
|
@ -104,7 +197,7 @@ export function getUserData(
|
|||
}
|
||||
|
||||
/**
|
||||
* Only attempt to load a user's profile if it hasn't been loaded yet
|
||||
* Attempts to load a user's profile if it hasn't been loaded yet.
|
||||
*
|
||||
* @param userId The user's ID.
|
||||
* @param gameVersion The game's version.
|
||||
|
@ -150,7 +243,7 @@ export async function loadUserData(
|
|||
}
|
||||
|
||||
const userProfile = castUserProfile(
|
||||
JSON.parse((await readFile(path)).toString()),
|
||||
JSON.parse((await asyncGuard.getFs().readFile(path)).toString()),
|
||||
gameVersion,
|
||||
path,
|
||||
)
|
||||
|
@ -199,16 +292,18 @@ export async function getExternalUserData(
|
|||
externalFolder: string,
|
||||
gameVersion: GameVersion,
|
||||
): Promise<string> {
|
||||
const fs = asyncGuard.getFs()
|
||||
|
||||
if (["scpc", "h1", "h2"].includes(gameVersion)) {
|
||||
return (
|
||||
await readFile(
|
||||
await fs.readFile(
|
||||
join("userdata", gameVersion, externalFolder, `${userId}.json`),
|
||||
)
|
||||
).toString()
|
||||
}
|
||||
|
||||
return (
|
||||
await readFile(join("userdata", externalFolder, `${userId}.json`))
|
||||
await fs.readFile(join("userdata", externalFolder, `${userId}.json`))
|
||||
).toString()
|
||||
}
|
||||
|
||||
|
@ -226,14 +321,16 @@ export async function writeExternalUserData(
|
|||
userData: string,
|
||||
gameVersion: GameVersion,
|
||||
): Promise<void> {
|
||||
const fs = asyncGuard.getFs()
|
||||
|
||||
if (["scpc", "h1", "h2"].includes(gameVersion)) {
|
||||
return await writeFile(
|
||||
return await fs.writeFile(
|
||||
join("userdata", gameVersion, externalFolder, `${userId}.json`),
|
||||
userData,
|
||||
)
|
||||
}
|
||||
|
||||
return await writeFile(
|
||||
return await fs.writeFile(
|
||||
join("userdata", externalFolder, `${userId}.json`),
|
||||
userData,
|
||||
)
|
||||
|
@ -248,7 +345,8 @@ export async function writeExternalUserData(
|
|||
export async function getContractSession(
|
||||
identifier: string,
|
||||
): Promise<ContractSession> {
|
||||
const files = await readdir("contractSessions")
|
||||
const fs = asyncGuard.getFs()
|
||||
const files = await fs.readdir("contractSessions")
|
||||
const filtered = files.filter((fn) => fn.endsWith(`_${identifier}.json`))
|
||||
|
||||
if (filtered.length === 0) {
|
||||
|
@ -256,10 +354,12 @@ export async function getContractSession(
|
|||
}
|
||||
|
||||
// The filtered files have the same identifier, they are just stored at different slots
|
||||
// So we can read any of them and it will be the same.
|
||||
// So we can read any of them, and it will be the same.
|
||||
return deserializeSession(
|
||||
JSON.parse(
|
||||
(await readFile(join("contractSessions", filtered[0]))).toString(),
|
||||
(
|
||||
await fs.readFile(join("contractSessions", filtered[0]))
|
||||
).toString(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -274,7 +374,9 @@ export async function writeContractSession(
|
|||
identifier: string,
|
||||
session: ContractSession,
|
||||
): Promise<void> {
|
||||
return await writeFile(
|
||||
const fs = asyncGuard.getFs()
|
||||
|
||||
return await fs.writeFile(
|
||||
join("contractSessions", `${identifier}.json`),
|
||||
JSON.stringify(serializeSession(session)),
|
||||
)
|
||||
|
@ -287,5 +389,46 @@ export async function writeContractSession(
|
|||
* @throws ENOENT if the file is not found.
|
||||
*/
|
||||
export async function deleteContractSession(fileName: string): Promise<void> {
|
||||
return await unlink(join("contractSessions", `${fileName}.json`))
|
||||
const fs = asyncGuard.getFs()
|
||||
|
||||
return await fs.unlink(join("contractSessions", `${fileName}.json`))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the required file structure for the server.
|
||||
*
|
||||
* @param joinFunc The path join function to use, defaulting to Node's. You may need to specify it if working in a VFS.
|
||||
*/
|
||||
export async function setupFileStructure(joinFunc = join) {
|
||||
const fs = asyncGuard.getFs()
|
||||
|
||||
for (const dir of [
|
||||
"contractSessions",
|
||||
"plugins",
|
||||
"userdata",
|
||||
"contracts",
|
||||
joinFunc("userdata", "epicids"),
|
||||
joinFunc("userdata", "steamids"),
|
||||
joinFunc("userdata", "users"),
|
||||
joinFunc("userdata", "h1", "steamids"),
|
||||
joinFunc("userdata", "h1", "epicids"),
|
||||
joinFunc("userdata", "h1", "users"),
|
||||
joinFunc("userdata", "h2", "steamids"),
|
||||
joinFunc("userdata", "h2", "users"),
|
||||
joinFunc("userdata", "scpc", "users"),
|
||||
joinFunc("userdata", "scpc", "steamids"),
|
||||
joinFunc("images", "actors"),
|
||||
joinFunc("images", "contracts"),
|
||||
joinFunc("images", "contracts", "elusive"),
|
||||
joinFunc("images", "contracts", "escalation"),
|
||||
joinFunc("images", "contracts", "featured"),
|
||||
joinFunc("images", "unlockables_override"),
|
||||
]) {
|
||||
if (await fs.exists(dir)) {
|
||||
continue
|
||||
}
|
||||
|
||||
log(LogLevel.DEBUG, `Creating missing directory ${dir}`)
|
||||
await fs.mkdir(dir, { recursive: true })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,28 +16,28 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { RPCClient } from "./discord/client"
|
||||
import { getConfig } from "./configSwizzleManager"
|
||||
import type { GameVersion, MissionType } from "./types/types"
|
||||
import { getFlag } from "./flags"
|
||||
import { log, LogLevel } from "./loggingInterop"
|
||||
import { RPCClient } from "./client"
|
||||
import { getConfig } from "../configSwizzleManager"
|
||||
import type { GameVersion, MissionType } from "../types/types"
|
||||
import { getFlag } from "../flags"
|
||||
import { log, LogLevel } from "../loggingInterop"
|
||||
|
||||
let rpcClient: undefined | RPCClient
|
||||
/*@__NOINLINE__*/
|
||||
const processStartTime = Math.round(Date.now() / 1000)
|
||||
|
||||
export function initRp(): void {
|
||||
Object.keys(getConfig("Entrances", false)).forEach((key) => {
|
||||
for (const key of Object.keys(getConfig("Entrances", false))) {
|
||||
if (!scenePathToRpAsset(key, []) && PEACOCK_DEV) {
|
||||
log(LogLevel.DEBUG, `WARNING missing scene ${key} for RP!`)
|
||||
log(LogLevel.WARN, `Missing scene ${key} for RP!`, "discord")
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// creates a new rp client, pretty self-explanatory.
|
||||
rpcClient = new RPCClient()
|
||||
|
||||
// connects to the Peacock discord developer app, which contains the images for the rp.
|
||||
rpcClient.login({
|
||||
void rpcClient.login({
|
||||
clientId: "846361353027584013",
|
||||
})
|
||||
|
||||
|
@ -143,7 +143,7 @@ type BrickDataMap = Record<string, [string, string, string]>
|
|||
export function scenePathToRpAsset(
|
||||
scenePath: string,
|
||||
bricks: string[],
|
||||
): string[] | undefined {
|
||||
): [string, string, string] | undefined {
|
||||
const brickAssetsMap = getConfig<BrickDataMap>(
|
||||
"DiscordRichAssetsForBricks",
|
||||
false,
|
||||
|
@ -220,11 +220,16 @@ export function scenePathToRpAsset(
|
|||
|
||||
// dartmoor
|
||||
case "assembly:/_pro/scenes/missions/ancestral/scene_bulldog.entity":
|
||||
case "assembly:/_pro/scenes/missions/ancestral/scene_bulldog_fern.entity":
|
||||
return ["dartmoordeathoffamily", "Death in the Family", "Dartmoor"]
|
||||
case "assembly:/_pro/scenes/missions/ancestral/scene_smoothsnake.entity":
|
||||
return ["dartmoorgardenshow", "Dartmoor Garden Show", "Dartmoor"]
|
||||
case "assembly:/_pro/scenes/missions/ancestral/scene_ancestral_vesper.entity":
|
||||
return ["elusivevesper", "The Procurers", "Dartmoor"]
|
||||
case "assembly:/_pro/scenes/missions/ancestral/scene_ancestral_harebell.entity":
|
||||
return ["harebell", "The Sloth Depletion", "Dartmoor"]
|
||||
case "assembly:/_pro/scenes/missions/ancestral/scene_hollyhock.entity":
|
||||
return ["hollyhock", "The Wrath Termination", "Dartmoor"]
|
||||
|
||||
// columbia
|
||||
case "assembly:/_pro/scenes/missions/colombia/mission_millipede/scene_millipede.entity":
|
||||
|
@ -258,6 +263,8 @@ export function scenePathToRpAsset(
|
|||
return ["berlinegghunt", "Berlin Egg Hunt", "Berlin"]
|
||||
case "assembly:/_pro/scenes/missions/edgy/mission_fox/scene_fox_tomorrowland.entity":
|
||||
return ["elusivetomorrowland", "The Drop", "Berlin"]
|
||||
case "assembly:/_pro/scenes/missions/edgy/mission_fox/scene_ambrosia.entity":
|
||||
return ["ambrosia", "The Lust Assignation", "Berlin"]
|
||||
|
||||
// mendozer
|
||||
case "assembly:/_pro/scenes/missions/elegant/scene_llama_elusive_clerico.entity":
|
||||
|
@ -270,6 +277,8 @@ export function scenePathToRpAsset(
|
|||
case "assembly:/_pro/scenes/missions/elegant/scene_whitedryas_level2.entity":
|
||||
case "assembly:/_pro/scenes/missions/elegant/scene_whitedryas_level3.entity":
|
||||
return ["mendozafarewell", "The Farewell", "Mendoza"]
|
||||
case "assembly:/_pro/scenes/missions/elegant/scene_frangipani.entity":
|
||||
return ["frangipani", "The Envy Contention", "Mendoza"]
|
||||
|
||||
// dubai
|
||||
case "assembly:/_pro/scenes/missions/golden/mission_gecko/scene_gecko_angelica.entity":
|
||||
|
@ -290,6 +299,8 @@ export function scenePathToRpAsset(
|
|||
return ["chongqingendofanera", "End Of An Era", "Chongqing"]
|
||||
case "assembly:/_pro/scenes/missions/wet/scene_rat_elusive_redsnapper.entity":
|
||||
return ["elusiveredsnapper", "The Rage", "Chongqing"]
|
||||
case "assembly:/_pro/scenes/missions/wet/scene_wet_azalea.entity":
|
||||
return ["azalea", "The Gluttony Gobble", "Chongqing"]
|
||||
|
||||
// training
|
||||
case "assembly:/_pro/scenes/missions/thefacility/_scene_polarbear_005.entity":
|
||||
|
@ -366,6 +377,10 @@ export function scenePathToRpAsset(
|
|||
return ["austria", "The Last Yardbird", "Austria"]
|
||||
case "assembly:/_pro/scenes/missions/salty/mission_seagull/scene_seagull.entity":
|
||||
return ["hantuport", "The Pen and the Sword", "Hantu Port"]
|
||||
|
||||
// snug
|
||||
case "assembly:/_pro/scenes/missions/snug/scene_vanilla.entity":
|
||||
return ["snug", "Safehouse", "Safehouse"]
|
||||
}
|
||||
|
||||
return undefined
|
|
@ -64,7 +64,10 @@ export class EpicH3Strategy extends EntitlementStrategy {
|
|||
export class IOIStrategy extends EntitlementStrategy {
|
||||
private readonly _remoteService: string
|
||||
|
||||
constructor(gameVersion: GameVersion, private readonly issuerId: string) {
|
||||
constructor(
|
||||
gameVersion: GameVersion,
|
||||
private readonly issuerId: string,
|
||||
) {
|
||||
super()
|
||||
this.issuerId = issuerId
|
||||
this._remoteService = getRemoteService(gameVersion)!
|
||||
|
|
|
@ -33,10 +33,10 @@ import {
|
|||
} from "./types/types"
|
||||
import { contractTypes, gameDifficulty, ServerVer } from "./utils"
|
||||
import { json as jsonMiddleware } from "body-parser"
|
||||
import { log, LogLevel } from "./loggingInterop"
|
||||
import { log, logDebug, LogLevel } from "./loggingInterop"
|
||||
import { getUserData, writeUserData } from "./databaseHandler"
|
||||
import { controller } from "./controller"
|
||||
import { swapToLocationStatus } from "./discordRp"
|
||||
import { swapToLocationStatus } from "./discord/discordRp"
|
||||
import { randomUUID } from "crypto"
|
||||
import { liveSplitManager } from "./livesplit/liveSplitManager"
|
||||
import { handleMultiplayerEvent } from "./multiplayer/multiplayerService"
|
||||
|
@ -86,7 +86,6 @@ const pushMessageQueue = new Map<string, PushMessage[]>()
|
|||
* @param userId The push message's target user.
|
||||
* @param message The raw push message to send.
|
||||
* @see enqueueEvent
|
||||
* @author grappigegovert
|
||||
*/
|
||||
export function enqueuePushMessage(userId: string, message: unknown): void {
|
||||
let userQueue
|
||||
|
@ -113,7 +112,6 @@ export function enqueuePushMessage(userId: string, message: unknown): void {
|
|||
*
|
||||
* @param session The contract session.
|
||||
* @param objective The objective object.
|
||||
* @author Reece Dunham
|
||||
*/
|
||||
export function registerObjectiveListener(
|
||||
session: ContractSession,
|
||||
|
@ -225,7 +223,6 @@ export function setupScoring(
|
|||
* @param userId The event's target user.
|
||||
* @param event The event to send.
|
||||
* @see enqueuePushMessage
|
||||
* @author grappigegovert
|
||||
*/
|
||||
export function enqueueEvent(userId: string, event: ServerToClientEvent): void {
|
||||
let userQueue: S2CEventWithTimestamp[] | undefined
|
||||
|
@ -352,7 +349,9 @@ export function newSession(
|
|||
failedObjectives: new Set(),
|
||||
recording: PeacockCameraStatus.NotSpotted,
|
||||
lastAccident: 0,
|
||||
lastKill: {},
|
||||
lastKill: {
|
||||
legacyIsUnnoticed: true,
|
||||
},
|
||||
kills: new Set(),
|
||||
compat: doScoring,
|
||||
markedTargets: new Set(),
|
||||
|
@ -520,7 +519,6 @@ eventRouter.post(
|
|||
*
|
||||
* @param uId The user's ID.
|
||||
* @returns The ID for the user's active session.
|
||||
* @author Reece Dunham
|
||||
*/
|
||||
export function getActiveSessionIdForUser(uId: string): string | undefined {
|
||||
return userIdToTempSession.get(uId)
|
||||
|
@ -531,7 +529,6 @@ export function getActiveSessionIdForUser(uId: string): string | undefined {
|
|||
*
|
||||
* @param uId The user's ID.
|
||||
* @returns The user's active contract session.
|
||||
* @author Reece Dunham
|
||||
*/
|
||||
export function getSession(uId: string): ContractSession | undefined {
|
||||
const currentSession = getActiveSessionIdForUser(uId)
|
||||
|
@ -643,7 +640,8 @@ function saveEvents(
|
|||
const response: string[] = []
|
||||
const processed: string[] = []
|
||||
const userData = getUserData(userId, gameVersion)
|
||||
events.forEach((event) => {
|
||||
|
||||
for (const event of events) {
|
||||
let session = contractSessions.get(event.ContractSessionId)
|
||||
|
||||
if (!session) {
|
||||
|
@ -669,13 +667,11 @@ function saveEvents(
|
|||
session.contractId !== event.ContractId ||
|
||||
session.userId !== userId
|
||||
) {
|
||||
if (PEACOCK_DEV) {
|
||||
log(LogLevel.DEBUG, "No session or session user ID mismatch!")
|
||||
console.debug(session)
|
||||
console.debug(event)
|
||||
}
|
||||
logDebug(session)
|
||||
logDebug(event)
|
||||
|
||||
return // session does not exist or contractid/userid doesn't match
|
||||
continue // session does not exist or contractid/userid doesn't match
|
||||
}
|
||||
|
||||
session.duration = event.Timestamp
|
||||
|
@ -717,7 +713,7 @@ function saveEvents(
|
|||
)
|
||||
|
||||
if (val.state === "Failure") {
|
||||
if (PEACOCK_DEV && contractType !== "evergreen") {
|
||||
if (contractType !== "evergreen") {
|
||||
log(LogLevel.DEBUG, `Objective failed: ${objectiveId}`)
|
||||
}
|
||||
|
||||
|
@ -772,7 +768,7 @@ function saveEvents(
|
|||
processed.push(event.Name)
|
||||
response.push(process.hrtime.bigint().toString())
|
||||
|
||||
return
|
||||
continue
|
||||
}
|
||||
|
||||
// these events are important but may be fired after the timer is over
|
||||
|
@ -790,14 +786,14 @@ function saveEvents(
|
|||
) {
|
||||
// Do not handle events that occur after exiting the level
|
||||
response.push(process.hrtime.bigint().toString())
|
||||
return
|
||||
continue
|
||||
}
|
||||
|
||||
if (handleMultiplayerEvent(event, session)) {
|
||||
processed.push(event.Name)
|
||||
response.push(process.hrtime.bigint().toString())
|
||||
|
||||
return
|
||||
continue
|
||||
}
|
||||
|
||||
switch (event.Name) {
|
||||
|
@ -808,6 +804,8 @@ function saveEvents(
|
|||
)
|
||||
break
|
||||
case "Kill": {
|
||||
let couldCauseNoticedKill = true
|
||||
|
||||
const killValue = (event as KillC2SEvent).Value
|
||||
|
||||
if (session.firstKillTimestamp === undefined) {
|
||||
|
@ -821,6 +819,12 @@ function saveEvents(
|
|||
timestamp: event.Timestamp,
|
||||
repositoryIds: [killValue.RepositoryId],
|
||||
}
|
||||
|
||||
if (gameVersion === "h1" && killValue.Accident) {
|
||||
// this was an accident, can't be a noticed kill
|
||||
session.lastKill.legacyIsUnnoticed = true
|
||||
couldCauseNoticedKill = false
|
||||
}
|
||||
}
|
||||
|
||||
if (killValue.KillContext === EDeathContext.eDC_NOT_HERO) {
|
||||
|
@ -830,7 +834,9 @@ function saveEvents(
|
|||
`${killValue.RepositoryId} eliminated, 47 not responsible`,
|
||||
)
|
||||
response.push(process.hrtime.bigint().toString())
|
||||
return
|
||||
session.lastKill.legacyIsUnnoticed = true
|
||||
couldCauseNoticedKill = false
|
||||
continue
|
||||
}
|
||||
|
||||
log(
|
||||
|
@ -838,6 +844,10 @@ function saveEvents(
|
|||
`Actor ${killValue.RepositoryId} eliminated.`,
|
||||
)
|
||||
|
||||
if (couldCauseNoticedKill) {
|
||||
session.lastKill.legacyIsUnnoticed = false
|
||||
}
|
||||
|
||||
if (killValue.IsTarget || contractType === "creation") {
|
||||
const kill: RatingKill = {
|
||||
KillClass: killValue.KillClass,
|
||||
|
@ -859,6 +869,9 @@ function saveEvents(
|
|||
|
||||
break
|
||||
}
|
||||
case "Unnoticed_Kill":
|
||||
session.lastKill.legacyIsUnnoticed = true
|
||||
break
|
||||
case "CrowdNPC_Died":
|
||||
session.crowdNpcKills += 1
|
||||
break
|
||||
|
@ -1120,9 +1133,9 @@ function saveEvents(
|
|||
processed.push(event.Name)
|
||||
|
||||
response.push(process.hrtime.bigint().toString())
|
||||
})
|
||||
}
|
||||
|
||||
if (PEACOCK_DEV && processed.length > 0) {
|
||||
if (processed.length > 0) {
|
||||
log(
|
||||
LogLevel.DEBUG,
|
||||
`Event summary: ${picocolors.gray(processed.join(", "))}`,
|
||||
|
|
|
@ -22,6 +22,10 @@ import { ContractProgressionData } from "./types/types"
|
|||
import { getFlag } from "./flags"
|
||||
import { EVERGREEN_LEVEL_INFO } from "./utils"
|
||||
|
||||
type DefaultCpdConfigs = {
|
||||
[cpdId: string]: ContractProgressionData
|
||||
}
|
||||
|
||||
export function setCpd(
|
||||
data: ContractProgressionData,
|
||||
uID: string,
|
||||
|
@ -42,15 +46,22 @@ export function getCpd(uID: string, cpdID: string): ContractProgressionData {
|
|||
|
||||
if (!Object.keys(userData.Extensions.CPD).includes(cpdID)) {
|
||||
const defaultCPD = getConfig(
|
||||
"DefaultCpdConfig",
|
||||
"DefaultCpdConfigs",
|
||||
false,
|
||||
) as ContractProgressionData
|
||||
) as DefaultCpdConfigs
|
||||
|
||||
setCpd(defaultCPD, uID, cpdID)
|
||||
setCpd(
|
||||
Object.keys(defaultCPD).includes(cpdID) ? defaultCPD[cpdID] : {},
|
||||
uID,
|
||||
cpdID,
|
||||
)
|
||||
}
|
||||
|
||||
// NOTE: Override the EvergreenLevel with the latest Mastery Level
|
||||
if (getFlag("gameplayUnlockAllFreelancerMasteries")) {
|
||||
if (
|
||||
getFlag("gameplayUnlockAllFreelancerMasteries") &&
|
||||
cpdID === "f8ec92c2-4fa2-471e-ae08-545480c746ee"
|
||||
) {
|
||||
userData.Extensions.CPD[cpdID]["EvergreenLevel"] =
|
||||
EVERGREEN_LEVEL_INFO.length
|
||||
}
|
||||
|
|
|
@ -45,6 +45,10 @@ const defaultFlags: Flags = {
|
|||
desc: "[Gameplay] Show elusive targets in instinct like normal targets would appear on normal missions. (for speedrunners who are submitting to speedrun.com, just as a reminder, this tool is for practice only!)",
|
||||
default: false,
|
||||
},
|
||||
legacyNoticedKillScoring: {
|
||||
desc: '[Gameplay] In the HITMAN 2016 engine, if noticed kills should behave in the official way ("vanilla"), or how they were previously handled by Peacock ("sane")',
|
||||
default: "vanilla",
|
||||
},
|
||||
jokes: {
|
||||
desc: "[Services] The Peacock server window will tell you a joke on startup if this is set to true.",
|
||||
default: false,
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
import * as configSwizzleManager from "./configSwizzleManager"
|
||||
import * as controller from "./controller"
|
||||
import * as databaseHandler from "./databaseHandler"
|
||||
import * as discordRp from "./discordRp"
|
||||
import * as entitlementStrategies from "./entitlementStrategies"
|
||||
import * as eventHandler from "./eventHandler"
|
||||
import * as evergreen from "./evergreen"
|
||||
|
@ -57,6 +56,7 @@ import * as leaderboards from "./contracts/leaderboards"
|
|||
import * as missionsInLocation from "./contracts/missionsInLocation"
|
||||
import * as sessions from "./contracts/sessions"
|
||||
import * as client from "./discord/client"
|
||||
import * as discordRp from "./discord/discordRp"
|
||||
import * as ipc from "./discord/ipc"
|
||||
import * as liveSplitClient from "./livesplit/liveSplitClient"
|
||||
import * as liveSplitManager from "./livesplit/liveSplitManager"
|
||||
|
@ -79,176 +79,66 @@ import * as contractCreation from "./statemachines/contractCreation"
|
|||
import * as escalationService from "./contracts/escalations/escalationService"
|
||||
|
||||
export default {
|
||||
"@peacockproject/core/configSwizzleManager": {
|
||||
__esModule: true,
|
||||
...configSwizzleManager,
|
||||
},
|
||||
"@peacockproject/core/controller": { __esModule: true, ...controller },
|
||||
"@peacockproject/core/databaseHandler": {
|
||||
__esModule: true,
|
||||
...databaseHandler,
|
||||
},
|
||||
"@peacockproject/core/discordRp": { __esModule: true, ...discordRp },
|
||||
"@peacockproject/core/entitlementStrategies": {
|
||||
__esModule: true,
|
||||
...entitlementStrategies,
|
||||
},
|
||||
"@peacockproject/core/eventHandler": { __esModule: true, ...eventHandler },
|
||||
"@peacockproject/core/evergreen": { __esModule: true, ...evergreen },
|
||||
"@peacockproject/core/flags": { __esModule: true, ...flags },
|
||||
"@peacockproject/core/hooksImpl": { __esModule: true, ...hooksImpl },
|
||||
"@peacockproject/core/hotReloadService": {
|
||||
__esModule: true,
|
||||
...hotReloadService,
|
||||
},
|
||||
"@peacockproject/core/inventory": { __esModule: true, ...inventory },
|
||||
"@peacockproject/core/loadouts": { __esModule: true, ...loadouts },
|
||||
"@peacockproject/core/loggingInterop": {
|
||||
__esModule: true,
|
||||
...loggingInterop,
|
||||
},
|
||||
"@peacockproject/core/menuData": { __esModule: true, ...menuData },
|
||||
"@peacockproject/core/oauthToken": { __esModule: true, ...oauthToken },
|
||||
"@peacockproject/core/officialServerAuth": {
|
||||
__esModule: true,
|
||||
...officialServerAuth,
|
||||
},
|
||||
"@peacockproject/core/ownership": { __esModule: true, ...ownership },
|
||||
"@peacockproject/core/platformEntitlements": {
|
||||
__esModule: true,
|
||||
...platformEntitlements,
|
||||
},
|
||||
"@peacockproject/core/playStyles": { __esModule: true, ...playStyles },
|
||||
"@peacockproject/core/profileHandler": {
|
||||
__esModule: true,
|
||||
...profileHandler,
|
||||
},
|
||||
"@peacockproject/core/scoreHandler": { __esModule: true, ...scoreHandler },
|
||||
"@peacockproject/core/smfSupport": { __esModule: true, ...smfSupport },
|
||||
"@peacockproject/core/utils": { __esModule: true, ...utils },
|
||||
"@peacockproject/core/webFeatures": { __esModule: true, ...webFeatures },
|
||||
"@peacockproject/core/2016/legacyContractHandler": {
|
||||
__esModule: true,
|
||||
...legacyContractHandler,
|
||||
},
|
||||
"@peacockproject/core/2016/legacyMenuData": {
|
||||
__esModule: true,
|
||||
...legacyMenuData,
|
||||
},
|
||||
"@peacockproject/core/2016/legacyProfileRouter": {
|
||||
__esModule: true,
|
||||
...legacyProfileRouter,
|
||||
},
|
||||
"@peacockproject/core/candle/challengeHelpers": {
|
||||
__esModule: true,
|
||||
...challengeHelpers,
|
||||
},
|
||||
"@peacockproject/core/candle/challengeService": {
|
||||
__esModule: true,
|
||||
...challengeService,
|
||||
},
|
||||
"@peacockproject/core/candle/masteryService": {
|
||||
__esModule: true,
|
||||
...masteryService,
|
||||
},
|
||||
"@peacockproject/core/candle/progressionService": {
|
||||
__esModule: true,
|
||||
...progressionService,
|
||||
},
|
||||
"@peacockproject/core/contracts/contractRouting": {
|
||||
__esModule: true,
|
||||
...contractRouting,
|
||||
},
|
||||
"@peacockproject/core/contracts/contractsModeRouting": {
|
||||
__esModule: true,
|
||||
...contractsModeRouting,
|
||||
},
|
||||
"@peacockproject/core/contracts/dataGen": { __esModule: true, ...dataGen },
|
||||
"@peacockproject/core/contracts/elusiveTargetArcades": {
|
||||
__esModule: true,
|
||||
...elusiveTargetArcades,
|
||||
},
|
||||
"@peacockproject/core/contracts/elusiveTargets": {
|
||||
__esModule: true,
|
||||
...elusiveTargets,
|
||||
},
|
||||
"@peacockproject/core/contracts/hitsCategoryService": {
|
||||
__esModule: true,
|
||||
...hitsCategoryService,
|
||||
},
|
||||
"@peacockproject/core/contracts/leaderboards": {
|
||||
__esModule: true,
|
||||
...leaderboards,
|
||||
},
|
||||
"@peacockproject/core/contracts/missionsInLocation": {
|
||||
__esModule: true,
|
||||
...missionsInLocation,
|
||||
},
|
||||
"@peacockproject/core/contracts/sessions": {
|
||||
__esModule: true,
|
||||
...sessions,
|
||||
},
|
||||
"@peacockproject/core/discord/client": { __esModule: true, ...client },
|
||||
"@peacockproject/core/discord/ipc": { __esModule: true, ...ipc },
|
||||
"@peacockproject/core/livesplit/liveSplitClient": {
|
||||
__esModule: true,
|
||||
...liveSplitClient,
|
||||
},
|
||||
"@peacockproject/core/livesplit/liveSplitManager": {
|
||||
__esModule: true,
|
||||
...liveSplitManager,
|
||||
},
|
||||
"@peacockproject/core/menus/campaigns": { __esModule: true, ...campaigns },
|
||||
"@peacockproject/core/menus/destinations": {
|
||||
__esModule: true,
|
||||
...destinations,
|
||||
},
|
||||
"@peacockproject/core/menus/favoriteContracts": {
|
||||
__esModule: true,
|
||||
...favoriteContracts,
|
||||
},
|
||||
"@peacockproject/core/menus/hub": { __esModule: true, ...hub },
|
||||
"@peacockproject/core/menus/imageHandler": {
|
||||
__esModule: true,
|
||||
...imageHandler,
|
||||
},
|
||||
"@peacockproject/core/menus/menuSystem": {
|
||||
__esModule: true,
|
||||
...menuSystem,
|
||||
},
|
||||
"@peacockproject/core/menus/planning": { __esModule: true, ...planning },
|
||||
"@peacockproject/core/menus/playerProfile": {
|
||||
__esModule: true,
|
||||
...playerProfile,
|
||||
},
|
||||
"@peacockproject/core/menus/playnext": { __esModule: true, ...playnext },
|
||||
"@peacockproject/core/menus/sniper": { __esModule: true, ...sniper },
|
||||
"@peacockproject/core/menus/stashpoints": {
|
||||
__esModule: true,
|
||||
...stashpoints,
|
||||
},
|
||||
"@peacockproject/core/multiplayer/multiplayerMenuData": {
|
||||
__esModule: true,
|
||||
...multiplayerMenuData,
|
||||
},
|
||||
"@peacockproject/core/multiplayer/multiplayerService": {
|
||||
__esModule: true,
|
||||
...multiplayerService,
|
||||
},
|
||||
"@peacockproject/core/multiplayer/multiplayerUtils": {
|
||||
__esModule: true,
|
||||
...multiplayerUtils,
|
||||
},
|
||||
"@peacockproject/core/statemachines/contextListeners": {
|
||||
__esModule: true,
|
||||
...contextListeners,
|
||||
},
|
||||
"@peacockproject/core/statemachines/contractCreation": {
|
||||
__esModule: true,
|
||||
...contractCreation,
|
||||
},
|
||||
"@peacockproject/core/contracts/escalations/escalationService": {
|
||||
__esModule: true,
|
||||
...escalationService,
|
||||
},
|
||||
"@peacockproject/core/configSwizzleManager": configSwizzleManager,
|
||||
"@peacockproject/core/controller": controller,
|
||||
"@peacockproject/core/databaseHandler": databaseHandler,
|
||||
"@peacockproject/core/entitlementStrategies": entitlementStrategies,
|
||||
"@peacockproject/core/eventHandler": eventHandler,
|
||||
"@peacockproject/core/evergreen": evergreen,
|
||||
"@peacockproject/core/flags": flags,
|
||||
"@peacockproject/core/hooksImpl": hooksImpl,
|
||||
"@peacockproject/core/hotReloadService": hotReloadService,
|
||||
"@peacockproject/core/inventory": inventory,
|
||||
"@peacockproject/core/loadouts": loadouts,
|
||||
"@peacockproject/core/loggingInterop": loggingInterop,
|
||||
"@peacockproject/core/menuData": menuData,
|
||||
"@peacockproject/core/oauthToken": oauthToken,
|
||||
"@peacockproject/core/officialServerAuth": officialServerAuth,
|
||||
"@peacockproject/core/ownership": ownership,
|
||||
"@peacockproject/core/platformEntitlements": platformEntitlements,
|
||||
"@peacockproject/core/playStyles": playStyles,
|
||||
"@peacockproject/core/profileHandler": profileHandler,
|
||||
"@peacockproject/core/scoreHandler": scoreHandler,
|
||||
"@peacockproject/core/smfSupport": smfSupport,
|
||||
"@peacockproject/core/utils": utils,
|
||||
"@peacockproject/core/webFeatures": webFeatures,
|
||||
"@peacockproject/core/2016/legacyContractHandler": legacyContractHandler,
|
||||
"@peacockproject/core/2016/legacyMenuData": legacyMenuData,
|
||||
"@peacockproject/core/2016/legacyProfileRouter": legacyProfileRouter,
|
||||
"@peacockproject/core/candle/challengeHelpers": challengeHelpers,
|
||||
"@peacockproject/core/candle/challengeService": challengeService,
|
||||
"@peacockproject/core/candle/masteryService": masteryService,
|
||||
"@peacockproject/core/candle/progressionService": progressionService,
|
||||
"@peacockproject/core/contracts/contractRouting": contractRouting,
|
||||
"@peacockproject/core/contracts/contractsModeRouting": contractsModeRouting,
|
||||
"@peacockproject/core/contracts/dataGen": dataGen,
|
||||
"@peacockproject/core/contracts/elusiveTargetArcades": elusiveTargetArcades,
|
||||
"@peacockproject/core/contracts/elusiveTargets": elusiveTargets,
|
||||
"@peacockproject/core/contracts/hitsCategoryService": hitsCategoryService,
|
||||
"@peacockproject/core/contracts/leaderboards": leaderboards,
|
||||
"@peacockproject/core/contracts/missionsInLocation": missionsInLocation,
|
||||
"@peacockproject/core/contracts/sessions": sessions,
|
||||
"@peacockproject/core/discord/client": client,
|
||||
"@peacockproject/core/discord/discordRp": discordRp,
|
||||
"@peacockproject/core/discord/ipc": ipc,
|
||||
"@peacockproject/core/livesplit/liveSplitClient": liveSplitClient,
|
||||
"@peacockproject/core/livesplit/liveSplitManager": liveSplitManager,
|
||||
"@peacockproject/core/menus/campaigns": campaigns,
|
||||
"@peacockproject/core/menus/destinations": destinations,
|
||||
"@peacockproject/core/menus/favoriteContracts": favoriteContracts,
|
||||
"@peacockproject/core/menus/hub": hub,
|
||||
"@peacockproject/core/menus/imageHandler": imageHandler,
|
||||
"@peacockproject/core/menus/menuSystem": menuSystem,
|
||||
"@peacockproject/core/menus/planning": planning,
|
||||
"@peacockproject/core/menus/playerProfile": playerProfile,
|
||||
"@peacockproject/core/menus/playnext": playnext,
|
||||
"@peacockproject/core/menus/sniper": sniper,
|
||||
"@peacockproject/core/menus/stashpoints": stashpoints,
|
||||
"@peacockproject/core/multiplayer/multiplayerMenuData": multiplayerMenuData,
|
||||
"@peacockproject/core/multiplayer/multiplayerService": multiplayerService,
|
||||
"@peacockproject/core/multiplayer/multiplayerUtils": multiplayerUtils,
|
||||
"@peacockproject/core/statemachines/contextListeners": contextListeners,
|
||||
"@peacockproject/core/statemachines/contractCreation": contractCreation,
|
||||
"@peacockproject/core/contracts/escalations/escalationService":
|
||||
escalationService,
|
||||
}
|
||||
|
|
|
@ -16,15 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// load as soon as possible to prevent dependency issues
|
||||
import "./generatedPeacockRequireTable"
|
||||
|
||||
// load flags as soon as possible
|
||||
import { getFlag, loadFlags } from "./flags"
|
||||
|
||||
loadFlags()
|
||||
|
||||
import { program } from "commander"
|
||||
import express, { Request, Router } from "express"
|
||||
import http from "http"
|
||||
import {
|
||||
|
@ -33,7 +24,6 @@ import {
|
|||
handleAxiosError,
|
||||
IS_LAUNCHER,
|
||||
jokes,
|
||||
PEACOCKVER,
|
||||
PEACOCKVERSTRING,
|
||||
ServerVer,
|
||||
} from "./utils"
|
||||
|
@ -49,8 +39,7 @@ import type {
|
|||
S2CEventWithTimestamp,
|
||||
ServerConnectionConfig,
|
||||
} from "./types/types"
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs"
|
||||
import { join } from "path"
|
||||
import { readFileSync } from "fs"
|
||||
import {
|
||||
errorLoggingMiddleware,
|
||||
log,
|
||||
|
@ -73,7 +62,7 @@ import {
|
|||
import { legacyProfileRouter } from "./2016/legacyProfileRouter"
|
||||
import { legacyMenuDataRouter } from "./2016/legacyMenuData"
|
||||
import { legacyContractRouter } from "./2016/legacyContractHandler"
|
||||
import { initRp } from "./discordRp"
|
||||
import { initRp } from "./discord/discordRp"
|
||||
import random from "random"
|
||||
import { generateUserCentric } from "./contracts/dataGen"
|
||||
import { json as jsonMiddleware, urlencoded } from "body-parser"
|
||||
|
@ -82,15 +71,12 @@ import { setupHotListener } from "./hotReloadService"
|
|||
import type { AxiosError } from "axios"
|
||||
import serveStatic from "serve-static"
|
||||
import { webFeaturesRouter } from "./webFeatures"
|
||||
import { toolsMenu } from "./tools"
|
||||
import picocolors from "picocolors"
|
||||
import { multiplayerRouter } from "./multiplayer/multiplayerService"
|
||||
import { multiplayerMenuDataRouter } from "./multiplayer/multiplayerMenuData"
|
||||
import { pack, unpack } from "msgpackr"
|
||||
import { liveSplitManager } from "./livesplit/liveSplitManager"
|
||||
import { cheapLoadUserData } from "./databaseHandler"
|
||||
|
||||
loadFlags()
|
||||
import { cheapLoadUserData, setupFileStructure } from "./databaseHandler"
|
||||
import { getFlag } from "./flags"
|
||||
|
||||
const host = process.env.HOST || "0.0.0.0"
|
||||
const port = process.env.PORT || 80
|
||||
|
@ -115,6 +101,10 @@ function uncaught(error: Error): void {
|
|||
LogLevel.ERROR,
|
||||
` - Your user account doesn't have permission (firewall can block it)`,
|
||||
)
|
||||
log(
|
||||
LogLevel.INFO,
|
||||
`Check this wiki page: https://thepeacockproject.org/wiki/troubleshooting/fix-port-in-use for steps on how to fix this!`,
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
|
@ -317,7 +307,7 @@ app.use(
|
|||
break
|
||||
case "fghi4567xQOCheZIin0pazB47qGUvZw4":
|
||||
case STEAM_NAMESPACE_2021:
|
||||
req.serverVersion = "8-14"
|
||||
req.serverVersion = "8-15"
|
||||
break
|
||||
default:
|
||||
res.status(400).json({ message: "no game data" })
|
||||
|
@ -508,7 +498,7 @@ app.use(
|
|||
}
|
||||
|
||||
if (
|
||||
["6-74", "7-3", "7-17", "8-14"].includes(
|
||||
["6-74", "7-3", "7-17", "8-15"].includes(
|
||||
<string>req.serverVersion,
|
||||
)
|
||||
) {
|
||||
|
@ -527,11 +517,10 @@ app.all("*", (req, res) => {
|
|||
|
||||
app.use(errorLoggingMiddleware)
|
||||
|
||||
program.description(
|
||||
"The Peacock Project is a HITMAN™ World of Assassination Trilogy server replacement.",
|
||||
)
|
||||
|
||||
function startServer(options: { hmr: boolean; pluginDevHost: boolean }): void {
|
||||
export async function startServer(options: {
|
||||
hmr: boolean
|
||||
pluginDevHost: boolean
|
||||
}): Promise<void> {
|
||||
void checkForUpdates()
|
||||
|
||||
if (!IS_LAUNCHER) {
|
||||
|
@ -551,10 +540,9 @@ function startServer(options: { hmr: boolean; pluginDevHost: boolean }): void {
|
|||
|
||||
log(
|
||||
LogLevel.INFO,
|
||||
`This is Peacock v${PEACOCKVERSTRING} (rev ${PEACOCKVER}), with Node v${process.versions.node}.`,
|
||||
`This is Peacock v${PEACOCKVERSTRING} with Node v${process.versions.node}.`,
|
||||
)
|
||||
|
||||
// jokes lol
|
||||
if (getFlag("jokes") === true) {
|
||||
log(
|
||||
LogLevel.INFO,
|
||||
|
@ -564,49 +552,23 @@ function startServer(options: { hmr: boolean; pluginDevHost: boolean }): void {
|
|||
)
|
||||
}
|
||||
|
||||
try {
|
||||
// make sure required folder structure is in place
|
||||
for (const dir of [
|
||||
"contractSessions",
|
||||
"plugins",
|
||||
"userdata",
|
||||
"contracts",
|
||||
join("userdata", "epicids"),
|
||||
join("userdata", "steamids"),
|
||||
join("userdata", "users"),
|
||||
join("userdata", "h1", "steamids"),
|
||||
join("userdata", "h1", "epicids"),
|
||||
join("userdata", "h1", "users"),
|
||||
join("userdata", "h2", "steamids"),
|
||||
join("userdata", "h2", "users"),
|
||||
join("userdata", "scpc", "users"),
|
||||
join("userdata", "scpc", "steamids"),
|
||||
join("images", "actors"),
|
||||
join("images", "contracts"),
|
||||
join("images", "contracts", "elusive"),
|
||||
join("images", "contracts", "escalation"),
|
||||
join("images", "contracts", "featured"),
|
||||
join("images", "unlockables_override"),
|
||||
]) {
|
||||
if (existsSync(dir)) {
|
||||
continue
|
||||
}
|
||||
|
||||
log(LogLevel.DEBUG, `Creating missing directory ${dir}`)
|
||||
mkdirSync(dir, { recursive: true })
|
||||
}
|
||||
await setupFileStructure()
|
||||
|
||||
if (options.hmr) {
|
||||
log(LogLevel.DEBUG, "Experimental HMR enabled.")
|
||||
|
||||
void setupHotListener("contracts", () => {
|
||||
log(LogLevel.INFO, "Detected a change in contracts! Re-indexing...")
|
||||
log(
|
||||
LogLevel.INFO,
|
||||
"Detected a change in contracts! Re-indexing...",
|
||||
)
|
||||
controller.index()
|
||||
})
|
||||
}
|
||||
|
||||
// once contracts directory is present, we are clear to boot
|
||||
loadouts.init()
|
||||
void controller.boot(options.pluginDevHost)
|
||||
await loadouts.init()
|
||||
await controller.boot(options.pluginDevHost)
|
||||
|
||||
const httpServer = http.createServer(app)
|
||||
|
||||
|
@ -619,62 +581,11 @@ function startServer(options: { hmr: boolean; pluginDevHost: boolean }): void {
|
|||
}
|
||||
|
||||
// initialize livesplit
|
||||
void liveSplitManager.init()
|
||||
await liveSplitManager.init()
|
||||
|
||||
return
|
||||
} catch (e) {
|
||||
log(LogLevel.ERROR, "Critical error during bootstrap!")
|
||||
log(LogLevel.ERROR, e)
|
||||
}
|
||||
}
|
||||
|
||||
program.option(
|
||||
"--hmr",
|
||||
"enable experimental hot reloading of contracts",
|
||||
getFlag("experimentalHMR") as boolean,
|
||||
)
|
||||
program.option(
|
||||
"--plugin-dev-host",
|
||||
"activate plugin development features - requires plugin dev workspace setup",
|
||||
getFlag("developmentPluginDevHost") as boolean,
|
||||
)
|
||||
program.action(startServer)
|
||||
|
||||
program
|
||||
.command("tools")
|
||||
.description("open the tools UI")
|
||||
.action(() => {
|
||||
void toolsMenu()
|
||||
})
|
||||
|
||||
// noinspection RequiredAttributes
|
||||
program
|
||||
.command("pack")
|
||||
.argument("<input>", "input file to pack")
|
||||
.option("-o, --output <path>", "where to output the packed file to", "")
|
||||
.description("packs an input file into a Challenge Resource Package")
|
||||
.action((input, options: { output: string }) => {
|
||||
const outputPath =
|
||||
options.output || input.replace(/\.[^/\\.]+$/, ".crp")
|
||||
|
||||
writeFileSync(
|
||||
outputPath,
|
||||
pack(JSON.parse(readFileSync(input).toString())),
|
||||
)
|
||||
|
||||
log(LogLevel.INFO, `Packed "${input}" to "${outputPath}" successfully.`)
|
||||
})
|
||||
|
||||
// noinspection RequiredAttributes
|
||||
program
|
||||
.command("unpack")
|
||||
.argument("<input>", "input file to unpack")
|
||||
.option("-o, --output <path>", "where to output the unpacked file to", "")
|
||||
.description("unpacks a Challenge Resource Package")
|
||||
.action((input, options: { output: string }) => {
|
||||
const outputPath =
|
||||
options.output || input.replace(/\.[^/\\.]+$/, ".json")
|
||||
|
||||
writeFileSync(outputPath, JSON.stringify(unpack(readFileSync(input))))
|
||||
|
||||
log(
|
||||
LogLevel.INFO,
|
||||
`Unpacked "${input}" to "${outputPath}" successfully.`,
|
||||
)
|
||||
})
|
||||
|
||||
program.parse(process.argv)
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
H1_REQUIEM_UNLOCKABLES,
|
||||
H2_RACCOON_STINGRAY_UNLOCKABLES,
|
||||
MAKESHIFT_UNLOCKABLES,
|
||||
SAMBUCA_UNLOCKABLES,
|
||||
SIN_ENVY_UNLOCKABLES,
|
||||
SIN_GLUTTONY_UNLOCKABLES,
|
||||
SIN_GREED_UNLOCKABLES,
|
||||
|
@ -34,6 +35,7 @@ import {
|
|||
SIN_PRIDE_UNLOCKABLES,
|
||||
SIN_SLOTH_UNLOCKABLES,
|
||||
SIN_WRATH_UNLOCKABLES,
|
||||
SMART_CASUAL_UNLOCKABLES,
|
||||
TRINITY_UNLOCKABLES,
|
||||
WINTERSPORTS_UNLOCKABLES,
|
||||
} from "./ownership"
|
||||
|
@ -63,6 +65,7 @@ const DELUXE_DATA = [
|
|||
...SIN_WRATH_UNLOCKABLES,
|
||||
...TRINITY_UNLOCKABLES,
|
||||
...WINTERSPORTS_UNLOCKABLES,
|
||||
...SAMBUCA_UNLOCKABLES,
|
||||
]
|
||||
|
||||
/**
|
||||
|
@ -250,6 +253,15 @@ function filterAllowedContent(gameVersion: GameVersion, entP: string[]) {
|
|||
)
|
||||
}
|
||||
|
||||
if (SMART_CASUAL_UNLOCKABLES.includes(id)) {
|
||||
return (
|
||||
e.includes("6408de14f7dc46b9a33adcf6cbc4d159") ||
|
||||
e.includes("afa4b921503f43339c360d4b53910791") ||
|
||||
e.includes("84a1a6fda4fb48afbb78ee9b2addd475") || // WoA Deluxe
|
||||
e.includes("1829590")
|
||||
)
|
||||
}
|
||||
|
||||
if (H1_REQUIEM_UNLOCKABLES.includes(id)) {
|
||||
return (
|
||||
e.includes("e698e1a4b63947b0bc9349a5ae2dc015") ||
|
||||
|
@ -301,16 +313,6 @@ function filterAllowedContent(gameVersion: GameVersion, entP: string[]) {
|
|||
)
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: Fix this entitlement check (confirmed its broken with Blazer)
|
||||
if (LEGACY_UNLOCKABLES.includes(id)) {
|
||||
return (
|
||||
e.includes("0b59243cb8aa420691b66be1ecbe68c0") ||
|
||||
e.includes("1829593")
|
||||
)
|
||||
}
|
||||
*/
|
||||
|
||||
if (SIN_GREED_UNLOCKABLES.includes(id)) {
|
||||
return (
|
||||
e.includes("0e8632b4cdfb415e94291d97d727b98d") ||
|
||||
|
@ -374,7 +376,6 @@ function filterAllowedContent(gameVersion: GameVersion, entP: string[]) {
|
|||
)
|
||||
}
|
||||
|
||||
// The following two must be confirmed, epic entitlements may be in the wrong order! - AF
|
||||
if (MAKESHIFT_UNLOCKABLES.includes(id)) {
|
||||
return (
|
||||
e.includes("08d2bc4d20754191b6c488541d2b4fa1") ||
|
||||
|
@ -389,6 +390,13 @@ function filterAllowedContent(gameVersion: GameVersion, entP: string[]) {
|
|||
)
|
||||
}
|
||||
|
||||
if (SAMBUCA_UNLOCKABLES.includes(id)) {
|
||||
return (
|
||||
e.includes("9220c020262f420da06eb46a4b1ce86f") ||
|
||||
e.includes("2828470")
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,10 +19,10 @@
|
|||
import { LiveSplitClient, LiveSplitResult } from "./liveSplitClient"
|
||||
import { log, LogLevel } from "../loggingInterop"
|
||||
import { getAllCampaigns } from "../menus/campaigns"
|
||||
import { Campaign, GameVersion, IHit, Seconds, StoryData } from "../types/types"
|
||||
import { Campaign, GameVersion, Hit, Seconds, StoryData } from "../types/types"
|
||||
import { getFlag } from "../flags"
|
||||
import { controller } from "../controller"
|
||||
import { scenePathToRpAsset } from "../discordRp"
|
||||
import { scenePathToRpAsset } from "../discord/discordRp"
|
||||
import { LiveSplitTimeCalcEntry } from "../types/livesplit"
|
||||
import assert from "assert"
|
||||
|
||||
|
@ -593,7 +593,7 @@ function getCampaignMissions(
|
|||
const campaignMissionIds = (campaignStoryData: StoryData[]): string[] => {
|
||||
return campaignStoryData
|
||||
.filter((data) => data.Type === "Mission")
|
||||
.map((sd) => (sd.Data as IHit).Id)
|
||||
.map((sd) => (sd.Data as Hit).Id)
|
||||
}
|
||||
|
||||
// the trilogy is the only place where multiple campaigns are merged together
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { existsSync, readFileSync, writeFileSync } from "fs"
|
||||
import type {
|
||||
GameVersion,
|
||||
Loadout,
|
||||
|
@ -25,9 +24,10 @@ import type {
|
|||
} from "./types/types"
|
||||
import { Request, Router } from "express"
|
||||
import { json as jsonMiddleware } from "body-parser"
|
||||
import { writeFile } from "atomically"
|
||||
import { writeFile } from "fs/promises"
|
||||
import { nanoid } from "nanoid"
|
||||
import { versions } from "./utils"
|
||||
import { asyncGuard } from "./databaseHandler"
|
||||
|
||||
const LOADOUT_PROFILES_FILE = "userdata/users/lop.json"
|
||||
|
||||
|
@ -50,47 +50,33 @@ const defaultValue: LoadoutFile = {
|
|||
* A class for managing loadouts.
|
||||
*/
|
||||
export class Loadouts {
|
||||
private _loadouts!: LoadoutFile
|
||||
|
||||
/**
|
||||
* Get the loadouts data.
|
||||
*
|
||||
* @returns The loadouts data.
|
||||
*/
|
||||
public get loadouts(): LoadoutFile {
|
||||
return this._loadouts
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutate the current LoadoutFile object.
|
||||
*
|
||||
* @internal Intended for internal use only.
|
||||
* @param newValue The object after the mutation.
|
||||
*/
|
||||
set loadouts(newValue: LoadoutFile) {
|
||||
this._loadouts = newValue
|
||||
}
|
||||
loadouts!: LoadoutFile
|
||||
|
||||
/**
|
||||
* Initializes the loadouts manager.
|
||||
*/
|
||||
public init(): void {
|
||||
if (!existsSync(LOADOUT_PROFILES_FILE)) {
|
||||
this._loadouts = defaultValue
|
||||
async init(): Promise<void> {
|
||||
const fs = asyncGuard.getFs()
|
||||
|
||||
writeFileSync(LOADOUT_PROFILES_FILE, JSON.stringify(defaultValue))
|
||||
if (!(await fs.exists(LOADOUT_PROFILES_FILE))) {
|
||||
this.loadouts = defaultValue
|
||||
|
||||
await fs.writeFile(
|
||||
LOADOUT_PROFILES_FILE,
|
||||
JSON.stringify(defaultValue),
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
this._loadouts = JSON.parse(
|
||||
readFileSync(LOADOUT_PROFILES_FILE).toString(),
|
||||
this.loadouts = JSON.parse(
|
||||
(await fs.readFile(LOADOUT_PROFILES_FILE)).toString(),
|
||||
)
|
||||
|
||||
let dirty = false
|
||||
|
||||
// make sure they all have IDs
|
||||
for (const gameVersion of versions) {
|
||||
for (const loadout of this._loadouts[gameVersion].loadouts) {
|
||||
for (const loadout of this.loadouts[gameVersion].loadouts) {
|
||||
if (!loadout.id) {
|
||||
dirty = true
|
||||
loadout.id = nanoid()
|
||||
|
@ -99,26 +85,28 @@ export class Loadouts {
|
|||
|
||||
// if the selected value is null/undefined or is not length 0 or 21, it's not a valid id
|
||||
if (
|
||||
!this._loadouts[gameVersion].selected ||
|
||||
!this.loadouts[gameVersion].selected ||
|
||||
// first condition ensures selected is truthy, but TS doesn't know
|
||||
![0, 21].includes(
|
||||
this._loadouts[gameVersion].selected?.length || -1,
|
||||
this.loadouts[gameVersion].selected?.length || -1,
|
||||
)
|
||||
) {
|
||||
dirty = true
|
||||
|
||||
// long story short: find a loadout with a name matching the selected value,
|
||||
// and if found, set selected to the id
|
||||
this._loadouts[gameVersion].selected =
|
||||
this._loadouts[gameVersion].loadouts.find(
|
||||
(lo) =>
|
||||
lo.name === this._loadouts[gameVersion].selected,
|
||||
this.loadouts[gameVersion].selected =
|
||||
this.loadouts[gameVersion].loadouts.find(
|
||||
(lo) => lo.name === this.loadouts[gameVersion].selected,
|
||||
)?.id || ""
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty) {
|
||||
writeFileSync(LOADOUT_PROFILES_FILE, JSON.stringify(this._loadouts))
|
||||
await writeFile(
|
||||
LOADOUT_PROFILES_FILE,
|
||||
JSON.stringify(this.loadouts),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,7 +117,7 @@ export class Loadouts {
|
|||
* @param name The optional name for the new loadout set, defaults to "Unnamed loadout set".
|
||||
* @returns The Loadout object.
|
||||
*/
|
||||
public createDefault(
|
||||
createDefault(
|
||||
gameVersion: GameVersion,
|
||||
name = "Unnamed loadout set",
|
||||
): Loadout {
|
||||
|
@ -143,8 +131,8 @@ export class Loadouts {
|
|||
data: {},
|
||||
}
|
||||
|
||||
this._loadouts[gameVersion].loadouts.push(l)
|
||||
this._loadouts[gameVersion].selected = l.id
|
||||
this.loadouts[gameVersion].loadouts.push(l)
|
||||
this.loadouts[gameVersion].selected = l.id
|
||||
|
||||
return l
|
||||
}
|
||||
|
@ -155,12 +143,12 @@ export class Loadouts {
|
|||
* @param gameVersion The game version.
|
||||
* @returns The loadout profile or undefined if one isn't selected or none exist.
|
||||
*/
|
||||
public getLoadoutFor(gameVersion: GameVersion): Loadout | undefined {
|
||||
getLoadoutFor(gameVersion: GameVersion): Loadout | undefined {
|
||||
if (gameVersion === "scpc") {
|
||||
gameVersion = "h1"
|
||||
}
|
||||
|
||||
const theLoadouts = this._loadouts[gameVersion] as LoadoutsGameVersion
|
||||
const theLoadouts = this.loadouts[gameVersion] as LoadoutsGameVersion
|
||||
return theLoadouts.loadouts.find((s) => s.id === theLoadouts.selected)
|
||||
}
|
||||
|
||||
|
@ -168,12 +156,13 @@ export class Loadouts {
|
|||
* Saves the loadout data to the Peacock userdata/users folder.
|
||||
*/
|
||||
public async save(): Promise<void> {
|
||||
await writeFile(LOADOUT_PROFILES_FILE, JSON.stringify(this._loadouts))
|
||||
await writeFile(LOADOUT_PROFILES_FILE, JSON.stringify(this.loadouts))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A synthetic default bind to the global Loadouts instance.
|
||||
* @todo Move this somewhere that makes more sense with a dependency injection model.
|
||||
*/
|
||||
export const loadouts = new Loadouts()
|
||||
|
||||
|
|
|
@ -95,28 +95,46 @@ import { getPlayerProfileData } from "./menus/playerProfile"
|
|||
|
||||
const menuDataRouter = Router()
|
||||
|
||||
// We make this lookup table to quickly get it, there's no other quick way for it.
|
||||
export const SNIPER_UNLOCK_TO_LOCATION: Record<string, string> = {
|
||||
FIREARMS_SC_HERO_SNIPER_HM: "LOCATION_PARENT_AUSTRIA",
|
||||
FIREARMS_SC_HERO_SNIPER_KNIGHT: "LOCATION_PARENT_AUSTRIA",
|
||||
FIREARMS_SC_HERO_SNIPER_STONE: "LOCATION_PARENT_AUSTRIA",
|
||||
FIREARMS_SC_SEAGULL_HM: "LOCATION_PARENT_SALTY",
|
||||
FIREARMS_SC_SEAGULL_KNIGHT: "LOCATION_PARENT_SALTY",
|
||||
FIREARMS_SC_SEAGULL_STONE: "LOCATION_PARENT_SALTY",
|
||||
FIREARMS_SC_FALCON_HM: "LOCATION_PARENT_CAGED",
|
||||
FIREARMS_SC_FALCON_KNIGHT: "LOCATION_PARENT_CAGED",
|
||||
FIREARMS_SC_FALCON_STONE: "LOCATION_PARENT_CAGED",
|
||||
}
|
||||
|
||||
// /profiles/page/
|
||||
|
||||
menuDataRouter.get(
|
||||
"/ChallengeLocation",
|
||||
// @ts-expect-error Jwt props.
|
||||
(req: RequestWithJwt<ChallengeLocationQuery>, res) => {
|
||||
const pack = controller.challengeService.challengePacks.get(
|
||||
req.query.locationId,
|
||||
)
|
||||
|
||||
const location = getVersionedConfig<PeacockLocationsData>(
|
||||
"LocationsData",
|
||||
req.gameVersion,
|
||||
true,
|
||||
).children[req.query.locationId]
|
||||
|
||||
if (!location) {
|
||||
if (!pack && !location) {
|
||||
res.status(400).send("Invalid locationId")
|
||||
return
|
||||
}
|
||||
|
||||
const data = {
|
||||
Name: location.DisplayNameLocKey,
|
||||
Name: pack ? pack.Name : location.DisplayNameLocKey,
|
||||
Location: location,
|
||||
Children: controller.challengeService.getChallengeDataForLocation(
|
||||
req.query.locationId,
|
||||
Children: controller.challengeService.getChallengeDataForCategory(
|
||||
pack ? req.query.locationId : null,
|
||||
pack ? undefined : location,
|
||||
req.gameVersion,
|
||||
req.jwt.unique_name,
|
||||
),
|
||||
|
@ -1395,25 +1413,12 @@ menuDataRouter.get(
|
|||
"/GetMasteryCompletionDataForUnlockable",
|
||||
// @ts-expect-error Has jwt props.
|
||||
(req: RequestWithJwt<GetMasteryCompletionDataForUnlockableQuery>, res) => {
|
||||
// We make this lookup table to quickly get it, there's no other quick way for it.
|
||||
const unlockToLoc: Record<string, string> = {
|
||||
FIREARMS_SC_HERO_SNIPER_HM: "LOCATION_PARENT_AUSTRIA",
|
||||
FIREARMS_SC_HERO_SNIPER_KNIGHT: "LOCATION_PARENT_AUSTRIA",
|
||||
FIREARMS_SC_HERO_SNIPER_STONE: "LOCATION_PARENT_AUSTRIA",
|
||||
FIREARMS_SC_SEAGULL_HM: "LOCATION_PARENT_SALTY",
|
||||
FIREARMS_SC_SEAGULL_KNIGHT: "LOCATION_PARENT_SALTY",
|
||||
FIREARMS_SC_SEAGULL_STONE: "LOCATION_PARENT_SALTY",
|
||||
FIREARMS_SC_FALCON_HM: "LOCATION_PARENT_CAGED",
|
||||
FIREARMS_SC_FALCON_KNIGHT: "LOCATION_PARENT_CAGED",
|
||||
FIREARMS_SC_FALCON_STONE: "LOCATION_PARENT_CAGED",
|
||||
}
|
||||
|
||||
res.json({
|
||||
template: null,
|
||||
data: {
|
||||
CompletionData: controller.masteryService.getLocationCompletion(
|
||||
unlockToLoc[req.query.unlockableId],
|
||||
unlockToLoc[req.query.unlockableId],
|
||||
SNIPER_UNLOCK_TO_LOCATION[req.query.unlockableId],
|
||||
SNIPER_UNLOCK_TO_LOCATION[req.query.unlockableId],
|
||||
req.gameVersion,
|
||||
req.jwt.unique_name,
|
||||
"sniper",
|
||||
|
|
|
@ -23,7 +23,7 @@ import type {
|
|||
GenSingleMissionFunc,
|
||||
CampaignMission,
|
||||
CampaignVideo,
|
||||
IVideo,
|
||||
Video,
|
||||
StoryData,
|
||||
} from "../types/types"
|
||||
import { log, LogLevel } from "../loggingInterop"
|
||||
|
@ -65,7 +65,7 @@ function genSingleVideo(
|
|||
videoId: string,
|
||||
gameVersion: GameVersion,
|
||||
): CampaignVideo {
|
||||
const videos = getConfig<Record<string, IVideo>>("Videos", true) // we modify videos so we need to clone this
|
||||
const videos = getConfig<Record<string, Video>>("Videos", true) // we modify videos so we need to clone this
|
||||
const video = videos[videoId]
|
||||
|
||||
switch (gameVersion) {
|
||||
|
|
|
@ -23,7 +23,7 @@ import type {
|
|||
CompletionData,
|
||||
GameLocationsData,
|
||||
GameVersion,
|
||||
IHit,
|
||||
Hit,
|
||||
MissionStory,
|
||||
OpportunityStatistics,
|
||||
PeacockLocationsData,
|
||||
|
@ -68,13 +68,13 @@ type GameFacingDestination = {
|
|||
type LocationMissionData = {
|
||||
Location: Unlockable
|
||||
SubLocation: Unlockable
|
||||
Missions: IHit[]
|
||||
SarajevoSixMissions: IHit[]
|
||||
ElusiveMissions: IHit[]
|
||||
EscalationMissions: IHit[]
|
||||
SniperMissions: IHit[]
|
||||
PlaceholderMissions: IHit[]
|
||||
CampaignMissions: IHit[]
|
||||
Missions: Hit[]
|
||||
SarajevoSixMissions: Hit[]
|
||||
ElusiveMissions: Hit[]
|
||||
EscalationMissions: Hit[]
|
||||
SniperMissions: Hit[]
|
||||
PlaceholderMissions: Hit[]
|
||||
CampaignMissions: Hit[]
|
||||
CompletionData: CompletionData
|
||||
}
|
||||
|
||||
|
@ -409,9 +409,7 @@ export function getDestination(
|
|||
}
|
||||
}
|
||||
|
||||
if (PEACOCK_DEV) {
|
||||
log(LogLevel.DEBUG, `Looking up locations details for ${LOCATION}.`)
|
||||
}
|
||||
|
||||
const sublocationsData = Object.values(locData.children).filter(
|
||||
(subLocation) => subLocation.Properties.ParentLocation === LOCATION,
|
||||
|
@ -425,7 +423,7 @@ export function getDestination(
|
|||
SubLocation: locationData,
|
||||
Missions: [controller.missionsInLocations.pro1[LOCATION as Cast]]
|
||||
.map((id) => contractIdToHitObject(id, gameVersion, userId))
|
||||
.filter(Boolean) as IHit[],
|
||||
.filter(Boolean) as Hit[],
|
||||
SarajevoSixMissions: [],
|
||||
ElusiveMissions: [],
|
||||
EscalationMissions: [],
|
||||
|
@ -447,7 +445,7 @@ export function getDestination(
|
|||
for (const e of sublocationsData) {
|
||||
log(LogLevel.DEBUG, `Looking up sublocation details for ${e.Id}`)
|
||||
|
||||
const escalations: IHit[] = []
|
||||
const escalations: Hit[] = []
|
||||
|
||||
type ECast = keyof typeof controller.missionsInLocations.escalations
|
||||
// every unique escalation from the sublocation
|
||||
|
@ -476,7 +474,7 @@ export function getDestination(
|
|||
}
|
||||
}
|
||||
|
||||
const sniperMissions: IHit[] = []
|
||||
const sniperMissions: Hit[] = []
|
||||
type SCast = keyof typeof controller.missionsInLocations.sniper
|
||||
|
||||
for (const sniperMission of controller.missionsInLocations.sniper[
|
||||
|
|
|
@ -17,12 +17,13 @@
|
|||
*/
|
||||
|
||||
import type {
|
||||
ChallengeCompletion,
|
||||
CompletionData,
|
||||
GameVersion,
|
||||
PeacockLocationsData,
|
||||
Unlockable,
|
||||
} from "../types/types"
|
||||
import { swapToBrowsingMenusStatus } from "../discordRp"
|
||||
import { swapToBrowsingMenusStatus } from "../discord/discordRp"
|
||||
import { getUserData } from "../databaseHandler"
|
||||
import { controller } from "../controller"
|
||||
import { contractCreationTutorialId, getMaxProfileLevel } from "../utils"
|
||||
|
@ -53,7 +54,32 @@ type CareerEntryChild = {
|
|||
ImageLocked: string
|
||||
RequiredResources: string[]
|
||||
IsPack?: boolean
|
||||
CompletionData: CompletionData
|
||||
CompletionData: CompletionData | Record<string, never>
|
||||
}
|
||||
|
||||
function generateCareerEntryChild(
|
||||
location: Unlockable,
|
||||
completion: ChallengeCompletion,
|
||||
categoryId: string,
|
||||
completionData?: CompletionData,
|
||||
): CareerEntryChild {
|
||||
const pack = controller.challengeService.challengePacks.get(categoryId)
|
||||
|
||||
return {
|
||||
IsLocked: Boolean(location.Properties.IsLocked),
|
||||
Name: pack ? pack.Name : location.DisplayNameLocKey,
|
||||
Image: pack ? pack.Image : location.Properties.Icon || "",
|
||||
Icon: pack ? pack.Icon : location.Type,
|
||||
CompletedChallengesCount: completion.CompletedChallengesCount,
|
||||
ChallengesCount: completion.ChallengesCount,
|
||||
CategoryId: categoryId,
|
||||
Description: pack ? pack.Description : `UI_${categoryId}_PRIMARY_DESC`,
|
||||
Location: location,
|
||||
ImageLocked: location.Properties.LockedIcon || "",
|
||||
RequiredResources: location.Properties.RequiredResources || [],
|
||||
IsPack: pack !== undefined,
|
||||
CompletionData: completionData || {},
|
||||
}
|
||||
}
|
||||
|
||||
export function getHubData(gameVersion: GameVersion, userId: string) {
|
||||
|
@ -71,17 +97,33 @@ export function getHubData(gameVersion: GameVersion, userId: string) {
|
|||
gameVersion,
|
||||
true,
|
||||
)
|
||||
const career: Record<string, CareerEntry> =
|
||||
gameVersion === "h3"
|
||||
? {}
|
||||
: {
|
||||
// TODO: Add data on elusive challenges. They are only shown on the Career->Challenges page for H1 and H2. They are not supported by Peacock as of v6.0.0.
|
||||
ELUSIVES_UNSUPPORTED: {
|
||||
Children: [],
|
||||
Name: "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
Location:
|
||||
|
||||
const career: Record<string, CareerEntry> = {}
|
||||
|
||||
for (const [
|
||||
id,
|
||||
pack,
|
||||
] of controller.challengeService.challengePacks.entries()) {
|
||||
if (!pack.GameVersions.includes(gameVersion)) continue
|
||||
|
||||
career[id] = {
|
||||
Children: [
|
||||
generateCareerEntryChild(
|
||||
locations.parents["LOCATION_PARENT_ICA_FACILITY"],
|
||||
},
|
||||
controller.challengeService.countTotalNCompletedChallenges(
|
||||
controller.challengeService.getChallengesForGroup(
|
||||
id,
|
||||
gameVersion,
|
||||
),
|
||||
userId,
|
||||
gameVersion,
|
||||
),
|
||||
id,
|
||||
),
|
||||
],
|
||||
Name: pack.Name,
|
||||
Location: locations.parents["LOCATION_PARENT_ICA_FACILITY"],
|
||||
}
|
||||
}
|
||||
|
||||
const masteryData = []
|
||||
|
@ -168,22 +210,14 @@ export function getHubData(gameVersion: GameVersion, userId: string) {
|
|||
gameVersion,
|
||||
)
|
||||
|
||||
career[parent!]?.Children.push({
|
||||
IsLocked: Boolean(location.Properties.IsLocked),
|
||||
Name: location.DisplayNameLocKey,
|
||||
Image: location.Properties.Icon || "",
|
||||
Icon: location.Type, // should be "location" for all locations
|
||||
CompletedChallengesCount:
|
||||
challengeCompletion.CompletedChallengesCount,
|
||||
ChallengesCount: challengeCompletion.ChallengesCount,
|
||||
CategoryId: child,
|
||||
Description: `UI_${child}_PRIMARY_DESC`,
|
||||
Location: location,
|
||||
ImageLocked: location.Properties.LockedIcon || "",
|
||||
RequiredResources: location.Properties.RequiredResources || [],
|
||||
IsPack: false, // should be false for all locations
|
||||
CompletionData: generateCompletionData(child, userId, gameVersion),
|
||||
})
|
||||
career[parent!]?.Children.push(
|
||||
generateCareerEntryChild(
|
||||
location,
|
||||
challengeCompletion,
|
||||
child,
|
||||
generateCompletionData(child, userId, gameVersion),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -21,7 +21,7 @@ import serveStatic from "serve-static"
|
|||
import { join } from "path"
|
||||
import md5File from "md5-file"
|
||||
import { getConfig } from "../configSwizzleManager"
|
||||
import { readFile } from "atomically"
|
||||
import { readFile } from "fs/promises"
|
||||
import { GameVersion, RequestWithJwt } from "../types/types"
|
||||
import { log, LogLevel } from "../loggingInterop"
|
||||
import { imageFetchingMiddleware } from "./imageHandler"
|
||||
|
@ -33,7 +33,7 @@ import { SyncBailHook, SyncHook } from "../hooksImpl"
|
|||
const menuSystemPreRouter = Router()
|
||||
const menuSystemRouter = Router()
|
||||
|
||||
// /resources-8-14/
|
||||
// /resources-8-15/
|
||||
|
||||
/**
|
||||
* A class for managing the menu system's fetched JSON data.
|
||||
|
|
|
@ -154,6 +154,8 @@ export async function getPlanningData(
|
|||
const escalationGroupId =
|
||||
contractData.Metadata.InGroup ?? contractData.Metadata.Id
|
||||
|
||||
controller.hooks.onEscalationReset.call(escalationGroupId)
|
||||
|
||||
resetUserEscalationProgress(userData, escalationGroupId)
|
||||
|
||||
writeUserData(userId, gameVersion)
|
||||
|
|
|
@ -30,6 +30,13 @@ import { getDestinationCompletion } from "./destinations"
|
|||
import { getUserData } from "../databaseHandler"
|
||||
import { isSniperLocation } from "../utils"
|
||||
|
||||
type XpData = {
|
||||
[location: string]: {
|
||||
Xp: number
|
||||
ActionXp: number
|
||||
}
|
||||
}
|
||||
|
||||
export function getPlayerProfileData(
|
||||
gameVersion: GameVersion,
|
||||
userId: string,
|
||||
|
@ -47,7 +54,27 @@ export function getPlayerProfileData(
|
|||
|
||||
playerProfilePage.SubLocationData = []
|
||||
|
||||
const userProfile = getUserData(userId, gameVersion)
|
||||
const subLocationMap =
|
||||
userProfile.Extensions.progression.PlayerProfileXP.Sublocations
|
||||
const xpData: XpData = {}
|
||||
|
||||
for (const subLocationKey in locationData.children) {
|
||||
const subLocation = locationData.children[subLocationKey]
|
||||
const parentLocation =
|
||||
locationData.parents[subLocation.Properties.ParentLocation || ""]
|
||||
|
||||
// We find all sublocations and add their XP for the season data
|
||||
const subLocationData = subLocationMap[subLocationKey]
|
||||
|
||||
xpData[parentLocation.Id] ??= {
|
||||
Xp: 0,
|
||||
ActionXp: 0,
|
||||
}
|
||||
|
||||
xpData[parentLocation.Id].Xp += subLocationData?.Xp ?? 0
|
||||
xpData[parentLocation.Id].ActionXp += subLocationData?.ActionXp ?? 0
|
||||
|
||||
// Ewww...
|
||||
if (
|
||||
subLocationKey === "LOCATION_ICA_FACILITY_ARRIVAL" ||
|
||||
|
@ -56,10 +83,6 @@ export function getPlayerProfileData(
|
|||
continue
|
||||
}
|
||||
|
||||
const subLocation = locationData.children[subLocationKey]
|
||||
const parentLocation =
|
||||
locationData.parents[subLocation.Properties.ParentLocation || ""]
|
||||
|
||||
const completionData = generateCompletionData(
|
||||
subLocation.Id,
|
||||
userId,
|
||||
|
@ -109,24 +132,17 @@ export function getPlayerProfileData(
|
|||
})
|
||||
}
|
||||
|
||||
const userProfile = getUserData(userId, gameVersion)
|
||||
playerProfilePage.PlayerProfileXp.Total =
|
||||
userProfile.Extensions.progression.PlayerProfileXP.Total
|
||||
playerProfilePage.PlayerProfileXp.Level =
|
||||
userProfile.Extensions.progression.PlayerProfileXP.ProfileLevel
|
||||
|
||||
const subLocationMap = new Map(
|
||||
userProfile.Extensions.progression.PlayerProfileXP.Sublocations.map(
|
||||
(obj) => [obj.Location, obj],
|
||||
),
|
||||
)
|
||||
|
||||
for (const season of playerProfilePage.PlayerProfileXp.Seasons) {
|
||||
for (const location of season.Locations) {
|
||||
const subLocationData = subLocationMap.get(location.LocationId)
|
||||
const locationData = xpData[location.LocationId]
|
||||
|
||||
location.Xp = subLocationData?.Xp || 0
|
||||
location.ActionXp = subLocationData?.ActionXp || 0
|
||||
location.Xp = locationData?.Xp || 0
|
||||
location.ActionXp = locationData?.ActionXp || 0
|
||||
|
||||
if (
|
||||
location.LocationProgression &&
|
||||
|
|
|
@ -26,7 +26,6 @@ import type { GameVersion } from "./types/types"
|
|||
|
||||
/**
|
||||
* Creates the body for the authentication request (urlencoded format).
|
||||
*
|
||||
* @param params The parameters object.
|
||||
* @returns The urlencoded body.
|
||||
*/
|
||||
|
@ -46,7 +45,7 @@ function createUrlencodedBody(params: Record<string, string>): string {
|
|||
|
||||
const requestHeadersH3 = {
|
||||
"User-agent": "G2 Http/1.0 (Windows NT 10.0; DX12/1; d3d12/1)",
|
||||
Version: "8.14.0",
|
||||
Version: "8.15.0",
|
||||
}
|
||||
|
||||
const requestHeadersH2 = {
|
||||
|
@ -79,24 +78,24 @@ export class OfficialServerAuth {
|
|||
|
||||
/**
|
||||
* Kick things off.
|
||||
*
|
||||
* @param gameVersion The game version.
|
||||
* @param gameAuthToken The token for the 3rd party game provider (steam or epic).
|
||||
* @param gameAuthToken The token for the 3rd party game provider (Steam or Epic).
|
||||
*/
|
||||
constructor(gameVersion: GameVersion, gameAuthToken: string) {
|
||||
this._gameAuthToken = gameAuthToken
|
||||
this._headers =
|
||||
gameVersion === "h1"
|
||||
? requestHeadersH1
|
||||
: gameVersion === "h2"
|
||||
? requestHeadersH2
|
||||
: requestHeadersH3
|
||||
this._headers = requestHeadersH1
|
||||
|
||||
if (gameVersion === "h2") {
|
||||
this._headers = requestHeadersH2
|
||||
} else if (gameVersion === "h3") {
|
||||
this._headers = requestHeadersH3
|
||||
}
|
||||
|
||||
this.initialized = false
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticates the client with the official service the first time.
|
||||
*
|
||||
* @param req The initial client request.
|
||||
*/
|
||||
async _initiallyAuthenticate(req: Request): Promise<void> {
|
||||
|
@ -119,7 +118,6 @@ export class OfficialServerAuth {
|
|||
|
||||
/**
|
||||
* Makes a request with the required context.
|
||||
*
|
||||
* @param url The URL to fetch.
|
||||
* @param get If the request should be a GET (true), or POST (false).
|
||||
* @param body The request's body (defaults to {}).
|
||||
|
@ -181,7 +179,6 @@ export class OfficialServerAuth {
|
|||
|
||||
/**
|
||||
* Authenticate for the first time.
|
||||
*
|
||||
* @param req The request from the Hitman client connecting to Peacock.
|
||||
* @returns The token data fetched from the official servers.
|
||||
*/
|
||||
|
|
|
@ -170,6 +170,12 @@ export const EXECUTIVE_UNLOCKABLES = [
|
|||
"PROP_CONTAINER_SUITCASE_ICA_DELUXE",
|
||||
]
|
||||
|
||||
export const SMART_CASUAL_UNLOCKABLES = [
|
||||
"TOKEN_OUTFIT_URBAN_CLASSIC",
|
||||
"PROP_DEVICE_ICA_REMOTE_FLASH_PHONE",
|
||||
"PROP_MELEE_BLACK_PHONE_CORD",
|
||||
]
|
||||
|
||||
export const DELUXE_UNLOCKABLES = [
|
||||
// dubai
|
||||
"PROP_CONTAINER_SUITCASE_GOLDEN",
|
||||
|
@ -271,6 +277,13 @@ export const CONCRETEART_UNLOCKABLES = [
|
|||
"TOKEN_OUTFIT_HERO_CONCRETEART",
|
||||
]
|
||||
|
||||
export const SAMBUCA_UNLOCKABLES = [
|
||||
"TOKEN_OUTFIT_REWARD_HERO_SB_PATCH_SUIT",
|
||||
"PROP_EXPLOSIVE_PEN_SAMBUCA",
|
||||
"PROP_GADGET_ROBOT_FLASH_SAMBUCA",
|
||||
"PROP_MELEE_BLACK_PHONE_CORD_SAMBUCA",
|
||||
]
|
||||
|
||||
export const brokenItems = [
|
||||
// duped dart gun (thanks IOI)
|
||||
"835ad050-6d19-4e94-80b1-f5cec9815ba3",
|
||||
|
|
|
@ -81,6 +81,8 @@ export const H3_EPIC_ENTITLEMENTS = [
|
|||
"a1e9a63fa4f3425aa66b9b8fa3c9cc35",
|
||||
// THESARAJEVOSIX:
|
||||
"28455871cd0d4ffab52f557cc012ea5e",
|
||||
// SAMBUCA:
|
||||
"9220c020262f420da06eb46a4b1ce86f",
|
||||
]
|
||||
|
||||
export const H2_STEAM_ENTITLEMENTS = [
|
||||
|
@ -138,9 +140,7 @@ export function getPlatformEntitlements(
|
|||
req: RequestWithJwt,
|
||||
res: Response,
|
||||
): void {
|
||||
if (PEACOCK_DEV) {
|
||||
log(LogLevel.DEBUG, `Platform issuer: ${req.body.issuerId}`)
|
||||
}
|
||||
|
||||
const exts = getUserData(req.jwt.unique_name, req.gameVersion).Extensions
|
||||
.entP
|
||||
|
|
|
@ -58,7 +58,11 @@ import {
|
|||
compileRuntimeChallenge,
|
||||
inclusionDataCheck,
|
||||
} from "./candle/challengeHelpers"
|
||||
import { LoadSaveBody, ResolveGamerTagsBody } from "./types/gameSchemas"
|
||||
import {
|
||||
GetChallengeProgressionBody,
|
||||
LoadSaveBody,
|
||||
ResolveGamerTagsBody,
|
||||
} from "./types/gameSchemas"
|
||||
import assert from "assert"
|
||||
|
||||
const profileRouter = Router()
|
||||
|
@ -285,7 +289,6 @@ export async function resolveProfiles(
|
|||
Gamertag: null,
|
||||
DevId: "IOI",
|
||||
SteamId: null,
|
||||
StadiaId: null,
|
||||
EpicId: null,
|
||||
NintendoId: null,
|
||||
XboxLiveId: null,
|
||||
|
@ -312,7 +315,6 @@ export async function resolveProfiles(
|
|||
fakePlayer.platform === "steam"
|
||||
? fakePlayer.platformId
|
||||
: null,
|
||||
StadiaId: null,
|
||||
EpicId:
|
||||
fakePlayer.platform === "epic"
|
||||
? fakePlayer.platformId
|
||||
|
@ -579,6 +581,54 @@ profileRouter.post(
|
|||
},
|
||||
)
|
||||
|
||||
profileRouter.post(
|
||||
"/ChallengesService/GetProgression",
|
||||
jsonMiddleware(),
|
||||
// @ts-expect-error Has jwt props.
|
||||
(req: RequestWithJwt<never, GetChallengeProgressionBody>, res) => {
|
||||
if (!Array.isArray(req.body.challengeids)) {
|
||||
res.status(400).send("invalid body")
|
||||
return
|
||||
}
|
||||
|
||||
if (req.jwt.unique_name !== req.body.profileid) {
|
||||
res.status(403).send("unauthorised")
|
||||
return
|
||||
}
|
||||
|
||||
const challenges: ChallengeProgressionData[] = []
|
||||
|
||||
for (const challengeId of req.body.challengeids) {
|
||||
const challenge = controller.challengeService.getChallengeById(
|
||||
challengeId,
|
||||
req.gameVersion,
|
||||
)
|
||||
|
||||
if (!challenge) {
|
||||
log(LogLevel.ERROR, `Unknown challenge in CSGP: ${challengeId}`)
|
||||
continue
|
||||
}
|
||||
|
||||
const progression =
|
||||
controller.challengeService.getPersistentChallengeProgression(
|
||||
req.jwt.unique_name,
|
||||
challengeId,
|
||||
req.gameVersion,
|
||||
)
|
||||
|
||||
challenges.push({
|
||||
ChallengeId: challengeId,
|
||||
ProfileId: req.jwt.unique_name,
|
||||
State: progression.State,
|
||||
CompletedAt: progression.CompletedAt,
|
||||
Completed: progression.Completed,
|
||||
})
|
||||
}
|
||||
|
||||
res.json(challenges)
|
||||
},
|
||||
)
|
||||
|
||||
profileRouter.post(
|
||||
"/HubPagesService/GetChallengeTreeFor",
|
||||
jsonMiddleware(),
|
||||
|
@ -732,13 +782,6 @@ profileRouter.post(
|
|||
"No such save detected! Might be an official servers save.",
|
||||
)
|
||||
|
||||
if (PEACOCK_DEV) {
|
||||
log(
|
||||
LogLevel.DEBUG,
|
||||
`(Save-context: ${req.body.contractSessionId}; ${req.body.saveToken})`,
|
||||
)
|
||||
}
|
||||
|
||||
log(
|
||||
LogLevel.WARN,
|
||||
"Creating a fake session to avoid problems... scoring will not work!",
|
||||
|
|
|
@ -71,7 +71,7 @@ import {
|
|||
MissionEndResult,
|
||||
} from "./types/score"
|
||||
import { MasteryData } from "./types/mastery"
|
||||
import { createInventory, InventoryItem, getUnlockablesById } from "./inventory"
|
||||
import { createInventory, getUnlockablesById, InventoryItem } from "./inventory"
|
||||
import { calculatePlaystyle } from "./playStyles"
|
||||
import assert from "assert"
|
||||
|
||||
|
@ -132,6 +132,9 @@ export function calculateScore(
|
|||
contractData: MissionManifest,
|
||||
timeTotal: Seconds,
|
||||
): CalculateScoreResult {
|
||||
const noticedKillsAreVanilla =
|
||||
getFlag("legacyNoticedKillScoring") === "vanilla"
|
||||
|
||||
// Bonuses
|
||||
const bonuses = [
|
||||
{
|
||||
|
@ -173,11 +176,12 @@ export function calculateScore(
|
|||
{
|
||||
headline: "UI_SCORING_SUMMARY_NO_NOTICED_KILLS",
|
||||
bonusId: "NoWitnessedKillsBonus",
|
||||
condition: [...contractSession.killsNoticedBy].every(
|
||||
condition:
|
||||
gameVersion === "h1" && noticedKillsAreVanilla
|
||||
? contractSession.lastKill.legacyIsUnnoticed
|
||||
: [...contractSession.killsNoticedBy].every(
|
||||
(witness) =>
|
||||
(gameVersion === "h1"
|
||||
? true
|
||||
: contractSession.targetKills.has(witness)) ||
|
||||
contractSession.targetKills.has(witness) ||
|
||||
contractSession.npcKills.has(witness),
|
||||
),
|
||||
},
|
||||
|
|
|
@ -16,19 +16,21 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import type { MissionManifestObjective } from "../types/types"
|
||||
import type { MissionManifestObjective, RepositoryId } from "../types/types"
|
||||
import { randomUUID } from "crypto"
|
||||
import assert from "assert"
|
||||
|
||||
/**
|
||||
* The payload provided to the game for each target in the create menu route.
|
||||
*/
|
||||
export interface IContractCreationPayload {
|
||||
export type ContractCreationNpcTargetPayload = {
|
||||
RepositoryId: string
|
||||
Selected: boolean
|
||||
Weapon: {
|
||||
RepositoryId: string
|
||||
KillMethodBroad: string
|
||||
KillMethodStrict: string
|
||||
RequiredKillMethod: string
|
||||
RequiredKillMethodType: number
|
||||
}
|
||||
Outfit: {
|
||||
|
@ -39,43 +41,24 @@ export interface IContractCreationPayload {
|
|||
}
|
||||
|
||||
/**
|
||||
* The target creator API.
|
||||
* @internal
|
||||
*/
|
||||
export class TargetCreator {
|
||||
// @ts-expect-error TODO: type this
|
||||
private _targetSm
|
||||
// @ts-expect-error TODO: type this
|
||||
private _outfitSm
|
||||
private _targetConds: undefined | unknown[] = undefined
|
||||
|
||||
/**
|
||||
* The constructor.
|
||||
*
|
||||
* @param _params The contract creation payload.
|
||||
*/
|
||||
public constructor(private readonly _params: IContractCreationPayload) {}
|
||||
|
||||
private _requireTargetConds(): void {
|
||||
if (!this._targetConds || !Array.isArray(this._targetConds)) {
|
||||
this._targetConds = []
|
||||
}
|
||||
}
|
||||
|
||||
private _bootstrapEntrySm(): void {
|
||||
this._targetSm = {
|
||||
export const createTargetKillObjective = (
|
||||
params: ContractCreationNpcTargetPayload,
|
||||
): MissionManifestObjective => ({
|
||||
Type: "statemachine",
|
||||
Id: randomUUID(),
|
||||
BriefingText: {
|
||||
$loc: {
|
||||
key: "UI_CONTRACT_GENERAL_OBJ_KILL",
|
||||
data: `$($repository ${this._params.RepositoryId}).Name`,
|
||||
data: `$($repository ${params.RepositoryId}).Name`,
|
||||
},
|
||||
},
|
||||
HUDTemplate: {
|
||||
display: {
|
||||
$loc: {
|
||||
key: "UI_CONTRACT_GENERAL_OBJ_KILL",
|
||||
data: `$($repository ${this._params.RepositoryId}).Name`,
|
||||
data: `$($repository ${params.RepositoryId}).Name`,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -83,39 +66,34 @@ export class TargetCreator {
|
|||
Definition: {
|
||||
Scope: "Hit",
|
||||
Context: {
|
||||
Targets: [this._params.RepositoryId],
|
||||
Targets: [params.RepositoryId],
|
||||
},
|
||||
States: {
|
||||
Start: {
|
||||
Kill: [
|
||||
// this is the base state machine, we don't have fail states
|
||||
{
|
||||
Kill: {
|
||||
Condition: {
|
||||
$eq: [
|
||||
"$Value.RepositoryId",
|
||||
this._params.RepositoryId,
|
||||
],
|
||||
$eq: ["$Value.RepositoryId", params.RepositoryId],
|
||||
},
|
||||
Transition: "Success",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
private _createOutfitSm(hmSuit: boolean): void {
|
||||
this._requireTargetConds()
|
||||
|
||||
this._outfitSm = {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export const createRequiredOutfitObjective = (
|
||||
params: ContractCreationNpcTargetPayload,
|
||||
): MissionManifestObjective => ({
|
||||
Type: "statemachine",
|
||||
Id: randomUUID(),
|
||||
Category: "secondary",
|
||||
Definition: {
|
||||
Scope: "Hit",
|
||||
Context: {
|
||||
Targets: [this._params.RepositoryId],
|
||||
Targets: [params.RepositoryId],
|
||||
},
|
||||
States: {
|
||||
Start: {
|
||||
|
@ -126,10 +104,10 @@ export class TargetCreator {
|
|||
{
|
||||
$eq: [
|
||||
"$Value.RepositoryId",
|
||||
this._params.RepositoryId,
|
||||
params.RepositoryId,
|
||||
],
|
||||
},
|
||||
hmSuit
|
||||
params.Outfit.IsHitmanSuit
|
||||
? {
|
||||
$eq: [
|
||||
"$Value.OutfitIsHitmanSuit",
|
||||
|
@ -139,21 +117,130 @@ export class TargetCreator {
|
|||
: {
|
||||
$eq: [
|
||||
"$Value.OutfitRepositoryId",
|
||||
this._params.Outfit
|
||||
.RepositoryId,
|
||||
params.Outfit.RepositoryId,
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
Transition: "Success",
|
||||
},
|
||||
// state machines fall through to the next state if the condition is not met
|
||||
{
|
||||
Condition: {
|
||||
$eq: [
|
||||
"$Value.RepositoryId",
|
||||
this._params.RepositoryId,
|
||||
$eq: ["$Value.RepositoryId", params.RepositoryId],
|
||||
},
|
||||
Transition: "Failure",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
TargetConditions: [],
|
||||
})
|
||||
|
||||
/**
|
||||
* Create the target, weapon, and kill conditions for a contracts target.
|
||||
* @param params The parameters from the request.
|
||||
* @param customIds Custom objective IDs for testing purposes.
|
||||
*/
|
||||
export function createObjectivesForTarget(
|
||||
params: ContractCreationNpcTargetPayload,
|
||||
customIds?: { base: string; kill: string; outfit: string },
|
||||
): MissionManifestObjective[] {
|
||||
const targetSm = createTargetKillObjective(params)
|
||||
|
||||
if (customIds?.base) {
|
||||
targetSm.Id = customIds.base
|
||||
}
|
||||
|
||||
const objectives: MissionManifestObjective[] = [targetSm]
|
||||
|
||||
// if the required field is true, that means the user requested something OTHER than any disguise
|
||||
if (params.Outfit.Required) {
|
||||
const outfitSm = createRequiredOutfitObjective(params)
|
||||
|
||||
if (customIds?.outfit) {
|
||||
outfitSm.Id = customIds.outfit
|
||||
}
|
||||
|
||||
targetSm.TargetConditions ??= []
|
||||
|
||||
targetSm.TargetConditions.push({
|
||||
Type: params.Outfit.IsHitmanSuit ? "hitmansuit" : "disguise",
|
||||
RepositoryId: params.Outfit.RepositoryId,
|
||||
// for contract creation it's always optional, only escalations set hard fail conditions
|
||||
HardCondition: false,
|
||||
ObjectiveId: outfitSm.Id,
|
||||
// "Amazing!" - Athena Savalas
|
||||
KillMethod: "",
|
||||
})
|
||||
|
||||
objectives.push(outfitSm)
|
||||
}
|
||||
|
||||
if (params.Weapon.RequiredKillMethodType !== 0) {
|
||||
const weaponSm = createWeaponObjective(
|
||||
params.Weapon,
|
||||
params.RepositoryId,
|
||||
)
|
||||
|
||||
if (customIds?.kill) {
|
||||
weaponSm.Id = customIds.kill
|
||||
}
|
||||
|
||||
targetSm.TargetConditions ??= []
|
||||
|
||||
targetSm.TargetConditions.push({
|
||||
Type: "killmethod",
|
||||
RepositoryId: params.Weapon.RepositoryId,
|
||||
// for contract creation it's always optional, only escalations set hard fail conditions
|
||||
HardCondition: false,
|
||||
ObjectiveId: weaponSm.Id,
|
||||
KillMethod: params.Weapon.RequiredKillMethod,
|
||||
})
|
||||
|
||||
objectives.push(weaponSm)
|
||||
}
|
||||
|
||||
return objectives
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an objective for killing a target with a specific weapon.
|
||||
* @param weapon The weapon details from the request.
|
||||
* @param npcId The target NPC's repository ID.
|
||||
*/
|
||||
function createWeaponObjective(
|
||||
weapon: Weapon,
|
||||
npcId: RepositoryId,
|
||||
): MissionManifestObjective {
|
||||
return {
|
||||
Type: "statemachine",
|
||||
Id: randomUUID(),
|
||||
Category: "secondary",
|
||||
Definition: {
|
||||
Scope: "Hit",
|
||||
Context: {
|
||||
Targets: [npcId],
|
||||
},
|
||||
States: {
|
||||
Start: {
|
||||
Kill: [
|
||||
{
|
||||
Condition: {
|
||||
$and: [
|
||||
{
|
||||
$eq: ["$Value.RepositoryId", npcId],
|
||||
},
|
||||
genStateMachineKillSuccessCondition(weapon),
|
||||
],
|
||||
},
|
||||
Transition: "Success",
|
||||
},
|
||||
{
|
||||
Condition: {
|
||||
$eq: ["$Value.RepositoryId", npcId],
|
||||
},
|
||||
Transition: "Failure",
|
||||
},
|
||||
],
|
||||
|
@ -161,47 +248,10 @@ export class TargetCreator {
|
|||
},
|
||||
},
|
||||
}
|
||||
|
||||
this._targetConds?.push({
|
||||
Type: hmSuit ? "hitmansuit" : "disguise",
|
||||
RepositoryId: this._params.Outfit.RepositoryId,
|
||||
HardCondition: false,
|
||||
ObjectiveId: this._outfitSm.Id,
|
||||
// ioi moment
|
||||
KillMethod: "",
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the array of finalized state machines.
|
||||
*
|
||||
* @returns The state machines.
|
||||
*/
|
||||
public build(): MissionManifestObjective[] {
|
||||
this._bootstrapEntrySm()
|
||||
|
||||
if (this._params.Outfit.Required) {
|
||||
// not any disguise
|
||||
this._createOutfitSm(this._params.Outfit.IsHitmanSuit)
|
||||
}
|
||||
|
||||
const values = [this._targetSm]
|
||||
|
||||
if (this._outfitSm) {
|
||||
values.push(this._outfitSm)
|
||||
}
|
||||
|
||||
if (this._targetConds && Array.isArray(this._targetConds)) {
|
||||
this._targetSm.TargetConditions = this._targetConds
|
||||
}
|
||||
|
||||
return values
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The time limit creator API.
|
||||
*
|
||||
* Create a time limit objective.
|
||||
* @param time The amount of time to use in seconds.
|
||||
* @param optional If the objective should be optional or not.
|
||||
* @returns The generated state machine.
|
||||
|
@ -258,18 +308,14 @@ export function createTimeLimit(
|
|||
Scope: "session",
|
||||
States: {
|
||||
Start: {
|
||||
IntroCutEnd: [
|
||||
{
|
||||
IntroCutEnd: {
|
||||
Transition: "TimerRunning",
|
||||
},
|
||||
],
|
||||
},
|
||||
TimerRunning: {
|
||||
exit_gate: [
|
||||
{
|
||||
exit_gate: {
|
||||
Transition: "Success",
|
||||
},
|
||||
],
|
||||
$timer: [
|
||||
{
|
||||
Condition: {
|
||||
|
@ -283,3 +329,150 @@ export function createTimeLimit(
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* These are all the possible ways to get a kill in contracts mode.
|
||||
*/
|
||||
export const enum ContractKillMethod {
|
||||
Any,
|
||||
AnyMelee,
|
||||
ObjectMelee,
|
||||
AnyThrown,
|
||||
ObjectThrown,
|
||||
Pistol,
|
||||
PistolElimination,
|
||||
Smg,
|
||||
AssaultRifle,
|
||||
Shotgun,
|
||||
SniperRifle,
|
||||
AnyPoison,
|
||||
ConsumedPoison,
|
||||
InjectedPoison,
|
||||
AnyAccident,
|
||||
ExplosiveDevice,
|
||||
FiberWire,
|
||||
UnarmedNeckSnap,
|
||||
}
|
||||
|
||||
type Weapon = ContractCreationNpcTargetPayload["Weapon"]
|
||||
type KillSuccessStateCondition = unknown
|
||||
|
||||
export function genStateMachineKillSuccessCondition(
|
||||
weapon: Weapon,
|
||||
): KillSuccessStateCondition {
|
||||
const km = weaponToKillMethod(weapon)
|
||||
|
||||
if (km === ContractKillMethod.PistolElimination) {
|
||||
return {
|
||||
$any: {
|
||||
"?": {
|
||||
$or: [
|
||||
{
|
||||
$eq: ["$.#", "pistol"],
|
||||
},
|
||||
{
|
||||
$eq: ["$.#", "close_combat_pistol_elimination"],
|
||||
},
|
||||
],
|
||||
},
|
||||
in: ["$Value.KillMethodBroad", "$Value.KillMethodStrict"],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (km === ContractKillMethod.Pistol) {
|
||||
return {
|
||||
$any: {
|
||||
"?": {
|
||||
$or: [
|
||||
{
|
||||
$eq: ["$.#", "pistol"],
|
||||
},
|
||||
{
|
||||
$eq: ["$.#", "close_combat_pistol_elimination"],
|
||||
},
|
||||
],
|
||||
},
|
||||
in: ["$Value.KillMethodBroad", "$Value.KillMethodStrict"],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
$any: {
|
||||
"?": {
|
||||
$eq: ["$.#", weapon.RequiredKillMethod],
|
||||
},
|
||||
in: ["$Value.KillMethodBroad", "$Value.KillMethodStrict"],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the equivalent kill method from a weapon object.
|
||||
* @param weapon The weapon's details.
|
||||
*/
|
||||
export function weaponToKillMethod(weapon: Weapon): ContractKillMethod {
|
||||
const type = weapon.RequiredKillMethodType
|
||||
|
||||
if (type === 0) {
|
||||
return ContractKillMethod.Any
|
||||
}
|
||||
|
||||
switch (weapon.KillMethodBroad) {
|
||||
case "pistol":
|
||||
return ContractKillMethod.Pistol
|
||||
case "smg":
|
||||
return ContractKillMethod.Smg
|
||||
case "sniperrifle":
|
||||
return ContractKillMethod.SniperRifle
|
||||
case "assaultrifle":
|
||||
return ContractKillMethod.AssaultRifle
|
||||
case "shotgun":
|
||||
return ContractKillMethod.Shotgun
|
||||
case "close_combat_pistol_elimination":
|
||||
return ContractKillMethod.PistolElimination
|
||||
case "fiberwire":
|
||||
return ContractKillMethod.FiberWire
|
||||
case "throw": {
|
||||
return type === 1
|
||||
? ContractKillMethod.AnyThrown
|
||||
: ContractKillMethod.ObjectThrown
|
||||
}
|
||||
case "melee_lethal": {
|
||||
return type === 1
|
||||
? ContractKillMethod.AnyMelee
|
||||
: ContractKillMethod.ObjectMelee
|
||||
}
|
||||
case "poison": {
|
||||
if (weapon.KillMethodStrict === "consumed_poison" && type === 2) {
|
||||
return ContractKillMethod.ConsumedPoison
|
||||
}
|
||||
|
||||
if (
|
||||
weapon.KillMethodStrict === "injected_poison" &&
|
||||
weapon.RequiredKillMethodType === 2
|
||||
) {
|
||||
return ContractKillMethod.InjectedPoison
|
||||
}
|
||||
|
||||
assert(
|
||||
type === 1,
|
||||
`Unhandled poison: ${weapon.KillMethodStrict} ${type}`,
|
||||
)
|
||||
|
||||
return ContractKillMethod.AnyPoison
|
||||
}
|
||||
case "unarmed":
|
||||
return ContractKillMethod.UnarmedNeckSnap
|
||||
case "accident":
|
||||
return ContractKillMethod.AnyAccident
|
||||
case "explosive":
|
||||
return ContractKillMethod.ExplosiveDevice
|
||||
default: {
|
||||
assert.fail(
|
||||
`Unhandled condition: ${weapon.KillMethodBroad} ${type}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
import prompts from "prompts"
|
||||
import { log, LogLevel } from "./loggingInterop"
|
||||
import { readdir, writeFile } from "fs/promises"
|
||||
import { PEACOCKVER, PEACOCKVERSTRING } from "./utils"
|
||||
import { PEACOCKVERSTRING } from "./utils"
|
||||
import md5File from "md5-file"
|
||||
import { arch, cpus as cpuList, platform, version } from "os"
|
||||
import { Controller, isPlugin } from "./controller"
|
||||
|
@ -116,7 +116,6 @@ async function exportDebugInfo(): Promise<void> {
|
|||
|
||||
const data = {
|
||||
version: PEACOCKVERSTRING,
|
||||
ident: PEACOCKVER,
|
||||
presentConfigs: Object.keys(configs),
|
||||
chunkDigest: await md5File("chunk0.js"),
|
||||
patcherDigest: await md5File("PeacockPatcher.exe"),
|
||||
|
|
|
@ -47,6 +47,7 @@ export interface SavedChallenge {
|
|||
Xp: number
|
||||
XpModifier?: unknown
|
||||
DifficultyLevels: (keyof typeof gameDifficulty)[]
|
||||
OrderIndex: number
|
||||
Definition: MissionManifestObjective["Definition"] & {
|
||||
Scope: ContextScopedStorageLocation
|
||||
Repeatable?: {
|
||||
|
@ -68,6 +69,7 @@ export interface SavedChallengeGroup {
|
|||
Icon: string
|
||||
CategoryId: string
|
||||
Description: string
|
||||
OrderIndex?: number
|
||||
Challenges: SavedChallenge[]
|
||||
}
|
||||
|
||||
|
@ -78,7 +80,7 @@ export interface ChallengePackage {
|
|||
* The parent location.
|
||||
*/
|
||||
Location: string
|
||||
GameVersion: GameVersion
|
||||
GameVersions: GameVersion[]
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ export type MultiplayerMatchStatsQuery = Partial<{
|
|||
contractSessionId: string
|
||||
}>
|
||||
|
||||
export type LegacyGetProgressionBody = Partial<{
|
||||
export type GetChallengeProgressionBody = Partial<{
|
||||
profileid: string
|
||||
challengeids: string[]
|
||||
}>
|
||||
|
|
|
@ -27,9 +27,3 @@ declare let PEACOCK_DEV: boolean
|
|||
* Injected during the build process.
|
||||
*/
|
||||
declare let HUMAN_VERSION: string
|
||||
|
||||
/**
|
||||
* The revision identifier for the global version.
|
||||
* Injected during the build process.
|
||||
*/
|
||||
declare let REV_IDENT: number
|
||||
|
|
|
@ -43,8 +43,7 @@ export type ScoringHeadline = {
|
|||
scoreTotal: number
|
||||
}
|
||||
|
||||
export type ManifestScoringModule =
|
||||
| ScoringModule & {
|
||||
export type ManifestScoringModule = ScoringModule & {
|
||||
Type: string
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
import type * as core from "express-serve-static-core"
|
||||
|
||||
import type { IContractCreationPayload } from "../statemachines/contractCreation"
|
||||
import type { ContractCreationNpcTargetPayload } from "../statemachines/contractCreation"
|
||||
import { Request } from "express"
|
||||
import {
|
||||
ChallengeContext,
|
||||
|
@ -192,7 +192,7 @@ export type MissionType =
|
|||
/**
|
||||
* The data acquired when using the "contract search" functionality.
|
||||
*/
|
||||
export interface ContractSearchResult {
|
||||
export type ContractSearchResult = {
|
||||
Data: {
|
||||
Contracts: {
|
||||
UserCentricContract: UserCentricContract
|
||||
|
@ -210,9 +210,14 @@ export interface ContractSearchResult {
|
|||
*
|
||||
* @see ContractSession
|
||||
*/
|
||||
export interface ContractSessionLastKill {
|
||||
export type ContractSessionLastKill = {
|
||||
timestamp?: Date | number
|
||||
repositoryIds?: RepositoryId[]
|
||||
/**
|
||||
* If the last kill was unnoticed in H2016.
|
||||
* See [this video](https://www.youtube.com/watch?v=4fMDqRZg3Ik) for an explanation on how it's supposed to work.
|
||||
*/
|
||||
legacyIsUnnoticed?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -328,7 +333,7 @@ export interface SaveFile {
|
|||
*
|
||||
* @see SaveFile
|
||||
*/
|
||||
export interface UpdateUserSaveFileTableBody {
|
||||
export type UpdateUserSaveFileTableBody = {
|
||||
clientSaveFileList: SaveFile[]
|
||||
deletedSaveFileList: SaveFile[]
|
||||
}
|
||||
|
@ -336,12 +341,12 @@ export interface UpdateUserSaveFileTableBody {
|
|||
/**
|
||||
* The Hitman server version in object form.
|
||||
*/
|
||||
export interface ServerVersion {
|
||||
readonly _Major: number
|
||||
readonly _Minor: number
|
||||
readonly _Build: number
|
||||
readonly _Revision: number
|
||||
}
|
||||
export type ServerVersion = Readonly<{
|
||||
_Major: number
|
||||
_Minor: number
|
||||
_Build: number
|
||||
_Revision: number
|
||||
}>
|
||||
|
||||
/**
|
||||
* An event sent from the game client to the server.
|
||||
|
@ -367,7 +372,7 @@ export interface S2CEventWithTimestamp<EventValue = unknown> {
|
|||
/**
|
||||
* A server to client push message. The message component is encoded JSON.
|
||||
*/
|
||||
export interface PushMessage {
|
||||
export type PushMessage = {
|
||||
time: number | string | bigint
|
||||
message: string
|
||||
}
|
||||
|
@ -393,7 +398,7 @@ export interface ServerToClientEvent<EventValue = unknown> {
|
|||
Origin?: string | null
|
||||
}
|
||||
|
||||
export interface MissionStory {
|
||||
export type MissionStory = {
|
||||
CommonRepositoryId: RepositoryId
|
||||
PreviouslyCompleted: boolean
|
||||
IsMainOpportunity: boolean
|
||||
|
@ -405,7 +410,7 @@ export interface MissionStory {
|
|||
Image: string
|
||||
}
|
||||
|
||||
export interface PlayerProfileView {
|
||||
export type PlayerProfileView = {
|
||||
SubLocationData: {
|
||||
ParentLocation: Unlockable
|
||||
Location: Unlockable
|
||||
|
@ -433,34 +438,34 @@ export interface PlayerProfileView {
|
|||
}
|
||||
}
|
||||
|
||||
export interface ChallengeCompletion {
|
||||
export type ChallengeCompletion = {
|
||||
ChallengesCount: number
|
||||
CompletedChallengesCount: number
|
||||
CompletionPercent?: number
|
||||
}
|
||||
|
||||
export interface ChallengeCategoryCompletion extends ChallengeCompletion {
|
||||
export type ChallengeCategoryCompletion = ChallengeCompletion & {
|
||||
Name: string
|
||||
}
|
||||
|
||||
export interface OpportunityStatistics {
|
||||
export type OpportunityStatistics = {
|
||||
Count: number
|
||||
Completed: number
|
||||
}
|
||||
|
||||
export interface ContractHistory {
|
||||
export type ContractHistory = {
|
||||
LastPlayedAt?: number
|
||||
Completed?: boolean
|
||||
IsEscalation?: boolean
|
||||
}
|
||||
|
||||
export interface ProgressionData {
|
||||
export type ProgressionData = {
|
||||
Xp: number
|
||||
Level: number
|
||||
PreviouslySeenXp: number
|
||||
}
|
||||
|
||||
export interface UserProfile {
|
||||
export type UserProfile = {
|
||||
Id: string
|
||||
LinkedAccounts: {
|
||||
dev?: string
|
||||
|
@ -506,10 +511,11 @@ export interface UserProfile {
|
|||
*/
|
||||
Total: number
|
||||
Sublocations: {
|
||||
Location: string
|
||||
[location: string]: {
|
||||
Xp: number
|
||||
ActionXp: number
|
||||
}[]
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* If the mastery location has subpackages and not drops, it will
|
||||
|
@ -555,12 +561,12 @@ export interface UserProfile {
|
|||
[opportunityId: RepositoryId]: boolean
|
||||
}
|
||||
CPD: CPDStore
|
||||
LastOfficialSync: Date | string | null
|
||||
}
|
||||
ETag: string | null
|
||||
Gamertag: string
|
||||
DevId: string | null
|
||||
SteamId: string | null
|
||||
StadiaId: string | null
|
||||
EpicId: string | null
|
||||
NintendoId: string | null
|
||||
XboxLiveId: string | null
|
||||
|
@ -572,7 +578,7 @@ export interface UserProfile {
|
|||
Version: number
|
||||
}
|
||||
|
||||
export interface RatingKill {
|
||||
export type RatingKill = {
|
||||
IsHeadshot: boolean
|
||||
KillClass: string
|
||||
KillItemCategory: string
|
||||
|
@ -588,7 +594,7 @@ export interface RatingKill {
|
|||
OutfitRepoId: string
|
||||
}
|
||||
|
||||
export interface NamespaceEntitlementEpic {
|
||||
export type NamespaceEntitlementEpic = {
|
||||
namespace: string
|
||||
itemId: string
|
||||
owned: boolean
|
||||
|
@ -597,7 +603,7 @@ export interface NamespaceEntitlementEpic {
|
|||
/**
|
||||
* An unlockable item.
|
||||
*/
|
||||
export interface Unlockable {
|
||||
export type Unlockable = {
|
||||
Id: string
|
||||
DisplayNameLocKey: string
|
||||
GameAsset: string | null
|
||||
|
@ -702,14 +708,14 @@ export interface Unlockable {
|
|||
Rarity?: string | null
|
||||
}
|
||||
|
||||
export interface ItemGameplay {
|
||||
export type ItemGameplay = {
|
||||
range?: number
|
||||
damage?: number
|
||||
clipsize?: number
|
||||
rateoffire?: number
|
||||
}
|
||||
|
||||
export interface CompletionData {
|
||||
export type CompletionData = {
|
||||
Level: number
|
||||
MaxLevel: number
|
||||
XP: number
|
||||
|
@ -723,7 +729,7 @@ export interface CompletionData {
|
|||
Name: string | null
|
||||
}
|
||||
|
||||
export interface UserCentricContract {
|
||||
export type UserCentricContract = {
|
||||
Contract: MissionManifest
|
||||
Data: {
|
||||
IsLocked: boolean
|
||||
|
@ -755,12 +761,27 @@ export interface UserCentricContract {
|
|||
}
|
||||
}
|
||||
|
||||
export interface TargetCondition {
|
||||
Type: string
|
||||
export type TargetCondition = {
|
||||
/**
|
||||
* The target condition type. This can be one of the following:
|
||||
* - `killmethod` - A way to kill the target.
|
||||
* - `hitmansuit` - Specifies the outfit must be any suit that you can start a level with which (but not a disguise).
|
||||
* - `disguise` - Specifies the outfit must be a specific disguise.
|
||||
*/
|
||||
Type: "killmethod" | "hitmansuit" | "disguise"
|
||||
RepositoryId?: RepositoryId
|
||||
/**
|
||||
* If the game should display the objective as optional or not.
|
||||
*/
|
||||
HardCondition?: boolean
|
||||
/**
|
||||
* The objective ID that this condition is tied to. When specified, the game can mark the condition with a check mark or X in the F1 menu.
|
||||
*/
|
||||
ObjectiveId?: string
|
||||
KillMethod?: string
|
||||
/**
|
||||
* For outfit requirements, this is just an empty string. For kill methods, this is the kill method.
|
||||
*/
|
||||
KillMethod: "" | string
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -781,7 +802,7 @@ export interface HUDTemplate {
|
|||
/**
|
||||
* Data structure for a mission manifest's `Data.VR` bricks property.
|
||||
*/
|
||||
export interface VRQualityDefinition {
|
||||
export type VRQualityDefinition = {
|
||||
Quality: string
|
||||
Bricks: string[]
|
||||
}
|
||||
|
@ -983,7 +1004,7 @@ export interface MissionManifestMetadata {
|
|||
Modules?: ManifestScoringModule[] | null
|
||||
}
|
||||
|
||||
export interface GroupObjectiveDisplayOrderItem {
|
||||
export type GroupObjectiveDisplayOrderItem = {
|
||||
Id: string
|
||||
IsNew?: boolean
|
||||
}
|
||||
|
@ -1060,7 +1081,7 @@ export interface MissionManifest {
|
|||
* 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 {
|
||||
export type ServerConnectionConfig = {
|
||||
Versions: {
|
||||
Name: string
|
||||
GAME_VER: string
|
||||
|
@ -1093,7 +1114,7 @@ export interface ServerConnectionConfig {
|
|||
*
|
||||
* @see GameLocationsData
|
||||
*/
|
||||
export interface PeacockLocationsData {
|
||||
export type PeacockLocationsData = {
|
||||
/**
|
||||
* The parent locations.
|
||||
*/
|
||||
|
@ -1125,7 +1146,7 @@ export interface GameLocationsData {
|
|||
/**
|
||||
* The body sent with the CreateFromParams request from the game during the final phase of contract creation.
|
||||
*
|
||||
* @see IContractCreationPayload
|
||||
* @see ContractCreationNpcTargetPayload
|
||||
*/
|
||||
export interface CreateFromParamsBody {
|
||||
creationData: {
|
||||
|
@ -1133,12 +1154,12 @@ export interface CreateFromParamsBody {
|
|||
Description: string
|
||||
ContractId: string
|
||||
ContractPublicId: string
|
||||
Targets: IContractCreationPayload[]
|
||||
Targets: ContractCreationNpcTargetPayload[]
|
||||
ContractConditionIds: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface SelectEntranceOrPickupData {
|
||||
export type SelectEntranceOrPickupData = {
|
||||
Unlocked: string[]
|
||||
Contract: MissionManifest
|
||||
OrderedUnlocks: Unlockable[]
|
||||
|
@ -1150,7 +1171,7 @@ export type CompiledIoiStatemachine = unknown
|
|||
/**
|
||||
* The `ChallengeProgress` data for a `challengetree` context listener.
|
||||
*/
|
||||
export interface ChallengeProgressCTreeContextListener {
|
||||
export type ChallengeProgressCTreeContextListener = {
|
||||
total: number
|
||||
completed: string[] | readonly string[]
|
||||
missing: number
|
||||
|
@ -1161,28 +1182,29 @@ export interface ChallengeProgressCTreeContextListener {
|
|||
/**
|
||||
* The `ChallengeProgress` data for a `challengecount` context listener.
|
||||
*/
|
||||
export interface ChallengeProgressCCountContextListener {
|
||||
export type ChallengeProgressCCountContextListener = {
|
||||
total: number
|
||||
count: number
|
||||
}
|
||||
|
||||
export interface CompiledChallengeTreeCategory {
|
||||
export type CompiledChallengeTreeCategory = {
|
||||
CategoryId: string
|
||||
ChallengesCount: number
|
||||
CompletedChallengesCount: number
|
||||
CompletionData: CompletionData
|
||||
CompletionData: CompletionData | Record<string, never>
|
||||
Icon: string
|
||||
Image: string
|
||||
ImageLocked?: string
|
||||
IsLocked: boolean
|
||||
Location: Unlockable
|
||||
Location: Unlockable | null
|
||||
Name: string
|
||||
RequiredResources: string[]
|
||||
OrderIndex: number
|
||||
SwitchData: {
|
||||
Data: {
|
||||
CategoryData: CompiledChallengeTreeCategoryInfo
|
||||
Challenges: CompiledChallengeTreeData[]
|
||||
CompletionData: CompletionData
|
||||
CompletionData: CompletionData | Record<string, never>
|
||||
HasNext: boolean
|
||||
HasPrevious: boolean
|
||||
NextCategoryIcon?: string
|
||||
|
@ -1192,7 +1214,7 @@ export interface CompiledChallengeTreeCategory {
|
|||
}
|
||||
}
|
||||
|
||||
export interface CompiledChallengeTreeCategoryInfo {
|
||||
export type CompiledChallengeTreeCategoryInfo = {
|
||||
Name: string
|
||||
Image: string
|
||||
Icon: string
|
||||
|
@ -1209,7 +1231,7 @@ export type ChallengeTreeWaterfallState =
|
|||
| ChallengeProgressCCountContextListener
|
||||
| null
|
||||
|
||||
export interface CompiledChallengeTreeData {
|
||||
export type CompiledChallengeTreeData = {
|
||||
CategoryName: string
|
||||
ChallengeProgress?: ChallengeTreeWaterfallState
|
||||
Completed: boolean
|
||||
|
@ -1230,6 +1252,7 @@ export interface CompiledChallengeTreeData {
|
|||
LocationId: string
|
||||
Name: string
|
||||
ParentLocationId: string
|
||||
OrderIndex: number
|
||||
Rewards: {
|
||||
MasteryXP?: number
|
||||
}
|
||||
|
@ -1244,7 +1267,7 @@ export interface InclusionData {
|
|||
GameModes?: string[]
|
||||
}
|
||||
|
||||
export interface CompiledChallengeIngameData {
|
||||
export type CompiledChallengeIngameData = {
|
||||
Id: string
|
||||
GroupId?: string
|
||||
Name: string
|
||||
|
@ -1268,15 +1291,15 @@ export interface CompiledChallengeIngameData {
|
|||
/**
|
||||
* Game-facing challenge progression data.
|
||||
*/
|
||||
export interface ChallengeProgressionData {
|
||||
export type ChallengeProgressionData = {
|
||||
ChallengeId: string
|
||||
ProfileId: string
|
||||
Completed: boolean
|
||||
Ticked: boolean
|
||||
Ticked?: boolean
|
||||
ETag?: string
|
||||
State: Record<string, unknown>
|
||||
CompletedAt: Date | string | null
|
||||
MustBeSaved: boolean
|
||||
MustBeSaved?: boolean
|
||||
}
|
||||
|
||||
export interface CompiledChallengeRuntimeData {
|
||||
|
@ -1295,7 +1318,7 @@ export type Flags = Record<
|
|||
/**
|
||||
* A "hit" object.
|
||||
*/
|
||||
export interface IHit {
|
||||
export type Hit = {
|
||||
Id: string
|
||||
UserCentricContract: UserCentricContract
|
||||
Location: Unlockable
|
||||
|
@ -1315,7 +1338,7 @@ export interface IHit {
|
|||
* @see CampaignVideo
|
||||
* @see StoryData
|
||||
*/
|
||||
export interface IVideo {
|
||||
export type Video = {
|
||||
VideoTitle: string
|
||||
VideoHeader: string
|
||||
VideoId: string
|
||||
|
@ -1334,21 +1357,21 @@ export interface IVideo {
|
|||
/**
|
||||
* A campaign mission item.
|
||||
*
|
||||
* @see IHit
|
||||
* @see Hit
|
||||
*/
|
||||
export type CampaignMission = {
|
||||
Type: "Mission"
|
||||
Data: IHit
|
||||
Data: Hit
|
||||
}
|
||||
|
||||
/**
|
||||
* A campaign video item.
|
||||
*
|
||||
* @see IVideo
|
||||
* @see Video
|
||||
*/
|
||||
export type CampaignVideo = {
|
||||
Type: "Video"
|
||||
Data: IVideo
|
||||
Data: Video
|
||||
}
|
||||
|
||||
export interface RegistryChallenge extends SavedChallenge {
|
||||
|
@ -1368,7 +1391,7 @@ export type StoryData = CampaignMission | CampaignVideo
|
|||
/**
|
||||
* A campaign object.
|
||||
*/
|
||||
export interface Campaign {
|
||||
export type Campaign = {
|
||||
Name: string
|
||||
Image: string
|
||||
Type: MissionType | string
|
||||
|
@ -1383,7 +1406,7 @@ export interface Campaign {
|
|||
/**
|
||||
* A loadout.
|
||||
*/
|
||||
export interface Loadout {
|
||||
export type Loadout = {
|
||||
/**
|
||||
* Random ID.
|
||||
*
|
||||
|
@ -1439,25 +1462,25 @@ export type GenSingleVideoFunc = (
|
|||
/**
|
||||
* A "hits category" is used to display lists of contracts in-game.
|
||||
*
|
||||
* @see IHit
|
||||
* @see Hit
|
||||
*/
|
||||
export interface HitsCategoryCategory {
|
||||
export type HitsCategoryCategory = {
|
||||
Category: string
|
||||
Data: {
|
||||
Type: string
|
||||
Hits: IHit[]
|
||||
Hits: Hit[]
|
||||
Page: number
|
||||
HasMore: boolean
|
||||
}
|
||||
CurrentSubType: string
|
||||
}
|
||||
|
||||
export interface PlayNextCampaignDetails {
|
||||
export type PlayNextCampaignDetails = {
|
||||
CampaignName: string
|
||||
ParentCampaignName?: string
|
||||
}
|
||||
|
||||
export interface PlayNextGetCampaignsHookReturn {
|
||||
export type PlayNextGetCampaignsHookReturn = {
|
||||
/**
|
||||
* The UUID of the next contract in the campaign.
|
||||
*/
|
||||
|
@ -1528,7 +1551,7 @@ 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 {
|
||||
export type SMFLastDeploy = {
|
||||
runtimePath: string
|
||||
retailPath: string
|
||||
skipIntro: boolean
|
||||
|
@ -1551,3 +1574,9 @@ export interface SMFLastDeploy {
|
|||
peacockPlugins?: string[]
|
||||
}
|
||||
}
|
||||
|
||||
export type OfficialSublocation = {
|
||||
Location: string
|
||||
Xp: number
|
||||
ActionXp: number
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import type { NextFunction, Response } from "express"
|
|||
import type {
|
||||
GameVersion,
|
||||
MissionManifestObjective,
|
||||
OfficialSublocation,
|
||||
PeacockLocationsData,
|
||||
RepositoryId,
|
||||
RequestWithJwt,
|
||||
|
@ -33,6 +34,7 @@ import { log, LogLevel } from "./loggingInterop"
|
|||
import { writeFileSync } from "fs"
|
||||
import { getFlag } from "./flags"
|
||||
import { getConfig, getVersionedConfig } from "./configSwizzleManager"
|
||||
import { compare } from "semver"
|
||||
|
||||
/**
|
||||
* True if the server is being run by the launcher, false otherwise.
|
||||
|
@ -41,12 +43,11 @@ export const IS_LAUNCHER = process.env.IS_PEACOCK_LAUNCHER === "true"
|
|||
|
||||
export const ServerVer: ServerVersion = {
|
||||
_Major: 8,
|
||||
_Minor: 14,
|
||||
_Minor: 15,
|
||||
_Build: 0,
|
||||
_Revision: 0,
|
||||
}
|
||||
|
||||
export const PEACOCKVER = REV_IDENT
|
||||
export const PEACOCKVERSTRING = HUMAN_VERSION
|
||||
|
||||
export const uuidRegex =
|
||||
|
@ -54,6 +55,9 @@ export const uuidRegex =
|
|||
|
||||
export const contractTypes = ["featured", "usercreated"]
|
||||
|
||||
/**
|
||||
* A list of game versions, except scpc.
|
||||
*/
|
||||
export const versions: Exclude<GameVersion, "scpc">[] = ["h1", "h2", "h3"]
|
||||
|
||||
export const contractCreationTutorialId = "d7e2607c-6916-48e2-9588-976c7d8998bb"
|
||||
|
@ -63,7 +67,7 @@ export const contractCreationTutorialId = "d7e2607c-6916-48e2-9588-976c7d8998bb"
|
|||
*
|
||||
* See docs/USER_PROFILES.md for more.
|
||||
*/
|
||||
export const LATEST_PROFILE_VERSION = 1
|
||||
export const LATEST_PROFILE_VERSION = 2
|
||||
|
||||
export async function checkForUpdates(): Promise<void> {
|
||||
if (getFlag("updateChecking") === false) {
|
||||
|
@ -71,29 +75,38 @@ export async function checkForUpdates(): Promise<void> {
|
|||
}
|
||||
|
||||
try {
|
||||
const res = await fetch(
|
||||
"https://backend.rdil.rocks/peacock/latest-version",
|
||||
)
|
||||
const current = parseInt(await res.text(), 10)
|
||||
type VersionCheckResponse = { id: string; channel: string }
|
||||
|
||||
if (PEACOCKVER < 0 && current < -PEACOCKVER) {
|
||||
const res = await fetch(
|
||||
"https://backend.rdil.rocks/peacock/latest-version/data",
|
||||
)
|
||||
const data = (await res.json()) as VersionCheckResponse
|
||||
|
||||
const current = compare(PEACOCKVERSTRING, data.id)
|
||||
const isNewer = current === 1
|
||||
|
||||
if (isNewer) {
|
||||
log(
|
||||
LogLevel.INFO,
|
||||
`Thank you for trying out this testing version of Peacock! Please report any bugs by posting in the #help channel on Discord or by submitting an issue on GitHub.`,
|
||||
`You're ahead of the latest release! The latest version of Peacock (${data.id}) is older than this build.`,
|
||||
"updates",
|
||||
)
|
||||
} else if (PEACOCKVER > 0 && current === PEACOCKVER) {
|
||||
log(LogLevel.DEBUG, "Peacock is up to date.")
|
||||
} else if (current === 0) {
|
||||
log(LogLevel.DEBUG, "Peacock is up to date.", "updates")
|
||||
} else {
|
||||
log(
|
||||
LogLevel.WARN,
|
||||
`Peacock is out-of-date! Check the Discord for the latest release.`,
|
||||
"updates",
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
log(LogLevel.WARN, "Failed to check for updates!")
|
||||
log(LogLevel.WARN, "Failed to check for updates!", "updates")
|
||||
}
|
||||
}
|
||||
|
||||
export * from "semver"
|
||||
|
||||
export function getRemoteService(gameVersion: GameVersion): string | undefined {
|
||||
switch (gameVersion) {
|
||||
case "h3":
|
||||
|
@ -181,14 +194,12 @@ export const EVERGREEN_LEVEL_INFO: number[] = [
|
|||
|
||||
export function evergreenLevelForXp(xp: number): number {
|
||||
for (let i = 1; i < EVERGREEN_LEVEL_INFO.length; i++) {
|
||||
if (xp >= EVERGREEN_LEVEL_INFO[i]) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (xp < EVERGREEN_LEVEL_INFO[i]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return 1
|
||||
return EVERGREEN_LEVEL_INFO.length
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -209,14 +220,12 @@ export const SNIPER_LEVEL_INFO: number[] = [
|
|||
|
||||
export function sniperLevelForXp(xp: number): number {
|
||||
for (let i = 1; i < SNIPER_LEVEL_INFO.length; i++) {
|
||||
if (xp >= SNIPER_LEVEL_INFO[i]) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (xp < SNIPER_LEVEL_INFO[i]) {
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
return 1
|
||||
return SNIPER_LEVEL_INFO.length
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -241,7 +250,6 @@ export function clampValue(value: number, min: number, max: number) {
|
|||
*
|
||||
* @param profile The user profile to update
|
||||
* @param gameVersion The game version
|
||||
* @returns The updated user profile.
|
||||
*/
|
||||
function updateUserProfile(
|
||||
profile: UserProfile,
|
||||
|
@ -258,6 +266,29 @@ function updateUserProfile(
|
|||
case LATEST_PROFILE_VERSION:
|
||||
// This profile updated to the latest version, we're done.
|
||||
return
|
||||
case 1: {
|
||||
/* ////// VERSION 2 ////// */
|
||||
|
||||
const sublocations = profile.Extensions.progression.PlayerProfileXP
|
||||
.Sublocations as unknown as OfficialSublocation[]
|
||||
|
||||
profile.Extensions.progression.PlayerProfileXP.Sublocations =
|
||||
Object.fromEntries(
|
||||
sublocations.map((value) => [
|
||||
value.Location,
|
||||
{
|
||||
Xp: value.Xp,
|
||||
ActionXp: value.ActionXp,
|
||||
},
|
||||
]),
|
||||
)
|
||||
|
||||
profile.Extensions.LastOfficialSync = null
|
||||
|
||||
profile.Version = 2
|
||||
|
||||
return updateUserProfile(profile, gameVersion)
|
||||
}
|
||||
default: {
|
||||
// Check that the profile version is indeed undefined. If it isn't,
|
||||
// we've forgotten to add a version to the switch.
|
||||
|
@ -647,14 +678,14 @@ export function handleAxiosError(error: AxiosError): void {
|
|||
if (error.response) {
|
||||
// The request was made and the server responded with a status code
|
||||
// that falls out of the range of 2xx
|
||||
log(LogLevel.DEBUG, `code ${error.response.status}`)
|
||||
log(LogLevel.DEBUG, `Request error: code ${error.response.status}`)
|
||||
} else if (error.request) {
|
||||
// The request was made but no response was received
|
||||
// `error.request` is an instance of http.ClientRequest
|
||||
log(LogLevel.DEBUG, `bad fetch`)
|
||||
log(LogLevel.DEBUG, `Request error: bad fetch`)
|
||||
} else {
|
||||
// Something happened in setting up the request that triggered an Error
|
||||
log(LogLevel.DEBUG, `generic`)
|
||||
log(LogLevel.DEBUG, `Request error: something went wrong`)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -718,3 +749,26 @@ export function isSuit(repoId: string): boolean {
|
|||
? suitsToTypeMap[repoId] !== "disguise"
|
||||
: false
|
||||
}
|
||||
|
||||
type SublocationMap = {
|
||||
[parentId: string]: string[]
|
||||
}
|
||||
|
||||
export function getSublocations(gameVersion: GameVersion): SublocationMap {
|
||||
const sublocations: SublocationMap = {}
|
||||
const locations = getVersionedConfig<PeacockLocationsData>(
|
||||
"LocationsData",
|
||||
gameVersion,
|
||||
false,
|
||||
)
|
||||
|
||||
for (const child of Object.values(locations.children)) {
|
||||
if (!child.Properties.ParentLocation) continue
|
||||
|
||||
sublocations[child.Properties.ParentLocation] ??= []
|
||||
|
||||
sublocations[child.Properties.ParentLocation].push(child.Id)
|
||||
}
|
||||
|
||||
return sublocations
|
||||
}
|
||||
|
|
|
@ -18,14 +18,44 @@
|
|||
|
||||
import { NextFunction, Request, Response, Router } from "express"
|
||||
import { getConfig } from "./configSwizzleManager"
|
||||
import { readFileSync } from "atomically"
|
||||
import { GameVersion, UserProfile } from "./types/types"
|
||||
import { readdir, readFile } from "fs/promises"
|
||||
import {
|
||||
ChallengeProgressionData,
|
||||
GameVersion,
|
||||
HitsCategoryCategory,
|
||||
OfficialSublocation,
|
||||
ProgressionData,
|
||||
UserProfile,
|
||||
} from "./types/types"
|
||||
import { join } from "path"
|
||||
import { uuidRegex, versions } from "./utils"
|
||||
import {
|
||||
getRemoteService,
|
||||
getSublocations,
|
||||
isSniperLocation,
|
||||
levelForXp,
|
||||
uuidRegex,
|
||||
versions,
|
||||
} from "./utils"
|
||||
import { getUserData, loadUserData, writeUserData } from "./databaseHandler"
|
||||
import { readdirSync } from "fs"
|
||||
import { controller } from "./controller"
|
||||
import { log, LogLevel } from "./loggingInterop"
|
||||
import { OfficialServerAuth, userAuths } from "./officialServerAuth"
|
||||
import { AxiosError } from "axios"
|
||||
import { SNIPER_UNLOCK_TO_LOCATION } from "./menuData"
|
||||
|
||||
type OfficialProfileResponse = UserProfile & {
|
||||
Extensions: {
|
||||
progression: {
|
||||
Unlockables: {
|
||||
[unlockableId: string]: ProgressionData
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SubPackageData = {
|
||||
[id: string]: ProgressionData
|
||||
}
|
||||
|
||||
const webFeaturesRouter = Router()
|
||||
|
||||
|
@ -34,9 +64,9 @@ if (PEACOCK_DEV) {
|
|||
res.set("Access-Control-Allow-Origin", "*")
|
||||
res.set(
|
||||
"Access-Control-Allow-Methods",
|
||||
"GET,HEAD,PUT,PATCH,POST,DELETE",
|
||||
"GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS",
|
||||
)
|
||||
res.set("Access-Control-Allow-Headers", "Content-Type")
|
||||
res.set("Access-Control-Allow-Headers", "content-type")
|
||||
next()
|
||||
})
|
||||
}
|
||||
|
@ -86,8 +116,13 @@ webFeaturesRouter.get("/codenames", (_, res) => {
|
|||
res.json(getConfig("EscalationCodenames", false))
|
||||
})
|
||||
|
||||
webFeaturesRouter.get("/local-users", (req: CommonRequest, res) => {
|
||||
if (!req.query.gv || !versions.includes(req.query.gv ?? null)) {
|
||||
webFeaturesRouter.get("/local-users", async (req: CommonRequest, res) => {
|
||||
// Validate that gv is h1, h2, or h3
|
||||
function validateGv(gv: unknown): gv is "h1" | "h2" | "h3" {
|
||||
return versions.includes(gv as Exclude<GameVersion, "scpc">)
|
||||
}
|
||||
|
||||
if (!validateGv(req.query.gv)) {
|
||||
res.json([])
|
||||
return
|
||||
}
|
||||
|
@ -100,21 +135,35 @@ webFeaturesRouter.get("/local-users", (req: CommonRequest, res) => {
|
|||
dir = join("userdata", req.query.gv, "users")
|
||||
}
|
||||
|
||||
const files: string[] = readdirSync(dir).filter(
|
||||
const files: string[] = (await readdir(dir)).filter(
|
||||
(name) => name !== "lop.json",
|
||||
)
|
||||
|
||||
const result = []
|
||||
/**
|
||||
* Sync this type with `webui/src/utils`!
|
||||
*/
|
||||
type BasicUser = Readonly<{
|
||||
id: string
|
||||
name: string
|
||||
platform: string
|
||||
lastOfficialSync: string | null
|
||||
}>
|
||||
|
||||
const result: BasicUser[] = []
|
||||
|
||||
for (const file of files) {
|
||||
if (file === "lop.json") continue
|
||||
|
||||
const read = JSON.parse(
|
||||
readFileSync(join(dir, file)).toString(),
|
||||
(await readFile(join(dir, file))).toString(),
|
||||
) as UserProfile
|
||||
|
||||
result.push({
|
||||
id: read.Id,
|
||||
name: read.Gamertag,
|
||||
platform: read.EpicId ? "Epic" : "Steam",
|
||||
lastOfficialSync:
|
||||
read.Extensions.LastOfficialSync?.toString() || null,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -213,4 +262,360 @@ webFeaturesRouter.get(
|
|||
},
|
||||
)
|
||||
|
||||
type EscalationData = {
|
||||
PeacockEscalations: {
|
||||
[escalationId: string]: number
|
||||
}
|
||||
PeacockCompletedEscalations: string[]
|
||||
}
|
||||
|
||||
type OfficialHitsCategory = {
|
||||
data: HitsCategoryCategory
|
||||
}
|
||||
|
||||
async function getHitsCategory(
|
||||
auth: OfficialServerAuth,
|
||||
remoteService: string,
|
||||
category: string,
|
||||
page: number,
|
||||
): Promise<[results: EscalationData, hasMore: boolean]> {
|
||||
const data: EscalationData = {
|
||||
PeacockEscalations: {},
|
||||
PeacockCompletedEscalations: [],
|
||||
}
|
||||
|
||||
const hits = await auth._useService<OfficialHitsCategory>(
|
||||
`https://${remoteService}.hitman.io/profiles/page/HitsCategory?page=${page}&type=${category}&mode=dataonly`,
|
||||
true,
|
||||
)
|
||||
|
||||
for (const hit of hits.data.data.Data.Hits) {
|
||||
data.PeacockEscalations[hit.Id] =
|
||||
hit.UserCentricContract.Data.EscalationCompletedLevels! + 1
|
||||
|
||||
if (hit.UserCentricContract.Data.EscalationCompleted)
|
||||
data.PeacockCompletedEscalations.push(hit.Id)
|
||||
}
|
||||
|
||||
return [data, hits.data.data.Data.HasMore]
|
||||
}
|
||||
|
||||
async function getAllHitsCategory(
|
||||
auth: OfficialServerAuth,
|
||||
remoteService: string,
|
||||
category: string,
|
||||
): Promise<EscalationData> {
|
||||
const data: EscalationData = {
|
||||
PeacockEscalations: {},
|
||||
PeacockCompletedEscalations: [],
|
||||
}
|
||||
|
||||
let page = 0
|
||||
let hasMore = true
|
||||
|
||||
while (hasMore) {
|
||||
const [results, more] = await getHitsCategory(
|
||||
auth,
|
||||
remoteService,
|
||||
category,
|
||||
page,
|
||||
)
|
||||
|
||||
data.PeacockEscalations = {
|
||||
...data.PeacockEscalations,
|
||||
...results.PeacockEscalations,
|
||||
}
|
||||
|
||||
data.PeacockCompletedEscalations = [
|
||||
...data.PeacockCompletedEscalations,
|
||||
...results.PeacockCompletedEscalations,
|
||||
]
|
||||
|
||||
page++
|
||||
hasMore = more
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
webFeaturesRouter.post(
|
||||
"/sync-progress",
|
||||
commonValidationMiddleware,
|
||||
async (req: CommonRequest, res) => {
|
||||
const remoteService = getRemoteService(req.query.gv)
|
||||
const auth = userAuths.get(req.query.user)
|
||||
|
||||
if (!auth) {
|
||||
formErrorMessage(
|
||||
res,
|
||||
"Failed to get official authentication data. Please connect to Peacock first.",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
const userdata = getUserData(req.query.user, req.query.gv)
|
||||
|
||||
try {
|
||||
// Challenge Progression
|
||||
log(LogLevel.DEBUG, "Getting challenge progression...")
|
||||
|
||||
const challengeProgression = await auth._useService<
|
||||
ChallengeProgressionData[]
|
||||
>(
|
||||
`https://${remoteService}.hitman.io/authentication/api/userchannel/ChallengesService/GetProgression`,
|
||||
false,
|
||||
{
|
||||
profileid: req.query.user,
|
||||
challengeids: controller.challengeService.getChallengeIds(
|
||||
req.query.gv,
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
userdata.Extensions.ChallengeProgression = Object.fromEntries(
|
||||
challengeProgression.data.map((data) => {
|
||||
return [
|
||||
data.ChallengeId,
|
||||
{
|
||||
Ticked: data.Completed,
|
||||
Completed: data.Completed,
|
||||
CurrentState:
|
||||
(data.State["CurrentState"] as string) ??
|
||||
"Start",
|
||||
State: data.State,
|
||||
},
|
||||
]
|
||||
}),
|
||||
)
|
||||
|
||||
// Profile Progression
|
||||
log(LogLevel.DEBUG, "Getting profile progression...")
|
||||
|
||||
const exts = await auth._useService<OfficialProfileResponse>(
|
||||
`https://${remoteService}.hitman.io/authentication/api/userchannel/ProfileService/GetProfile`,
|
||||
false,
|
||||
{
|
||||
id: req.query.user,
|
||||
extensions: [
|
||||
"achievements",
|
||||
"friends",
|
||||
"gameclient",
|
||||
"gamepersistentdata",
|
||||
"opportunityprogression",
|
||||
"progression",
|
||||
"defaultloadout",
|
||||
],
|
||||
},
|
||||
)
|
||||
|
||||
if (req.query.gv !== "h1") {
|
||||
log(LogLevel.DEBUG, "Processing PlayerProfileXP...")
|
||||
|
||||
const sublocations = exts.data.Extensions.progression
|
||||
.PlayerProfileXP
|
||||
.Sublocations as unknown as OfficialSublocation[]
|
||||
|
||||
userdata.Extensions.progression.PlayerProfileXP = {
|
||||
...userdata.Extensions.progression.PlayerProfileXP,
|
||||
Total: exts.data.Extensions.progression.PlayerProfileXP
|
||||
.Total,
|
||||
ProfileLevel: levelForXp(
|
||||
exts.data.Extensions.progression.PlayerProfileXP.Total,
|
||||
),
|
||||
Sublocations: Object.fromEntries(
|
||||
sublocations.map((value) => [
|
||||
value.Location,
|
||||
{
|
||||
Xp: value.Xp,
|
||||
ActionXp: value.ActionXp,
|
||||
},
|
||||
]),
|
||||
),
|
||||
}
|
||||
|
||||
log(LogLevel.DEBUG, "Processing opportunity progression...")
|
||||
|
||||
userdata.Extensions.opportunityprogression = Object.fromEntries(
|
||||
Object.keys(
|
||||
exts.data.Extensions.opportunityprogression || {},
|
||||
).map((value) => [value, true]),
|
||||
)
|
||||
|
||||
if (exts.data.Extensions.progression.Unlockables) {
|
||||
log(LogLevel.DEBUG, "Processing unlockables...")
|
||||
|
||||
for (const [unlockId, data] of Object.entries(
|
||||
exts.data.Extensions.progression.Unlockables,
|
||||
)) {
|
||||
const unlockableId = unlockId.toUpperCase()
|
||||
|
||||
if (!(unlockableId in SNIPER_UNLOCK_TO_LOCATION))
|
||||
continue
|
||||
;(
|
||||
userdata.Extensions.progression.Locations[
|
||||
SNIPER_UNLOCK_TO_LOCATION[unlockableId]
|
||||
] as SubPackageData
|
||||
)[unlockableId] = {
|
||||
Xp: data.Xp,
|
||||
Level: data.Level,
|
||||
PreviouslySeenXp: data.PreviouslySeenXp,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userdata.Extensions.gamepersistentdata =
|
||||
exts.data.Extensions.gamepersistentdata || {}
|
||||
|
||||
const sublocations = getSublocations(req.query.gv)
|
||||
userdata.Extensions.defaultloadout ??= {}
|
||||
|
||||
if (exts.data.Extensions.defaultloadout) {
|
||||
for (const [parent, loadout] of Object.entries(
|
||||
exts.data.Extensions.defaultloadout,
|
||||
)) {
|
||||
for (const child of sublocations[parent]) {
|
||||
userdata.Extensions.defaultloadout[child] = loadout
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userdata.Extensions.achievements =
|
||||
exts.data.Extensions.achievements || []
|
||||
|
||||
for (const [locId, data] of Object.entries(
|
||||
exts.data.Extensions.progression.Locations,
|
||||
)) {
|
||||
const location = (
|
||||
locId.startsWith("location_parent")
|
||||
? locId
|
||||
: locId.replace("location_", "location_parent_")
|
||||
).toUpperCase()
|
||||
|
||||
if (isSniperLocation(location)) continue
|
||||
|
||||
if (req.query.gv === "h1") {
|
||||
const parent = location.endsWith("PRO1")
|
||||
? location.substring(0, location.length - 5)
|
||||
: location
|
||||
|
||||
const packageId: string = location.endsWith("PRO1")
|
||||
? "pro1"
|
||||
: "normal"
|
||||
|
||||
;(
|
||||
userdata.Extensions.progression.Locations[
|
||||
parent
|
||||
] as SubPackageData
|
||||
)[packageId] = {
|
||||
Xp: data.Xp as number,
|
||||
Level: data.Level as number,
|
||||
PreviouslySeenXp: data.Xp as number,
|
||||
}
|
||||
} else {
|
||||
userdata.Extensions.progression.Locations[location] = {
|
||||
Xp: data.Xp as number,
|
||||
Level: data.Level as number,
|
||||
PreviouslySeenXp: data.PreviouslySeenXp as number,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Escalation & Arcade Progression
|
||||
log(
|
||||
LogLevel.DEBUG,
|
||||
`Getting escalation${req.query.gv === "h3" ? " & arcade" : ""} progression...`,
|
||||
)
|
||||
|
||||
const escalations = await getAllHitsCategory(
|
||||
auth,
|
||||
remoteService!,
|
||||
"ContractAttack",
|
||||
)
|
||||
|
||||
const arcade =
|
||||
req.query.gv === "h3"
|
||||
? await getAllHitsCategory(auth, remoteService!, "Arcade")
|
||||
: {
|
||||
PeacockEscalations: {},
|
||||
PeacockCompletedEscalations: [],
|
||||
}
|
||||
|
||||
userdata.Extensions.PeacockEscalations = {
|
||||
...userdata.Extensions.PeacockEscalations,
|
||||
...escalations.PeacockEscalations,
|
||||
...arcade.PeacockEscalations,
|
||||
}
|
||||
|
||||
userdata.Extensions.PeacockCompletedEscalations = [
|
||||
...userdata.Extensions.PeacockCompletedEscalations,
|
||||
...escalations.PeacockCompletedEscalations,
|
||||
...arcade.PeacockCompletedEscalations,
|
||||
]
|
||||
|
||||
for (const id of userdata.Extensions.PeacockCompletedEscalations) {
|
||||
userdata.Extensions.PeacockPlayedContracts[id] = {
|
||||
LastPlayedAt: new Date().getTime(),
|
||||
Completed: true,
|
||||
IsEscalation: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Freelancer Progression
|
||||
// TODO: Try and see if there is a less intensive way to do this
|
||||
// GetForPlay2 is quite intensive on IOI's side as it starts a session
|
||||
if (req.query.gv === "h3") {
|
||||
log(LogLevel.DEBUG, "Getting freelancer progression...")
|
||||
|
||||
await auth._useService(
|
||||
`https://${remoteService}.hitman.io/authentication/api/configuration/Init?configName=pc-prod&lockedContentDisabled=false&isFreePrologueUser=false&isIntroPackUser=false&isFullExperienceUser=true`,
|
||||
true,
|
||||
)
|
||||
|
||||
const freelancerSession = await auth._useService<{
|
||||
ContractProgressionData: Record<
|
||||
string,
|
||||
string | number | boolean
|
||||
>
|
||||
}>(
|
||||
`https://${remoteService}.hitman.io/authentication/api/userchannel/ContractsService/GetForPlay2`,
|
||||
false,
|
||||
{
|
||||
id: "f8ec92c2-4fa2-471e-ae08-545480c746ee",
|
||||
locationId: "",
|
||||
extraGameChangerIds: [],
|
||||
difficultyLevel: 0,
|
||||
},
|
||||
)
|
||||
|
||||
userdata.Extensions.CPD[
|
||||
"f8ec92c2-4fa2-471e-ae08-545480c746ee"
|
||||
] = freelancerSession.data.ContractProgressionData
|
||||
}
|
||||
|
||||
userdata.Extensions.LastOfficialSync = new Date().toISOString()
|
||||
|
||||
writeUserData(req.query.user, req.query.gv)
|
||||
} catch (error) {
|
||||
if (error instanceof AxiosError) {
|
||||
formErrorMessage(
|
||||
res,
|
||||
`Failed to sync official data: got ${error.response?.status} ${error.response?.statusText}.`,
|
||||
)
|
||||
return
|
||||
} else {
|
||||
formErrorMessage(
|
||||
res,
|
||||
`Failed to sync official data: got ${JSON.stringify(error)}.`,
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
export { webFeaturesRouter }
|
||||
|
|
|
@ -113,7 +113,8 @@
|
|||
}
|
||||
],
|
||||
"Bricks": [],
|
||||
"GameChangers": []
|
||||
"GameChangers": [],
|
||||
"EnableSaving": false
|
||||
},
|
||||
"Metadata": {
|
||||
"TileImage": "images/contracts/escalation/contractescalation_pontus.jpg",
|
||||
|
|
|
@ -142,7 +142,8 @@
|
|||
}
|
||||
],
|
||||
"Bricks": [],
|
||||
"GameChangers": ["07b1bc1d-f52b-4004-a760-846c4bc3f172"]
|
||||
"GameChangers": ["07b1bc1d-f52b-4004-a760-846c4bc3f172"],
|
||||
"EnableSaving": false
|
||||
},
|
||||
"Metadata": {
|
||||
"TileImage": "images/contracts/escalation/contractescalation_pontus.jpg",
|
||||
|
|
|
@ -205,7 +205,8 @@
|
|||
}
|
||||
],
|
||||
"Bricks": [],
|
||||
"GameChangers": ["07b1bc1d-f52b-4004-a760-846c4bc3f172"]
|
||||
"GameChangers": ["07b1bc1d-f52b-4004-a760-846c4bc3f172"],
|
||||
"EnableSaving": false
|
||||
},
|
||||
"Metadata": {
|
||||
"TileImage": "images/contracts/escalation/contractescalation_pontus.jpg",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_ROCKY",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "2638c6ab-20e5-4f5f-bcbb-2f940c7adee3",
|
||||
|
@ -962,6 +963,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "ad549a16-c8c7-40af-9265-f5c42065be87",
|
||||
|
@ -1671,6 +1673,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "8d27e376-c658-4f1a-8b97-a76e99575539",
|
||||
|
@ -2103,6 +2106,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "b5213925-b20e-4ce9-9c70-d60b2771d541",
|
||||
|
@ -2196,6 +2200,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "425a7a49-1892-4a24-aa40-f46cdb8d6c7f",
|
||||
|
@ -3047,6 +3052,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "argon-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 6.6,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "0409d6ed-bb3b-4628-a9d4-90cbfb7c1793",
|
||||
|
@ -3084,6 +3090,142 @@
|
|||
"Tags": ["argon-pack", "story", "live", "medium"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_CHEESECAKE",
|
||||
"Image": "images/challenges/categories/packcheesecake/tile.jpg",
|
||||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "cheesecake-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 6.1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "b5386255-0e7e-2bd7-6c28-c1096d4902c5",
|
||||
"Name": "UI_PEACOCK_CHALLENGEPACK_CHEESECAKE_AMBROSEHOOK_NAME",
|
||||
"ImageName": "images/challenges/categories/packcheesecake/cheesecake_ambrosehook.jpg",
|
||||
"Description": "UI_PEACOCK_CHALLENGEPACK_CHEESECAKE_AMBROSEHOOK_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": ["PROP_MELEE_BUTCHERS_MEATHOOK"],
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_CHEESECAKE",
|
||||
"Icon": "challenge_category_feats",
|
||||
"LocationId": "LOCATION_PARENT_ROCKY",
|
||||
"ParentLocationId": "LOCATION_PARENT_ROCKY",
|
||||
"Type": "parentlocation",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 100004,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.RepositoryId",
|
||||
"1312f620-bf61-4b7f-8f5c-ea4e07763a98"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillMethodStrict",
|
||||
"accident_burn"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "SmithDown"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.RepositoryId",
|
||||
"9454339d-8f29-4ae6-97f4-96523a48bf08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillItemRepositoryId",
|
||||
"58a036dc-79d4-4d64-8bf5-3faafa3cfead"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "MateDown"
|
||||
}
|
||||
]
|
||||
},
|
||||
"SmithDown": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.RepositoryId",
|
||||
"9454339d-8f29-4ae6-97f4-96523a48bf08"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillItemRepositoryId",
|
||||
"58a036dc-79d4-4d64-8bf5-3faafa3cfead"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "CheckShower"
|
||||
}
|
||||
},
|
||||
"MateDown": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.RepositoryId",
|
||||
"1312f620-bf61-4b7f-8f5c-ea4e07763a98"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillMethodStrict",
|
||||
"accident_burn"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "CheckShower"
|
||||
}
|
||||
},
|
||||
"CheckShower": {
|
||||
"setpieces": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.RepositoryId",
|
||||
"168b0d90-2b0b-44ec-98fb-bc728cfbc12b"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["cheesecake-pack", "story", "live", "medium"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b2aac100-dfc7-4f85-b9cd-528114436f6c"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"Data": {
|
||||
"EnableSaving": false,
|
||||
"Objectives": [],
|
||||
"Bricks": [],
|
||||
"VR": [
|
||||
{
|
||||
"Quality": "base",
|
||||
"Bricks": ["assembly:/_pro/Scenes/Bricks/vr_setup.brick"]
|
||||
}
|
||||
],
|
||||
"GameChangers": [],
|
||||
"GameChangerReferences": []
|
||||
},
|
||||
"Metadata": {
|
||||
"Id": "de9788cc-b9c4-47fc-b5df-86451cd82c43",
|
||||
"IsPublished": true,
|
||||
"CreationTimestamp": "2024-03-22T10:54:18.3690079Z",
|
||||
"CreatorUserId": "fadb923c-e6bb-4283-a537-eb4d1150262e",
|
||||
"TileImage": "images/contracts/arcade/Arcade_AniseStar_Group.jpg",
|
||||
"Title": "UI_CONTRACT_ANISESTAR_GROUP_TITLE",
|
||||
"Description": "UI_CONTRACT_ANISESTAR_GROUP_DESC",
|
||||
"CodeName_Hint": "Arcade AniseStar - Group",
|
||||
"Location": "LOCATION_MIAMI",
|
||||
"Type": "arcade",
|
||||
"Release": "3.180.0 Arcade",
|
||||
"ScenePath": "assembly:/_pro/scenes/missions/miami/scene_et_sambuca.entity",
|
||||
"Entitlements": ["H3_ET_SAMBUCA"],
|
||||
"GroupDefinition": {
|
||||
"Type": "arcade",
|
||||
"Order": [
|
||||
"a07b7a11-f318-4905-9826-4fc10505bef6",
|
||||
"8e5a8e33-6f23-4a2f-8e4a-402c296dacc8",
|
||||
"8e7e9f08-94c5-4667-ac8a-61aa15b74c0a"
|
||||
]
|
||||
},
|
||||
"PublicId": "011709683047"
|
||||
},
|
||||
"UserData": {}
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
{
|
||||
"Data": {
|
||||
"EnableSaving": false,
|
||||
"Objectives": [
|
||||
{
|
||||
"Id": "c2b3f8e3-0eaf-41b3-8b8c-3cf012a1d0bc",
|
||||
"Primary": true,
|
||||
"IsHidden": true,
|
||||
"SuccessEvent": {
|
||||
"EventName": "Kill",
|
||||
"EventValues": {
|
||||
"RepositoryId": "63283fb2-a653-486d-a505-e471efbc8c54"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"_comment": "----- Eliminate Faba with a Pen -----",
|
||||
"Id": "2428f45d-51f2-4043-9d9f-237ee856ccd8",
|
||||
"Primary": false,
|
||||
"ObjectiveType": "custom",
|
||||
"ForceShowOnLoadingScreen": true,
|
||||
"ExcludeFromScoring": true,
|
||||
"OnActive": {
|
||||
"IfCompleted": {
|
||||
"Visible": true
|
||||
}
|
||||
},
|
||||
"Image": "images/challenges/elusive_target/et_sambuca_started_pen.jpg",
|
||||
"BriefingName": "$loc UI_CONTRACT_ANISESTAR_PENKILL_NAME",
|
||||
"BriefingText": "$loc UI_CONTRACT_ANISESTAR_PENKILL_DESC",
|
||||
"HUDTemplate": {
|
||||
"display": "$loc UI_CONTRACT_ANISESTAR_PENKILL_NAME",
|
||||
"iconType": 17
|
||||
},
|
||||
"Type": "statemachine",
|
||||
"Definition": {
|
||||
"display": {
|
||||
"iconType": 17
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Faba_Pen_Fail": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"Faba_Pen_Completed": {
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Entrances": ["f1de2373-eebe-4448-808a-8c2764b2c931"],
|
||||
"Bricks": [
|
||||
"assembly:/_pro/scenes/missions/miami/mission_sambuca.brick"
|
||||
],
|
||||
"VR": [
|
||||
{
|
||||
"Quality": "base",
|
||||
"Bricks": [
|
||||
"assembly:/_pro/Scenes/Bricks/vr_setup.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_flamingo.brick",
|
||||
"assembly:/_PRO/scenes/missions/miami/vr_overrides_ps4perf.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_sambuca.brick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Quality": "better",
|
||||
"Bricks": [
|
||||
"assembly:/_pro/Scenes/Bricks/vr_setup.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_flamingo.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_sambuca.brick"
|
||||
]
|
||||
}
|
||||
],
|
||||
"GameChangers": []
|
||||
},
|
||||
"Metadata": {
|
||||
"Id": "a07b7a11-f318-4905-9826-4fc10505bef6",
|
||||
"Title": "UI_CONTRACT_ANISESTAR_LEVEL01_NAME",
|
||||
"CodeName_Hint": "Arcade AniseStar Sambuca",
|
||||
"Description": "UI_CONTRACT_SAMBUCA_DESC",
|
||||
"BriefingVideo": "briefing_sambuca",
|
||||
"DebriefingVideo": "debriefing_sambuca",
|
||||
"ScenePath": "assembly:/_pro/scenes/missions/miami/scene_et_sambuca.entity",
|
||||
"TileImage": "images/contracts/elusive/046_sambuca/Title.jpg",
|
||||
"InGroup": "de9788cc-b9c4-47fc-b5df-86451cd82c43",
|
||||
"Location": "LOCATION_MIAMI",
|
||||
"IsPublished": true,
|
||||
"LastUpdate": "2024-02-02T12:00:00.441Z",
|
||||
"CreationTimestamp": "2024-03-22T10:54:18.9583011Z",
|
||||
"CreatorUserId": "fadb923c-e6bb-4283-a537-eb4d1150262e",
|
||||
"Type": "arcade",
|
||||
"Release": "3.180.0 Arcade",
|
||||
"Entitlements": ["H3_ET_SAMBUCA"],
|
||||
"PublicId": "011002667847"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"Data": {
|
||||
"GameChangers": [],
|
||||
"Bricks": [
|
||||
"assembly:/_pro/scenes/missions/miami/scenario_sambuca2.brick"
|
||||
],
|
||||
"VR": [
|
||||
{
|
||||
"Quality": "base",
|
||||
"Bricks": [
|
||||
"assembly:/_pro/Scenes/Bricks/vr_setup.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_flamingo.brick",
|
||||
"assembly:/_PRO/scenes/missions/miami/vr_overrides_ps4perf.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_sambuca.brick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Quality": "better",
|
||||
"Bricks": [
|
||||
"assembly:/_pro/Scenes/Bricks/vr_setup.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_flamingo.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_sambuca.brick"
|
||||
]
|
||||
}
|
||||
],
|
||||
"EnableSaving": false,
|
||||
"Entrances": ["f1de2373-eebe-4448-808a-8c2764b2c931"],
|
||||
"Objectives": [
|
||||
{
|
||||
"Primary": true,
|
||||
"SuccessEvent": {
|
||||
"EventValues": {
|
||||
"RepositoryId": "0eda939e-db2b-4344-9c3f-9d00eb2fb1bc"
|
||||
},
|
||||
"EventName": "Kill"
|
||||
},
|
||||
"Id": "9eaf0208-4736-4554-aeed-cfc871efab40",
|
||||
"IsHidden": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"Metadata": {
|
||||
"DebriefingVideo": "debriefing_sambuca2",
|
||||
"BriefingVideo": "briefing_sambuca2",
|
||||
"Title": "UI_CONTRACT_ANISESTAR_LEVEL02_NAME",
|
||||
"Id": "8e5a8e33-6f23-4a2f-8e4a-402c296dacc8",
|
||||
"ScenePath": "assembly:/_pro/scenes/missions/miami/scene_et_sambuca.entity",
|
||||
"CreationTimestamp": "2024-03-22T10:54:19.018087Z",
|
||||
"IsPublished": true,
|
||||
"Type": "arcade",
|
||||
"LastUpdate": "2015-03-10T12:00:00.441Z",
|
||||
"TileImage": "images/contracts/elusive/s2_sambuca2/title.jpg",
|
||||
"Release": "3.180.0 Arcade",
|
||||
"InGroup": "de9788cc-b9c4-47fc-b5df-86451cd82c43",
|
||||
"CodeName_Hint": "Arcade AniseStar Sambuca2",
|
||||
"Description": "UI_CONTRACT_SAMBUCA2_DESC",
|
||||
"CreatorUserId": "fadb923c-e6bb-4283-a537-eb4d1150262e",
|
||||
"Location": "LOCATION_MIAMI",
|
||||
"Entitlements": ["H3_ET_SAMBUCA"],
|
||||
"PublicId": "011330641347"
|
||||
},
|
||||
"UserData": {}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"Data": {
|
||||
"GameChangers": [
|
||||
"63055f1a-bcd2-4e0f-8caf-b446f01d02f3",
|
||||
"9f409781-0a06-4748-b08d-784e78c6d481"
|
||||
],
|
||||
"Bricks": [
|
||||
"assembly:/_pro/scenes/missions/miami/scenario_sambuca2.brick"
|
||||
],
|
||||
"VR": [
|
||||
{
|
||||
"Quality": "base",
|
||||
"Bricks": [
|
||||
"assembly:/_pro/Scenes/Bricks/vr_setup.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_flamingo.brick",
|
||||
"assembly:/_PRO/scenes/missions/miami/vr_overrides_ps4perf.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_sambuca.brick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Quality": "better",
|
||||
"Bricks": [
|
||||
"assembly:/_pro/Scenes/Bricks/vr_setup.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_flamingo.brick",
|
||||
"assembly:/_pro/scenes/missions/miami/vr_overrides_sambuca.brick"
|
||||
]
|
||||
}
|
||||
],
|
||||
"EnableSaving": false,
|
||||
"Entrances": ["f1de2373-eebe-4448-808a-8c2764b2c931"],
|
||||
"Objectives": [
|
||||
{
|
||||
"Primary": true,
|
||||
"SuccessEvent": {
|
||||
"EventValues": {
|
||||
"RepositoryId": "0eda939e-db2b-4344-9c3f-9d00eb2fb1bc"
|
||||
},
|
||||
"EventName": "Kill"
|
||||
},
|
||||
"Id": "9eaf0208-4736-4554-aeed-cfc871efab40",
|
||||
"IsHidden": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"Metadata": {
|
||||
"DebriefingVideo": "debriefing_sambuca2",
|
||||
"BriefingVideo": "briefing_sambuca2",
|
||||
"Title": "UI_CONTRACT_ANISESTAR_LEVEL03_NAME",
|
||||
"Id": "8e7e9f08-94c5-4667-ac8a-61aa15b74c0a",
|
||||
"ScenePath": "assembly:/_pro/scenes/missions/miami/scene_et_sambuca.entity",
|
||||
"CreationTimestamp": "2024-02-02T12:12:12.743Z",
|
||||
"IsPublished": true,
|
||||
"Type": "arcade",
|
||||
"LastUpdate": "2024-03-22T13:30:07.5899447Z",
|
||||
"TileImage": "images/contracts/elusive/s2_sambuca2/title.jpg",
|
||||
"Release": "3.180.0 Arcade",
|
||||
"InGroup": "de9788cc-b9c4-47fc-b5df-86451cd82c43",
|
||||
"CodeName_Hint": "Arcade AniseStar Sambuca2",
|
||||
"Description": "UI_CONTRACT_SAMBUCA2_DESC",
|
||||
"CreatorUserId": "fadb923c-e6bb-4283-a537-eb4d1150262e",
|
||||
"Location": "LOCATION_MIAMI",
|
||||
"Entitlements": [],
|
||||
"PublicId": "011545095147"
|
||||
},
|
||||
"UserData": {}
|
||||
}
|
|
@ -52,8 +52,8 @@
|
|||
"Entitlements": ["LOCATION_GOLDEN"],
|
||||
"InGroup": "797e204a-ef3d-463b-a386-57df0fe29b8f",
|
||||
"GroupObjectiveDisplayOrder": [
|
||||
{ "Id": "611cbf7d-7871-4a02-843a-06d523563519" },
|
||||
{ "Id": "b43f84f7-6c26-4adf-b74a-6d598f03cbe3", "IsNew": true }
|
||||
{ "Id": "b43f84f7-6c26-4adf-b74a-6d598f03cbe3", "IsNew": true },
|
||||
{ "Id": "611cbf7d-7871-4a02-843a-06d523563519" }
|
||||
]
|
||||
},
|
||||
"UserData": {}
|
||||
|
|
|
@ -53,8 +53,8 @@
|
|||
"Entitlements": ["LOCATION_GOLDEN"],
|
||||
"InGroup": "797e204a-ef3d-463b-a386-57df0fe29b8f",
|
||||
"GroupObjectiveDisplayOrder": [
|
||||
{ "Id": "c2b3f8e3-0eaf-41b3-8b8c-3cf012a1d0bc" },
|
||||
{ "Id": "b43f84f7-6c26-4adf-b74a-6d598f03cbe3" }
|
||||
{ "Id": "b43f84f7-6c26-4adf-b74a-6d598f03cbe3" },
|
||||
{ "Id": "c2b3f8e3-0eaf-41b3-8b8c-3cf012a1d0bc" }
|
||||
]
|
||||
},
|
||||
"UserData": {}
|
||||
|
|
|
@ -59,8 +59,8 @@
|
|||
"Entitlements": ["LOCATION_GOLDEN"],
|
||||
"InGroup": "797e204a-ef3d-463b-a386-57df0fe29b8f",
|
||||
"GroupObjectiveDisplayOrder": [
|
||||
{ "Id": "3b6a356b-550f-4097-98c2-59b250136443" },
|
||||
{ "Id": "b43f84f7-6c26-4adf-b74a-6d598f03cbe3" }
|
||||
{ "Id": "b43f84f7-6c26-4adf-b74a-6d598f03cbe3" },
|
||||
{ "Id": "3b6a356b-550f-4097-98c2-59b250136443" }
|
||||
]
|
||||
},
|
||||
"UserData": {}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "GLOBAL_ARCADE_CHALLENGES",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,7 +10,51 @@
|
|||
"Icon": "arcademode",
|
||||
"CategoryId": "arcade",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ARCADE",
|
||||
"OrderIndex": 6,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "9e91db0f-8b9e-469c-bed0-163404ec1eff",
|
||||
"Name": "UI_ANISESTAR_COMPLETION_CHALLENGE_NAME",
|
||||
"ImageName": "images/contracts/arcade/Arcade_AniseStar_Group.jpg",
|
||||
"Description": "UI_ANISESTAR_COMPLETION_CHALLENGE_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ARCADE",
|
||||
"Icon": "arcademode",
|
||||
"LocationId": "LOCATION_MIAMI",
|
||||
"ParentLocationId": "LOCATION_PARENT_MIAMI",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$ContractId",
|
||||
"8e7e9f08-94c5-4667-ac8a-61aa15b74c0a"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["arcade", "hard"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["de9788cc-b9c4-47fc-b5df-86451cd82c43"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "d00653ca-d00d-4b30-bff9-3c03c358adc8",
|
||||
"Name": "UI_APPLE_COMPLETION_CHALLENGE_NAME",
|
||||
|
@ -664,7 +708,7 @@
|
|||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": ["TOKEN_OUTFIT_HERO_BUTCHER_SUIT"],
|
||||
"Drops": ["TOKEN_OUTFIT_HERO_PURPLESPECIAL_SUIT"],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_BANGKOK",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "43737b81-d253-44c8-aac8-f64d6711bdaf",
|
||||
|
@ -2523,6 +2524,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "af07c27a-5d85-47d0-b22b-daee2fed6218",
|
||||
|
@ -3461,6 +3463,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "02abef2d-ca6b-4748-ac2a-42354b6a6aca",
|
||||
|
@ -4242,6 +4245,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "07736d0b-3c6b-48c6-9ff0-6d951cd07ee7",
|
||||
|
@ -4380,6 +4384,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "2db8ca18-bd44-483d-b7d9-eb726b259583",
|
||||
|
@ -5991,187 +5996,43 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGENTUM",
|
||||
"Image": "images/challenges/categories/packargentum/tile.jpg",
|
||||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "argentum-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 6.5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "3151f909-0a87-4e71-81f6-02b52405a8f1",
|
||||
"Name": "UI_CHALLENGES_ET_BRASSMONKEY_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_brassmonkey_M_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRASSMONKEY_TARGETDOWN_DESC",
|
||||
"Id": "5b943968-7142-406e-bd6c-9b9018554667",
|
||||
"Name": "CHALLENGEPACK_ARGENTUM_TIGERDROWNER_NAME",
|
||||
"ImageName": "images/challenges/Categories/PackArgentum/Argentum_TigerDrowner.jpg",
|
||||
"Description": "CHALLENGEPACK_ARGENTUM_TIGERDROWNER_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGENTUM",
|
||||
"Icon": "challenge_category_feats",
|
||||
"LocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"Type": "parentlocation",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"OrderIndex": 100003,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.RepositoryId",
|
||||
"7387e648-ad6b-408d-a0ee-3b3943767e78"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b0bed170-8652-4188-8b9a-92caf9f97e5b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "c2275eac-daad-429d-88ef-453eeaab33b6",
|
||||
"Name": "UI_CHALLENGES_ET_BRASSMONKEY_F_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_brassmonkey_F_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRASSMONKEY_F_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.RepositoryId",
|
||||
"978ad630-8d31-4416-8976-8ed1009a4dbd"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b0bed170-8652-4188-8b9a-92caf9f97e5b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "2f57c52c-cd9e-4193-9060-aa4c496ce30e",
|
||||
"Name": "UI_CHALLENGES_ET_BRASSMONKEY_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_brassmonkey_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRASSMONKEY_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
"TigerDrownerActive": {
|
||||
"Transition": "ChallengeActive"
|
||||
}
|
||||
},
|
||||
"ChallengeActive": {
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
|
@ -6179,726 +6040,54 @@
|
|||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
"$Value.KillMethodStrict",
|
||||
"accident_drown"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b0bed170-8652-4188-8b9a-92caf9f97e5b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "9262f862-0960-4ade-8491-eecb522d80e2",
|
||||
"Name": "UI_CHALLENGES_ET_MARTINI_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_martini_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_MARTINI_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b0b8995c-7b3f-4fa6-91a2-be4bc8edc046"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "1661ba81-60eb-4973-9a2e-e17b62483cfc",
|
||||
"Name": "UI_CHALLENGES_ET_MARTINI_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_martini_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_MARTINI_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
"$Value.KillMethodStrict",
|
||||
"accident_push"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
"Transition": "Check_Kill"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Check_Kill": {
|
||||
"$timer": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
"$after": 2
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b0b8995c-7b3f-4fa6-91a2-be4bc8edc046"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
"Transition": "ChallengeActive"
|
||||
},
|
||||
"DumpInOcean": {
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Description": "UI_CHALLENGES_ET_BLOODYMARY_TARGETDOWN_DESC",
|
||||
"DifficultyLevels": [],
|
||||
"Drops": [],
|
||||
"HideProgression": false,
|
||||
"Icon": "elusive",
|
||||
"Id": "9b6e65c2-22cd-4637-8ab1-e48e07acdedc",
|
||||
"ImageName": "images/challenges/elusive_target/et_bloodymary_targetdown.jpg",
|
||||
"InclusionData": {
|
||||
"ContractIds": ["87f8293a-29cd-4cb1-ade7-dd6bb056d38e"]
|
||||
},
|
||||
"IsLocked": false,
|
||||
"IsPlayable": false,
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"Name": "UI_CHALLENGES_ET_BLOODYMARY_TARGETDOWN_NAME",
|
||||
"OrderIndex": 10000,
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"RuntimeType": "Hit",
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"Type": "contract",
|
||||
"XpModifier": {}
|
||||
},
|
||||
{
|
||||
"Id": "265ff025-9e07-4224-b6db-675e44bd62b5",
|
||||
"Name": "UI_CHALLENGES_ET_BLOODYMARY_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bloodymary_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BLOODYMARY_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["87f8293a-29cd-4cb1-ade7-dd6bb056d38e"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "12937274-4d85-477d-b20d-fddb408a1015",
|
||||
"Name": "UI_CHALLENGES_ELUSIVE_TARGET_BANGKOK_NAME",
|
||||
"ImageName": "images/challenges/profile_challenges/elusive_target_bangkok.jpg",
|
||||
"Description": "UI_CHALLENGES_ELUSIVE_TARGET_BANGKOK_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 0
|
||||
},
|
||||
"Drops": ["TOKEN_OUTFIT_HERO_BANGKOKSUITANDGLOVES"],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractStart": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.ContractType",
|
||||
"elusive"
|
||||
]
|
||||
},
|
||||
"Transition": "State_ValidContract"
|
||||
}
|
||||
},
|
||||
"State_ValidContract": {
|
||||
"ContractEnd": [
|
||||
{
|
||||
"Transition": "Success"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractTypes": ["elusive"]
|
||||
}
|
||||
"Tags": ["argentum-pack", "story", "live", "medium"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,921 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_BANGKOK",
|
||||
"GameVersions": ["h2", "h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"OrderIndex": 5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "3151f909-0a87-4e71-81f6-02b52405a8f1",
|
||||
"Name": "UI_CHALLENGES_ET_BRASSMONKEY_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_brassmonkey_M_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRASSMONKEY_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.RepositoryId",
|
||||
"7387e648-ad6b-408d-a0ee-3b3943767e78"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b0bed170-8652-4188-8b9a-92caf9f97e5b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "c2275eac-daad-429d-88ef-453eeaab33b6",
|
||||
"Name": "UI_CHALLENGES_ET_BRASSMONKEY_F_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_brassmonkey_F_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRASSMONKEY_F_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.RepositoryId",
|
||||
"978ad630-8d31-4416-8976-8ed1009a4dbd"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b0bed170-8652-4188-8b9a-92caf9f97e5b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "2f57c52c-cd9e-4193-9060-aa4c496ce30e",
|
||||
"Name": "UI_CHALLENGES_ET_BRASSMONKEY_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_brassmonkey_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRASSMONKEY_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b0bed170-8652-4188-8b9a-92caf9f97e5b"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "9262f862-0960-4ade-8491-eecb522d80e2",
|
||||
"Name": "UI_CHALLENGES_ET_MARTINI_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_martini_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_MARTINI_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b0b8995c-7b3f-4fa6-91a2-be4bc8edc046"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "1661ba81-60eb-4973-9a2e-e17b62483cfc",
|
||||
"Name": "UI_CHALLENGES_ET_MARTINI_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_martini_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_MARTINI_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b0b8995c-7b3f-4fa6-91a2-be4bc8edc046"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Description": "UI_CHALLENGES_ET_BLOODYMARY_TARGETDOWN_DESC",
|
||||
"DifficultyLevels": [],
|
||||
"Drops": [],
|
||||
"HideProgression": false,
|
||||
"Icon": "elusive",
|
||||
"Id": "9b6e65c2-22cd-4637-8ab1-e48e07acdedc",
|
||||
"ImageName": "images/challenges/elusive_target/et_bloodymary_targetdown.jpg",
|
||||
"InclusionData": {
|
||||
"ContractIds": ["87f8293a-29cd-4cb1-ade7-dd6bb056d38e"]
|
||||
},
|
||||
"IsLocked": false,
|
||||
"IsPlayable": false,
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"Name": "UI_CHALLENGES_ET_BLOODYMARY_TARGETDOWN_NAME",
|
||||
"OrderIndex": 10000,
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"RuntimeType": "Hit",
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"Type": "contract",
|
||||
"XpModifier": {}
|
||||
},
|
||||
{
|
||||
"Id": "265ff025-9e07-4224-b6db-675e44bd62b5",
|
||||
"Name": "UI_CHALLENGES_ET_BLOODYMARY_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bloodymary_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BLOODYMARY_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["87f8293a-29cd-4cb1-ade7-dd6bb056d38e"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "12937274-4d85-477d-b20d-fddb408a1015",
|
||||
"Name": "UI_CHALLENGES_ELUSIVE_TARGET_BANGKOK_NAME",
|
||||
"ImageName": "images/challenges/profile_challenges/elusive_target_bangkok.jpg",
|
||||
"Description": "UI_CHALLENGES_ELUSIVE_TARGET_BANGKOK_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 0
|
||||
},
|
||||
"Drops": ["TOKEN_OUTFIT_HERO_BANGKOKSUITANDGLOVES"],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_BANGKOK",
|
||||
"ParentLocationId": "LOCATION_PARENT_BANGKOK",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractStart": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.ContractType",
|
||||
"elusive"
|
||||
]
|
||||
},
|
||||
"Transition": "State_ValidContract"
|
||||
}
|
||||
},
|
||||
"State_ValidContract": {
|
||||
"ContractEnd": [
|
||||
{
|
||||
"Transition": "Success"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractTypes": ["elusive"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_BANGKOK",
|
||||
"GameVersion": "h2"
|
||||
"GameVersions": ["h2"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "43737b81-d253-44c8-aac8-f64d6711bdaf",
|
||||
|
@ -2066,6 +2067,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "8c613c59-8b59-46ac-8d4a-1f9516f9c790",
|
||||
|
@ -2832,6 +2834,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "148e8d0e-7676-46a2-8d55-3f4680cfce58",
|
||||
|
@ -4007,6 +4010,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "07736d0b-3c6b-48c6-9ff0-6d951cd07ee7",
|
||||
|
@ -4100,6 +4104,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "2db8ca18-bd44-483d-b7d9-eb726b259583",
|
||||
|
@ -8201,6 +8206,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "horror-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 10000,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "16d4b17d-ccc5-4d51-a27c-3d25c1b36fbb",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_BANGKOK",
|
||||
"GameVersion": "h1"
|
||||
"GameVersions": ["h1"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "701ba2b9-4611-472f-9a30-124e44b2b7ae",
|
||||
|
@ -2708,6 +2709,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "8c613c59-8b59-46ac-8d4a-1f9516f9c790",
|
||||
|
@ -3898,6 +3900,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "b73d4e03-d0a3-4882-b372-1ab606bf897f",
|
||||
|
@ -5198,6 +5201,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "5dbd7d1d-3f16-45ce-994e-646247a8e61f",
|
||||
|
@ -5293,6 +5297,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "horror-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 10000,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "8faaac29-153f-44c3-8c00-2ef8db4183c7",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_EDGY",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "2f812cf3-8ff2-4fbe-a74d-1f589cd86083",
|
||||
|
@ -872,6 +873,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "2113732a-0518-4b50-b490-45422d9fcf29",
|
||||
|
@ -1539,6 +1541,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "1ae5e7db-7cec-468c-bb98-0335836e8d8d",
|
||||
|
@ -1982,6 +1985,71 @@
|
|||
"ContractIds": ["12d83cb0-a2d6-4c01-b9d8-675ac635ee61"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "7423cc98-31d0-4e91-943b-ceb62d0b595b",
|
||||
"Name": "UI_CHALLENGE_GRASSSNAKE_EGGHUNT_NAME",
|
||||
"ImageName": "images/contracts/escalation/ContractEscalation_Edgy_Grassnake_Egghunt.jpg",
|
||||
"Description": "UI_CHALLENGE_GRASSSNAKE_EGGHUNT_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": ["PROP_MELEE_BLUE_EASTEREGG_PACIFYGAS"],
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_COMMUNITY",
|
||||
"Icon": "challenge_category_feats",
|
||||
"LocationId": "LOCATION_PARENT_EDGY",
|
||||
"ParentLocationId": "LOCATION_PARENT_EDGY",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Scope": "hit",
|
||||
"States": {
|
||||
"Start": {
|
||||
"BlueEgg": {
|
||||
"Transition": "CheckKill"
|
||||
}
|
||||
},
|
||||
"CheckKill": {
|
||||
"$timer": {
|
||||
"Condition": {
|
||||
"$after": 15
|
||||
},
|
||||
"Transition": "Start"
|
||||
},
|
||||
"Pacify": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
{
|
||||
"$any": {
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"Sedated"
|
||||
]
|
||||
},
|
||||
"in": "$Value.DamageEvents"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["hard", "feats"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["9d88605f-6871-46a8-bd46-9804ea04fca9"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "a015ff25-f9e8-481e-ab60-203eb8ecac40",
|
||||
"Name": "UI_CONTRACT_GRASSSNAKE_GROUP_TITLE",
|
||||
|
@ -2070,8 +2138,8 @@
|
|||
"MasteryXP": 1000
|
||||
},
|
||||
"Drops": [
|
||||
"PROP_DEVICE_REMOTE_EXPLOSIVE_LUST",
|
||||
"FIREARMS_PISTOL_DARTGUN_BLINDING_LUST"
|
||||
"FIREARMS_PISTOL_DARTGUN_BLINDING_LUST",
|
||||
"PROP_DEVICE_REMOTE_EXPLOSIVE_LUST"
|
||||
],
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
|
@ -2312,8 +2380,8 @@
|
|||
"MasteryXP": 1000
|
||||
},
|
||||
"Drops": [
|
||||
"PROP_MELEE_LEATHERBELT_ASYLUM",
|
||||
"FIREARMS_PISTOL_DARTGUN_SEDATIVE_ASYLUM"
|
||||
"FIREARMS_PISTOL_DARTGUN_SEDATIVE_ASYLUM",
|
||||
"PROP_MELEE_LEATHERBELT_ASYLUM"
|
||||
],
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
|
@ -2632,7 +2700,7 @@
|
|||
{
|
||||
"Id": "54ddadb4-edc7-47eb-a442-9c9536626168",
|
||||
"Name": "UI_PEACOCK_SHANGRILA",
|
||||
"ImageName": "images/contracts/escalation/contractescalation-shangrila.png",
|
||||
"ImageName": "images/contracts/escalation/contractescalation_shangrila.jpg",
|
||||
"Description": "UI_CHALLENGES_ESCLATION_COMPLETE_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
|
@ -2680,6 +2748,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "36c368c2-5808-40b5-8805-f62e458ac87c",
|
||||
|
@ -3192,6 +3261,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "0b63d39b-c53e-4fa4-a2a3-310a973eb819",
|
||||
|
@ -4038,585 +4108,109 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGENTUM",
|
||||
"Image": "images/challenges/categories/packargentum/tile.jpg",
|
||||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "argentum-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 6.5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "c2ae25d2-e4d0-4125-928d-44632ff2f7d1",
|
||||
"Name": "UI_CHALLENGES_ET_RADLER_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_radler_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_RADLER_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_EDGY_FOX",
|
||||
"ParentLocationId": "LOCATION_PARENT_EDGY",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["3f0b8f19-d5d4-4611-ac8f-480f81c18f54"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "00d2fefd-6bdb-4450-81bd-45a4bb3c3887",
|
||||
"Name": "UI_CHALLENGES_ET_RADLER_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_radler_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_RADLER_SILENT_ASSASSIN_DESC",
|
||||
"Id": "2b9f479e-6789-4f23-9eef-e17f268a2b11",
|
||||
"Name": "CHALLENGEPACK_ARGENTUM_FOXEXPLODER_NAME",
|
||||
"ImageName": "images/challenges/Categories/PackArgentum/Argentum_FoxExploder.jpg",
|
||||
"Description": "CHALLENGEPACK_ARGENTUM_FOXEXPLODER_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_EDGY_FOX",
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGENTUM",
|
||||
"Icon": "challenge_category_feats",
|
||||
"LocationId": "LOCATION_PARENT_EDGY",
|
||||
"ParentLocationId": "LOCATION_PARENT_EDGY",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"OrderIndex": 100004,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Constants": {
|
||||
"Goal": 5
|
||||
},
|
||||
"ContextListeners": {
|
||||
"Count": {
|
||||
"type": "challengecounter",
|
||||
"count": "$.Count",
|
||||
"total": "$.Goal"
|
||||
}
|
||||
},
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
"Count": 0,
|
||||
"Targets": [
|
||||
"8b29da09-461f-44d7-9042-d4fde829b9f2",
|
||||
"b8e7e65b-587e-471b-894d-282cda6614d4",
|
||||
"922deccd-7fb4-45d9-ae3d-2cf11915c403",
|
||||
"28cb7e91-bf9c-46ee-a371-1bd1448f1994",
|
||||
"633398ac-c4b4-4441-852d-ae6460172025",
|
||||
"1305c2e4-6394-4cfa-b873-22adbd0c9702",
|
||||
"abd1c0e7-e406-43bd-9185-419029c5bf3d",
|
||||
"eb024a5e-9580-49dc-a519-bb92c886f3b1",
|
||||
"252428ca-3f8e-4477-b2b9-58f18cff3e44",
|
||||
"2ab07903-e958-4af6-b01c-b62058745ce1",
|
||||
"f83376a4-6e56-4f2a-8122-151b272108fd"
|
||||
]
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
"FoxExploderActive": {
|
||||
"Transition": "ChallengeActive"
|
||||
}
|
||||
},
|
||||
"ChallengeActive": {
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["3f0b8f19-d5d4-4611-ac8f-480f81c18f54"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "d4e7f657-dfe7-4ea4-9ab6-923e209eac7b",
|
||||
"Name": "UI_CHALLENGES_ET_TOMORROWLAND_CONTRACT_STARTED_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/ET_Tomorrowland_ContractStarted.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_TOMORROWLAND_CONTRACT_STARTED_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 0
|
||||
},
|
||||
"Drops": ["TOKEN_OUTFIT_TOMORROWLAND_SUIT_REWARD"],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_EDGY_FOX",
|
||||
"ParentLocationId": "LOCATION_PARENT_EDGY",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractStart": {
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["1f0f3c70-b559-48ea-aca4-b64c8c762b69"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "ff37f182-1282-4e02-9cba-10396af41e17",
|
||||
"Name": "UI_CHALLENGES_ET_TOMORROWLAND_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/ET_Tomorrowland_TargetDown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_TOMORROWLAND_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_EDGY_FOX",
|
||||
"ParentLocationId": "LOCATION_PARENT_EDGY",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["1f0f3c70-b559-48ea-aca4-b64c8c762b69"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "caa00727-6063-44ec-ba2d-e62d42d50977",
|
||||
"Name": "UI_CHALLENGES_ET_TOMORROWLAND_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/ET_Tomorrowland_SilentAssassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_TOMORROWLAND_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_EDGY_FOX",
|
||||
"ParentLocationId": "LOCATION_PARENT_EDGY",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0.0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"$inarray": {
|
||||
"in": "$.Targets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillClass",
|
||||
"explosion"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$inc": "Count"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$.Count", "$.Goal"]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"Tags": ["argentum-pack", "story", "live", "hard"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["1f0f3c70-b559-48ea-aca4-b64c8c762b69"]
|
||||
"ContractIds": ["ebcd14b2-0786-4ceb-a2a4-e771f60d0125"]
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
@ -0,0 +1,593 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_EDGY",
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"OrderIndex": 5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "c2ae25d2-e4d0-4125-928d-44632ff2f7d1",
|
||||
"Name": "UI_CHALLENGES_ET_RADLER_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_radler_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_RADLER_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_EDGY_FOX",
|
||||
"ParentLocationId": "LOCATION_PARENT_EDGY",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["3f0b8f19-d5d4-4611-ac8f-480f81c18f54"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "00d2fefd-6bdb-4450-81bd-45a4bb3c3887",
|
||||
"Name": "UI_CHALLENGES_ET_RADLER_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_radler_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_RADLER_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_EDGY_FOX",
|
||||
"ParentLocationId": "LOCATION_PARENT_EDGY",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["3f0b8f19-d5d4-4611-ac8f-480f81c18f54"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "d4e7f657-dfe7-4ea4-9ab6-923e209eac7b",
|
||||
"Name": "UI_CHALLENGES_ET_TOMORROWLAND_CONTRACT_STARTED_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/ET_Tomorrowland_ContractStarted.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_TOMORROWLAND_CONTRACT_STARTED_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 0
|
||||
},
|
||||
"Drops": ["TOKEN_OUTFIT_TOMORROWLAND_SUIT_REWARD"],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_EDGY_FOX",
|
||||
"ParentLocationId": "LOCATION_PARENT_EDGY",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractStart": {
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["1f0f3c70-b559-48ea-aca4-b64c8c762b69"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "ff37f182-1282-4e02-9cba-10396af41e17",
|
||||
"Name": "UI_CHALLENGES_ET_TOMORROWLAND_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/ET_Tomorrowland_TargetDown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_TOMORROWLAND_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_EDGY_FOX",
|
||||
"ParentLocationId": "LOCATION_PARENT_EDGY",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["1f0f3c70-b559-48ea-aca4-b64c8c762b69"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "caa00727-6063-44ec-ba2d-e62d42d50977",
|
||||
"Name": "UI_CHALLENGES_ET_TOMORROWLAND_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/ET_Tomorrowland_SilentAssassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_TOMORROWLAND_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_EDGY_FOX",
|
||||
"ParentLocationId": "LOCATION_PARENT_EDGY",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0.0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["1f0f3c70-b559-48ea-aca4-b64c8c762b69"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_TRAPPED",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "3d0ff5c3-6e51-4827-951f-d45d8d21b3ea",
|
||||
|
@ -101,6 +102,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "18636f91-e1a9-4091-935c-adbf6cc3de55",
|
||||
|
@ -587,6 +589,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "22070cef-2ecb-4a65-ab16-cb4bfd63785e",
|
||||
|
@ -676,8 +679,8 @@
|
|||
"MasteryXP": 1000
|
||||
},
|
||||
"Drops": [
|
||||
"PROP_MELEE_KATANA_WHITE_NINJA",
|
||||
"FIREARMS_SNIPER_SIEGER_300_WHITE_NINJA"
|
||||
"FIREARMS_SNIPER_SIEGER_300_WHITE_NINJA",
|
||||
"PROP_MELEE_KATANA_WHITE_NINJA"
|
||||
],
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
|
@ -1146,6 +1149,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "3210fa62-67a5-4e1f-ba50-02c451d47875",
|
||||
|
@ -1200,6 +1204,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "92643706-6f98-4b1c-9700-ee4819aaadb4",
|
||||
|
@ -1725,6 +1730,145 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGENTUM",
|
||||
"Image": "images/challenges/categories/packargentum/tile.jpg",
|
||||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "argentum-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 6.5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "1bcab2a7-626e-4bf2-a9af-7adb823cdd4a",
|
||||
"Name": "CHALLENGEPACK_ARGENTUM_WOLVERINEFINISHER_NAME",
|
||||
"ImageName": "images/challenges/Categories/PackArgentum/Argentum_WolverineFinisher.jpg",
|
||||
"Description": "CHALLENGEPACK_ARGENTUM_WOLVERINEFINISHER_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGENTUM",
|
||||
"Icon": "challenge_category_feats",
|
||||
"LocationId": "LOCATION_PARENT_TRAPPED",
|
||||
"ParentLocationId": "LOCATION_PARENT_TRAPPED",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 100005,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Constants": {
|
||||
"Goal": 47
|
||||
},
|
||||
"ContextListeners": {
|
||||
"Count": {
|
||||
"type": "challengecounter",
|
||||
"count": "$.Count",
|
||||
"total": "$.Goal"
|
||||
}
|
||||
},
|
||||
"Context": {
|
||||
"Count": 0,
|
||||
"Targets": [
|
||||
"006bff27-d8b9-480e-9308-fac9a447d038",
|
||||
"0f8fd017-f30f-48aa-bb95-f0eed8f99eb0",
|
||||
"1063ec04-ae7f-47ce-9f67-e3ed87b910fb",
|
||||
"10a1344f-25b5-4d9b-99df-cc1a3e132701",
|
||||
"16bc3b56-63a5-48fd-beab-d4d2adf13b29",
|
||||
"17aa42ce-9de5-42c3-a0d7-42650aeb5ccb",
|
||||
"28a8ac36-edba-4b02-81f9-4d8e85a6e37c",
|
||||
"29f7350c-f28d-4756-b03b-8158806c1a19",
|
||||
"2db48fc3-1f43-4681-82b4-26289c7373bc",
|
||||
"3354e12a-3e7b-4693-bb63-a73d12a682e3",
|
||||
"49c4b42b-773d-416a-a48d-5cb7f6254290",
|
||||
"4c549646-3605-406a-8eb6-a56da52045a1",
|
||||
"56ba1725-4d2b-4ba0-9842-79457af2eca3",
|
||||
"5a62d8d2-1208-4f11-b95d-8f6cb11cd02b",
|
||||
"5b82b41f-f95e-4403-919c-6a67522fc94e",
|
||||
"606d9bbb-76b7-4e0f-a24f-e5b7a1257313",
|
||||
"6504cb26-c5d0-4c99-9aa2-7df9d18c5201",
|
||||
"6c232a4d-cd64-42d8-a78f-e0570dae7cd2",
|
||||
"6ecd7f77-bebc-4f7a-939d-7e16f4b678b1",
|
||||
"71854eb2-8e6c-4d19-83d2-fbdc7879bf6f",
|
||||
"759c67d0-18e2-49b2-a417-5daa9a450819",
|
||||
"796b0ee9-9261-4e55-ab6f-5a4c53f86e4e",
|
||||
"7bba60be-4b86-4a3c-be67-3937ab982ef2",
|
||||
"8255c9aa-1a59-4750-89f1-46542207e00d",
|
||||
"82615a87-f968-4be8-82d9-eb8089ad92ee",
|
||||
"8d6aebab-d94a-4adf-8770-5985a3809ece",
|
||||
"9830d3c6-93e3-49c8-b0c8-abdf3dc018ce",
|
||||
"a0766e5c-fadb-4900-8cb1-c776b56fabf5",
|
||||
"a3570e03-43f8-46ae-896a-e0a5803cfb6f",
|
||||
"a6f7e9f3-e25c-48c6-8f0b-755a2e864123",
|
||||
"aaafb8c3-8697-4121-8c97-1ec2c3502742",
|
||||
"b3840fb0-e732-4f35-b858-fc4bed036e46",
|
||||
"bb3efb54-cb83-4c63-adda-12b7da545a77",
|
||||
"bb7b6b31-adce-4a50-91fe-7447128b874b",
|
||||
"bbfbf4da-e6e1-4f14-b2ee-9325f9615408",
|
||||
"bdf572b3-7e6e-46f6-98b6-887ec99804de",
|
||||
"c2dcbadf-4101-4172-b458-0f02b9145c0b",
|
||||
"c7840085-c12a-4438-b032-f54bbe006030",
|
||||
"cd6e9ee7-282c-49fa-a537-1802b4749e2c",
|
||||
"ce95c773-3153-4909-a356-16f071297582",
|
||||
"ced15cfa-21f7-4078-9ee7-f8b3ef1b1b59",
|
||||
"cf3c9e6e-4e3c-43dd-a663-897ffaa165f2",
|
||||
"d47a96d9-91cb-42cf-8193-6bccc74a775e",
|
||||
"d4fea004-6a7d-409f-bf4b-b229ca3cb355",
|
||||
"dca83dc1-c96e-4b75-b252-8abddd08617d",
|
||||
"f423f166-ed6d-4c3b-8d55-cd1c8a8b84ac",
|
||||
"38e6e74d-fe56-4ae8-8194-d5ec44c4d335"
|
||||
]
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"WolverineFinisherActive": {
|
||||
"Transition": "ChallengeActive"
|
||||
}
|
||||
},
|
||||
"ChallengeActive": {
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$inarray": {
|
||||
"in": "$.Targets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Actions": {
|
||||
"$inc": "Count"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$.Count", "$.Goal"]
|
||||
},
|
||||
"Transition": "AllKilled"
|
||||
}
|
||||
]
|
||||
},
|
||||
"AllKilled": {
|
||||
"EndingTriggered": {
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["argentum-pack", "story", "live", "hard"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["a3e19d55-64a6-4282-bb3c-d18c3f3e6e29"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_WET",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "00fcea23-9659-447a-a717-9502810e2930",
|
||||
|
@ -880,6 +881,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "04ef301f-a62d-47f6-8cb8-3b8697e0c06c",
|
||||
|
@ -1709,6 +1711,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "1a68c357-02a0-40a2-99e7-73d76ec5095d",
|
||||
|
@ -3379,6 +3382,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "09675ea9-4cf3-4b80-a722-329b8e48e687",
|
||||
|
@ -3472,6 +3476,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "0af3980d-6bcb-42f6-ac4c-d0241606500a",
|
||||
|
@ -4318,31 +4323,32 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGENTUM",
|
||||
"Image": "images/challenges/categories/packargentum/tile.jpg",
|
||||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "argentum-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 6.5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "cfd77118-ee34-49b0-b2e6-0fcbb69948f6",
|
||||
"Name": "UI_CHALLENGES_ET_REDSNAPPER_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_redsnapper_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_REDSNAPPER_TARGETDOWN_DESC",
|
||||
"Id": "f8ba2bd3-dcd7-453f-aa64-c0ed6992e24b",
|
||||
"Name": "CHALLENGEPACK_ARGENTUM_RATSNIPER_NAME",
|
||||
"ImageName": "images/challenges/Categories/PackArgentum/Argentum_RatSniper.jpg",
|
||||
"Description": "CHALLENGEPACK_ARGENTUM_RATSNIPER_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
"MasteryXP": 1000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGENTUM",
|
||||
"Icon": "challenge_category_feats",
|
||||
"LocationId": "LOCATION_WET_RAT",
|
||||
"ParentLocationId": "LOCATION_PARENT_WET",
|
||||
"Type": "contract",
|
||||
"Type": "location",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"OrderIndex": 100001,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
|
@ -4350,248 +4356,125 @@
|
|||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"EnterARGENTUM_Volume": {
|
||||
"Transition": "InPosition"
|
||||
}
|
||||
},
|
||||
"InPosition": {
|
||||
"ExitARGENTUM_Volume": {
|
||||
"Transition": "Start"
|
||||
},
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillItemCategory",
|
||||
"sniperrifle"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsHeadshot",
|
||||
true
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["6fad7901-279f-45df-ab8d-087a3cb06dcc"]
|
||||
"Tags": ["argentum-pack", "story", "live", "easy"]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Id": "c823eaa7-aaec-42e5-a160-7b7d7b3c719e",
|
||||
"Name": "UI_CHALLENGES_ET_REDSNAPPER_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_redsnapper_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_REDSNAPPER_SILENT_ASSASSIN_DESC",
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_CHEESECAKE",
|
||||
"Image": "images/challenges/categories/packcheesecake/tile.jpg",
|
||||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "cheesecake-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 6.1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "eff06c02-2410-4226-1abc-076a2e71ee97",
|
||||
"Name": "UI_PEACOCK_CHALLENGEPACK_CHEESECAKE_CHONGQINGMARINATE_NAME",
|
||||
"ImageName": "images/challenges/categories/packcheesecake/cheesecake_chongqingmarinate.jpg",
|
||||
"Description": "UI_PEACOCK_CHALLENGEPACK_CHEESECAKE_CHONGQINGMARINATE_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_WET_RAT",
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_CHEESECAKE",
|
||||
"Icon": "challenge_category_feats",
|
||||
"LocationId": "LOCATION_PARENT_WET",
|
||||
"ParentLocationId": "LOCATION_PARENT_WET",
|
||||
"Type": "contract",
|
||||
"Type": "parentlocation",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"OrderIndex": 100003,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"Context": { "PoisonedTargets": [] },
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
"$Value.KillClass",
|
||||
"poison"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"$eq": [
|
||||
"$Value.OutfitRepositoryId",
|
||||
"c5f6dd2a-3600-40be-9a82-bbf5d360c379"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"PoisonedTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
},
|
||||
"Transition": "CheckDumpInOcean"
|
||||
}
|
||||
},
|
||||
"CheckDumpInOcean": {
|
||||
"DumpInOcean": {
|
||||
"Condition": {
|
||||
"$inarray": {
|
||||
"in": "$.PoisonedTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["6fad7901-279f-45df-ab8d-087a3cb06dcc"]
|
||||
}
|
||||
"Tags": ["cheesecake-pack", "story", "live", "medium"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,287 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_WET",
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"OrderIndex": 5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "cfd77118-ee34-49b0-b2e6-0fcbb69948f6",
|
||||
"Name": "UI_CHALLENGES_ET_REDSNAPPER_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_redsnapper_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_REDSNAPPER_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_WET_RAT",
|
||||
"ParentLocationId": "LOCATION_PARENT_WET",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["6fad7901-279f-45df-ab8d-087a3cb06dcc"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "c823eaa7-aaec-42e5-a160-7b7d7b3c719e",
|
||||
"Name": "UI_CHALLENGES_ET_REDSNAPPER_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_redsnapper_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_REDSNAPPER_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_WET_RAT",
|
||||
"ParentLocationId": "LOCATION_PARENT_WET",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["6fad7901-279f-45df-ab8d-087a3cb06dcc"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_COLORADO",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "8ade8bd8-f23d-4c7a-b453-231c3c93f8f0",
|
||||
|
@ -2424,6 +2425,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "12790e00-2e8f-4fb4-a492-074520fbae13",
|
||||
|
@ -3057,6 +3059,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "0324a4ae-6e73-4736-8c67-09be6263f847",
|
||||
|
@ -3963,6 +3966,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "22709cab-3fb9-423e-a297-1bde39485aad",
|
||||
|
@ -4187,6 +4191,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "5146dec1-9b6e-4037-9462-134348b1722d",
|
||||
|
@ -5260,603 +5265,6 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "d8f8a8fb-4b3c-4e2a-ac20-2d864a449ebe",
|
||||
"Name": "UI_CHALLENGES_ET_BUSHWACKER_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bushwacker_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BUSHWACKER_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_COLORADO",
|
||||
"ParentLocationId": "LOCATION_PARENT_COLORADO",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["550c4d75-ca87-4be7-a18e-caf30e6c8136"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "bade239d-1102-4ddf-9dfa-c848c007bf3e",
|
||||
"Name": "UI_CHALLENGES_ET_BUSHWACKER_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bushwacker_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BUSHWACKER_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_COLORADO",
|
||||
"ParentLocationId": "LOCATION_PARENT_COLORADO",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["550c4d75-ca87-4be7-a18e-caf30e6c8136"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "3d2e38c4-a96c-43cb-bec1-d775e28f3fd2",
|
||||
"Name": "UI_CHALLENGES_ET_FLIRTINI_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_flirtini_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_FLIRTINI_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_COLORADO",
|
||||
"ParentLocationId": "LOCATION_PARENT_COLORADO",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["655c5a57-69d1-48b6-a14b-2ae396c16174"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "96377c1f-e2e4-4008-bf9f-ced0e9a016e9",
|
||||
"Name": "UI_CHALLENGES_ET_FLIRTINI_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_flirtini_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_FLIRTINI_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_COLORADO",
|
||||
"ParentLocationId": "LOCATION_PARENT_COLORADO",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["655c5a57-69d1-48b6-a14b-2ae396c16174"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "efd539cf-7b0e-4940-b8dd-a7407deea12f",
|
||||
"Name": "UI_CHALLENGES_ELUSIVE_TARGET_COLORADO_NAME",
|
||||
"ImageName": "images/challenges/profile_challenges/elusive_target_colorado.jpg",
|
||||
"Description": "UI_CHALLENGES_ELUSIVE_TARGET_COLORADO_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 0
|
||||
},
|
||||
"Drops": ["TOKEN_OUTFIT_HERO_COLORADOSUIT_ALTERNATIVE"],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_COLORADO",
|
||||
"ParentLocationId": "LOCATION_PARENT_COLORADO",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractStart": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.ContractType",
|
||||
"elusive"
|
||||
]
|
||||
},
|
||||
"Transition": "State_ValidContract"
|
||||
}
|
||||
},
|
||||
"State_ValidContract": {
|
||||
"ContractEnd": [
|
||||
{
|
||||
"Transition": "Success"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractTypes": ["elusive"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,606 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_COLORADO",
|
||||
"GameVersions": ["h2", "h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"OrderIndex": 5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "d8f8a8fb-4b3c-4e2a-ac20-2d864a449ebe",
|
||||
"Name": "UI_CHALLENGES_ET_BUSHWACKER_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bushwacker_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BUSHWACKER_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_COLORADO",
|
||||
"ParentLocationId": "LOCATION_PARENT_COLORADO",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["550c4d75-ca87-4be7-a18e-caf30e6c8136"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "bade239d-1102-4ddf-9dfa-c848c007bf3e",
|
||||
"Name": "UI_CHALLENGES_ET_BUSHWACKER_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bushwacker_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BUSHWACKER_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_COLORADO",
|
||||
"ParentLocationId": "LOCATION_PARENT_COLORADO",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["550c4d75-ca87-4be7-a18e-caf30e6c8136"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "3d2e38c4-a96c-43cb-bec1-d775e28f3fd2",
|
||||
"Name": "UI_CHALLENGES_ET_FLIRTINI_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_flirtini_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_FLIRTINI_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_COLORADO",
|
||||
"ParentLocationId": "LOCATION_PARENT_COLORADO",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["655c5a57-69d1-48b6-a14b-2ae396c16174"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "96377c1f-e2e4-4008-bf9f-ced0e9a016e9",
|
||||
"Name": "UI_CHALLENGES_ET_FLIRTINI_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_flirtini_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_FLIRTINI_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_COLORADO",
|
||||
"ParentLocationId": "LOCATION_PARENT_COLORADO",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["655c5a57-69d1-48b6-a14b-2ae396c16174"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "efd539cf-7b0e-4940-b8dd-a7407deea12f",
|
||||
"Name": "UI_CHALLENGES_ELUSIVE_TARGET_COLORADO_NAME",
|
||||
"ImageName": "images/challenges/profile_challenges/elusive_target_colorado.jpg",
|
||||
"Description": "UI_CHALLENGES_ELUSIVE_TARGET_COLORADO_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 0
|
||||
},
|
||||
"Drops": ["TOKEN_OUTFIT_HERO_COLORADOSUIT_ALTERNATIVE"],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_COLORADO",
|
||||
"ParentLocationId": "LOCATION_PARENT_COLORADO",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractStart": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.ContractType",
|
||||
"elusive"
|
||||
]
|
||||
},
|
||||
"Transition": "State_ValidContract"
|
||||
}
|
||||
},
|
||||
"State_ValidContract": {
|
||||
"ContractEnd": [
|
||||
{
|
||||
"Transition": "Success"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractTypes": ["elusive"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_COLORADO",
|
||||
"GameVersion": "h2"
|
||||
"GameVersions": ["h2"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "8ade8bd8-f23d-4c7a-b453-231c3c93f8f0",
|
||||
|
@ -1864,6 +1865,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "12790e00-2e8f-4fb4-a492-074520fbae13",
|
||||
|
@ -2395,6 +2397,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "0324a4ae-6e73-4736-8c67-09be6263f847",
|
||||
|
@ -3575,6 +3578,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "22709cab-3fb9-423e-a297-1bde39485aad",
|
||||
|
@ -3752,6 +3756,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "5146dec1-9b6e-4037-9462-134348b1722d",
|
||||
|
@ -7117,6 +7122,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "scare-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 10000,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "2f1ef5b2-2df9-428f-ad62-9df951864152",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_COLORADO",
|
||||
"GameVersion": "h1"
|
||||
"GameVersions": ["h1"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "5146dec1-9b6e-4037-9462-134348b1722d",
|
||||
|
@ -2297,6 +2298,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "47b2298d-d772-4926-ac60-05085eaa17a1",
|
||||
|
@ -3258,6 +3260,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "0324a4ae-6e73-4736-8c67-09be6263f847",
|
||||
|
@ -4667,6 +4670,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "22709cab-3fb9-423e-a297-1bde39485aad",
|
||||
|
@ -4848,6 +4852,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "scare-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 10000,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "ab5edd71-b1f9-416c-bd62-46129b75a1dc",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_ANCESTRAL",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "150f0c8a-34a7-4bdf-8d92-97915c8ac64d",
|
||||
|
@ -1171,6 +1172,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "43186811-5dab-4378-be8d-3e742eac6159",
|
||||
|
@ -2079,6 +2081,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "12e2d93c-1433-429f-a802-32e611a7ba64",
|
||||
|
@ -4587,7 +4590,7 @@
|
|||
{
|
||||
"Id": "2934a0ea-8e0a-4aee-bc38-f3a015274746",
|
||||
"Name": "UI_PEACOCK_ROSEBUSH",
|
||||
"ImageName": "images/contracts/escalation/contractescalation_rosebush.png",
|
||||
"ImageName": "images/contracts/escalation/contractescalation_rosebush.jpg",
|
||||
"Description": "UI_CHALLENGES_ESCLATION_COMPLETE_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
|
@ -4635,6 +4638,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "1524234b-6f85-426d-92d6-a2dc6d42cfbd",
|
||||
|
@ -4686,6 +4690,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "647278f4-e20f-4c42-8a8b-5d2cf30591c0",
|
||||
|
@ -5530,623 +5535,6 @@
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "aed4cac6-0f11-4e74-9a7c-90748b3c7384",
|
||||
"Name": "UI_CHALLENGES_ET_BRAMBLE_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bramble_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRAMBLE_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_ANCESTRAL_BULLDOG",
|
||||
"ParentLocationId": "LOCATION_PARENT_ANCESTRAL",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["92951377-419d-4c31-aa21-2a3f03ef82d0"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "5c9ebf15-0f66-4a3c-8117-b0515b81f6f1",
|
||||
"Name": "UI_CHALLENGES_ET_BRAMBLE_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bramble_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRAMBLE_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_ANCESTRAL_BULLDOG",
|
||||
"ParentLocationId": "LOCATION_PARENT_ANCESTRAL",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["92951377-419d-4c31-aa21-2a3f03ef82d0"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "009da118-9bbd-4378-a3af-7eb6f65467e0",
|
||||
"Name": "UI_CHALLENGES_ET_BRAMBLE_RETRIEVEPAINTING_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bramble_retrievedpainting.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRAMBLE_RETRIEVEPAINTING_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_ANCESTRAL_BULLDOG",
|
||||
"ParentLocationId": "LOCATION_PARENT_ANCESTRAL",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Constants": {
|
||||
"Target": "49853af0-d50b-4959-a446-15429b1f4530"
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ItemPickedUp": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$.Target",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["92951377-419d-4c31-aa21-2a3f03ef82d0"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "e33b5539-b15e-4652-be2b-f154f12336fd",
|
||||
"Name": "UI_CHALLENGES_ET_VESPER_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_vesper_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_VESPER_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_ANCESTRAL_BULLDOG",
|
||||
"ParentLocationId": "LOCATION_PARENT_ANCESTRAL",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Constants": {
|
||||
"RequiredTargetCount": 2
|
||||
},
|
||||
"Context": {
|
||||
"KilledTargetCount": 0
|
||||
},
|
||||
"ContextListeners": {
|
||||
"KilledTargetCount": {
|
||||
"type": "challengecounter",
|
||||
"count": "$.KilledTargetCount",
|
||||
"total": "$.RequiredTargetCount"
|
||||
}
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$inc": "KilledTargetCount"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$.KilledTargetCount",
|
||||
"$.RequiredTargetCount"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["1fcaff1b-7fa3-4b9f-a586-9c7a1689b48d"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "4dfe894f-a690-4d7e-bcf7-778fe8cf0647",
|
||||
"Name": "UI_CHALLENGES_ET_VESPER_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_vesper_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_VESPER_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_ANCESTRAL_BULLDOG",
|
||||
"ParentLocationId": "LOCATION_PARENT_ANCESTRAL",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["1fcaff1b-7fa3-4b9f-a586-9c7a1689b48d"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,626 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_ANCESTRAL",
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"OrderIndex": 5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "aed4cac6-0f11-4e74-9a7c-90748b3c7384",
|
||||
"Name": "UI_CHALLENGES_ET_BRAMBLE_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bramble_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRAMBLE_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_ANCESTRAL_BULLDOG",
|
||||
"ParentLocationId": "LOCATION_PARENT_ANCESTRAL",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["92951377-419d-4c31-aa21-2a3f03ef82d0"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "5c9ebf15-0f66-4a3c-8117-b0515b81f6f1",
|
||||
"Name": "UI_CHALLENGES_ET_BRAMBLE_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bramble_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRAMBLE_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_ANCESTRAL_BULLDOG",
|
||||
"ParentLocationId": "LOCATION_PARENT_ANCESTRAL",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["92951377-419d-4c31-aa21-2a3f03ef82d0"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "009da118-9bbd-4378-a3af-7eb6f65467e0",
|
||||
"Name": "UI_CHALLENGES_ET_BRAMBLE_RETRIEVEPAINTING_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_bramble_retrievedpainting.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_BRAMBLE_RETRIEVEPAINTING_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_ANCESTRAL_BULLDOG",
|
||||
"ParentLocationId": "LOCATION_PARENT_ANCESTRAL",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Constants": {
|
||||
"Target": "49853af0-d50b-4959-a446-15429b1f4530"
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ItemPickedUp": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$.Target",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["92951377-419d-4c31-aa21-2a3f03ef82d0"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "e33b5539-b15e-4652-be2b-f154f12336fd",
|
||||
"Name": "UI_CHALLENGES_ET_VESPER_TARGETDOWN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_vesper_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_VESPER_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_ANCESTRAL_BULLDOG",
|
||||
"ParentLocationId": "LOCATION_PARENT_ANCESTRAL",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Constants": {
|
||||
"RequiredTargetCount": 2
|
||||
},
|
||||
"Context": {
|
||||
"KilledTargetCount": 0
|
||||
},
|
||||
"ContextListeners": {
|
||||
"KilledTargetCount": {
|
||||
"type": "challengecounter",
|
||||
"count": "$.KilledTargetCount",
|
||||
"total": "$.RequiredTargetCount"
|
||||
}
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$inc": "KilledTargetCount"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$.KilledTargetCount",
|
||||
"$.RequiredTargetCount"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["1fcaff1b-7fa3-4b9f-a586-9c7a1689b48d"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "4dfe894f-a690-4d7e-bcf7-778fe8cf0647",
|
||||
"Name": "UI_CHALLENGES_ET_VESPER_SILENT_ASSASSIN_NAME",
|
||||
"ImageName": "images/challenges/elusive_target/et_vesper_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_VESPER_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_ANCESTRAL_BULLDOG",
|
||||
"ParentLocationId": "LOCATION_PARENT_ANCESTRAL",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["1fcaff1b-7fa3-4b9f-a586-9c7a1689b48d"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_GOLDEN",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "02eed2d0-872d-4c42-a813-f7083686a8c1",
|
||||
|
@ -953,6 +954,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "0d160e0a-99ad-4ec3-9865-30d8da8076df",
|
||||
|
@ -2062,6 +2064,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "0c668dd3-bd73-4246-adf0-87b4c8e38bda",
|
||||
|
@ -3265,6 +3268,7 @@
|
|||
"Icon": "challenge_category_targets",
|
||||
"CategoryId": "targets",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_PROFESSIONAL",
|
||||
"OrderIndex": 3,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "16b2d5e1-43a2-4cea-adf1-1d6af2fe8df6",
|
||||
|
@ -3358,6 +3362,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "2694b2ff-224c-4bde-858d-1671a1dbc580",
|
||||
|
@ -4204,280 +4209,112 @@
|
|||
]
|
||||
},
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_CHEESECAKE",
|
||||
"Image": "images/challenges/categories/packcheesecake/tile.jpg",
|
||||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "cheesecake-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 6.1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "3beb74aa-45f7-4b70-bbf0-75045f6c525a",
|
||||
"Name": "UI_CHALLENGES_ET_GIBSON_TARGETDOWN_TITLE",
|
||||
"ImageName": "images/challenges/elusive_target/et_gibson_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_GIBSON_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_GOLDEN_GECKO",
|
||||
"ParentLocationId": "LOCATION_PARENT_GOLDEN",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b2c0251e-1803-4e12-b860-b9fa6ce5c004"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "05efca7c-7184-4792-8d40-fb1d0912ab68",
|
||||
"Name": "UI_CHALLENGES_ET_GIBSON_SILENT_ASSASSIN_TITLE",
|
||||
"ImageName": "images/challenges/elusive_target/et_gibson_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_GIBSON_SILENT_ASSASSIN_DESC",
|
||||
"Id": "c098a7cf-8c49-02ce-475e-20beaed99712",
|
||||
"Name": "UI_PEACOCK_CHALLENGEPACK_CHEESECAKE_DUBAIPANPACIFY_NAME",
|
||||
"ImageName": "images/challenges/categories/packcheesecake/cheesecake_dubaipanpacify.jpg",
|
||||
"Description": "UI_PEACOCK_CHALLENGEPACK_CHEESECAKE_DUBAIPANPACIFY_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsPlayable": true,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_GOLDEN_GECKO",
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_CHEESECAKE",
|
||||
"Icon": "challenge_category_feats",
|
||||
"LocationId": "LOCATION_PARENT_GOLDEN",
|
||||
"ParentLocationId": "LOCATION_PARENT_GOLDEN",
|
||||
"Type": "contract",
|
||||
"Type": "parentlocation",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"OrderIndex": 100005,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"Constants": {
|
||||
"Goal": 2
|
||||
},
|
||||
"Context": {
|
||||
"PacifiedTargets": 0
|
||||
},
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"setpieces": {
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.RepositoryId",
|
||||
"fbfa76d6-9f9b-40dd-869a-2b3fc2361ce5"
|
||||
]
|
||||
},
|
||||
"Transition": "MeetingRoomLocked"
|
||||
}
|
||||
},
|
||||
"MeetingRoomLocked": {
|
||||
"Pacify": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
"$Value.RepositoryId",
|
||||
"bd0689d6-07b4-4757-b8ee-cac19f1c9e16"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
"$Value.RepositoryId",
|
||||
"9571d196-8d67-4d94-8dad-6e2d970d7a91"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillItemRepositoryId",
|
||||
"bce6ce09-6ead-4d72-8438-2c7780770e70"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$inc": "PacifiedTargets"
|
||||
},
|
||||
"Transition": "CheckCount"
|
||||
}
|
||||
},
|
||||
"CheckCount": {
|
||||
"-": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$.PacifiedTargets",
|
||||
"$.Goal"
|
||||
]
|
||||
},
|
||||
"Transition": "AwaitingContractEnd"
|
||||
},
|
||||
{
|
||||
"Transition": "MeetingRoomLocked"
|
||||
}
|
||||
]
|
||||
},
|
||||
"AwaitingContractEnd": {
|
||||
"ContractEnd": {
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b2c0251e-1803-4e12-b860-b9fa6ce5c004"]
|
||||
}
|
||||
"Tags": ["cheesecake-pack", "story", "live", "medium"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -0,0 +1,287 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_GOLDEN",
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Image": "images/challenges/categories/elusive/tile.jpg",
|
||||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"OrderIndex": 5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "3beb74aa-45f7-4b70-bbf0-75045f6c525a",
|
||||
"Name": "UI_CHALLENGES_ET_GIBSON_TARGETDOWN_TITLE",
|
||||
"ImageName": "images/challenges/elusive_target/et_gibson_targetdown.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_GIBSON_TARGETDOWN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 2000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_GOLDEN_GECKO",
|
||||
"ParentLocationId": "LOCATION_PARENT_GOLDEN",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"Kill": {
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "medium", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b2c0251e-1803-4e12-b860-b9fa6ce5c004"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Id": "05efca7c-7184-4792-8d40-fb1d0912ab68",
|
||||
"Name": "UI_CHALLENGES_ET_GIBSON_SILENT_ASSASSIN_TITLE",
|
||||
"ImageName": "images/challenges/elusive_target/et_gibson_silentassassin.jpg",
|
||||
"Description": "UI_CHALLENGES_ET_GIBSON_SILENT_ASSASSIN_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": [],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
"Icon": "elusive",
|
||||
"LocationId": "LOCATION_GOLDEN_GECKO",
|
||||
"ParentLocationId": "LOCATION_PARENT_GOLDEN",
|
||||
"Type": "contract",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 10000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Context": {
|
||||
"Witnesses": [],
|
||||
"KilledTargets": [],
|
||||
"RecordingDestroyed": true,
|
||||
"LastAccidentTime": 0
|
||||
},
|
||||
"Scope": "session",
|
||||
"States": {
|
||||
"Start": {
|
||||
"ContractEnd": {
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
true,
|
||||
"$.RecordingDestroyed"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$all": {
|
||||
"in": "$.Witnesses",
|
||||
"?": {
|
||||
"$any": {
|
||||
"in": "$.KilledTargets",
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$.##"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
},
|
||||
"AccidentBodyFound": {
|
||||
"$set": ["LastAccidentTime", "$Timestamp"]
|
||||
},
|
||||
"Witnesses": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Spotted": {
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"in": "$Value",
|
||||
"?": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$.#"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"Kill": [
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.KillContext",
|
||||
1
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": ["$Value.IsTarget", true]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"KilledTargets",
|
||||
"$Value.RepositoryId"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"CrowdNPC_Died": {
|
||||
"Transition": "Failure"
|
||||
},
|
||||
"MurderedBodySeen": [
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
true
|
||||
]
|
||||
},
|
||||
"Actions": {
|
||||
"$pushunique": [
|
||||
"Witnesses",
|
||||
"$Value.Witness"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$and": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.IsWitnessTarget",
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"$not": {
|
||||
"$eq": [
|
||||
"$.LastAccidentTime",
|
||||
"$Timestamp"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Transition": "Failure"
|
||||
}
|
||||
],
|
||||
"SecuritySystemRecorder": [
|
||||
{
|
||||
"Actions": {
|
||||
"$set": [
|
||||
"RecordingDestroyed",
|
||||
false
|
||||
]
|
||||
},
|
||||
"Condition": {
|
||||
"$eq": ["$Value.event", "spotted"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"Actions": {
|
||||
"$set": ["RecordingDestroyed", true]
|
||||
},
|
||||
"Condition": {
|
||||
"$or": [
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"erased"
|
||||
]
|
||||
},
|
||||
{
|
||||
"$eq": [
|
||||
"$Value.event",
|
||||
"destroyed"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["story", "hard", "elusive"],
|
||||
"InclusionData": {
|
||||
"ContractIds": ["b2c0251e-1803-4e12-b860-b9fa6ce5c004"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_ICA_FACILITY",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "0b0289de-73c9-42e5-8133-b2dbe46b454b",
|
||||
|
@ -600,6 +601,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "5b630176-2d35-4328-b1b2-51df4c2c0bb3",
|
||||
|
@ -1088,6 +1090,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "18f5e1fd-736c-44de-a4e1-c3e70357ec7c",
|
||||
|
@ -2290,6 +2293,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "85bbb639-40e3-4aa5-87aa-388a1cad2404",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_ICA_FACILITY",
|
||||
"GameVersion": "h2"
|
||||
"GameVersions": ["h2"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "0b0289de-73c9-42e5-8133-b2dbe46b454b",
|
||||
|
@ -590,6 +591,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "210f75bf-8094-4a4a-90f3-da23b4e73ad9",
|
||||
|
@ -1109,6 +1111,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "18f5e1fd-736c-44de-a4e1-c3e70357ec7c",
|
||||
|
@ -2216,6 +2219,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "068dc309-f4e7-4551-9719-31e064f4cc6e",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "LOCATION_PARENT_ICA_FACILITY",
|
||||
"GameVersion": "h1"
|
||||
"GameVersions": ["h1"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_assassination",
|
||||
"CategoryId": "assassination",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_SIGNATUREKILL",
|
||||
"OrderIndex": 0,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "eed87874-5a23-4230-a5ba-220e90372721",
|
||||
|
@ -974,6 +975,7 @@
|
|||
"Icon": "challenge_category_discovery",
|
||||
"CategoryId": "discovery",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_EXPLORATION",
|
||||
"OrderIndex": 1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "72db2871-a24c-4fa9-8f78-25c9a004ee02",
|
||||
|
@ -1523,6 +1525,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "c984bb30-920c-4d0e-9088-06f7c9b4629e",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "GLOBAL_FEATURED_CHALLENGES",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "featured",
|
||||
"CategoryId": "featured_hm1_hm2",
|
||||
"Description": "",
|
||||
"OrderIndex": 9,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "3d6eb80b-16af-4504-b23a-3465578b2cf9",
|
||||
|
@ -1216,6 +1217,7 @@
|
|||
"Icon": "featured",
|
||||
"CategoryId": "featured_hm3",
|
||||
"Description": "",
|
||||
"OrderIndex": 10,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "2f58dac5-8170-4e85-a1b8-1c1cdca9cbd3",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "GLOBAL_FEATURED_CHALLENGES",
|
||||
"GameVersion": "h2"
|
||||
"GameVersions": ["h2"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "featured",
|
||||
"CategoryId": "featured",
|
||||
"Description": "",
|
||||
"OrderIndex": 11,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "31e8e58f-86c1-4f1b-9341-d312cd9f28f8",
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "GLOBAL_ARGENTUM_CHALLENGES",
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGENTUM",
|
||||
"Image": "images/challenges/categories/packargentum/tile.jpg",
|
||||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "argentum-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 6.5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "eca7ab97-c312-4d71-81ce-45146dd19123",
|
||||
"Name": "CHALLENGEPACK_ARGENTUM_WRAPPER_NAME",
|
||||
"ImageName": "images/challenges/Categories/PackArgentum/Argentum_Wrapper.jpg",
|
||||
"Description": "CHALLENGEPACK_ARGENTUM_WRAPPER_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": ["TOKEN_OUTFIT_REWARD_HERO_LEGACY47_SUIT"],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_ARGENTUM",
|
||||
"Icon": "challenge_category_feats",
|
||||
"LocationId": "",
|
||||
"ParentLocationId": "",
|
||||
"Type": "global",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 1,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Scope": "profile",
|
||||
"Constants": {
|
||||
"RequiredChallenges": [
|
||||
"f8ba2bd3-dcd7-453f-aa64-c0ed6992e24b",
|
||||
"86e11742-f0e6-4594-b914-9ab0d8f7ab1b",
|
||||
"5b943968-7142-406e-bd6c-9b9018554667",
|
||||
"2b9f479e-6789-4f23-9eef-e17f268a2b11",
|
||||
"1bcab2a7-626e-4bf2-a9af-7adb823cdd4a"
|
||||
]
|
||||
},
|
||||
"Context": {
|
||||
"CompletedChallenges": []
|
||||
},
|
||||
"ContextListeners": {
|
||||
"CompletedChallenges": {
|
||||
"comparand": "$.RequiredChallenges",
|
||||
"type": "challengetree"
|
||||
}
|
||||
},
|
||||
"States": {
|
||||
"Start": {
|
||||
"ChallengeCompleted": [
|
||||
{
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$Value.ChallengeId"
|
||||
]
|
||||
},
|
||||
"in": "$.RequiredChallenges"
|
||||
}
|
||||
},
|
||||
"$pushunique": [
|
||||
"CompletedChallenges",
|
||||
"$Value.ChallengeId"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"($.CompletedChallenges).Count",
|
||||
"($.RequiredChallenges).Count"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["argentum-pack", "story", "hard"],
|
||||
"InclusionData": {
|
||||
"ContractIds": [
|
||||
"ebcd14b2-0786-4ceb-a2a4-e771f60d0125",
|
||||
"a3e19d55-64a6-4282-bb3c-d18c3f3e6e29"
|
||||
],
|
||||
"ContractTypes": null,
|
||||
"Locations": [
|
||||
"LOCATION_WET_RAT",
|
||||
"LOCATION_COLOMBIA",
|
||||
"LOCATION_COLOMBIA_ANACONDA",
|
||||
"LOCATION_BANGKOK",
|
||||
"LOCATION_BANGKOK_ZIKA"
|
||||
],
|
||||
"GameModes": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "GLOBAL_CHEESECAKE_CHALLENGES",
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
"Name": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_CHEESECAKE",
|
||||
"Image": "images/challenges/categories/packcheesecake/tile.jpg",
|
||||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "cheesecake-pack",
|
||||
"Description": "",
|
||||
"OrderIndex": 6.1,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "0e08ee97-8f70-c82e-f04a-9d2cd60ae5b5",
|
||||
"Name": "UI_PEACOCK_CHALLENGEPACK_CHEESECAKE_WRAPPER_NAME",
|
||||
"ImageName": "images/challenges/Categories/PackCheesecake/tile.jpg",
|
||||
"Description": "UI_PEACOCK_CHALLENGEPACK_CHEESECAKE_WRAPPER_DESC",
|
||||
"Rewards": {
|
||||
"MasteryXP": 4000
|
||||
},
|
||||
"Drops": ["TOKEN_OUTFIT_HERO_BUTCHER_SUIT"],
|
||||
"IsPlayable": false,
|
||||
"IsLocked": false,
|
||||
"HideProgression": false,
|
||||
"CategoryName": "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_PACK_CHEESECAKE",
|
||||
"Icon": "challenge_category_feats",
|
||||
"LocationId": "",
|
||||
"ParentLocationId": "",
|
||||
"Type": "global",
|
||||
"DifficultyLevels": [],
|
||||
"OrderIndex": 100000,
|
||||
"XpModifier": {},
|
||||
"RuntimeType": "Hit",
|
||||
"Definition": {
|
||||
"Scope": "profile",
|
||||
"Constants": {
|
||||
"RequiredChallenges": [
|
||||
"0570b6e5-3c27-e715-042a-59313b0a0916",
|
||||
"15dfb94e-72cc-9f43-5281-a0f7e5180f24",
|
||||
"eff06c02-2410-4226-1abc-076a2e71ee97",
|
||||
"b5386255-0e7e-2bd7-6c28-c1096d4902c5",
|
||||
"c098a7cf-8c49-02ce-475e-20beaed99712"
|
||||
]
|
||||
},
|
||||
"Context": {
|
||||
"CompletedChallenges": []
|
||||
},
|
||||
"ContextListeners": {
|
||||
"CompletedChallenges": {
|
||||
"comparand": "$.RequiredChallenges",
|
||||
"type": "challengetree"
|
||||
}
|
||||
},
|
||||
"States": {
|
||||
"Start": {
|
||||
"ChallengeCompleted": [
|
||||
{
|
||||
"Condition": {
|
||||
"$any": {
|
||||
"?": {
|
||||
"$eq": [
|
||||
"$.#",
|
||||
"$Value.ChallengeId"
|
||||
]
|
||||
},
|
||||
"in": "$.RequiredChallenges"
|
||||
}
|
||||
},
|
||||
"$pushunique": [
|
||||
"CompletedChallenges",
|
||||
"$Value.ChallengeId"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Condition": {
|
||||
"$eq": [
|
||||
"($.CompletedChallenges).Count",
|
||||
"($.RequiredChallenges).Count"
|
||||
]
|
||||
},
|
||||
"Transition": "Success"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tags": ["cheesecake-pack", "story", "hard"],
|
||||
"InclusionData": {
|
||||
"ContractIds": [
|
||||
"179563a4-727a-4072-b354-c9fff4e8bff0",
|
||||
"b2aac100-dfc7-4f85-b9cd-528114436f6c"
|
||||
],
|
||||
"ContractTypes": null,
|
||||
"Locations": [
|
||||
"LOCATION_COASTALTOWN",
|
||||
"LOCATION_COASTALTOWN_NIGHT",
|
||||
"LOCATION_COASTALTOWN_MOVIESET",
|
||||
"LOCATION_WET",
|
||||
"LOCATION_GOLDEN"
|
||||
],
|
||||
"GameModes": null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "GLOBAL_CLASSIC_CHALLENGES",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "1c7d6618-26c2-4b9a-86f0-c3afa127fc8d",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "GLOBAL_ELUSIVES_CHALLENGES",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h2", "h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "elusive",
|
||||
"CategoryId": "elusive",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_ELUSIVE",
|
||||
"OrderIndex": 5,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "1cb6662f-b471-427d-af62-14906ea8f2ed",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "GLOBAL_ESCALATION_CHALLENGES",
|
||||
"GameVersion": "h3"
|
||||
"GameVersions": ["h3"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "featured",
|
||||
"CategoryId": "escalation_hm1",
|
||||
"Description": "",
|
||||
"OrderIndex": 7,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "7c4e7906-ad19-46c0-96f4-4fc301a562b4",
|
||||
|
@ -871,6 +872,7 @@
|
|||
"Icon": "featured",
|
||||
"CategoryId": "escalation_hm2",
|
||||
"Description": "",
|
||||
"OrderIndex": 8,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "c573a9aa-73cf-4efb-acc6-f391239cbeb5",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "GLOBAL_CLASSIC_CHALLENGES",
|
||||
"GameVersion": "h2"
|
||||
"GameVersions": ["h2"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "profile",
|
||||
"CategoryId": "classic",
|
||||
"Description": "",
|
||||
"OrderIndex": 4,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "24946af7-cfd3-4fd3-8aba-aabad50bce14",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "GLOBAL_ESCALATION_CHALLENGES",
|
||||
"GameVersion": "h2"
|
||||
"GameVersions": ["h2"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "239dd317-ea34-4689-b730-0726b51f71bd",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"meta": {
|
||||
"Location": "GLOBAL_ESCALATION_CHALLENGES",
|
||||
"GameVersion": "h1"
|
||||
"GameVersions": ["h1"]
|
||||
},
|
||||
"groups": [
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
|||
"Icon": "challenge_category_feats",
|
||||
"CategoryId": "feats",
|
||||
"Description": "UI_MENU_PAGE_CHALLENGE_CATEGORY_DESCRIPTION_COMMUNITY",
|
||||
"OrderIndex": 2,
|
||||
"Challenges": [
|
||||
{
|
||||
"Id": "239dd317-ea34-4689-b730-0726b51f71bd",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue