transition to closest valid pose for species/color

This commit is contained in:
Matt Dunn-Rankin 2020-05-23 13:23:24 -07:00
parent 75a0fe2e8c
commit bcdd9af806
5 changed files with 146 additions and 10 deletions

View file

@ -48,9 +48,15 @@ function HomePage() {
function StartOutfitForm() { function StartOutfitForm() {
const history = useHistory(); const history = useHistory();
const idealPose = React.useMemo(
() => (Math.random() > 0.5 ? "HAPPY_FEM" : "HAPPY_MASC"),
[]
);
const [speciesId, setSpeciesId] = React.useState("1"); const [speciesId, setSpeciesId] = React.useState("1");
const [colorId, setColorId] = React.useState("8"); const [colorId, setColorId] = React.useState("8");
const [isValid, setIsValid] = React.useState(true); const [isValid, setIsValid] = React.useState(true);
const [closestPose, setClosestPose] = React.useState(idealPose);
const onSubmit = (e) => { const onSubmit = (e) => {
e.preventDefault(); e.preventDefault();
@ -62,6 +68,7 @@ function StartOutfitForm() {
const params = new URLSearchParams({ const params = new URLSearchParams({
species: speciesId, species: speciesId,
color: colorId, color: colorId,
pose: closestPose,
}); });
history.push(`/outfits/new?${params}`); history.push(`/outfits/new?${params}`);
@ -73,11 +80,13 @@ function StartOutfitForm() {
<SpeciesColorPicker <SpeciesColorPicker
speciesId={speciesId} speciesId={speciesId}
colorId={colorId} colorId={colorId}
idealPose={idealPose}
showPlaceholders showPlaceholders
onChange={(species, color, isValid) => { onChange={(species, color, isValid, closestPose) => {
setSpeciesId(species.id); setSpeciesId(species.id);
setColorId(color.id); setColorId(color.id);
setIsValid(isValid); setIsValid(isValid);
setClosestPose(closestPose);
}} }}
/> />
<Box width="4" /> <Box width="4" />

View file

@ -87,13 +87,15 @@ function OutfitControls({ outfitState, dispatchToOutfit }) {
<SpeciesColorPicker <SpeciesColorPicker
speciesId={outfitState.speciesId} speciesId={outfitState.speciesId}
colorId={outfitState.colorId} colorId={outfitState.colorId}
idealPose={outfitState.pose}
dark dark
onChange={(species, color, isValid) => { onChange={(species, color, isValid, closestPose) => {
if (isValid) { if (isValid) {
dispatchToOutfit({ dispatchToOutfit({
type: "setSpeciesAndColor", type: "setSpeciesAndColor",
speciesId: species.id, speciesId: species.id,
colorId: color.id, colorId: color.id,
pose: closestPose,
}); });
} else { } else {
toast({ toast({

View file

@ -14,6 +14,7 @@ import { Delay, useFetch } from "./util";
function SpeciesColorPicker({ function SpeciesColorPicker({
speciesId, speciesId,
colorId, colorId,
idealPose,
showPlaceholders, showPlaceholders,
dark = false, dark = false,
onChange, onChange,
@ -99,7 +100,11 @@ function SpeciesColorPicker({
const species = allSpecies.find((s) => s.id === speciesId); const species = allSpecies.find((s) => s.id === speciesId);
const newColor = allColors.find((c) => c.id === newColorId); const newColor = allColors.find((c) => c.id === newColorId);
onChange(species, newColor, pairIsValid(valids, speciesId, newColorId)); const validPoses = getValidPoses(valids, speciesId, newColorId);
const isValid = validPoses.size > 0;
const closestPose = getClosestPose(validPoses, idealPose);
console.log(idealPose, closestPose, validPoses);
onChange(species, newColor, isValid, closestPose);
}; };
// When the species changes, check if the new pair is valid, and update the // When the species changes, check if the new pair is valid, and update the
@ -109,7 +114,11 @@ function SpeciesColorPicker({
const newSpecies = allSpecies.find((s) => s.id === newSpeciesId); const newSpecies = allSpecies.find((s) => s.id === newSpeciesId);
const color = allColors.find((c) => c.id === colorId); const color = allColors.find((c) => c.id === colorId);
onChange(newSpecies, color, pairIsValid(valids, newSpeciesId, colorId)); const validPoses = getValidPoses(valids, newSpeciesId, colorId);
const isValid = validPoses.size > 0;
const closestPose = getClosestPose(validPoses, idealPose);
console.log(idealPose, closestPose, validPoses);
onChange(newSpecies, color, isValid, closestPose);
}; };
return ( return (
@ -157,14 +166,131 @@ function SpeciesColorPicker({
); );
} }
function pairIsValid(valids, speciesId, colorId) { function getPairByte(valids, speciesId, colorId) {
// Reading a bit table, owo! // Reading a bit table, owo!
const speciesIndex = speciesId - 1; const speciesIndex = speciesId - 1;
const colorIndex = colorId - 1; const colorIndex = colorId - 1;
const numColors = valids.getUint8(1); const numColors = valids.getUint8(1);
const pairByteIndex = speciesIndex * numColors + colorIndex + 2; const pairByteIndex = speciesIndex * numColors + colorIndex + 2;
const pairByte = valids.getUint8(pairByteIndex); return valids.getUint8(pairByteIndex);
return pairByte !== 0;
} }
function pairIsValid(valids, speciesId, colorId) {
return getPairByte(valids, speciesId, colorId) !== 0;
}
function getValidPoses(valids, speciesId, colorId) {
const pairByte = getPairByte(valids, speciesId, colorId);
console.log("pair byte", pairByte.toString(2).padStart(8, "0"));
const validPoses = new Set();
if (pairByte & 0b00000001) validPoses.add("HAPPY_MASC");
if (pairByte & 0b00000010) validPoses.add("SAD_MASC");
if (pairByte & 0b00000100) validPoses.add("SICK_MASC");
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 & 0b10000000) validPoses.add("UNKNOWN");
return validPoses;
}
function getClosestPose(validPoses, idealPose) {
return closestPosesInOrder[idealPose].find((p) => validPoses.has(p)) || null;
}
// For each pose, in what order do we prefer to match other poses?
//
// The principles of this ordering are:
// - Happy/sad matters more than gender presentation.
// - "Sick" is an unpopular emotion, and it's better to change gender
// presentation and stay happy/sad than to become sick.
// - Sad is a better fallback for sick than happy.
// - Unconverted vs converted is the biggest possible difference.
// - Unknown is the pose of last resort - even coming from another unknown.
const closestPosesInOrder = {
HAPPY_MASC: [
"HAPPY_MASC",
"HAPPY_FEM",
"SAD_MASC",
"SAD_FEM",
"SICK_MASC",
"SICK_FEM",
"UNCONVERTED",
"UNKNOWN",
],
HAPPY_FEM: [
"HAPPY_FEM",
"HAPPY_MASC",
"SAD_FEM",
"SAD_MASC",
"SICK_FEM",
"SICK_MASC",
"UNCONVERTED",
"UNKNOWN",
],
SAD_MASC: [
"SAD_MASC",
"SAD_FEM",
"HAPPY_MASC",
"HAPPY_FEM",
"SICK_MASC",
"SICK_FEM",
"UNCONVERTED",
"UNKNOWN",
],
SAD_FEM: [
"SAD_FEM",
"SAD_MASC",
"HAPPY_FEM",
"HAPPY_MASC",
"SICK_FEM",
"SICK_MASC",
"UNCONVERTED",
"UNKNOWN",
],
SICK_MASC: [
"SICK_MASC",
"SICK_FEM",
"SAD_MASC",
"SAD_FEM",
"HAPPY_MASC",
"HAPPY_FEM",
"UNCONVERTED",
"UNKNOWN",
],
SICK_FEM: [
"SICK_FEM",
"SICK_MASC",
"SAD_FEM",
"SAD_MASC",
"HAPPY_FEM",
"HAPPY_MASC",
"UNCONVERTED",
"UNKNOWN",
],
UNCONVERTED: [
"UNCONVERTED",
"HAPPY_FEM",
"HAPPY_MASC",
"SAD_FEM",
"SAD_MASC",
"SICK_FEM",
"SICK_MASC",
"UNKNOWN",
],
UNKNOWN: [
"HAPPY_FEM",
"HAPPY_MASC",
"SAD_FEM",
"SAD_MASC",
"SICK_FEM",
"SICK_MASC",
"UNCONVERTED",
"UNKNOWN",
],
};
export default SpeciesColorPicker; export default SpeciesColorPicker;

View file

@ -105,6 +105,7 @@ const outfitStateReducer = (apolloClient) => (baseState, action) => {
...baseState, ...baseState,
speciesId: action.speciesId, speciesId: action.speciesId,
colorId: action.colorId, colorId: action.colorId,
pose: action.pose,
}; };
case "wearItem": case "wearItem":
return produce(baseState, (state) => { return produce(baseState, (state) => {

View file

@ -38,9 +38,7 @@ export default async function getValidPetPoses() {
for (let colorId = 1; colorId <= numColors; colorId++) { for (let colorId = 1; colorId <= numColors; colorId++) {
const colorIndex = colorId - 1; const colorIndex = colorId - 1;
// We fill in the high bits first. If we add more things later, write // We fill in the high bits first, and shift left as we go!
// them first, so that they fill in the currently-empty high bits and
// everything else stays in the same position as before!
let byte = 0; let byte = 0;
byte += hasPose(speciesId, colorId, "UNKNOWN") ? 1 : 0; byte += hasPose(speciesId, colorId, "UNKNOWN") ? 1 : 0;
byte <<= 1; byte <<= 1;