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 }) {
const glitchMessages = [];
// Look for conflicts between Static pet zones (UCs), and Static items.
const petHasStaticZone = appearance.petAppearance?.layers.some(
(l) => l.zone.id === "46"
const { petAppearance, items } = appearance;
// 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) {
for (const item of appearance.items) {
const itemHasStaticZone = item.appearance.layers.some(
(l) => l.zone.id === "46"
const petRestrictedZoneIds = new Set(
petAppearance?.restrictedZones.map((z) => z.id)
);
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(
<Box key={`uc-conflict-for-item-${item.id}`}>
<i>{item.name}</i> isn't actually compatible with this special pet.
We're still showing the old behavior, which is to hide the item.
Fixing this is in our todo list, sorry for the confusing UI!
</Box>
);
if (itemHasStaticZone) {
glitchMessages.push(
<Box key={`static-zone-conflict-for-item-${item.id}`}>
When you apply a Static-zone item like <i>{item.name}</i> to an
Unconverted pet, it hides the pet. This is a known bug on
Neopets.com, so we reproduce it here, too.
</Box>
);
}
}
}
// 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) =>
(l.knownGlitches || []).includes("OFFICIAL_SVG_IS_INCORRECT")
);
@ -321,7 +333,7 @@ function OutfitKnownGlitchesBadge({ appearance }) {
}
// 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 itemIsConverted = item.appearance.layers.every(layerUsesHTML5);

View file

@ -118,33 +118,68 @@ export function getVisibleLayers(petAppearance, itemAppearances) {
const validItemAppearances = itemAppearances.filter((a) => a);
const petLayers = petAppearance.layers.map((l) => ({ ...l, source: "pet" }));
const itemLayers = validItemAppearances
.map((a) => a.layers)
.flat()
.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];
const itemRestrictedZoneIds = validItemAppearances
.map((a) => a.restrictedZones)
.flat()
.map((z) => z.id);
const petRestrictedZoneIds = petAppearance.restrictedZones.map((z) => z.id);
const allRestrictedZoneIds = new Set([
...itemRestrictedZoneIds,
const itemRestrictedZoneIds = new Set(
validItemAppearances
.map((a) => a.restrictedZones)
.flat()
.map((z) => z.id)
);
const petOccupiedZoneIds = new Set(petLayers.map((l) => l.zone.id));
const petRestrictedZoneIds = new Set(
petAppearance.restrictedZones.map((z) => z.id)
);
const petOccupiedOrRestrictedZoneIds = new Set([
...petOccupiedZoneIds,
...petRestrictedZoneIds,
]);
const visibleLayers = allLayers.filter(
(l) => !allRestrictedZoneIds.has(l.zone.id)
);
const visibleLayers = allLayers.filter((layer) => {
// 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);
return visibleLayers;