add UC support!
I'm really into how the PosePicker button came out :3
This commit is contained in:
parent
17b00e295d
commit
47f55b1c3e
3 changed files with 161 additions and 115 deletions
|
@ -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)",
|
||||
|
|
|
@ -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;
|
||||
|
|
1
src/images/twemoji/sunglasses.svg
Normal file
1
src/images/twemoji/sunglasses.svg
Normal 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 |
Loading…
Reference in a new issue