add UC support!

I'm really into how the PosePicker button came out :3
This commit is contained in:
Emi Matchu 2020-08-31 20:26:15 -07:00
parent 17b00e295d
commit 47f55b1c3e
3 changed files with 161 additions and 115 deletions

View file

@ -30,6 +30,7 @@ import { useLocalStorage } from "../util";
import twemojiSmile from "../../images/twemoji/smile.svg";
import twemojiCry from "../../images/twemoji/cry.svg";
import twemojiSick from "../../images/twemoji/sick.svg";
import twemojiSunglasses from "../../images/twemoji/sunglasses.svg";
import twemojiQuestion from "../../images/twemoji/question.svg";
import twemojiMasc from "../../images/twemoji/masc.svg";
import twemojiFem from "../../images/twemoji/fem.svg";
@ -143,18 +144,7 @@ function PosePicker({
isOpen && "is-open"
)}
>
{getEmotion(pose) === "HAPPY" && (
<EmojiImage src={twemojiSmile} alt="Choose a pose" />
)}
{getEmotion(pose) === "SAD" && (
<EmojiImage src={twemojiCry} alt="Choose a pose" />
)}
{getEmotion(pose) === "SICK" && (
<EmojiImage src={twemojiSick} alt="Choose a pose" />
)}
{getEmotion(pose) === null && (
<EmojiImage src={twemojiQuestion} alt="Choose a pose" />
)}
<EmojiImage src={getIcon(pose)} alt="Choose a pose" />
</Button>
</PopoverTrigger>
<Portal>
@ -213,76 +203,88 @@ function PosePicker({
function PosePickerTable({ poseInfos, onChange, initialFocusRef }) {
return (
<table width="100%">
<thead>
<tr>
<th />
<Cell as="th">
<EmojiImage src={twemojiSmile} alt="Happy" />
</Cell>
<Cell as="th">
<EmojiImage src={twemojiCry} alt="Sad" />
</Cell>
<Cell as="th">
<EmojiImage src={twemojiSick} alt="Sick" />
</Cell>
</tr>
</thead>
<tbody>
<tr>
<Cell as="th">
<EmojiImage src={twemojiMasc} alt="Masculine" />
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.happyMasc}
onChange={onChange}
inputRef={poseInfos.happyMasc.isSelected && initialFocusRef}
/>
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.sadMasc}
onChange={onChange}
inputRef={poseInfos.sadMasc.isSelected && initialFocusRef}
/>
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.sickMasc}
onChange={onChange}
inputRef={poseInfos.sickMasc.isSelected && initialFocusRef}
/>
</Cell>
</tr>
<tr>
<Cell as="th">
<EmojiImage src={twemojiFem} alt="Feminine" />
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.happyFem}
onChange={onChange}
inputRef={poseInfos.happyFem.isSelected && initialFocusRef}
/>
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.sadFem}
onChange={onChange}
inputRef={poseInfos.sadFem.isSelected && initialFocusRef}
/>
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.sickFem}
onChange={onChange}
inputRef={poseInfos.sickFem.isSelected && initialFocusRef}
/>
</Cell>
</tr>
</tbody>
</table>
<Box display="flex" flexDirection="column" alignItems="center">
<table width="100%">
<thead>
<tr>
<th />
<Cell as="th">
<EmojiImage src={twemojiSmile} alt="Happy" />
</Cell>
<Cell as="th">
<EmojiImage src={twemojiCry} alt="Sad" />
</Cell>
<Cell as="th">
<EmojiImage src={twemojiSick} alt="Sick" />
</Cell>
</tr>
</thead>
<tbody>
<tr>
<Cell as="th">
<EmojiImage src={twemojiMasc} alt="Masculine" />
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.happyMasc}
onChange={onChange}
inputRef={poseInfos.happyMasc.isSelected && initialFocusRef}
/>
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.sadMasc}
onChange={onChange}
inputRef={poseInfos.sadMasc.isSelected && initialFocusRef}
/>
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.sickMasc}
onChange={onChange}
inputRef={poseInfos.sickMasc.isSelected && initialFocusRef}
/>
</Cell>
</tr>
<tr>
<Cell as="th">
<EmojiImage src={twemojiFem} alt="Feminine" />
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.happyFem}
onChange={onChange}
inputRef={poseInfos.happyFem.isSelected && initialFocusRef}
/>
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.sadFem}
onChange={onChange}
inputRef={poseInfos.sadFem.isSelected && initialFocusRef}
/>
</Cell>
<Cell as="td">
<PoseOption
poseInfo={poseInfos.sickFem}
onChange={onChange}
inputRef={poseInfos.sickFem.isSelected && initialFocusRef}
/>
</Cell>
</tr>
</tbody>
</table>
{poseInfos.unconverted.isAvailable && (
<PoseOption
poseInfo={poseInfos.unconverted}
onChange={onChange}
inputRef={poseInfos.unconverted.isSelected && initialFocusRef}
size="sm"
label="Unconverted"
marginTop="2"
/>
)}
</Box>
);
}
@ -315,14 +317,24 @@ const GENDER_PRESENTATION_STRINGS = {
SICK_FEM: "Feminine",
};
function PoseOption({ poseInfo, onChange, inputRef }) {
function PoseOption({
poseInfo,
onChange,
inputRef,
size = "50px",
label,
...otherProps
}) {
const theme = useTheme();
const genderPresentationStr = GENDER_PRESENTATION_STRINGS[poseInfo.pose];
const emotionStr = EMOTION_STRINGS[poseInfo.pose];
let label = `${emotionStr} and ${genderPresentationStr}`;
let poseName =
poseInfo.pose === "UNCONVERTED"
? "Unconverted"
: `${emotionStr} and ${genderPresentationStr}`;
if (!poseInfo.isAvailable) {
label += ` (not modeled yet)`;
poseName += ` (not modeled yet)`;
}
const borderColor = useColorModeValue(
@ -334,16 +346,24 @@ function PoseOption({ poseInfo, onChange, inputRef }) {
<Box
as="label"
cursor="pointer"
display="flex"
alignItems="center"
borderColor={poseInfo.isSelected ? borderColor : "gray.400"}
boxShadow={label ? "md" : "none"}
borderWidth={label ? "1px" : "0"}
borderRadius={label ? "full" : "0"}
paddingRight={label ? "3" : "0"}
onClick={(e) => {
// HACK: We need the timeout to beat the popover's focus stealing!
const input = e.currentTarget.querySelector("input");
setTimeout(() => input.focus(), 0);
}}
{...otherProps}
>
<VisuallyHidden
as="input"
type="radio"
aria-label={label}
aria-label={poseName}
name="pose"
value={poseInfo.pose}
checked={poseInfo.isSelected}
@ -356,8 +376,8 @@ function PoseOption({ poseInfo, onChange, inputRef }) {
borderRadius="full"
boxShadow="md"
overflow="hidden"
width="50px"
height="50px"
width={size === "sm" ? "30px" : "50px"}
height={size === "sm" ? "30px" : "50px"}
title={
poseInfo.isAvailable
? // A lil debug output, so that we can quickly identify glitched
@ -408,34 +428,30 @@ function PoseOption({ poseInfo, onChange, inputRef }) {
)}
/>
{poseInfo.isAvailable ? (
<Box
width="50px"
height="50px"
transform={
transformsByBodyId[poseInfo.bodyId] || transformsByBodyId.default
}
>
<Box width="100%" height="100%" transform={getTransform(poseInfo)}>
<OutfitLayers visibleLayers={getVisibleLayers(poseInfo, [])} />
</Box>
) : (
<Flex align="center" justify="center">
<Box
fontFamily="Delicious, sans-serif"
fontSize="3xl"
fontWeight="900"
color="gray.600"
>
?
</Box>
<Flex align="center" justify="center" width="100%" height="100%">
<EmojiImage src={twemojiQuestion} boxSize="24px" />
</Flex>
)}
</Box>
{label && (
<Box
marginLeft="2"
fontSize="xs"
fontWeight={poseInfo.isSelected ? "bold" : "normal"}
>
{label}
</Box>
)}
</Box>
);
}
function EmojiImage({ src, alt }) {
return <img src={src} alt={alt} width="16px" height="16px" />;
function EmojiImage({ src, alt, boxSize = "16px" }) {
return <img src={src} alt={alt} width={boxSize} height={boxSize} />;
}
function usePoses(speciesId, colorId, selectedPose) {
@ -484,6 +500,13 @@ function usePoses(speciesId, colorId, selectedPose) {
) {
...PetAppearanceForPosePicker
}
unconverted: petAppearance(
speciesId: $speciesId
colorId: $colorId
pose: UNCONVERTED
) {
...PetAppearanceForPosePicker
}
}
fragment PetAppearanceForPosePicker on PetAppearance {
@ -500,53 +523,76 @@ function usePoses(speciesId, colorId, selectedPose) {
const poseInfos = {
happyMasc: {
...data?.happyMasc,
pose: "HAPPY_MASC",
isAvailable: Boolean(data?.happyMasc),
isSelected: selectedPose === "HAPPY_MASC",
},
sadMasc: {
...data?.sadMasc,
pose: "SAD_MASC",
isAvailable: Boolean(data?.sadMasc),
isSelected: selectedPose === "SAD_MASC",
},
sickMasc: {
...data?.sickMasc,
pose: "SICK_MASC",
isAvailable: Boolean(data?.sickMasc),
isSelected: selectedPose === "SICK_MASC",
},
happyFem: {
...data?.happyFem,
pose: "HAPPY_FEM",
isAvailable: Boolean(data?.happyFem),
isSelected: selectedPose === "HAPPY_FEM",
},
sadFem: {
...data?.sadFem,
pose: "SAD_FEM",
isAvailable: Boolean(data?.sadFem),
isSelected: selectedPose === "SAD_FEM",
},
sickFem: {
...data?.sickFem,
pose: "SICK_FEM",
isAvailable: Boolean(data?.sickFem),
isSelected: selectedPose === "SICK_FEM",
},
unconverted: {
...data?.unconverted,
pose: "UNCONVERTED",
isAvailable: Boolean(data?.unconverted),
isSelected: selectedPose === "UNCONVERTED",
},
};
return { loading, error, poseInfos };
}
function getEmotion(pose) {
function getIcon(pose) {
if (["HAPPY_MASC", "HAPPY_FEM"].includes(pose)) {
return "HAPPY";
return twemojiSmile;
} else if (["SAD_MASC", "SAD_FEM"].includes(pose)) {
return "SAD";
return twemojiCry;
} else if (["SICK_MASC", "SICK_FEM"].includes(pose)) {
return "SICK";
} else if (["UNCONVERTED", "UNKNOWN"].includes(pose)) {
return null;
return twemojiSick;
} else if (pose === "UNCONVERTED") {
return twemojiSunglasses;
} else {
throw new Error(`unrecognized pose ${JSON.stringify(pose)}`);
return twemojiQuestion;
}
}
function getTransform(poseInfo) {
const { pose, bodyId } = poseInfo;
if (pose === "UNCONVERTED") {
return transformsByBodyId.default;
}
if (bodyId in transformsByBodyId) {
return transformsByBodyId[bodyId];
}
return transformsByBodyId.default;
}
const transformsByBodyId = {
"93": "translate(-5px, 10px) scale(2.8)",
"106": "translate(-8px, 8px) scale(2.9)",

View file

@ -243,8 +243,7 @@ function getValidPoses(valids, speciesId, colorId) {
if (pairByte & 0b00001000) validPoses.add("HAPPY_FEM");
if (pairByte & 0b00010000) validPoses.add("SAD_FEM");
if (pairByte & 0b00100000) validPoses.add("SICK_FEM");
// TODO: Add unconverted support!
// if (pairByte & 0b01000000) validPoses.add("UNCONVERTED");
if (pairByte & 0b01000000) validPoses.add("UNCONVERTED");
if (pairByte & 0b10000000) validPoses.add("UNKNOWN");
return validPoses;

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36"><path fill="#FFCC4D" d="M36 18c0 9.941-8.059 18-18 18S0 27.941 0 18 8.059 0 18 0s18 8.059 18 18"/><path fill-rule="evenodd" clip-rule="evenodd" fill="#292F33" d="M1.24 11.018c.24.239 1.438.957 1.677 1.675.24.717.72 4.784 2.158 5.981 1.483 1.232 7.077.774 8.148.24 2.397-1.195 2.691-4.531 3.115-6.221.239-.957 1.677-.957 1.677-.957s1.438 0 1.678.956c.424 1.691.72 5.027 3.115 6.221 1.072.535 6.666.994 8.151-.238 1.436-1.197 1.915-5.264 2.155-5.982.238-.717 1.438-1.435 1.677-1.674.241-.239.241-1.196 0-1.436-.479-.478-6.134-.904-12.223-.239-1.215.133-1.677.478-4.554.478-2.875 0-3.339-.346-4.553-.478-6.085-.666-11.741-.24-12.221.238-.239.239-.239 1.197 0 1.436z"/><path fill="#664500" d="M27.335 23.629c-.178-.161-.444-.171-.635-.029-.039.029-3.922 2.9-8.7 2.9-4.766 0-8.662-2.871-8.7-2.9-.191-.142-.457-.13-.635.029-.177.16-.217.424-.094.628C8.7 24.472 11.788 29.5 18 29.5s9.301-5.028 9.429-5.243c.123-.205.084-.468-.094-.628z"/></svg>

After

Width:  |  Height:  |  Size: 997 B