2021-05-13 17:33:54 -07:00
|
|
|
import gql from "graphql-tag";
|
|
|
|
|
|
|
|
function getVisibleLayers(petAppearance, itemAppearances) {
|
|
|
|
if (!petAppearance) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
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" }));
|
|
|
|
|
|
|
|
let allLayers = [...petLayers, ...itemLayers];
|
|
|
|
|
|
|
|
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((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, or when the pet is
|
|
|
|
// Unconverted, it makes body-specific items incompatible. 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?)
|
|
|
|
//
|
|
|
|
// TODO: We shouldn't be *hiding* these zones, like we do with items; we
|
|
|
|
// should be doing this way earlier, to prevent the item from even
|
|
|
|
// showing up even in search results!
|
|
|
|
//
|
|
|
|
// NOTE: This can result in both pet layers and items occupying the same
|
|
|
|
// zone, like Static, so long as the item isn't body-specific! That's
|
|
|
|
// correct, and the item layer should be on top! (Here, we implement
|
|
|
|
// it by placing item layers second in the list, and rely on JS sort
|
|
|
|
// stability, and *then* rely on the UI to respect that ordering when
|
|
|
|
// rendering them by depth. Not great! 😅)
|
|
|
|
//
|
|
|
|
// NOTE: UCs used to implement their restrictions by listing specific
|
|
|
|
// zones, but it seems that the logic has changed to just be about
|
|
|
|
// UC-ness and body-specific-ness, and not necessarily involve the
|
|
|
|
// set of restricted zones at all. (This matters because e.g. UCs
|
|
|
|
// shouldn't show _any_ part of the Rainy Day Umbrella, but most UCs
|
|
|
|
// don't restrict Right-Hand Item (Zone 49).) Still, I'm keeping the
|
|
|
|
// zone restriction case running too, because I don't think it
|
|
|
|
// _hurts_ anything, and I'm not confident enough in this conclusion.
|
|
|
|
//
|
|
|
|
// TODO: Do Invisibles follow this new rule like UCs, too? Or do they still
|
|
|
|
// use zone restrictions?
|
|
|
|
if (
|
|
|
|
layer.source === "item" &&
|
|
|
|
layer.bodyId !== "0" &&
|
|
|
|
(petAppearance.pose === "UNCONVERTED" ||
|
|
|
|
petOccupiedOrRestrictedZoneIds.has(layer.zone.id))
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// A pet appearance can also restrict its own zones. The Wraith Uni is an
|
|
|
|
// interesting example: it has a horn, but its zone restrictions hide it!
|
|
|
|
if (layer.source === "pet" && petRestrictedZoneIds.has(layer.zone.id)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
visibleLayers.sort((a, b) => a.zone.depth - b.zone.depth);
|
|
|
|
|
|
|
|
return visibleLayers;
|
|
|
|
}
|
|
|
|
|
2021-05-13 18:03:56 -07:00
|
|
|
// TODO: The web client could save bandwidth by applying @client to the `depth`
|
|
|
|
// field, because it already has zone depths cached.
|
2021-05-13 17:33:54 -07:00
|
|
|
export const itemAppearanceFragmentForGetVisibleLayers = gql`
|
|
|
|
fragment ItemAppearanceForGetVisibleLayers on ItemAppearance {
|
|
|
|
id
|
|
|
|
layers {
|
|
|
|
id
|
|
|
|
zone {
|
|
|
|
id
|
2021-05-13 18:03:56 -07:00
|
|
|
depth
|
2021-05-13 17:33:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
restrictedZones {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
2021-05-13 18:03:56 -07:00
|
|
|
// TODO: The web client could save bandwidth by applying @client to the `depth`
|
|
|
|
// field, because it already has zone depths cached.
|
2021-05-13 17:33:54 -07:00
|
|
|
export const petAppearanceFragmentForGetVisibleLayers = gql`
|
|
|
|
fragment PetAppearanceForGetVisibleLayers on PetAppearance {
|
|
|
|
id
|
|
|
|
pose
|
|
|
|
layers {
|
|
|
|
id
|
|
|
|
zone {
|
|
|
|
id
|
2021-05-13 18:03:56 -07:00
|
|
|
depth
|
2021-05-13 17:33:54 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
restrictedZones {
|
|
|
|
id
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|
|
|
|
|
|
|
|
export default getVisibleLayers;
|