1
mirror of https://github.com/thepeacockproject/Peacock synced 2025-03-14 12:54:28 +01:00

Support configurable default starting suits lock/unlock ()

* 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:
moonysolari 2023-04-11 12:10:24 -04:00 committed by GitHub
parent 74a387445d
commit efd6ede347
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 168 additions and 68 deletions

@ -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"