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