mirror of
https://github.com/thepeacockproject/Peacock
synced 2025-03-14 12:54:28 +01:00
Support configurable default starting suits lock/unlock (#202)
* refactor: use object for suit lookup * improve sublocation default suits * change getDefaultSuitFor function signature * use TOKEN_OUTFIT_WET_SUIT to simplify logic * Correctly hide/give default starting suits
This commit is contained in:
parent
74a387445d
commit
efd6ede347
@ -74,10 +74,16 @@ legacyMenuDataRouter.get(
|
||||
|
||||
const userProfile = getUserData(req.jwt.unique_name, req.gameVersion)
|
||||
|
||||
const sublocation = getSubLocationByName(
|
||||
contractData.Metadata.Location,
|
||||
req.gameVersion,
|
||||
)
|
||||
|
||||
const inventory = createInventory(
|
||||
req.jwt.unique_name,
|
||||
req.gameVersion,
|
||||
userProfile.Extensions.entP,
|
||||
sublocation,
|
||||
)
|
||||
|
||||
const userCentricContract = generateUserCentric(
|
||||
@ -86,14 +92,9 @@ legacyMenuDataRouter.get(
|
||||
"h1",
|
||||
)
|
||||
|
||||
const sublocation = getSubLocationByName(
|
||||
contractData.Metadata.Location,
|
||||
req.gameVersion,
|
||||
)
|
||||
|
||||
const defaultLoadout = {
|
||||
2: "FIREARMS_HERO_PISTOL_TACTICAL_001_SU_SKIN01",
|
||||
3: getDefaultSuitFor(sublocation?.Properties?.ParentLocation),
|
||||
3: getDefaultSuitFor(sublocation),
|
||||
4: "TOKEN_FIBERWIRE",
|
||||
5: "PROP_TOOL_COIN",
|
||||
}
|
||||
|
@ -117,6 +117,10 @@ const defaultFlags: Flags = {
|
||||
desc: "When set to false, mastery progression will be disabled and all unlockables will be awarded at the beginning",
|
||||
default: true,
|
||||
},
|
||||
getDefaultSuits: {
|
||||
desc: `[Gameplay] Set this to true to add all the default starting suits to your inventory. Note: If you set both this and "enableMasteryProgression" to "true" at the same time, a starting suit that is also the unlock for a challenge/mastery will be locked behind its challenge/mastery.`,
|
||||
default: false,
|
||||
},
|
||||
}
|
||||
|
||||
const OLD_FLAGS_FILE = "flags.json5"
|
||||
|
@ -43,6 +43,7 @@ import { getUserData } from "./databaseHandler"
|
||||
import { log, LogLevel } from "./loggingInterop"
|
||||
import { getFlag } from "./flags"
|
||||
import { UnlockableMasteryData } from "./types/mastery"
|
||||
import { attainableDefaults, defaultSuits, getDefaultSuitFor } from "./utils"
|
||||
|
||||
const DELUXE_DATA = [
|
||||
...CONCRETEART_UNLOCKABLES,
|
||||
@ -384,13 +385,72 @@ function filterAllowedContent(gameVersion: GameVersion, entP: string[]) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an inventory and a sublocation, returns a new inventory with
|
||||
* the default suit for the sublocation added if it is not already present.
|
||||
* If the sublocation is undefined, the inputted inventory is returned.
|
||||
* Otherwise, a new inventory is cloned, appended with the default suit, and returned.
|
||||
* Either way, the inputted inventory is not modified.
|
||||
* @param profileId The profileId of the player
|
||||
* @param gameVersion The game version
|
||||
* @param inv The inventory to update
|
||||
* @param sublocation The sublocation to check for a default suit
|
||||
* @returns The updated inventory
|
||||
*/
|
||||
function updateWithDefaultSuit(
|
||||
profileId: string,
|
||||
gameVersion: GameVersion,
|
||||
inv: InventoryItem[],
|
||||
sublocation: Unlockable,
|
||||
): InventoryItem[] {
|
||||
if (sublocation === undefined) {
|
||||
return inv
|
||||
}
|
||||
|
||||
// We need to add a suit, so need to copy the cache to prevent modifying it.
|
||||
const newInv = [...inv]
|
||||
|
||||
// Yes this is slow. We should organize the unlockables into a { [Id: string]: Unlockable } map.
|
||||
const locationSuit = getVersionedConfig<Unlockable[]>(
|
||||
"allunlockables",
|
||||
gameVersion,
|
||||
true,
|
||||
).find((u) => u.Id === getDefaultSuitFor(sublocation))
|
||||
|
||||
// check if any inventoryItem's unlockable is the default suit for the sublocation
|
||||
if (newInv.every((i) => i.Unlockable.Id !== locationSuit.Id)) {
|
||||
// if not, add it
|
||||
newInv.push({
|
||||
InstanceId: locationSuit.Guid,
|
||||
ProfileId: profileId,
|
||||
Unlockable: locationSuit,
|
||||
Properties: {},
|
||||
})
|
||||
}
|
||||
return newInv
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a player's inventory with unlockables.
|
||||
* @param profileId The profile ID of the player
|
||||
* @param gameVersion The game version
|
||||
* @param entP The player's entitlements
|
||||
* @param sublocation The sublocation to generate the inventory for. Used to award default suits for the sublocation. Defaulted to undefined.
|
||||
* @returns The player's inventory
|
||||
*/
|
||||
export function createInventory(
|
||||
profileId: string,
|
||||
gameVersion: GameVersion,
|
||||
entP: string[],
|
||||
sublocation = undefined,
|
||||
): InventoryItem[] {
|
||||
if (inventoryUserCache.has(profileId)) {
|
||||
return inventoryUserCache.get(profileId)!
|
||||
return updateWithDefaultSuit(
|
||||
profileId,
|
||||
gameVersion,
|
||||
inventoryUserCache.get(profileId)!,
|
||||
sublocation,
|
||||
)
|
||||
}
|
||||
|
||||
// Get user data to check on location progression
|
||||
@ -436,6 +496,18 @@ export function createInventory(
|
||||
unlockables = [...unlockedItems, ...otherItems]
|
||||
}
|
||||
|
||||
// If getDefaultSuits is turned off, then only give attainable suits and lock everything else.
|
||||
// The mastery check has already locked any unattained attainable suits,
|
||||
// and location-wide default suits will be given afterwards.
|
||||
const defaults = Object.values(defaultSuits)
|
||||
if ((getFlag("getDefaultSuits") as boolean) === false) {
|
||||
unlockables = unlockables.filter(
|
||||
(u) =>
|
||||
!defaults.includes(u.Id) ||
|
||||
attainableDefaults(gameVersion).includes(u.Id),
|
||||
)
|
||||
}
|
||||
|
||||
// ts-expect-error It cannot be undefined.
|
||||
const filtered: InventoryItem[] = unlockables
|
||||
.map((unlockable) => {
|
||||
@ -467,7 +539,9 @@ export function createInventory(
|
||||
}
|
||||
|
||||
inventoryUserCache.set(profileId, filtered)
|
||||
return filtered
|
||||
|
||||
// It is highly unlikely that we need to add a suit but we don't have the inventory in cache, but just in case
|
||||
return updateWithDefaultSuit(profileId, gameVersion, filtered, sublocation)
|
||||
}
|
||||
|
||||
export function grantDrops(profileId: string, drops: Unlockable[]): void {
|
||||
|
@ -65,6 +65,7 @@ import {
|
||||
complications,
|
||||
generateCompletionData,
|
||||
generateUserCentric,
|
||||
getSubLocationByName,
|
||||
} from "./contracts/dataGen"
|
||||
import { log, LogLevel } from "./loggingInterop"
|
||||
import {
|
||||
@ -509,17 +510,21 @@ menuDataRouter.get(
|
||||
|
||||
const userData = getUserData(req.jwt.unique_name, req.gameVersion)
|
||||
|
||||
const inventory = createInventory(
|
||||
req.jwt.unique_name,
|
||||
req.gameVersion,
|
||||
userData.Extensions.entP,
|
||||
)
|
||||
|
||||
let contractData: MissionManifest | undefined = undefined
|
||||
if (req.query.contractid) {
|
||||
contractData = controller.resolveContract(req.query.contractid)
|
||||
}
|
||||
|
||||
const inventory = createInventory(
|
||||
req.jwt.unique_name,
|
||||
req.gameVersion,
|
||||
userData.Extensions.entP,
|
||||
getSubLocationByName(
|
||||
contractData.Metadata.Location,
|
||||
req.gameVersion,
|
||||
),
|
||||
)
|
||||
|
||||
if (req.query.slotname.endsWith(req.query.slotid!.toString())) {
|
||||
req.query.slotname = req.query.slotname.slice(
|
||||
0,
|
||||
|
@ -196,6 +196,7 @@ export async function planningView(
|
||||
req.jwt.unique_name,
|
||||
req.gameVersion,
|
||||
userData.Extensions.entP,
|
||||
sublocation,
|
||||
)
|
||||
|
||||
const unlockedEntrances = typedInv
|
||||
@ -221,10 +222,7 @@ export async function planningView(
|
||||
}
|
||||
|
||||
let pistol = "FIREARMS_HERO_PISTOL_TACTICAL_ICA_19"
|
||||
let suit =
|
||||
sublocation.Id === "LOCATION_ANCESTRAL_SMOOTHSNAKE"
|
||||
? "TOKEN_OUTFIT_ANCESTRAL_HERO_SMOOTHSNAKESUIT"
|
||||
: getDefaultSuitFor(sublocation?.Properties?.ParentLocation)
|
||||
let suit = getDefaultSuitFor(sublocation)
|
||||
let tool1 = "TOKEN_FIBERWIRE"
|
||||
let tool2 = "PROP_TOOL_COIN"
|
||||
let briefcaseProp: string | undefined = undefined
|
||||
|
@ -280,9 +280,9 @@ export const brokenItems = [
|
||||
"a8a8f0da-a69b-428d-b8c1-faf8660ec318",
|
||||
// PROP_DEVICE_REMOTE_EXPLOSIVE_ANCESTRAL (doesn't exist as of 3.140)
|
||||
"099afc37-609b-48c9-9278-d3389b45829b",
|
||||
// Remove TOKEN_OUTFIT_WET_SUIT, which is an unlock of The Mills Reverie challenge.
|
||||
// Duplicate of TOKEN_OUTFIT_NEWZEALAND_HERO_NEWZEALANDSUIT, which is the New Zealand default suit.
|
||||
"c2834cc6-0c71-4785-a976-c93aceb4c528",
|
||||
// Remove TOKEN_OUTFIT_NEWZEALAND_HERO_NEWZEALANDSUIT, which is the New Zealand default suit.
|
||||
// Duplicate of TOKEN_OUTFIT_WET_SUIT, which is an unlock of The Mills Reverie challenge.
|
||||
"3a4bcbbf-d451-4853-8a85-649120e384df",
|
||||
// Remove TOKEN_OUTFIT_COLORADO_HERO_RABIESSUIT
|
||||
// Duplicate of TOKEN_OUTFIT_COLORADO_HERO_COLORADOSUIT.
|
||||
"e3234256-c061-48e6-b543-008d990affa1",
|
||||
|
@ -317,53 +317,71 @@ export function castUserProfile(profile: UserProfile): UserProfile {
|
||||
return j
|
||||
}
|
||||
|
||||
export function getDefaultSuitFor(location: string) {
|
||||
switch (location) {
|
||||
case "LOCATION_PARENT_ICA_FACILITY":
|
||||
return "TOKEN_OUTFIT_GREENLAND_HERO_TRAININGSUIT"
|
||||
case "LOCATION_PARENT_PARIS":
|
||||
return "TOKEN_OUTFIT_PARIS_HERO_PARISSUIT"
|
||||
case "LOCATION_PARENT_COASTALTOWN":
|
||||
return "TOKEN_OUTFIT_SAPIENZA_HERO_SAPIENZASUIT"
|
||||
case "LOCATION_PARENT_MARRAKECH":
|
||||
return "TOKEN_OUTFIT_MARRAKESH_HERO_MARRAKESHSUIT"
|
||||
case "LOCATION_PARENT_BANGKOK":
|
||||
return "TOKEN_OUTFIT_BANGKOK_HERO_BANGKOKSUIT"
|
||||
case "LOCATION_PARENT_COLORADO":
|
||||
return "TOKEN_OUTFIT_COLORADO_HERO_COLORADOSUIT"
|
||||
case "LOCATION_PARENT_HOKKAIDO":
|
||||
return "TOKEN_OUTFIT_HOKKAIDO_HERO_HOKKAIDOSUIT"
|
||||
case "LOCATION_PARENT_NEWZEALAND":
|
||||
return "TOKEN_OUTFIT_NEWZEALAND_HERO_NEWZEALANDSUIT"
|
||||
case "LOCATION_PARENT_MIAMI":
|
||||
return "TOKEN_OUTFIT_MIAMI_HERO_MIAMISUIT"
|
||||
case "LOCATION_PARENT_COLOMBIA":
|
||||
return "TOKEN_OUTFIT_COLOMBIA_HERO_COLOMBIASUIT"
|
||||
case "LOCATION_PARENT_MUMBAI":
|
||||
return "TOKEN_OUTFIT_MUMBAI_HERO_MUMBAISUIT"
|
||||
case "LOCATION_PARENT_NORTHAMERICA":
|
||||
return "TOKEN_OUTFIT_NORTHAMERICA_HERO_NORTHAMERICASUIT"
|
||||
case "LOCATION_PARENT_NORTHSEA":
|
||||
return "TOKEN_OUTFIT_NORTHSEA_HERO_NORTHSEASUIT"
|
||||
case "LOCATION_PARENT_GREEDY":
|
||||
return "TOKEN_OUTFIT_GREEDY_HERO_GREEDYSUIT"
|
||||
case "LOCATION_PARENT_OPULENT":
|
||||
return "TOKEN_OUTFIT_OPULENT_HERO_OPULENTSUIT"
|
||||
case "LOCATION_PARENT_GOLDEN":
|
||||
return "TOKEN_OUTFIT_HERO_GECKO_SUIT"
|
||||
case "LOCATION_PARENT_ANCESTRAL":
|
||||
return "TOKEN_OUTFIT_ANCESTRAL_HERO_ANCESTRALSUIT"
|
||||
case "LOCATION_PARENT_EDGY":
|
||||
return "TOKEN_OUTFIT_EDGY_HERO_EDGYSUIT"
|
||||
case "LOCATION_PARENT_WET":
|
||||
return "TOKEN_OUTFIT_WET_HERO_WETSUIT"
|
||||
case "LOCATION_PARENT_ELEGANT":
|
||||
return "TOKEN_OUTFIT_ELEGANT_HERO_LLAMASUIT"
|
||||
case "LOCATION_PARENT_ROCKY":
|
||||
return "TOKEN_OUTFIT_HERO_DUGONG_SUIT"
|
||||
default:
|
||||
return "TOKEN_OUTFIT_HITMANSUIT"
|
||||
}
|
||||
export const defaultSuits = {
|
||||
LOCATION_PARENT_ICA_FACILITY: "TOKEN_OUTFIT_GREENLAND_HERO_TRAININGSUIT",
|
||||
LOCATION_PARENT_PARIS: "TOKEN_OUTFIT_PARIS_HERO_PARISSUIT",
|
||||
LOCATION_PARENT_COASTALTOWN: "TOKEN_OUTFIT_SAPIENZA_HERO_SAPIENZASUIT",
|
||||
LOCATION_COASTALTOWN_MOVIESET:
|
||||
"TOKEN_OUTFIT_SAPIENZA_HERO_SAPIENZASUIT_NOGLASSES",
|
||||
LOCATION_COASTALTOWN_EBOLA:
|
||||
"TOKEN_OUTFIT_SAPIENZA_HERO_SAPIENZASUIT_NOGLASSES",
|
||||
LOCATION_PARENT_MARRAKECH: "TOKEN_OUTFIT_MARRAKESH_HERO_MARRAKESHSUIT",
|
||||
LOCATION_PARENT_BANGKOK: "TOKEN_OUTFIT_BANGKOK_HERO_BANGKOKSUIT",
|
||||
LOCATION_PARENT_COLORADO: "TOKEN_OUTFIT_COLORADO_HERO_COLORADOSUIT",
|
||||
LOCATION_PARENT_HOKKAIDO: "TOKEN_OUTFIT_HOKKAIDO_HERO_HOKKAIDOSUIT",
|
||||
LOCATION_PARENT_NEWZEALAND: "TOKEN_OUTFIT_WET_SUIT",
|
||||
LOCATION_PARENT_MIAMI: "TOKEN_OUTFIT_MIAMI_HERO_MIAMISUIT",
|
||||
LOCATION_PARENT_COLOMBIA: "TOKEN_OUTFIT_COLOMBIA_HERO_COLOMBIASUIT",
|
||||
LOCATION_PARENT_MUMBAI: "TOKEN_OUTFIT_MUMBAI_HERO_MUMBAISUIT",
|
||||
LOCATION_PARENT_NORTHAMERICA:
|
||||
"TOKEN_OUTFIT_NORTHAMERICA_HERO_NORTHAMERICASUIT",
|
||||
LOCATION_PARENT_NORTHSEA: "TOKEN_OUTFIT_NORTHSEA_HERO_NORTHSEASUIT",
|
||||
LOCATION_PARENT_GREEDY: "TOKEN_OUTFIT_GREEDY_HERO_GREEDYSUIT",
|
||||
LOCATION_PARENT_OPULENT: "TOKEN_OUTFIT_OPULENT_HERO_OPULENTSUIT",
|
||||
LOCATION_PARENT_GOLDEN: "TOKEN_OUTFIT_HERO_GECKO_SUIT",
|
||||
LOCATION_PARENT_ANCESTRAL: "TOKEN_OUTFIT_ANCESTRAL_HERO_ANCESTRALSUIT",
|
||||
LOCATION_ANCESTRAL_SMOOTHSNAKE:
|
||||
"TOKEN_OUTFIT_ANCESTRAL_HERO_SMOOTHSNAKESUIT",
|
||||
LOCATION_PARENT_EDGY: "TOKEN_OUTFIT_EDGY_HERO_EDGYSUIT",
|
||||
LOCATION_PARENT_WET: "TOKEN_OUTFIT_WET_HERO_WETSUIT",
|
||||
LOCATION_PARENT_ELEGANT: "TOKEN_OUTFIT_ELEGANT_HERO_LLAMASUIT",
|
||||
LOCATION_PARENT_TRAPPED: "TOKEN_OUTFIT_TRAPPED_WOLVERINE_SUIT",
|
||||
LOCATION_PARENT_ROCKY: "TOKEN_OUTFIT_HERO_DUGONG_SUIT",
|
||||
}
|
||||
|
||||
/**
|
||||
* Default suits that are attainable via challenges or mastery in this version.
|
||||
* NOTE: Currently this is hardcoded. To allow for flexibility and extensibility, this should be generated in real-time
|
||||
* using the Drops of challenges and masteries. However, that would require looping through all challenges and masteries
|
||||
* for all default suits, which is slow. This is a trade-off.
|
||||
* @param gameVersion The game version.
|
||||
* @returns The default suits that are attainable via challenges or mastery.
|
||||
*/
|
||||
export function attainableDefaults(gameVersion: GameVersion): string[] {
|
||||
return gameVersion === "h1"
|
||||
? []
|
||||
: gameVersion === "h2"
|
||||
? ["TOKEN_OUTFIT_WET_SUIT"]
|
||||
: [
|
||||
"TOKEN_OUTFIT_GREENLAND_HERO_TRAININGSUIT",
|
||||
"TOKEN_OUTFIT_WET_SUIT",
|
||||
"TOKEN_OUTFIT_HERO_DUGONG_SUIT",
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default suit for a given sublocation and parent location.
|
||||
* Priority is given to the sublocation, then the parent location, then 47's signature suit.
|
||||
* @param sub The sublocation.
|
||||
* @param parent The parent location.
|
||||
* @returns The default suit for the given sublocation and parent location.
|
||||
*/
|
||||
export function getDefaultSuitFor(sublocation: Unlockable) {
|
||||
return (
|
||||
defaultSuits[sublocation.Id] ||
|
||||
defaultSuits[sublocation.Properties.ParentLocation] ||
|
||||
"TOKEN_OUTFIT_HITMANSUIT"
|
||||
)
|
||||
}
|
||||
|
||||
export const nilUuid = "00000000-0000-0000-0000-000000000000"
|
||||
|
Loading…
x
Reference in New Issue
Block a user