add Support mode to PosePicker
Still just read-only stuff, but now you can look at all the different poses we have for a species/color! Soon I'll make the pose/glitched stuff editable :3 Some sizable refactors here to add the ability to specify appearance ID as well as pose… most of the app still doesn't use it, it's mostly just lil extra logic to make it win if it's available! (The rationale for making it an override, rather than always tracking appearance ID, is that it gets really inconvenient in practice to //wait// on looking up the appearance ID in order to start loading various queries. Species/color/pose is a more intuitive key, and works better and faster when the canonical appearance is what you want!)
This commit is contained in:
parent
7954c11c74
commit
1e30e7c8b0
16 changed files with 552 additions and 302 deletions
|
@ -174,6 +174,7 @@ function OutfitControls({ outfitState, dispatchToOutfit }) {
|
|||
speciesId={outfitState.speciesId}
|
||||
colorId={outfitState.colorId}
|
||||
pose={outfitState.pose}
|
||||
appearanceId={outfitState.appearanceId}
|
||||
dispatchToOutfit={dispatchToOutfit}
|
||||
onLockFocus={onLockFocus}
|
||||
onUnlockFocus={onUnlockFocus}
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
} from "../components/useOutfitAppearance";
|
||||
import { OutfitLayers } from "../components/OutfitPreview";
|
||||
import SupportOnly from "./support/SupportOnly";
|
||||
import { useLocalStorage } from "../util";
|
||||
|
||||
// From https://twemoji.twitter.com/, thank you!
|
||||
import twemojiSmile from "../../images/twemoji/smile.svg";
|
||||
|
@ -54,6 +55,7 @@ function PosePicker({
|
|||
speciesId,
|
||||
colorId,
|
||||
pose,
|
||||
appearanceId,
|
||||
dispatchToOutfit,
|
||||
onLockFocus,
|
||||
onUnlockFocus,
|
||||
|
@ -61,7 +63,10 @@ function PosePicker({
|
|||
const theme = useTheme();
|
||||
const checkedInputRef = React.useRef();
|
||||
const { loading, error, poseInfos } = usePoses(speciesId, colorId, pose);
|
||||
const [isInSupportMode, setIsInSupportMode] = React.useState(false);
|
||||
const [isInSupportMode, setIsInSupportMode] = useLocalStorage(
|
||||
"DTIPosePickerIsInSupportMode",
|
||||
false
|
||||
);
|
||||
|
||||
if (loading) {
|
||||
return null;
|
||||
|
@ -136,12 +141,14 @@ function PosePicker({
|
|||
</PopoverTrigger>
|
||||
<Portal>
|
||||
<PopoverContent>
|
||||
<Box p="4" position="relative">
|
||||
<Box p="4" position="relative" minWidth="200px" minHeight="150px">
|
||||
{isInSupportMode ? (
|
||||
<PosePickerSupport
|
||||
speciesId={speciesId}
|
||||
colorId={colorId}
|
||||
onChange={onChange}
|
||||
pose={pose}
|
||||
appearanceId={appearanceId}
|
||||
dispatchToOutfit={dispatchToOutfit}
|
||||
/>
|
||||
) : (
|
||||
<PosePickerTable
|
||||
|
|
|
@ -87,6 +87,7 @@ function WardrobePage() {
|
|||
speciesId={outfitState.speciesId}
|
||||
colorId={outfitState.colorId}
|
||||
pose={outfitState.pose}
|
||||
appearanceId={outfitState.appearanceId}
|
||||
wornItemIds={outfitState.wornItemIds}
|
||||
/>
|
||||
</Box>
|
||||
|
|
|
@ -420,11 +420,12 @@ function ItemSupportPetCompatibilityRuleFields({
|
|||
*/
|
||||
function ItemSupportAppearanceLayers({ item }) {
|
||||
const outfitState = React.useContext(OutfitStateContext);
|
||||
const { speciesId, colorId, pose } = outfitState;
|
||||
const { speciesId, colorId, pose, appearanceId } = outfitState;
|
||||
const { error, visibleLayers } = useOutfitAppearance({
|
||||
speciesId,
|
||||
colorId,
|
||||
pose,
|
||||
appearanceId,
|
||||
wornItemIds: [item.id],
|
||||
});
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Box } from "@chakra-ui/core";
|
|||
* Metadata is a UI component for showing metadata about something, as labels
|
||||
* and their values.
|
||||
*/
|
||||
function Metadata({ children }) {
|
||||
function Metadata({ children, ...otherProps }) {
|
||||
return (
|
||||
<Box
|
||||
as="dl"
|
||||
|
|
|
@ -3,21 +3,75 @@ import gql from "graphql-tag";
|
|||
import { useQuery } from "@apollo/client";
|
||||
import { Box, Select, Switch } from "@chakra-ui/core";
|
||||
|
||||
import { petAppearanceFragment } from "../../components/useOutfitAppearance";
|
||||
import HangerSpinner from "../../components/HangerSpinner";
|
||||
import Metadata, { MetadataLabel, MetadataValue } from "./Metadata";
|
||||
|
||||
function PosePickerSupport({ speciesId, colorId }) {
|
||||
function PosePickerSupport({
|
||||
speciesId,
|
||||
colorId,
|
||||
pose,
|
||||
appearanceId,
|
||||
dispatchToOutfit,
|
||||
}) {
|
||||
const { loading, error, data } = useQuery(
|
||||
gql`
|
||||
query PosePickerSupport($speciesId: ID!, $colorId: ID!) {
|
||||
petAppearances(speciesId: $speciesId, colorId: $colorId) {
|
||||
id
|
||||
bodyId
|
||||
pose
|
||||
...PetAppearanceForOutfitPreview
|
||||
isGlitched
|
||||
layers {
|
||||
id
|
||||
zone {
|
||||
id
|
||||
label
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
happyMasc: petAppearance(
|
||||
speciesId: $speciesId
|
||||
colorId: $colorId
|
||||
pose: HAPPY_MASC
|
||||
) {
|
||||
id
|
||||
}
|
||||
sadMasc: petAppearance(
|
||||
speciesId: $speciesId
|
||||
colorId: $colorId
|
||||
pose: SAD_MASC
|
||||
) {
|
||||
id
|
||||
}
|
||||
sickMasc: petAppearance(
|
||||
speciesId: $speciesId
|
||||
colorId: $colorId
|
||||
pose: SICK_MASC
|
||||
) {
|
||||
id
|
||||
}
|
||||
happyFem: petAppearance(
|
||||
speciesId: $speciesId
|
||||
colorId: $colorId
|
||||
pose: HAPPY_FEM
|
||||
) {
|
||||
id
|
||||
}
|
||||
sadFem: petAppearance(
|
||||
speciesId: $speciesId
|
||||
colorId: $colorId
|
||||
pose: SAD_FEM
|
||||
) {
|
||||
id
|
||||
}
|
||||
sickFem: petAppearance(
|
||||
speciesId: $speciesId
|
||||
colorId: $colorId
|
||||
pose: SICK_FEM
|
||||
) {
|
||||
id
|
||||
}
|
||||
}
|
||||
${petAppearanceFragment}
|
||||
`,
|
||||
{ variables: { speciesId, colorId } }
|
||||
);
|
||||
|
@ -38,17 +92,102 @@ function PosePickerSupport({ speciesId, colorId }) {
|
|||
);
|
||||
}
|
||||
|
||||
const canonicalAppearanceIdsByPose = {
|
||||
HAPPY_MASC: data.happyMasc?.id,
|
||||
SAD_MASC: data.sadMasc?.id,
|
||||
SICK_MASC: data.sickMasc?.id,
|
||||
HAPPY_FEM: data.happyFem?.id,
|
||||
SAD_FEM: data.sadFem?.id,
|
||||
SICK_FEM: data.sickFem?.id,
|
||||
};
|
||||
const canonicalAppearanceIds = Object.values(
|
||||
canonicalAppearanceIdsByPose
|
||||
).filter((id) => id);
|
||||
|
||||
if (!appearanceId) {
|
||||
appearanceId = canonicalAppearanceIdsByPose[pose];
|
||||
}
|
||||
|
||||
const currentPetAppearance = data.petAppearances.find(
|
||||
(pa) => pa.id === appearanceId
|
||||
);
|
||||
if (!currentPetAppearance) {
|
||||
return (
|
||||
<Box color="red.400" marginTop="8">
|
||||
Pet appearance with ID {JSON.stringify(appearanceId)} not found
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<Box display="flex" justifyContent="flex-end">
|
||||
<Select size="sm" width="auto">
|
||||
<Box display="flex" justifyContent="flex-end" marginBottom="4">
|
||||
<Select
|
||||
size="sm"
|
||||
width="auto"
|
||||
value={appearanceId}
|
||||
onChange={(e) => {
|
||||
const id = e.target.value;
|
||||
const petAppearance = data.petAppearances.find(
|
||||
(pa) => pa.id === id
|
||||
);
|
||||
dispatchToOutfit({
|
||||
type: "setPose",
|
||||
pose: petAppearance.pose,
|
||||
appearanceId: petAppearance.id,
|
||||
});
|
||||
}}
|
||||
>
|
||||
{data.petAppearances.map((pa) => (
|
||||
<option key={pa.id}>
|
||||
{POSE_NAMES[pa.pose]} ({pa.id})
|
||||
<option key={pa.id} value={pa.id}>
|
||||
{POSE_NAMES[pa.pose]} ({pa.id}){" "}
|
||||
{canonicalAppearanceIds.includes(pa.id) && "⭐️"}
|
||||
{pa.isGlitched && "👾"}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
</Box>
|
||||
<Metadata>
|
||||
<MetadataLabel>DTI ID:</MetadataLabel>
|
||||
<MetadataValue>{appearanceId}</MetadataValue>
|
||||
<MetadataLabel>Pose:</MetadataLabel>
|
||||
<MetadataValue>
|
||||
<Box display="flex" flexDirection="row" alignItems="center">
|
||||
<Select
|
||||
size="sm"
|
||||
value={currentPetAppearance.pose}
|
||||
flex="0 1 200px"
|
||||
cursor="not-allowed"
|
||||
isReadOnly
|
||||
>
|
||||
{Object.entries(POSE_NAMES).map(([pose, name]) => (
|
||||
<option key={pose} value={pose}>
|
||||
{name}
|
||||
</option>
|
||||
))}
|
||||
</Select>
|
||||
<Select
|
||||
size="sm"
|
||||
marginLeft="2"
|
||||
flex="0 1 150px"
|
||||
value={currentPetAppearance.isGlitched}
|
||||
cursor="not-allowed"
|
||||
isReadOnly
|
||||
>
|
||||
<option value="false">Usable</option>
|
||||
<option value="true">Glitched</option>
|
||||
</Select>
|
||||
</Box>
|
||||
</MetadataValue>
|
||||
<MetadataLabel>Zones:</MetadataLabel>
|
||||
<MetadataValue>
|
||||
{currentPetAppearance.layers
|
||||
.map((l) => l.zone)
|
||||
.map((z) => `${z.label} (${z.id})`)
|
||||
.sort()
|
||||
.join(", ")}
|
||||
</MetadataValue>
|
||||
</Metadata>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ function useOutfitState() {
|
|||
initialState
|
||||
);
|
||||
|
||||
const { name, speciesId, colorId, pose } = state;
|
||||
const { name, speciesId, colorId, pose, appearanceId } = 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()`
|
||||
|
@ -87,6 +87,7 @@ function useOutfitState() {
|
|||
speciesId,
|
||||
colorId,
|
||||
pose,
|
||||
appearanceId,
|
||||
url,
|
||||
};
|
||||
|
||||
|
@ -108,6 +109,7 @@ const outfitStateReducer = (apolloClient) => (baseState, action) => {
|
|||
speciesId: action.speciesId,
|
||||
colorId: action.colorId,
|
||||
pose: action.pose,
|
||||
appearanceId: null,
|
||||
};
|
||||
case "wearItem":
|
||||
return produce(baseState, (state) => {
|
||||
|
@ -161,7 +163,15 @@ const outfitStateReducer = (apolloClient) => (baseState, action) => {
|
|||
closetedItemIds.delete(itemId);
|
||||
});
|
||||
case "setPose":
|
||||
return { ...baseState, pose: action.pose };
|
||||
return {
|
||||
...baseState,
|
||||
pose: action.pose,
|
||||
|
||||
// Usually only the `pose` is specified, but `PosePickerSupport` can
|
||||
// also specify a corresponding `appearanceId`, to get even more
|
||||
// particular about which version of the pose to show if more than one.
|
||||
appearanceId: action.appearanceId || null,
|
||||
};
|
||||
case "reset":
|
||||
return produce(baseState, (state) => {
|
||||
const {
|
||||
|
@ -195,6 +205,7 @@ function parseOutfitUrl() {
|
|||
speciesId: urlParams.get("species"),
|
||||
colorId: urlParams.get("color"),
|
||||
pose: urlParams.get("pose") || "HAPPY_FEM",
|
||||
appearanceId: urlParams.get("state") || null,
|
||||
wornItemIds: new Set(urlParams.getAll("objects[]")),
|
||||
closetedItemIds: new Set(urlParams.getAll("closet[]")),
|
||||
};
|
||||
|
@ -330,6 +341,7 @@ function buildOutfitUrl(state) {
|
|||
speciesId,
|
||||
colorId,
|
||||
pose,
|
||||
appearanceId,
|
||||
wornItemIds,
|
||||
closetedItemIds,
|
||||
} = state;
|
||||
|
@ -346,6 +358,11 @@ function buildOutfitUrl(state) {
|
|||
for (const itemId of closetedItemIds) {
|
||||
params.append("closet[]", itemId);
|
||||
}
|
||||
if (appearanceId != null) {
|
||||
// `state` is an old name for compatibility with old-style DTI URLs. It
|
||||
// refers to "PetState", the database table name for pet appearances.
|
||||
params.append("state", appearanceId);
|
||||
}
|
||||
|
||||
const { origin, pathname } = window.location;
|
||||
const url = origin + pathname + "?" + params.toString();
|
||||
|
|
|
@ -25,6 +25,7 @@ function OutfitPreview({
|
|||
speciesId,
|
||||
colorId,
|
||||
pose,
|
||||
appearanceId,
|
||||
wornItemIds,
|
||||
placeholder,
|
||||
loadingDelay,
|
||||
|
@ -33,6 +34,7 @@ function OutfitPreview({
|
|||
speciesId,
|
||||
colorId,
|
||||
pose,
|
||||
appearanceId,
|
||||
wornItemIds,
|
||||
});
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { useQuery } from "@apollo/client";
|
|||
* visibleLayers for rendering.
|
||||
*/
|
||||
export default function useOutfitAppearance(outfitState) {
|
||||
const { wornItemIds, speciesId, colorId, pose } = outfitState;
|
||||
const { wornItemIds, speciesId, colorId, pose, appearanceId } = outfitState;
|
||||
|
||||
// We split this query out from the other one, so that we can HTTP cache it.
|
||||
//
|
||||
|
@ -24,19 +24,37 @@ export default function useOutfitAppearance(outfitState) {
|
|||
// HomePage. At time of writing, Vercel isn't actually edge-caching these, I
|
||||
// assume because our traffic isn't enough - so let's keep an eye on this!
|
||||
const { loading: loading1, error: error1, data: data1 } = useQuery(
|
||||
gql`
|
||||
query OutfitPetAppearance($speciesId: ID!, $colorId: ID!, $pose: Pose!) {
|
||||
petAppearance(speciesId: $speciesId, colorId: $colorId, pose: $pose) {
|
||||
...PetAppearanceForOutfitPreview
|
||||
}
|
||||
}
|
||||
${petAppearanceFragment}
|
||||
`,
|
||||
appearanceId == null
|
||||
? gql`
|
||||
query OutfitPetAppearance(
|
||||
$speciesId: ID!
|
||||
$colorId: ID!
|
||||
$pose: Pose!
|
||||
) {
|
||||
petAppearance(
|
||||
speciesId: $speciesId
|
||||
colorId: $colorId
|
||||
pose: $pose
|
||||
) {
|
||||
...PetAppearanceForOutfitPreview
|
||||
}
|
||||
}
|
||||
${petAppearanceFragment}
|
||||
`
|
||||
: gql`
|
||||
query OutfitPetAppearanceById($appearanceId: ID!) {
|
||||
petAppearance: petAppearanceById(id: $appearanceId) {
|
||||
...PetAppearanceForOutfitPreview
|
||||
}
|
||||
}
|
||||
${petAppearanceFragment}
|
||||
`,
|
||||
{
|
||||
variables: {
|
||||
speciesId,
|
||||
colorId,
|
||||
pose,
|
||||
appearanceId,
|
||||
},
|
||||
skip: speciesId == null || colorId == null || pose == null,
|
||||
}
|
||||
|
|
|
@ -159,3 +159,32 @@ export function useFetch(url, { responseType }) {
|
|||
|
||||
return { loading, error, data };
|
||||
}
|
||||
|
||||
/**
|
||||
* useLocalStorage is like React.useState, but it persists the value in the
|
||||
* device's `localStorage`, so it comes back even after reloading the page.
|
||||
*
|
||||
* Adapted from https://usehooks.com/useLocalStorage/.
|
||||
*/
|
||||
export function useLocalStorage(key, initialValue) {
|
||||
const [storedValue, setStoredValue] = React.useState(() => {
|
||||
try {
|
||||
const item = window.localStorage.getItem(key);
|
||||
return item ? JSON.parse(item) : initialValue;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return initialValue;
|
||||
}
|
||||
});
|
||||
|
||||
const setValue = (value) => {
|
||||
try {
|
||||
setStoredValue(value);
|
||||
window.localStorage.setItem(key, JSON.stringify(value));
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
||||
return [storedValue, setValue];
|
||||
}
|
||||
|
|
|
@ -101,6 +101,10 @@ const typeDefs = gql`
|
|||
|
||||
layers: [AppearanceLayer!]!
|
||||
petStateId: ID! # Deprecated, an alias for id
|
||||
# Whether this PetAppearance is known to look incorrect. This is a manual
|
||||
# flag that we set, in the case where this glitchy PetAppearance really did
|
||||
# appear on Neopets.com, and has since been fixed.
|
||||
isGlitched: Boolean!
|
||||
}
|
||||
|
||||
type ItemAppearance {
|
||||
|
@ -210,10 +214,17 @@ const typeDefs = gql`
|
|||
offset: Int
|
||||
limit: Int
|
||||
): ItemSearchResult!
|
||||
|
||||
petAppearanceById(id: ID!): PetAppearance @cacheControl(maxAge: 10800) # Cache for 3 hours (Support might edit!)
|
||||
# The canonical pet appearance for the given species, color, and pose.
|
||||
# Null if we don't have any data for this combination.
|
||||
petAppearance(speciesId: ID!, colorId: ID!, pose: Pose!): PetAppearance
|
||||
@cacheControl(maxAge: 604800) # Cache for 1 week (unlikely to change)
|
||||
@cacheControl(maxAge: 10800) # Cache for 3 hours (we might model more!)
|
||||
# All pet appearances we've ever seen for the given species and color. Note
|
||||
# that this might include multiple copies for the same pose, and they might
|
||||
# even be glitched data. We use this for Support tools.
|
||||
petAppearances(speciesId: ID!, colorId: ID!): [PetAppearance!]!
|
||||
@cacheControl(maxAge: 10800) # Cache for 3 hours (we might add more!)
|
||||
@cacheControl(maxAge: 10800) # Cache for 3 hours (we might model more!)
|
||||
outfit(id: ID!): Outfit
|
||||
|
||||
petOnNeopetsDotCom(petName: String!): Outfit
|
||||
|
@ -347,6 +358,10 @@ const resolvers = {
|
|||
return swfAssets;
|
||||
},
|
||||
petStateId: ({ id }) => id,
|
||||
isGlitched: async ({ id }, _, { petStateLoader }) => {
|
||||
const petState = await petStateLoader.load(id);
|
||||
return petState.glitched;
|
||||
},
|
||||
},
|
||||
AppearanceLayer: {
|
||||
bodyId: async ({ id }, _, { swfAssetLoader }) => {
|
||||
|
@ -558,6 +573,7 @@ const resolvers = {
|
|||
});
|
||||
return { query, items };
|
||||
},
|
||||
petAppearanceById: (_, { id }) => ({ id }),
|
||||
petAppearance: async (
|
||||
_,
|
||||
{ speciesId, colorId, pose },
|
||||
|
@ -568,9 +584,12 @@ const resolvers = {
|
|||
colorId,
|
||||
});
|
||||
|
||||
// TODO: We could query for this more directly, instead of loading all
|
||||
// appearances 🤔
|
||||
const petStates = await petStatesForPetTypeLoader.load(petType.id);
|
||||
// TODO: This could be optimized into the query condition 🤔
|
||||
const petState = petStates.find((ps) => getPoseFromPetState(ps) === pose);
|
||||
const petState = petStates.find(
|
||||
(ps) => getPoseFromPetState(ps) === pose && !ps.glitched
|
||||
);
|
||||
if (!petState) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -354,8 +354,8 @@ const buildPetStatesForPetTypeLoader = (db, loaders) =>
|
|||
const qs = petTypeIds.map((_) => "?").join(",");
|
||||
const [rows, _] = await db.execute(
|
||||
`SELECT * FROM pet_states
|
||||
WHERE pet_type_id IN (${qs}) AND glitched = 0
|
||||
ORDER BY (mood_id = 1) DESC, id`,
|
||||
WHERE pet_type_id IN (${qs})
|
||||
ORDER BY mood_id ASC, female DESC, id DESC`,
|
||||
petTypeIds
|
||||
);
|
||||
|
||||
|
|
|
@ -46,8 +46,8 @@ describe("PetAppearance", () => {
|
|||
],
|
||||
Array [
|
||||
"SELECT * FROM pet_states
|
||||
WHERE pet_type_id IN (?) AND glitched = 0
|
||||
ORDER BY (mood_id = 1) DESC",
|
||||
WHERE pet_type_id IN (?)
|
||||
ORDER BY mood_id ASC, female DESC, id DESC",
|
||||
Array [
|
||||
"2",
|
||||
],
|
||||
|
@ -142,8 +142,8 @@ describe("PetAppearance", () => {
|
|||
],
|
||||
Array [
|
||||
"SELECT * FROM pet_states
|
||||
WHERE pet_type_id IN (?) AND glitched = 0
|
||||
ORDER BY (mood_id = 1) DESC",
|
||||
WHERE pet_type_id IN (?)
|
||||
ORDER BY mood_id ASC, female DESC, id DESC",
|
||||
Array [
|
||||
"2",
|
||||
],
|
||||
|
@ -155,14 +155,14 @@ describe("PetAppearance", () => {
|
|||
rel.swf_asset_id = sa.id
|
||||
WHERE rel.parent_id IN (?,?,?,?,?,?,?,?)",
|
||||
Array [
|
||||
"2",
|
||||
"436",
|
||||
"4751",
|
||||
"5991",
|
||||
"10014",
|
||||
"11089",
|
||||
"2",
|
||||
"17723",
|
||||
"17742",
|
||||
"5991",
|
||||
"436",
|
||||
"10014",
|
||||
"11089",
|
||||
],
|
||||
],
|
||||
Array [
|
||||
|
@ -186,8 +186,8 @@ describe("PetAppearance", () => {
|
|||
"5",
|
||||
"37",
|
||||
"30",
|
||||
"33",
|
||||
"34",
|
||||
"33",
|
||||
],
|
||||
],
|
||||
]
|
||||
|
|
|
@ -44,7 +44,7 @@ Object {
|
|||
"id": "34",
|
||||
"name": "Green",
|
||||
},
|
||||
"id": "54-34-UNKNOWN",
|
||||
"id": "3951",
|
||||
"pose": "UNKNOWN",
|
||||
"species": Object {
|
||||
"id": "54",
|
||||
|
|
|
@ -8,7 +8,7 @@ Object {
|
|||
"isStandard": true,
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "54-75-HAPPY_FEM",
|
||||
"id": "17723",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -76,130 +76,7 @@ Object {
|
|||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "54-75-UNKNOWN",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7941/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 18,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "5996",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7942/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 7,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "6000",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7946/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 40,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "16467",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/024/24008/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 34,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "19549",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/028/28548/600x600.png?v2-1345719457000",
|
||||
"zone": Object {
|
||||
"depth": 37,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "19550",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/028/28549/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "163528",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/028/28549/600x600.png?v2-1326455337000",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
],
|
||||
"petStateId": "2",
|
||||
"pose": "UNKNOWN",
|
||||
"species": Object {
|
||||
"id": "54",
|
||||
"name": "Zafara",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bodyId": "180",
|
||||
"color": Object {
|
||||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "54-75-SAD_MASC",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7941/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 18,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "5996",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7942/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 7,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "6000",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7946/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 40,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14790",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21057/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14792",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21060/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 37,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "16467",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/024/24008/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 34,
|
||||
},
|
||||
},
|
||||
],
|
||||
"petStateId": "436",
|
||||
"pose": "SAD_MASC",
|
||||
"species": Object {
|
||||
"id": "54",
|
||||
"name": "Zafara",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bodyId": "180",
|
||||
"color": Object {
|
||||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "54-75-UNKNOWN",
|
||||
"id": "4751",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -264,7 +141,7 @@ Object {
|
|||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "54-75-SAD_FEM",
|
||||
"id": "2",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -287,20 +164,6 @@ Object {
|
|||
"depth": 40,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14790",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21057/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14793",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21061/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 37,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "16467",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/024/24008/600x600.png?v2-0",
|
||||
|
@ -308,9 +171,30 @@ Object {
|
|||
"depth": 34,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "19549",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/028/28548/600x600.png?v2-1345719457000",
|
||||
"zone": Object {
|
||||
"depth": 37,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "19550",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/028/28549/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "163528",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/028/28549/600x600.png?v2-1326455337000",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
],
|
||||
"petStateId": "5991",
|
||||
"pose": "SAD_FEM",
|
||||
"petStateId": "2",
|
||||
"pose": "UNKNOWN",
|
||||
"species": Object {
|
||||
"id": "54",
|
||||
"name": "Zafara",
|
||||
|
@ -322,123 +206,7 @@ Object {
|
|||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "54-75-SICK_FEM",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7941/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 18,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "5996",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7942/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 7,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "6000",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7946/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 40,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14791",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21059/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14795",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21066/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 37,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "16467",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/024/24008/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 34,
|
||||
},
|
||||
},
|
||||
],
|
||||
"petStateId": "10014",
|
||||
"pose": "SICK_FEM",
|
||||
"species": Object {
|
||||
"id": "54",
|
||||
"name": "Zafara",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bodyId": "180",
|
||||
"color": Object {
|
||||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "54-75-SICK_MASC",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7941/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 18,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "5996",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7942/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 7,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "6000",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7946/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 40,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14791",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21059/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14794",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21064/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 37,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "16467",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/024/24008/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 34,
|
||||
},
|
||||
},
|
||||
],
|
||||
"petStateId": "11089",
|
||||
"pose": "SICK_MASC",
|
||||
"species": Object {
|
||||
"id": "54",
|
||||
"name": "Zafara",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bodyId": "180",
|
||||
"color": Object {
|
||||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "54-75-HAPPY_FEM",
|
||||
"id": "17723",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -496,7 +264,7 @@ Object {
|
|||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "54-75-HAPPY_MASC",
|
||||
"id": "17742",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
|
@ -548,6 +316,238 @@ Object {
|
|||
"name": "Zafara",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bodyId": "180",
|
||||
"color": Object {
|
||||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "5991",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7941/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 18,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "5996",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7942/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 7,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "6000",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7946/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 40,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14790",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21057/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14793",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21061/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 37,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "16467",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/024/24008/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 34,
|
||||
},
|
||||
},
|
||||
],
|
||||
"petStateId": "5991",
|
||||
"pose": "SAD_FEM",
|
||||
"species": Object {
|
||||
"id": "54",
|
||||
"name": "Zafara",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bodyId": "180",
|
||||
"color": Object {
|
||||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "436",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7941/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 18,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "5996",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7942/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 7,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "6000",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7946/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 40,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14790",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21057/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14792",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21060/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 37,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "16467",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/024/24008/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 34,
|
||||
},
|
||||
},
|
||||
],
|
||||
"petStateId": "436",
|
||||
"pose": "SAD_MASC",
|
||||
"species": Object {
|
||||
"id": "54",
|
||||
"name": "Zafara",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bodyId": "180",
|
||||
"color": Object {
|
||||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "10014",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7941/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 18,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "5996",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7942/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 7,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "6000",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7946/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 40,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14791",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21059/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14795",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21066/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 37,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "16467",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/024/24008/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 34,
|
||||
},
|
||||
},
|
||||
],
|
||||
"petStateId": "10014",
|
||||
"pose": "SICK_FEM",
|
||||
"species": Object {
|
||||
"id": "54",
|
||||
"name": "Zafara",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"bodyId": "180",
|
||||
"color": Object {
|
||||
"id": "75",
|
||||
"name": "Starry",
|
||||
},
|
||||
"id": "11089",
|
||||
"layers": Array [
|
||||
Object {
|
||||
"id": "5995",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7941/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 18,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "5996",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7942/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 7,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "6000",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/007/7946/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 40,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14791",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21059/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 38,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "14794",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/021/21064/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 37,
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"id": "16467",
|
||||
"imageUrl": "https://impress-asset-images.s3.amazonaws.com/biology/000/000/024/24008/600x600.png?v2-0",
|
||||
"zone": Object {
|
||||
"depth": 34,
|
||||
},
|
||||
},
|
||||
],
|
||||
"petStateId": "11089",
|
||||
"pose": "SICK_MASC",
|
||||
"species": Object {
|
||||
"id": "54",
|
||||
"name": "Zafara",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -9203,6 +9203,14 @@ Object {
|
|||
"id": "17",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"color": Object {
|
||||
"id": "105",
|
||||
},
|
||||
"species": Object {
|
||||
"id": "17",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"color": Object {
|
||||
"id": "106",
|
||||
|
@ -17539,6 +17547,14 @@ Object {
|
|||
"id": "33",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"color": Object {
|
||||
"id": "112",
|
||||
},
|
||||
"species": Object {
|
||||
"id": "33",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"color": Object {
|
||||
"id": "6",
|
||||
|
|
Loading…
Reference in a new issue