1
mirror of https://github.com/thepeacockproject/Peacock synced 2025-02-03 18:23:14 +01:00

Add Challenge completion and level completion percentage display (#79)

* Add challenges completion progress to responses

* Update components/scoreHandler.ts

Co-authored-by: Reece Dunham <me@rdil.rocks>
Signed-off-by: moonysolari <118079569+moonysolari@users.noreply.github.com>

Signed-off-by: moonysolari <118079569+moonysolari@users.noreply.github.com>
Co-authored-by: Reece Dunham <me@rdil.rocks>
This commit is contained in:
moonysolari 2023-01-12 10:35:27 -05:00 committed by GitHub
parent 95b71b79e7
commit b1dfdb8066
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 147 additions and 33 deletions

View File

@ -226,11 +226,9 @@ export class ChallengeService extends ChallengeRegistry {
/**
* Get challenge lists sorted into groups.
*
* @param locationId The parent location's ID.
* @param filter The filter to use.
*/
getGroupedChallengeLists(
locationId: string,
filter: ChallengeFilterOptions,
): GroupIndexedChallengeLists {
let challenges: [string, RegistryChallenge[]][] = []
@ -285,7 +283,7 @@ export class ChallengeService extends ChallengeRegistry {
assert.ok(contractParentLocation)
return this.getGroupedChallengeLists(contractParentLocation, {
return this.getGroupedChallengeLists({
type: ChallengeFilterType.Contract,
contractId: contractId,
locationId: contract.Metadata.Location,
@ -544,7 +542,7 @@ export class ChallengeService extends ChallengeRegistry {
return []
}
const forLocation = this.getGroupedChallengeLists(locationParentId, {
const forLocation = this.getGroupedChallengeLists({
type: ChallengeFilterType.ParentLocation,
locationParentId,
})
@ -750,6 +748,40 @@ export class ChallengeService extends ChallengeRegistry {
}
}
/**
* Counts the number of challenges and completed challenges in a GroupIndexedChallengeLists object.
* @param challengeLists A GroupIndexedChallengeLists object, holding some challenges to be counted
* @param userId The userId of the user to acquire completion information
* @param gameVersion The version of the game
* @returns An object with two properties: ChallengesCount and CompletedChallengesCount.
*/
countTotalNCompletedChallenges(
challengeLists: GroupIndexedChallengeLists,
userId: string,
gameVersion: GameVersion,
): { ChallengesCount: number; CompletedChallengesCount: number } {
let challengesCount = 0
let completedChallengesCount = 0
for (const groupId in challengeLists) {
const challenges = challengeLists[groupId]
const challengeProgressionData = challenges.map((challengeData) =>
this.getPersistentChallengeProgression(
userId,
challengeData.Id,
gameVersion,
),
)
challengesCount += challenges.length
completedChallengesCount += challengeProgressionData.filter(
(progressionData) => progressionData.Completed,
).length
}
return {
ChallengesCount: challengesCount,
CompletedChallengesCount: completedChallengesCount,
}
}
private onChallengeCompleted(
session: ContractSession,
challenge: RegistryChallenge,

View File

@ -82,6 +82,7 @@ import { brotliDecompress } from "zlib"
import assert from "assert"
import { Response } from "express"
import { MissionEndRequestQuery } from "./types/gameSchemas"
import { ChallengeFilterType } from "./candle/challengeHelpers"
/**
* An array of string arrays that contains the IDs of the featured contracts.
@ -650,7 +651,7 @@ export class Controller {
if (openCtJson) {
return fastClone(openCtJson)
}
log(LogLevel.WARN, `Contract ${id} not found!`)
log(LogLevel.TRACE, `Contract ${id} not found!`)
return undefined
}
@ -1177,14 +1178,24 @@ export function contractIdToHitObject(
log(LogLevel.ERROR, "No UC due to previous error?")
return undefined
}
const challenges = controller.challengeService.getGroupedChallengeLists({
type: ChallengeFilterType.ParentLocation,
locationParentId: parentLocation.Id,
})
const challengeCompletion =
controller.challengeService.countTotalNCompletedChallenges(
challenges,
userId,
gameVersion,
)
return {
Id: contract.Metadata.Id,
UserCentricContract: userCentric,
Location: parentLocation,
SubLocation: subLocation,
ChallengesCompleted: 0,
ChallengesTotal: 0,
ChallengesCompleted: challengeCompletion.CompletedChallengesCount,
ChallengesTotal: challengeCompletion.ChallengesCount,
LocationLevel: 1,
LocationMaxLevel: 1,
LocationCompletion: 0,

View File

@ -29,6 +29,7 @@ import type {
import { controller } from "../controller"
import { generateCompletionData } from "../contracts/dataGen"
import { getUserData } from "components/databaseHandler"
import { ChallengeFilterType } from "components/candle/challengeHelpers"
type GameFacingDestination = {
ChallengeCompletion: {
@ -53,6 +54,14 @@ export function getDestinationCompletion(
req: RequestWithJwt,
) {
const userData = getUserData(req.jwt.unique_name, req.gameVersion)
const challenges = controller.challengeService.getGroupedChallengeLists({
type: ChallengeFilterType.ParentLocation,
locationParentId: parent.Id,
})
if (parent.Opportunities === undefined) {
parent.Opportunities = 0
}
let opportunityCompletedCount = 0
for (const ms in userData.Extensions.opportunityprogression) {
@ -60,21 +69,54 @@ export function getDestinationCompletion(
opportunityCompletedCount++
}
}
const challengeCompletion =
controller.challengeService.countTotalNCompletedChallenges(
challenges,
userData.Id,
req.gameVersion,
)
return {
ChallengeCompletion: {
ChallengesCount: 0,
CompletedChallengesCount: 0, // TODO: Hook this up to challenge counts.
},
ChallengeCompletion: challengeCompletion,
OpportunityStatistics: {
Count: parent.Opportunities,
Completed: opportunityCompletedCount,
},
LocationCompletionPercent: 0,
LocationCompletionPercent: getCompletionPercent(
challengeCompletion.CompletedChallengesCount,
challengeCompletion.ChallengesCount,
opportunityCompletedCount,
parent.Opportunities,
),
Location: parent,
}
}
export function getCompletionPercent(
challengeDone: number,
challengeTotal: number,
opportunityDone: number,
opportunityTotal: number,
): number {
if (challengeDone === undefined) {
challengeDone = 0
}
if (challengeTotal === undefined) {
challengeTotal = 0
}
if (opportunityDone === undefined) {
opportunityDone = 0
}
if (opportunityTotal === undefined) {
opportunityTotal = 0
}
const totalCompletables = challengeTotal + opportunityTotal
const totalCompleted = challengeDone + opportunityDone
return totalCompletables === 0
? 0
: (100 * totalCompleted) / totalCompletables
}
export function destinationsMenu(req: RequestWithJwt): GameFacingDestination[] {
const result: GameFacingDestination[] = []
const locations = getVersionedConfig<PeacockLocationsData>(

View File

@ -25,11 +25,12 @@ import {
xpRequiredForLevel,
} from "./utils"
import { contractSessions, getCurrentState } from "./eventHandler"
import { getConfig } from "./configSwizzleManager"
import { getConfig, getVersionedConfig } from "./configSwizzleManager"
import { _theLastYardbirdScpc, controller } from "./controller"
import type {
ContractSession,
MissionManifestObjective,
PeacockLocationsData,
RequestWithJwt,
Seconds,
} from "./types/types"
@ -45,6 +46,8 @@ import { generateCompletionData } from "./contracts/dataGen"
import { liveSplitManager } from "./livesplit/liveSplitManager"
import { Playstyle, ScoringBonus, ScoringHeadline } from "./types/scoring"
import { MissionEndRequestQuery } from "./types/gameSchemas"
import { ChallengeFilterType } from "./candle/challengeHelpers"
import { getCompletionPercent } from "./menus/destinations"
/**
* Checks the criteria of each possible play-style, ranking them by scoring.
@ -261,9 +264,36 @@ export async function missionEnd(
? 0
: sessionDetails.npcKills.size + sessionDetails.crowdNpcKills
// const allMissionStories = getConfig("MissionStories")
// const missionStories = (contractData.Metadata.Opportunities || []).map((missionStoryId) => allMissionStories[missionStoryId])
const locations = getVersionedConfig<PeacockLocationsData>(
"LocationsData",
req.gameVersion,
true,
)
const location = contractData.Metadata.Location
const parent = locations.children[location].Properties.ParentLocation
const locationChallenges =
controller.challengeService.getGroupedChallengeLists({
type: ChallengeFilterType.ParentLocation,
locationParentId: parent,
})
const contractChallenges =
controller.challengeService.getChallengesForContract(
sessionDetails.contractId,
req.gameVersion,
)
const challengeCompletion =
controller.challengeService.countTotalNCompletedChallenges(
locationChallenges,
userData.Id,
req.gameVersion,
)
const opportunities = contractData.Metadata.Opportunities
const opportunityCount = opportunities ? opportunities.length : 0
const opportunityCompleted = opportunities
? opportunities.filter((ms) => (
ms in userData.Extensions.opportunityprogression
)).length
: 0
const result = {
MissionReward: {
LocationProgression: {
@ -284,12 +314,7 @@ export async function missionEnd(
.ProfileLevel,
XPGain: 0,
},
Challenges: Object.values(
controller.challengeService.getChallengesForContract(
sessionDetails.contractId,
req.gameVersion,
),
)
Challenges: Object.values(contractChallenges)
.flat()
// FIXME: This behaviour may not be accurate to original server
.filter(
@ -319,19 +344,23 @@ export async function missionEnd(
req.jwt.unique_name,
req.gameVersion,
),
ChallengeCompletion: {
ChallengesCount: 1,
CompletedChallengesCount: 0,
},
ContractChallengeCompletion: {
ChallengesCount: 1,
CompletedChallengesCount: 0,
},
ChallengeCompletion: challengeCompletion,
ContractChallengeCompletion:
controller.challengeService.countTotalNCompletedChallenges(
contractChallenges,
userData.Id,
req.gameVersion,
),
OpportunityStatistics: {
Count: (contractData.Metadata.Opportunities || []).length,
Completed: 0,
Count: opportunityCount,
Completed: opportunityCompleted,
},
LocationCompletionPercent: 0,
LocationCompletionPercent: getCompletionPercent(
challengeCompletion.CompletedChallengesCount,
challengeCompletion.ChallengesCount,
opportunityCompleted,
opportunityCount,
),
},
ScoreOverview: {
XP: 0,