mirror of
https://github.com/thepeacockproject/Peacock
synced 2024-11-22 22:12:45 +01:00
Implement the Challenges tab on the Career page (#80)
* Renders Career->Challenges page * Support sniper levels on Career->Challenges page * Refactor the code to get challenges for locations * Support clicking tiles on Career->Challenges page * Add elusives part (currently unsupported) * Add gameVersion check to avoid issues on old games
This commit is contained in:
parent
1cb640992f
commit
bfe82fe1e2
@ -66,6 +66,7 @@ export function compileRuntimeChallenge(
|
||||
export enum ChallengeFilterType {
|
||||
None = "None",
|
||||
Contract = "Contract",
|
||||
Contracts = "Contracts",
|
||||
ParentLocation = "ParentLocation",
|
||||
}
|
||||
|
||||
@ -79,11 +80,50 @@ export type ChallengeFilterOptions =
|
||||
locationId: string
|
||||
locationParentId: string
|
||||
}
|
||||
| {
|
||||
type: ChallengeFilterType.Contracts
|
||||
contractIds: string[]
|
||||
locationId: string
|
||||
locationParentId: string
|
||||
}
|
||||
| {
|
||||
type: ChallengeFilterType.ParentLocation
|
||||
locationParentId: string
|
||||
}
|
||||
|
||||
function isChallengeInContract(
|
||||
contractId: string,
|
||||
locationId: string,
|
||||
locationParentId: string,
|
||||
challenge: RegistryChallenge,
|
||||
) {
|
||||
assert.ok(contractId)
|
||||
assert.ok(locationId)
|
||||
assert.ok(locationParentId)
|
||||
if (!challenge) {
|
||||
return false
|
||||
}
|
||||
|
||||
// is this for the current contract?
|
||||
const isForContract = (challenge.InclusionData?.ContractIds || []).includes(
|
||||
contractId,
|
||||
)
|
||||
|
||||
// is this a location-wide challenge?
|
||||
const isForLocation = challenge.Type === "location"
|
||||
|
||||
// is this for the current location?
|
||||
const isCurrentLocation =
|
||||
// is this challenge for the current parent location?
|
||||
challenge.ParentLocationId === locationParentId &&
|
||||
// and, is this challenge's location the current sub-location
|
||||
// or the parent location? (yup, that can happen)
|
||||
(challenge.LocationId === locationId ||
|
||||
challenge.LocationId === locationParentId)
|
||||
|
||||
return isForContract || (isForLocation && isCurrentLocation)
|
||||
}
|
||||
|
||||
export function filterChallenge(
|
||||
options: ChallengeFilterOptions,
|
||||
challenge: RegistryChallenge,
|
||||
@ -92,32 +132,22 @@ export function filterChallenge(
|
||||
case ChallengeFilterType.None:
|
||||
return true
|
||||
case ChallengeFilterType.Contract: {
|
||||
assert.ok(options.contractId)
|
||||
assert.ok(options.locationId)
|
||||
assert.ok(options.locationParentId)
|
||||
|
||||
if (!challenge) {
|
||||
return false
|
||||
}
|
||||
|
||||
// is this for the current contract?
|
||||
const isForContract = (
|
||||
challenge.InclusionData?.ContractIds || []
|
||||
).includes(options.contractId)
|
||||
|
||||
// is this a location-wide challenge?
|
||||
const isForLocation = challenge.Type === "location"
|
||||
|
||||
// is this for the current location?
|
||||
const isCurrentLocation =
|
||||
// is this challenge for the current parent location?
|
||||
challenge.ParentLocationId === options.locationParentId &&
|
||||
// and, is this challenge's location the current sub-location
|
||||
// or the parent location? (yup, that can happen)
|
||||
(challenge.LocationId === options.locationId ||
|
||||
challenge.LocationId === options.locationParentId)
|
||||
|
||||
return isForContract || (isForLocation && isCurrentLocation)
|
||||
return isChallengeInContract(
|
||||
options.contractId,
|
||||
options.locationId,
|
||||
options.locationParentId,
|
||||
challenge,
|
||||
)
|
||||
}
|
||||
case ChallengeFilterType.Contracts: {
|
||||
return options.contractIds.some((contractId) =>
|
||||
isChallengeInContract(
|
||||
contractId,
|
||||
options.locationId,
|
||||
options.locationParentId,
|
||||
challenge,
|
||||
),
|
||||
)
|
||||
}
|
||||
case ChallengeFilterType.ParentLocation:
|
||||
assert.ok(options.locationParentId)
|
||||
|
@ -291,6 +291,37 @@ export class ChallengeService extends ChallengeRegistry {
|
||||
})
|
||||
}
|
||||
|
||||
getChallengesForLocation(
|
||||
child: string,
|
||||
gameVersion: GameVersion,
|
||||
): GroupIndexedChallengeLists {
|
||||
const locations = getVersionedConfig<PeacockLocationsData>(
|
||||
"LocationsData",
|
||||
gameVersion,
|
||||
true,
|
||||
)
|
||||
const parent = locations.children[child].Properties.ParentLocation
|
||||
const location = locations.children[child]
|
||||
assert.ok(location)
|
||||
|
||||
let contracts =
|
||||
child === "LOCATION_AUSTRIA" ||
|
||||
child === "LOCATION_SALTY_SEAGULL" ||
|
||||
child === "LOCATION_CAGED_FALCON"
|
||||
? this.controller.missionsInLocations.sniper[child]
|
||||
: this.controller.missionsInLocations[child]
|
||||
if (!contracts) {
|
||||
contracts = []
|
||||
}
|
||||
|
||||
return this.getGroupedChallengeLists({
|
||||
type: ChallengeFilterType.Contracts,
|
||||
contractIds: contracts,
|
||||
locationId: child,
|
||||
locationParentId: parent,
|
||||
})
|
||||
}
|
||||
|
||||
startContract(
|
||||
userId: string,
|
||||
sessionId: string,
|
||||
@ -556,6 +587,41 @@ export class ChallengeService extends ChallengeRegistry {
|
||||
)
|
||||
}
|
||||
|
||||
getChallengeDataForLocation(
|
||||
locationId: string,
|
||||
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,
|
||||
)
|
||||
|
||||
return this.reBatchIntoSwitchedData(
|
||||
forLocation,
|
||||
userId,
|
||||
gameVersion,
|
||||
locationData,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
private reBatchIntoSwitchedData(
|
||||
challengeLists: GroupIndexedChallengeLists,
|
||||
userId: string,
|
||||
|
@ -91,6 +91,7 @@ import FrankensteinMmMpTemplate from "../static/FrankensteinMmMpTemplate.json"
|
||||
import FrankensteinScoreOverviewTemplate from "../static/FrankensteinScoreOverviewTemplate.json"
|
||||
import FrankensteinPlanningTemplate from "../static/FrankensteinPlanningTemplate.json"
|
||||
import Videos from "../static/Videos.json"
|
||||
import ChallengeLocationTemplate from "../static/ChallengeLocationTemplate.json"
|
||||
import ContractSearchPageTemplate from "../static/ContractSearchPageTemplate.json"
|
||||
import ContractSearchResponseTemplate from "../static/ContractSearchResponseTemplate.json"
|
||||
import LegacyDebriefingChallengesTemplate from "../static/LegacyDebriefingChallengesTemplate.json"
|
||||
@ -183,6 +184,7 @@ const configs: Record<string, unknown> = {
|
||||
FrankensteinPlanningTemplate,
|
||||
FrankensteinScoreOverviewTemplate,
|
||||
Videos,
|
||||
ChallengeLocationTemplate,
|
||||
ContractSearchPageTemplate,
|
||||
ContractSearchResponseTemplate,
|
||||
MasteryUnlockablesTemplate,
|
||||
|
@ -138,6 +138,33 @@ menuDataRouter.get(
|
||||
"/dashboard/Dashboard_Category_Escalation/:subscriptionId/:type/:id/:mode",
|
||||
dashEscalations,
|
||||
)
|
||||
menuDataRouter.get(
|
||||
"/ChallengeLocation",
|
||||
(req: RequestWithJwt<{ locationId: string }>, res) => {
|
||||
const location = getVersionedConfig<PeacockLocationsData>(
|
||||
"LocationsData",
|
||||
req.gameVersion,
|
||||
true,
|
||||
).children[req.query.locationId]
|
||||
res.json({
|
||||
template: getVersionedConfig(
|
||||
"ChallengeLocationTemplate",
|
||||
req.gameVersion,
|
||||
false,
|
||||
),
|
||||
data: {
|
||||
Name: location.DisplayNameLocKey,
|
||||
Location: location,
|
||||
Children:
|
||||
controller.challengeService.getChallengeDataForLocation(
|
||||
req.query.locationId,
|
||||
req.gameVersion,
|
||||
req.jwt.unique_name,
|
||||
),
|
||||
},
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
menuDataRouter.get("/Hub", (req: RequestWithJwt, res) => {
|
||||
swapToBrowsingMenusStatus(req.gameVersion)
|
||||
@ -160,6 +187,72 @@ menuDataRouter.get("/Hub", (req: RequestWithJwt, res) => {
|
||||
"d7e2607c-6916-48e2-9588-976c7d8998bb",
|
||||
)!
|
||||
|
||||
const locations = getVersionedConfig<PeacockLocationsData>(
|
||||
"LocationsData",
|
||||
req.gameVersion,
|
||||
true,
|
||||
)
|
||||
const career = {
|
||||
// TODO: Add data on elusive challenges. They are not shown on the Career->Challenges page. What the client does with this information is unclear. They are not supported by Peacock as of v5.6.2.
|
||||
ELUSIVES_UNSUPPORTED:
|
||||
req.gameVersion === "h3"
|
||||
? {
|
||||
Children: [],
|
||||
Name: "UI_MENU_PAGE_PROFILE_CHALLENGES_CATEGORY_ELUSIVE",
|
||||
Location:
|
||||
locations.parents["LOCATION_PARENT_ICA_FACILITY"],
|
||||
}
|
||||
: {},
|
||||
}
|
||||
for (const parent in locations.parents) {
|
||||
career[parent] = {
|
||||
Children: [],
|
||||
Location: locations.parents[parent],
|
||||
Name: locations.parents[parent].DisplayNameLocKey,
|
||||
}
|
||||
}
|
||||
for (const child in locations.children) {
|
||||
if (
|
||||
child === "LOCATION_ICA_FACILITY_ARRIVAL" ||
|
||||
child === "LOCATION_HOKKAIDO_SHIM_MAMUSHI"
|
||||
) {
|
||||
continue
|
||||
}
|
||||
const parent = locations.children[child].Properties.ParentLocation
|
||||
const location = locations.children[child]
|
||||
const challenges = controller.challengeService.getChallengesForLocation(
|
||||
child,
|
||||
req.gameVersion,
|
||||
)
|
||||
const challengeCompletion =
|
||||
controller.challengeService.countTotalNCompletedChallenges(
|
||||
challenges,
|
||||
req.jwt.unique_name,
|
||||
req.gameVersion,
|
||||
)
|
||||
|
||||
career[parent].Children.push({
|
||||
IsLocked: 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,
|
||||
req.jwt.unique_name,
|
||||
req.gameVersion,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
res.json({
|
||||
template: theTemplate,
|
||||
data: {
|
||||
@ -187,7 +280,12 @@ menuDataRouter.get("/Hub", (req: RequestWithJwt, res) => {
|
||||
req.gameVersion,
|
||||
),
|
||||
LocationsData: createLocationsData(req.gameVersion),
|
||||
ProfileData: {},
|
||||
ProfileData: {
|
||||
ChallengeData: {
|
||||
Children: Object.values(career),
|
||||
},
|
||||
MasteryData: {},
|
||||
},
|
||||
StoryData: makeCampaigns(req.gameVersion, req.jwt.unique_name),
|
||||
FilterData: getVersionedConfig(
|
||||
"FilterData",
|
||||
|
@ -290,9 +290,9 @@ export async function missionEnd(
|
||||
const opportunities = contractData.Metadata.Opportunities
|
||||
const opportunityCount = opportunities ? opportunities.length : 0
|
||||
const opportunityCompleted = opportunities
|
||||
? opportunities.filter((ms) => (
|
||||
ms in userData.Extensions.opportunityprogression
|
||||
)).length
|
||||
? opportunities.filter(
|
||||
(ms) => ms in userData.Extensions.opportunityprogression,
|
||||
).length
|
||||
: 0
|
||||
const result = {
|
||||
MissionReward: {
|
||||
|
2144
static/ChallengeLocationTemplate.json
Normal file
2144
static/ChallengeLocationTemplate.json
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user