The NEW UC rule is that non-body-specific is okay!

This commit is contained in:
Emi Matchu 2021-03-13 07:21:30 -08:00
parent 36b0cb75e5
commit 927401fc92
2 changed files with 82 additions and 35 deletions

View file

@ -283,29 +283,41 @@ function OutfitHTML5Badge({ appearance }) {
function OutfitKnownGlitchesBadge({ appearance }) { function OutfitKnownGlitchesBadge({ appearance }) {
const glitchMessages = []; const glitchMessages = [];
// Look for conflicts between Static pet zones (UCs), and Static items. const { petAppearance, items } = appearance;
const petHasStaticZone = appearance.petAppearance?.layers.some(
(l) => l.zone.id === "46" // Look for UC/Invisible/etc incompatibilities that we hid, that we should
// just mark Incompatible someday instead.
//
// HACK: Most of this logic is copy-pasted from `useOutfitAppearance`.
const petOccupiedZoneIds = new Set(
petAppearance?.layers.map((l) => l.zone.id)
); );
if (petHasStaticZone) { const petRestrictedZoneIds = new Set(
for (const item of appearance.items) { petAppearance?.restrictedZones.map((z) => z.id)
const itemHasStaticZone = item.appearance.layers.some(
(l) => l.zone.id === "46"
); );
if (itemHasStaticZone) { const petOccupiedOrRestrictedZoneIds = new Set([
...petOccupiedZoneIds,
...petRestrictedZoneIds,
]);
for (const item of items) {
const itemHasZoneRestrictedByPet = item.appearance.layers.some(
(layer) =>
layer.bodyId !== "0" &&
petOccupiedOrRestrictedZoneIds.has(layer.zone.id)
);
if (itemHasZoneRestrictedByPet) {
glitchMessages.push( glitchMessages.push(
<Box key={`static-zone-conflict-for-item-${item.id}`}> <Box key={`uc-conflict-for-item-${item.id}`}>
When you apply a Static-zone item like <i>{item.name}</i> to an <i>{item.name}</i> isn't actually compatible with this special pet.
Unconverted pet, it hides the pet. This is a known bug on We're still showing the old behavior, which is to hide the item.
Neopets.com, so we reproduce it here, too. Fixing this is in our todo list, sorry for the confusing UI!
</Box> </Box>
); );
} }
} }
}
// Look for items with the OFFICIAL_SVG_IS_INCORRECT glitch. // Look for items with the OFFICIAL_SVG_IS_INCORRECT glitch.
for (const item of appearance.items) { for (const item of items) {
const itemHasOfficialSvgIsIncorrect = item.appearance.layers.some((l) => const itemHasOfficialSvgIsIncorrect = item.appearance.layers.some((l) =>
(l.knownGlitches || []).includes("OFFICIAL_SVG_IS_INCORRECT") (l.knownGlitches || []).includes("OFFICIAL_SVG_IS_INCORRECT")
); );
@ -321,7 +333,7 @@ function OutfitKnownGlitchesBadge({ appearance }) {
} }
// Look for Dyeworks items that aren't converted yet. // Look for Dyeworks items that aren't converted yet.
for (const item of appearance.items) { for (const item of items) {
const itemIsDyeworks = item.name.includes("Dyeworks"); const itemIsDyeworks = item.name.includes("Dyeworks");
const itemIsConverted = item.appearance.layers.every(layerUsesHTML5); const itemIsConverted = item.appearance.layers.every(layerUsesHTML5);

View file

@ -118,33 +118,68 @@ export function getVisibleLayers(petAppearance, itemAppearances) {
const validItemAppearances = itemAppearances.filter((a) => a); const validItemAppearances = itemAppearances.filter((a) => a);
const petLayers = petAppearance.layers.map((l) => ({ ...l, source: "pet" }));
const itemLayers = validItemAppearances const itemLayers = validItemAppearances
.map((a) => a.layers) .map((a) => a.layers)
.flat() .flat()
.map((l) => ({ ...l, source: "item" })); .map((l) => ({ ...l, source: "item" }));
const itemOccupiedZoneIds = new Set(itemLayers.map((l) => l.zone.id));
const petLayers = petAppearance.layers
.map((l) => ({ ...l, source: "pet" }))
// Copying weird Neopets.com behavior: if an item occupies a zone that the
// pet also occupies, the pet's corresponding zone is hidden.
.filter((l) => !itemOccupiedZoneIds.has(l.zone.id));
let allLayers = [...petLayers, ...itemLayers]; let allLayers = [...petLayers, ...itemLayers];
const itemRestrictedZoneIds = validItemAppearances const itemRestrictedZoneIds = new Set(
validItemAppearances
.map((a) => a.restrictedZones) .map((a) => a.restrictedZones)
.flat() .flat()
.map((z) => z.id); .map((z) => z.id)
const petRestrictedZoneIds = petAppearance.restrictedZones.map((z) => z.id); );
const allRestrictedZoneIds = new Set([ const petOccupiedZoneIds = new Set(petLayers.map((l) => l.zone.id));
...itemRestrictedZoneIds, const petRestrictedZoneIds = new Set(
petAppearance.restrictedZones.map((z) => z.id)
);
const petOccupiedOrRestrictedZoneIds = new Set([
...petOccupiedZoneIds,
...petRestrictedZoneIds, ...petRestrictedZoneIds,
]); ]);
const visibleLayers = allLayers.filter( const visibleLayers = allLayers.filter((layer) => {
(l) => !allRestrictedZoneIds.has(l.zone.id) // When an item restricts a zone, it hides pet layers of the same zone.
); // We use this to e.g. make a hat hide a hair ruff.
//
// NOTE: Items' restricted layers also affect what items you can wear at
// the same time. We don't enforce anything about that here, and
// instead assume that the input by this point is valid!
if (layer.source === "pet" && itemRestrictedZoneIds.has(layer.zone.id)) {
return false;
}
// When a pet appearance restricts or occupies a zone, it makes items
// that occupy the zone incompatible, but *only* if the item is
// body-specific. We use this to disallow UCs from wearing certain
// body-specific Biology Effects, Statics, etc, while still allowing
// non-body-specific items in those zones! (I think this happens for some
// Invisible pet stuff, too?)
//
// NOTE: This can result in both pet layers and items occupying the same
// zone, like Static! That's correct, and the item layer should be
// on top! (Here, we implement it by placing item layers second in
// the list, and depending on JS sort stability, and *then* depending
// on the UI to respect that ordering when rendering them by depth.
// Not great! 😅)
//
// TODO: Hiding the layer is the *old* behavior. Move this way deeper in
// the code to prevent these items from showing up in the first
// place!
if (
layer.source === "item" &&
layer.bodyId !== "0" &&
petOccupiedOrRestrictedZoneIds.has(layer.zone.id)
) {
return false;
}
return true;
});
visibleLayers.sort((a, b) => a.zone.depth - b.zone.depth); visibleLayers.sort((a, b) => a.zone.depth - b.zone.depth);
return visibleLayers; return visibleLayers;