refactor e/gp pairs to pose enum
This commit is contained in:
parent
772917fde6
commit
75a0fe2e8c
13 changed files with 210 additions and 182 deletions
|
@ -128,8 +128,7 @@ function SubmitPetForm() {
|
|||
name: petName,
|
||||
species: species.id,
|
||||
color: color.id,
|
||||
emotion: "HAPPY", // TODO: Ask PetService
|
||||
genderPresentation: "FEMININE", // TODO: Ask PetService
|
||||
pose: "HAPPY_FEM", // TODO: Ask PetService
|
||||
});
|
||||
for (const item of items) {
|
||||
params.append("objects[]", item.id);
|
||||
|
|
|
@ -32,7 +32,7 @@ function PosePicker({
|
|||
}) {
|
||||
const theme = useTheme();
|
||||
const checkedInputRef = React.useRef();
|
||||
const { loading, error, poses } = usePoses(outfitState);
|
||||
const { loading, error, poseInfos } = usePoses(outfitState);
|
||||
|
||||
if (loading) {
|
||||
return null;
|
||||
|
@ -45,19 +45,15 @@ function PosePicker({
|
|||
}
|
||||
|
||||
// If there's only one pose anyway, don't bother showing a picker!
|
||||
const numAvailablePoses = Object.values(poses).filter((p) => p.isAvailable)
|
||||
.length;
|
||||
const numAvailablePoses = Object.values(poseInfos).filter(
|
||||
(p) => p.isAvailable
|
||||
).length;
|
||||
if (numAvailablePoses <= 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const onChange = (e) => {
|
||||
const [emotion, genderPresentation] = e.target.value.split("-");
|
||||
dispatchToOutfit({
|
||||
type: "setPose",
|
||||
emotion,
|
||||
genderPresentation,
|
||||
});
|
||||
dispatchToOutfit({ type: "setPose", pose: e.target.value });
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -99,13 +95,13 @@ function PosePicker({
|
|||
isOpen && "is-open"
|
||||
)}
|
||||
>
|
||||
{outfitState.emotion === "HAPPY" && (
|
||||
{getEmotion(outfitState.pose) === "HAPPY" && (
|
||||
<EmojiImage src={twemojiSmile} alt="Choose a pose" />
|
||||
)}
|
||||
{outfitState.emotion === "SAD" && (
|
||||
{getEmotion(outfitState.pose) === "SAD" && (
|
||||
<EmojiImage src={twemojiCry} alt="Choose a pose" />
|
||||
)}
|
||||
{outfitState.emotion === "SICK" && (
|
||||
{getEmotion(outfitState.pose) === "SICK" && (
|
||||
<EmojiImage src={twemojiSick} alt="Choose a pose" />
|
||||
)}
|
||||
</Button>
|
||||
|
@ -133,24 +129,30 @@ function PosePicker({
|
|||
<EmojiImage src={twemojiMasc} alt="Masculine" />
|
||||
</Cell>
|
||||
<Cell as="td">
|
||||
<PoseButton
|
||||
pose={poses.happyMasc}
|
||||
<PoseOption
|
||||
poseInfo={poseInfos.happyMasc}
|
||||
onChange={onChange}
|
||||
inputRef={poses.happyMasc.isSelected && checkedInputRef}
|
||||
inputRef={
|
||||
poseInfos.happyMasc.isSelected && checkedInputRef
|
||||
}
|
||||
/>
|
||||
</Cell>
|
||||
<Cell as="td">
|
||||
<PoseButton
|
||||
pose={poses.sadMasc}
|
||||
<PoseOption
|
||||
poseInfo={poseInfos.sadMasc}
|
||||
onChange={onChange}
|
||||
inputRef={poses.sadMasc.isSelected && checkedInputRef}
|
||||
inputRef={
|
||||
poseInfos.sadMasc.isSelected && checkedInputRef
|
||||
}
|
||||
/>
|
||||
</Cell>
|
||||
<Cell as="td">
|
||||
<PoseButton
|
||||
pose={poses.sickMasc}
|
||||
<PoseOption
|
||||
poseInfo={poseInfos.sickMasc}
|
||||
onChange={onChange}
|
||||
inputRef={poses.sickMasc.isSelected && checkedInputRef}
|
||||
inputRef={
|
||||
poseInfos.sickMasc.isSelected && checkedInputRef
|
||||
}
|
||||
/>
|
||||
</Cell>
|
||||
</tr>
|
||||
|
@ -159,24 +161,30 @@ function PosePicker({
|
|||
<EmojiImage src={twemojiFem} alt="Feminine" />
|
||||
</Cell>
|
||||
<Cell as="td">
|
||||
<PoseButton
|
||||
pose={poses.happyFem}
|
||||
<PoseOption
|
||||
poseInfo={poseInfos.happyFem}
|
||||
onChange={onChange}
|
||||
inputRef={poses.happyFem.isSelected && checkedInputRef}
|
||||
inputRef={
|
||||
poseInfos.happyFem.isSelected && checkedInputRef
|
||||
}
|
||||
/>
|
||||
</Cell>
|
||||
<Cell as="td">
|
||||
<PoseButton
|
||||
pose={poses.sadFem}
|
||||
<PoseOption
|
||||
poseInfo={poseInfos.sadFem}
|
||||
onChange={onChange}
|
||||
inputRef={poses.sadFem.isSelected && checkedInputRef}
|
||||
inputRef={
|
||||
poseInfos.sadFem.isSelected && checkedInputRef
|
||||
}
|
||||
/>
|
||||
</Cell>
|
||||
<Cell as="td">
|
||||
<PoseButton
|
||||
pose={poses.sickFem}
|
||||
<PoseOption
|
||||
poseInfo={poseInfos.sickFem}
|
||||
onChange={onChange}
|
||||
inputRef={poses.sickFem.isSelected && checkedInputRef}
|
||||
inputRef={
|
||||
poseInfos.sickFem.isSelected && checkedInputRef
|
||||
}
|
||||
/>
|
||||
</Cell>
|
||||
</tr>
|
||||
|
@ -213,14 +221,14 @@ const GENDER_PRESENTATION_STRINGS = {
|
|||
FEMININE: "Feminine",
|
||||
};
|
||||
|
||||
function PoseButton({ pose, onChange, inputRef }) {
|
||||
function PoseOption({ poseInfo, onChange, inputRef }) {
|
||||
const theme = useTheme();
|
||||
const genderPresentationStr =
|
||||
GENDER_PRESENTATION_STRINGS[pose.genderPresentation];
|
||||
const emotionStr = EMOTION_STRINGS[pose.emotion];
|
||||
GENDER_PRESENTATION_STRINGS[poseInfo.genderPresentation];
|
||||
const emotionStr = EMOTION_STRINGS[poseInfo.emotion];
|
||||
|
||||
let label = `${emotionStr} and ${genderPresentationStr}`;
|
||||
if (!pose.isAvailable) {
|
||||
if (!poseInfo.isAvailable) {
|
||||
label += ` (not modeled yet)`;
|
||||
}
|
||||
|
||||
|
@ -239,9 +247,9 @@ function PoseButton({ pose, onChange, inputRef }) {
|
|||
type="radio"
|
||||
aria-label={label}
|
||||
name="pose"
|
||||
value={`${pose.emotion}-${pose.genderPresentation}`}
|
||||
checked={pose.isSelected}
|
||||
disabled={!pose.isAvailable}
|
||||
value={poseInfo.pose}
|
||||
checked={poseInfo.isSelected}
|
||||
disabled={!poseInfo.isAvailable}
|
||||
onChange={onChange}
|
||||
ref={inputRef || null}
|
||||
/>
|
||||
|
@ -253,11 +261,11 @@ function PoseButton({ pose, onChange, inputRef }) {
|
|||
width="50px"
|
||||
height="50px"
|
||||
title={
|
||||
pose.isAvailable
|
||||
poseInfo.isAvailable
|
||||
? // A lil debug output, so that we can quickly identify glitched
|
||||
// PetStates and manually mark them as glitched!
|
||||
window.location.hostname.includes("localhost") &&
|
||||
`#${pose.petStateId}`
|
||||
`#${poseInfo.petStateId}`
|
||||
: "Not modeled yet"
|
||||
}
|
||||
position="relative"
|
||||
|
@ -298,18 +306,18 @@ function PoseButton({ pose, onChange, inputRef }) {
|
|||
border-width: 3px;
|
||||
}
|
||||
`,
|
||||
!pose.isAvailable && "not-available"
|
||||
!poseInfo.isAvailable && "not-available"
|
||||
)}
|
||||
/>
|
||||
{pose.isAvailable ? (
|
||||
{poseInfo.isAvailable ? (
|
||||
<Box
|
||||
width="50px"
|
||||
height="50px"
|
||||
transform={
|
||||
transformsByBodyId[pose.bodyId] || transformsByBodyId.default
|
||||
transformsByBodyId[poseInfo.bodyId] || transformsByBodyId.default
|
||||
}
|
||||
>
|
||||
<OutfitLayers visibleLayers={getVisibleLayers(pose, [])} />
|
||||
<OutfitLayers visibleLayers={getVisibleLayers(poseInfo, [])} />
|
||||
</Box>
|
||||
) : (
|
||||
<Flex align="center" justify="center">
|
||||
|
@ -342,8 +350,7 @@ function usePoses(outfitState) {
|
|||
id
|
||||
petStateId
|
||||
bodyId
|
||||
genderPresentation
|
||||
emotion
|
||||
pose
|
||||
approximateThumbnailUrl
|
||||
...PetAppearanceForOutfitPreview
|
||||
}
|
||||
|
@ -354,28 +361,39 @@ function usePoses(outfitState) {
|
|||
);
|
||||
|
||||
const petAppearances = data?.petAppearances || [];
|
||||
const buildPose = (e, gp) => {
|
||||
const appearance = petAppearances.find(
|
||||
(pa) => pa.emotion === e && pa.genderPresentation === gp
|
||||
);
|
||||
const buildPoseInfo = (pose) => {
|
||||
const appearance = petAppearances.find((pa) => pa.pose === pose);
|
||||
return {
|
||||
...appearance,
|
||||
isAvailable: Boolean(appearance),
|
||||
isSelected:
|
||||
outfitState.emotion === e && outfitState.genderPresentation === gp,
|
||||
isSelected: outfitState.pose === pose,
|
||||
};
|
||||
};
|
||||
|
||||
const poses = {
|
||||
happyMasc: buildPose("HAPPY", "MASCULINE"),
|
||||
sadMasc: buildPose("SAD", "MASCULINE"),
|
||||
sickMasc: buildPose("SICK", "MASCULINE"),
|
||||
happyFem: buildPose("HAPPY", "FEMININE"),
|
||||
sadFem: buildPose("SAD", "FEMININE"),
|
||||
sickFem: buildPose("SICK", "FEMININE"),
|
||||
const poseInfos = {
|
||||
happyMasc: buildPoseInfo("HAPPY_MASC"),
|
||||
sadMasc: buildPoseInfo("SAD_MASC"),
|
||||
sickMasc: buildPoseInfo("SICK_MASC"),
|
||||
happyFem: buildPoseInfo("HAPPY_FEM"),
|
||||
sadFem: buildPoseInfo("SAD_FEM"),
|
||||
sickFem: buildPoseInfo("SICK_FEM"),
|
||||
};
|
||||
|
||||
return { loading, error, poses };
|
||||
return { loading, error, poseInfos };
|
||||
}
|
||||
|
||||
function getEmotion(pose) {
|
||||
if (["HAPPY_MASC", "HAPPY_FEM"].includes(pose)) {
|
||||
return "HAPPY";
|
||||
} else if (["SAD_MASC", "SAD_FEM"].includes(pose)) {
|
||||
return "SAD";
|
||||
} else if (["SICK_MASC", "SICK_FEM"].includes(pose)) {
|
||||
return "SICK";
|
||||
} else if (["UNCONVERTED", "UNKNOWN"].includes(pose)) {
|
||||
return null;
|
||||
} else {
|
||||
throw new Error(`unrecognized pose ${JSON.stringify(pose)}`);
|
||||
}
|
||||
}
|
||||
|
||||
const transformsByBodyId = {
|
||||
|
|
|
@ -16,8 +16,8 @@ const cacheRedirects = {
|
|||
// way, when you switch pet poses, Apollo knows it already has the
|
||||
// appearance data and doesn't need to ask the server again!
|
||||
petAppearance: (_, args, { getCacheKey }) => {
|
||||
const { speciesId, colorId, emotion, genderPresentation } = args;
|
||||
const id = `${speciesId}-${colorId}-${emotion}-${genderPresentation}`;
|
||||
const { speciesId, colorId, pose } = args;
|
||||
const id = `${speciesId}-${colorId}-${pose}`;
|
||||
return getCacheKey({ __typename: "PetAppearance", id });
|
||||
},
|
||||
},
|
||||
|
|
|
@ -6,13 +6,7 @@ import { useQuery } from "@apollo/react-hooks";
|
|||
* visibleLayers for rendering.
|
||||
*/
|
||||
export default function useOutfitAppearance(outfitState) {
|
||||
const {
|
||||
wornItemIds,
|
||||
speciesId,
|
||||
colorId,
|
||||
emotion,
|
||||
genderPresentation,
|
||||
} = outfitState;
|
||||
const { wornItemIds, speciesId, colorId, pose } = outfitState;
|
||||
|
||||
const { loading, error, data } = useQuery(
|
||||
gql`
|
||||
|
@ -20,15 +14,9 @@ export default function useOutfitAppearance(outfitState) {
|
|||
$wornItemIds: [ID!]!
|
||||
$speciesId: ID!
|
||||
$colorId: ID!
|
||||
$emotion: Emotion!
|
||||
$genderPresentation: GenderPresentation!
|
||||
) {
|
||||
petAppearance(
|
||||
speciesId: $speciesId
|
||||
colorId: $colorId
|
||||
emotion: $emotion
|
||||
genderPresentation: $genderPresentation
|
||||
$pose: Pose!
|
||||
) {
|
||||
petAppearance(speciesId: $speciesId, colorId: $colorId, pose: $pose) {
|
||||
...PetAppearanceForOutfitPreview
|
||||
}
|
||||
|
||||
|
@ -47,8 +35,7 @@ export default function useOutfitAppearance(outfitState) {
|
|||
wornItemIds,
|
||||
speciesId,
|
||||
colorId,
|
||||
emotion,
|
||||
genderPresentation,
|
||||
pose,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
|
@ -15,7 +15,7 @@ function useOutfitState() {
|
|||
initialState
|
||||
);
|
||||
|
||||
const { name, speciesId, colorId, emotion, genderPresentation } = state;
|
||||
const { name, speciesId, colorId, pose } = state;
|
||||
|
||||
// It's more convenient to manage these as a Set in state, but most callers
|
||||
// will find it more convenient to access them as arrays! e.g. for `.map()`
|
||||
|
@ -84,8 +84,7 @@ function useOutfitState() {
|
|||
allItemIds,
|
||||
speciesId,
|
||||
colorId,
|
||||
emotion,
|
||||
genderPresentation,
|
||||
pose,
|
||||
url,
|
||||
};
|
||||
|
||||
|
@ -159,28 +158,21 @@ const outfitStateReducer = (apolloClient) => (baseState, action) => {
|
|||
closetedItemIds.delete(itemId);
|
||||
});
|
||||
case "setPose":
|
||||
return produce(baseState, (state) => {
|
||||
const { emotion, genderPresentation } = action;
|
||||
state.emotion = emotion;
|
||||
state.genderPresentation = genderPresentation;
|
||||
});
|
||||
return { ...baseState, pose: action.pose };
|
||||
case "reset":
|
||||
return produce(baseState, (state) => {
|
||||
const {
|
||||
name,
|
||||
speciesId,
|
||||
colorId,
|
||||
emotion,
|
||||
genderPresentation,
|
||||
pose,
|
||||
wornItemIds,
|
||||
closetedItemIds,
|
||||
} = action;
|
||||
state.name = name;
|
||||
state.speciesId = speciesId ? String(speciesId) : baseState.speciesId;
|
||||
state.colorId = colorId ? String(colorId) : baseState.colorId;
|
||||
state.emotion = emotion || baseState.emotion;
|
||||
state.genderPresentation =
|
||||
genderPresentation || baseState.genderPresentation;
|
||||
state.pose = pose || baseState.pose;
|
||||
state.wornItemIds = wornItemIds
|
||||
? new Set(wornItemIds.map(String))
|
||||
: baseState.wornItemIds;
|
||||
|
@ -199,8 +191,7 @@ function parseOutfitUrl() {
|
|||
name: urlParams.get("name"),
|
||||
speciesId: urlParams.get("species"),
|
||||
colorId: urlParams.get("color"),
|
||||
emotion: urlParams.get("emotion") || "HAPPY",
|
||||
genderPresentation: urlParams.get("genderPresentation") || "FEMININE",
|
||||
pose: urlParams.get("pose") || "HAPPY_FEM",
|
||||
wornItemIds: new Set(urlParams.getAll("objects[]")),
|
||||
closetedItemIds: new Set(urlParams.getAll("closet[]")),
|
||||
};
|
||||
|
@ -335,8 +326,7 @@ function buildOutfitUrl(state) {
|
|||
name,
|
||||
speciesId,
|
||||
colorId,
|
||||
emotion,
|
||||
genderPresentation,
|
||||
pose,
|
||||
wornItemIds,
|
||||
closetedItemIds,
|
||||
} = state;
|
||||
|
@ -345,8 +335,7 @@ function buildOutfitUrl(state) {
|
|||
name: name || "",
|
||||
species: speciesId,
|
||||
color: colorId,
|
||||
emotion,
|
||||
genderPresentation,
|
||||
pose,
|
||||
});
|
||||
for (const itemId of wornItemIds) {
|
||||
params.append("objects[]", itemId);
|
||||
|
|
Binary file not shown.
|
@ -1,25 +1,25 @@
|
|||
import connectToDb from "./db";
|
||||
|
||||
import { getPose } from "./util";
|
||||
import { getPoseFromPetState, normalizeRow } from "./util";
|
||||
|
||||
export default async function getValidPetPoses() {
|
||||
const db = await connectToDb();
|
||||
|
||||
const numSpeciesPromise = getNumSpecies(db);
|
||||
const numColorsPromise = getNumColors(db);
|
||||
const poseTuplesPromise = getPoseTuples(db);
|
||||
const distinctPetStatesPromise = getDistinctPetStates(db);
|
||||
|
||||
const [numSpecies, numColors, poseTuples] = await Promise.all([
|
||||
const [numSpecies, numColors, distinctPetStates] = await Promise.all([
|
||||
numSpeciesPromise,
|
||||
numColorsPromise,
|
||||
poseTuplesPromise,
|
||||
distinctPetStatesPromise,
|
||||
]);
|
||||
|
||||
const poseStrs = new Set();
|
||||
for (const poseTuple of poseTuples) {
|
||||
const { species_id, color_id, mood_id, female, unconverted } = poseTuple;
|
||||
const pose = getPose(mood_id, female, unconverted);
|
||||
const poseStr = `${species_id}-${color_id}-${pose}`;
|
||||
for (const petState of distinctPetStates) {
|
||||
const { speciesId, colorId } = petState;
|
||||
const pose = getPoseFromPetState(petState);
|
||||
const poseStr = `${speciesId}-${colorId}-${pose}`;
|
||||
poseStrs.add(poseStr);
|
||||
}
|
||||
|
||||
|
@ -77,11 +77,11 @@ async function getNumColors(db) {
|
|||
return rows[0]["count(*)"];
|
||||
}
|
||||
|
||||
async function getPoseTuples(db) {
|
||||
async function getDistinctPetStates(db) {
|
||||
const [rows, _] = await db.query(`
|
||||
SELECT DISTINCT species_id, color_id, mood_id, female, unconverted
|
||||
FROM pet_states
|
||||
INNER JOIN pet_types ON pet_types.id = pet_states.pet_type_id
|
||||
WHERE glitched IS false AND color_id >= 1`);
|
||||
return rows;
|
||||
return rows.map(normalizeRow);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,15 @@ import getValidPetPoses from "./getValidPetPoses";
|
|||
describe("getValidPetPoses", () => {
|
||||
it("gets them and writes them to a buffer", async () => {
|
||||
const buffer = await getValidPetPoses();
|
||||
expect(buffer.toString()).toMatchSnapshot();
|
||||
expect(asBinaryString(buffer)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
function asBinaryString(buffer) {
|
||||
let str = "";
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
const byte = buffer.readUInt8(i);
|
||||
str += byte.toString(2).padStart(8, "0") + "\n";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,12 @@ const { gql } = require("apollo-server");
|
|||
const connectToDb = require("./db");
|
||||
const buildLoaders = require("./loaders");
|
||||
const neopets = require("./neopets");
|
||||
const { capitalize, getEmotion, getGenderPresentation } = require("./util");
|
||||
const {
|
||||
capitalize,
|
||||
getPoseFromPetState,
|
||||
getEmotion,
|
||||
getGenderPresentation,
|
||||
} = require("./util");
|
||||
|
||||
const typeDefs = gql`
|
||||
enum LayerImageSize {
|
||||
|
@ -12,6 +17,20 @@ const typeDefs = gql`
|
|||
SIZE_150
|
||||
}
|
||||
|
||||
"""
|
||||
The poses a PetAppearance can take!
|
||||
"""
|
||||
enum Pose {
|
||||
HAPPY_MASC
|
||||
SAD_MASC
|
||||
SICK_MASC
|
||||
HAPPY_FEM
|
||||
SAD_FEM
|
||||
SICK_FEM
|
||||
UNCONVERTED
|
||||
UNKNOWN # for when we have the data, but we don't know what it is
|
||||
}
|
||||
|
||||
"""
|
||||
A pet's gender presentation: masculine or feminine.
|
||||
|
||||
|
@ -50,8 +69,9 @@ const typeDefs = gql`
|
|||
id: ID!
|
||||
petStateId: ID!
|
||||
bodyId: ID!
|
||||
genderPresentation: GenderPresentation
|
||||
emotion: Emotion
|
||||
pose: Pose!
|
||||
genderPresentation: GenderPresentation # deprecated
|
||||
emotion: Emotion # deprecated
|
||||
approximateThumbnailUrl: String!
|
||||
layers: [AppearanceLayer!]!
|
||||
}
|
||||
|
@ -120,12 +140,7 @@ const typeDefs = gql`
|
|||
offset: Int
|
||||
limit: Int
|
||||
): ItemSearchResult!
|
||||
petAppearance(
|
||||
speciesId: ID!
|
||||
colorId: ID!
|
||||
emotion: Emotion!
|
||||
genderPresentation: GenderPresentation!
|
||||
): PetAppearance
|
||||
petAppearance(speciesId: ID!, colorId: ID!, pose: Pose!): PetAppearance
|
||||
petAppearances(speciesId: ID!, colorId: ID!): [PetAppearance!]!
|
||||
|
||||
petOnNeopetsDotCom(petName: String!): Outfit
|
||||
|
@ -177,15 +192,15 @@ const resolvers = {
|
|||
PetAppearance: {
|
||||
id: ({ petType, petState }) => {
|
||||
const { speciesId, colorId } = petType;
|
||||
const emotion = getEmotion(petState.moodId);
|
||||
const genderPresentation = getGenderPresentation(petState.female);
|
||||
return `${speciesId}-${colorId}-${emotion}-${genderPresentation}`;
|
||||
const pose = getPoseFromPetState(petState);
|
||||
return `${speciesId}-${colorId}-${pose}`;
|
||||
},
|
||||
petStateId: ({ petState }) => petState.id,
|
||||
bodyId: ({ petType }) => petType.bodyId,
|
||||
pose: ({ petState }) => getPoseFromPetState(petState),
|
||||
genderPresentation: ({ petState }) =>
|
||||
getGenderPresentation(petState.female),
|
||||
emotion: ({ petState }) => getEmotion(petState.moodId),
|
||||
getGenderPresentation(getPoseFromPetState(petState)),
|
||||
emotion: ({ petState }) => getEmotion(getPoseFromPetState(petState)),
|
||||
approximateThumbnailUrl: ({ petType, petState }) => {
|
||||
return `http://pets.neopets.com/cp/${petType.basicImageHash}/${petState.moodId}/1.png`;
|
||||
},
|
||||
|
@ -309,7 +324,7 @@ const resolvers = {
|
|||
},
|
||||
petAppearance: async (
|
||||
_,
|
||||
{ speciesId, colorId, emotion, genderPresentation },
|
||||
{ speciesId, colorId, pose },
|
||||
{ petTypeLoader, petStateLoader }
|
||||
) => {
|
||||
const petType = await petTypeLoader.load({
|
||||
|
@ -319,11 +334,7 @@ const resolvers = {
|
|||
|
||||
const petStates = await petStateLoader.load(petType.id);
|
||||
// TODO: This could be optimized into the query condition 🤔
|
||||
const petState = petStates.find(
|
||||
(ps) =>
|
||||
getEmotion(ps.moodId) === emotion &&
|
||||
getGenderPresentation(ps.female) === genderPresentation
|
||||
);
|
||||
const petState = petStates.find((ps) => getPoseFromPetState(ps) === pose);
|
||||
if (!petState) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const DataLoader = require("dataloader");
|
||||
const { normalizeRow } = require("./util");
|
||||
|
||||
const loadAllColors = (db) => async () => {
|
||||
const [rows, _] = await db.execute(`SELECT * FROM colors WHERE prank = 0`);
|
||||
|
@ -277,18 +278,6 @@ const buildZoneTranslationLoader = (db) =>
|
|||
);
|
||||
});
|
||||
|
||||
function normalizeRow(row) {
|
||||
const normalizedRow = {};
|
||||
for (let [key, value] of Object.entries(row)) {
|
||||
key = key.replace(/_([a-z])/gi, (m) => m[1].toUpperCase());
|
||||
if ((key === "id" || key.endsWith("Id")) && typeof value === "number") {
|
||||
value = String(value);
|
||||
}
|
||||
normalizedRow[key] = value;
|
||||
}
|
||||
return normalizedRow;
|
||||
}
|
||||
|
||||
function buildLoaders(db) {
|
||||
return {
|
||||
loadAllColors: loadAllColors(db),
|
||||
|
|
|
@ -6,12 +6,7 @@ describe("PetAppearance", () => {
|
|||
const res = await query({
|
||||
query: gql`
|
||||
query {
|
||||
petAppearance(
|
||||
speciesId: "54"
|
||||
colorId: "75"
|
||||
emotion: HAPPY
|
||||
genderPresentation: FEMININE
|
||||
) {
|
||||
petAppearance(speciesId: "54", colorId: "75", pose: HAPPY_FEM) {
|
||||
layers {
|
||||
id
|
||||
imageUrl(size: SIZE_600)
|
||||
|
@ -77,6 +72,7 @@ describe("PetAppearance", () => {
|
|||
id
|
||||
bodyId
|
||||
petStateId
|
||||
pose
|
||||
genderPresentation
|
||||
emotion
|
||||
approximateThumbnailUrl
|
||||
|
|
|
@ -64,8 +64,8 @@ Object {
|
|||
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/1/1.png",
|
||||
"bodyId": "180",
|
||||
"emotion": "HAPPY",
|
||||
"genderPresentation": "FEMININE",
|
||||
"id": "54-75-HAPPY-FEMININE",
|
||||
"genderPresentation": "MASCULINE",
|
||||
"id": "54-75-HAPPY_FEM",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -111,13 +111,14 @@ Object {
|
|||
},
|
||||
],
|
||||
"petStateId": "17723",
|
||||
"pose": "HAPPY_FEM",
|
||||
},
|
||||
Object {
|
||||
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/1/1.png",
|
||||
"bodyId": "180",
|
||||
"emotion": "HAPPY",
|
||||
"genderPresentation": "MASCULINE",
|
||||
"id": "54-75-HAPPY-MASCULINE",
|
||||
"id": "54-75-HAPPY_MASC",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -163,13 +164,14 @@ Object {
|
|||
},
|
||||
],
|
||||
"petStateId": "17742",
|
||||
"pose": "HAPPY_MASC",
|
||||
},
|
||||
Object {
|
||||
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/4/1.png",
|
||||
"bodyId": "180",
|
||||
"emotion": "SICK",
|
||||
"genderPresentation": "FEMININE",
|
||||
"id": "54-75-SICK-FEMININE",
|
||||
"genderPresentation": "MASCULINE",
|
||||
"id": "54-75-SICK_FEM",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -215,13 +217,14 @@ Object {
|
|||
},
|
||||
],
|
||||
"petStateId": "10014",
|
||||
"pose": "SICK_FEM",
|
||||
},
|
||||
Object {
|
||||
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/4/1.png",
|
||||
"bodyId": "180",
|
||||
"emotion": "SICK",
|
||||
"genderPresentation": "MASCULINE",
|
||||
"id": "54-75-SICK-MASCULINE",
|
||||
"id": "54-75-SICK_MASC",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -267,13 +270,14 @@ Object {
|
|||
},
|
||||
],
|
||||
"petStateId": "11089",
|
||||
"pose": "SICK_MASC",
|
||||
},
|
||||
Object {
|
||||
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/2/1.png",
|
||||
"bodyId": "180",
|
||||
"emotion": "SAD",
|
||||
"genderPresentation": "FEMININE",
|
||||
"id": "54-75-SAD-FEMININE",
|
||||
"genderPresentation": "MASCULINE",
|
||||
"id": "54-75-SAD_FEM",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -319,13 +323,14 @@ Object {
|
|||
},
|
||||
],
|
||||
"petStateId": "5991",
|
||||
"pose": "SAD_FEM",
|
||||
},
|
||||
Object {
|
||||
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/2/1.png",
|
||||
"bodyId": "180",
|
||||
"emotion": "SAD",
|
||||
"genderPresentation": "MASCULINE",
|
||||
"id": "54-75-SAD-MASCULINE",
|
||||
"id": "54-75-SAD_MASC",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -371,13 +376,14 @@ Object {
|
|||
},
|
||||
],
|
||||
"petStateId": "436",
|
||||
"pose": "SAD_MASC",
|
||||
},
|
||||
Object {
|
||||
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/null/1.png",
|
||||
"bodyId": "180",
|
||||
"emotion": null,
|
||||
"genderPresentation": null,
|
||||
"id": "54-75-null-null",
|
||||
"id": "54-75-UNKNOWN",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -430,13 +436,14 @@ Object {
|
|||
},
|
||||
],
|
||||
"petStateId": "2",
|
||||
"pose": "UNKNOWN",
|
||||
},
|
||||
Object {
|
||||
"approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/null/1.png",
|
||||
"bodyId": "180",
|
||||
"emotion": null,
|
||||
"genderPresentation": null,
|
||||
"id": "54-75-null-null",
|
||||
"id": "54-75-UNKNOWN",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -489,6 +496,7 @@ Object {
|
|||
},
|
||||
],
|
||||
"petStateId": "4751",
|
||||
"pose": "UNKNOWN",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
|
|
@ -2,55 +2,77 @@ function capitalize(str) {
|
|||
return str[0].toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
function getEmotion(moodId) {
|
||||
if (String(moodId) === "1") {
|
||||
function getEmotion(pose) {
|
||||
if (["HAPPY_MASC", "HAPPY_FEM"].includes(pose)) {
|
||||
return "HAPPY";
|
||||
} else if (String(moodId) === "2") {
|
||||
} else if (["SAD_MASC", "SAD_FEM"].includes(pose)) {
|
||||
return "SAD";
|
||||
} else if (String(moodId) === "4") {
|
||||
} else if (["SICK_MASC", "SICK_FEM"].includes(pose)) {
|
||||
return "SICK";
|
||||
} else if (moodId === null) {
|
||||
} else if (["UNCONVERTED", "UNKNOWN"].includes(pose)) {
|
||||
return null;
|
||||
} else {
|
||||
throw new Error(`unrecognized moodId ${JSON.stringify(moodId)}`);
|
||||
throw new Error(`unrecognized pose ${JSON.stringify(pose)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getGenderPresentation(modelPetWasFemale) {
|
||||
if (String(modelPetWasFemale) === "1") {
|
||||
return "FEMININE";
|
||||
} else if (String(modelPetWasFemale) === "0") {
|
||||
function getGenderPresentation(pose) {
|
||||
if (["HAPPY_MASC", "SAD_MASC", "SICK_MASC"].includes(pose)) {
|
||||
return "MASCULINE";
|
||||
} else {
|
||||
} else if (["HAPPY_FEM", "SAD_FEM", "SICK_FEM"].includes(pose)) {
|
||||
return "MASCULINE";
|
||||
} else if (["UNCONVERTED", "UNKNOWN"].includes(pose)) {
|
||||
return null;
|
||||
} else {
|
||||
throw new Error(`unrecognized pose ${JSON.stringify(pose)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function getPose(moodId, modelPetWasFemale, isUnconverted) {
|
||||
if (isUnconverted) {
|
||||
function getPoseFromPetState(petState) {
|
||||
const { moodId, female, unconverted } = petState;
|
||||
|
||||
if (unconverted) {
|
||||
return "UNCONVERTED";
|
||||
} else if (moodId == null || modelPetWasFemale == null) {
|
||||
} else if (moodId == null || female == null) {
|
||||
return "UNKNOWN";
|
||||
} else if (String(moodId) === "1" && String(modelPetWasFemale) === "0") {
|
||||
} else if (String(moodId) === "1" && String(female) === "0") {
|
||||
return "HAPPY_MASC";
|
||||
} else if (String(moodId) === "1" && String(modelPetWasFemale) === "1") {
|
||||
} else if (String(moodId) === "1" && String(female) === "1") {
|
||||
return "HAPPY_FEM";
|
||||
} else if (String(moodId) === "2" && String(modelPetWasFemale) === "0") {
|
||||
} else if (String(moodId) === "2" && String(female) === "0") {
|
||||
return "SAD_MASC";
|
||||
} else if (String(moodId) === "2" && String(modelPetWasFemale) === "1") {
|
||||
} else if (String(moodId) === "2" && String(female) === "1") {
|
||||
return "SAD_FEM";
|
||||
} else if (String(moodId) === "4" && String(modelPetWasFemale) === "0") {
|
||||
} else if (String(moodId) === "4" && String(female) === "0") {
|
||||
return "SICK_MASC";
|
||||
} else if (String(moodId) === "4" && String(modelPetWasFemale) === "1") {
|
||||
} else if (String(moodId) === "4" && String(female) === "1") {
|
||||
return "SICK_FEM";
|
||||
} else {
|
||||
throw new Error(
|
||||
`could not identify pose: ` +
|
||||
`moodId=${moodId}, ` +
|
||||
`modelPetWasFemale=${modelPetWasFemale}, ` +
|
||||
`isUnconverted=${isUnconverted}`
|
||||
`female=${female}, ` +
|
||||
`unconverted=${unconverted}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { capitalize, getEmotion, getGenderPresentation, getPose };
|
||||
function normalizeRow(row) {
|
||||
const normalizedRow = {};
|
||||
for (let [key, value] of Object.entries(row)) {
|
||||
key = key.replace(/_([a-z])/gi, (m) => m[1].toUpperCase());
|
||||
if ((key === "id" || key.endsWith("Id")) && typeof value === "number") {
|
||||
value = String(value);
|
||||
}
|
||||
normalizedRow[key] = value;
|
||||
}
|
||||
return normalizedRow;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
capitalize,
|
||||
getEmotion,
|
||||
getGenderPresentation,
|
||||
getPoseFromPetState,
|
||||
normalizeRow,
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue