A Typescript server emulator for Box Critters, a defunct virtual world.

Compare changes

Choose any two refs to compare.

+2
.env.build
···
+
EXECUTABLE=true
+
JWT_TOKEN="boxcritters"
+1
.env.example
···
+
EXECUTABLE=
JWT_TOKEN=
+2
.gitignore
···
Thumbs.db
accounts.json
/node_modules
+
Localbox
+
/public
+16 -9
Events.md
···
# Event Recreation Progress
-
A checklist for each party of which rooms are finished. Not all parties will be possible due to them not being archived sadly. This list ignores tedious animations which can be done at a later date.
+
A checklist for each party of which rooms are finished. Not all parties will be
+
possible due to them not being archived sadly. This list ignores tedious
+
animations which can be done at a later date.
## Default (no party)
···
## Battle Bears
-
This event had no significant changes. The Crash Site room became a default room, and is classified as that.
+
This event had no significant changes. The Crash Site room became a default
+
room, and is classified as that.
### Christmas
···
- [x] Tavern
- [x] Port
- [x] Shack
-
- [x] **Solar System** *(The background and foreground that were archived are corrupted)*
+
- [x] **Solar System** _(The background and foreground that were archived are
+
corrupted)_
### Halloween
···
### Club Penguin Celebration
-
- [x] Port (practically the same as `Halloween 2020`'s Port, just with a party hat on the outside of the `Shack`)
+
- [x] Port (practically the same as `Halloween 2020`'s Port, just with a party
+
hat on the outside of the `Shack`)
- [x] Shack
### CritterCon
···
### New Years
-
- [ ] Port
-
*The wiki only includes a screenshot of the Port, and no other rooms.*
+
- [ ] Port _The wiki only includes a screenshot of the Port, and no other
+
rooms._
### 2nd Anniversary
···
- [x] Port
- [x] Shack
- [x] Jungle
-
- [x] **Box Realm (work in progress)** *(the background is the only file found so far)*
+
- [x] **Box Realm (work in progress)** _(the background is the only file found
+
so far)_
### Easter
···
### Club Penguin Celebration
-
- [x] Port (practically the same as `Halloween 2021`'s Port, just with a party hat on the outside of the `Shack`)
+
- [x] Port (practically the same as `Halloween 2021`'s Port, just with a party
+
hat on the outside of the `Shack`)
- [x] Shack
### CritterCon
···
- [ ] Cellar
- [ ] Port
- [ ] Jungle
-
- [ ] Shack
+
- [ ] Shack
+21
LICENSE
···
+
MIT License
+
+
Copyright (c) 2025 Index
+
+
Permission is hereby granted, free of charge, to any person obtaining a copy
+
of this software and associated documentation files (the "Software"), to deal
+
in the Software without restriction, including without limitation the rights
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+
copies of the Software, and to permit persons to whom the Software is
+
furnished to do so, subject to the following conditions:
+
+
The above copyright notice and this permission notice shall be included in all
+
copies or substantial portions of the Software.
+
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+
SOFTWARE.
+89 -13
README.md
···
-
# Box Critters Localbox
+
# Localbox
+
+
Reopen the dusty box of the world of Box Critters! This repository features a
+
Typescript server emulator, **built using Deno**. The version of the game the
+
server is built around is **client version 161**.
+
+
This repository appears empty because it is a fork of my private repository so
+
that the assets didn't get included in the public version of the repository.
+
+
_project start: late November 2024_
+
+
## Assets
+
+
The assets for the game are not included in this repository, since I've been
+
told that Rocketsnail has taken down GitHub repositories for hosting the assets.
+
Though, I find it ironic since the modding community has been allowed to freely
+
host asset archives [here](https://github.com/boxcrittersmods/BCArchive) for 5
+
years at this point. As far as I can see, Localbox has the most comprehensive
+
archive of Box Critters assets, compiling several sources & custom spritesheet
+
JSON for spritesheet mis-matches.
+
+
### Archive Statistics
+
+
Below is a break down of the archive compilation, to be consider archived the
+
spritesheet has to be archived (spritesheet JSON is not taken into
+
consideration, as it is pretty easy to custom make). To get the assets, feel
+
free message me on Discord: @index.lua
+
+
> _Note:_ I do plan on manually cropping some rooms to make custom spritesheets,
+
> but when I do those will be marked as custom and not legit.
+
+
- **6/7** full-time rooms archived
+
- Full-time rooms are rooms that were always available, no matter the party.
+
- Missing: [_Jungle_](https://box-critters.fandom.com/wiki/Jungle)
+
+
- **6/10** party-exclusive rooms archived
+
- Party-exclusive rooms are rooms that were only available during specific
+
parties.
+
- Missing:
+
[_Holiday Cliff_](https://box-critters.fandom.com/wiki/Holiday_Cliff),
+
[_Holiday Forest_](https://box-critters.fandom.com/wiki/Holiday_Forest),
+
[_CritterCon Hall_](https://box-critters.fandom.com/wiki/Critter_Con_Hall),
+
[_Box Realm_](https://box-critters.fandom.com/wiki/Box_Realm)
-
Reopening the dusty box of the world of Box Critters! This is a Typescript server emulator using Deno.
+
- **465/590** released items archived
+
+
- **12/12** critters archived
+
+
- **2/2** mini-games archived
+
- This count excludes _Critter Ball_, because that requires an entirely
+
separate backend. If you want to relive _Critter Ball_, check out
+
[FarawayDrip30's Critter Ball server](https://farawaydrip30.itch.io/critterball-server).
## Party Switcher
-
A custom party switcher has been implemented, you can change the party on the log-in page, or using the `/party [ID]` command in-game. For a breakdown of party room recreation progress, go [here](Events.md).
+
A custom party switcher has been implemented, you can change the party on the
+
log-in page, or using the `/party [ID]` command in-game. For a breakdown of
+
party room recreation progress, go [here](Events.md).
## Development
+
### CLI
+
> Installation
+
```bash
deno install
```
> Serving
+
```bash
deno run start
> Listening on http://localhost:3257/
```
-
## APIs
+
> Building to an executable (shorthand invocation of `deno compile` using
+
> already-set flags & config)
-
The game has 4 APIs:
+
```bash
+
deno run build
+
> If the command is a success, a "Localbox" executable will appear in the project directory.
+
```
-
### (GET) `/api/server/players`
+
### APIs
-
This API returns information on the player(s) in-game, if any.
+
The game has 4 APIs for debugging or for use by the game client:
-
### (GET) `/api/server/rooms`
+
- (GET) `/api/server/players`
+
- This API returns information on the player(s) in-game, if any.
-
This API returns almost-identical information as the `/api/client/rooms` API, however it returns information on all hashes of all rooms with no required party ID URL parameter.
+
- (GET) `/api/server/rooms`
+
- This API returns almost-identical information as the `/api/client/rooms`
+
API, however it returns information on all hashes of all rooms with no
+
required party ID URL parameter.
-
### (POST) `/api/client/login`
+
- (POST) `/api/client/login`
+
- This API takes in all the information provided by the user on log-in and
+
generates a JWT for that session.
-
This API takes in all the information provided by the user on log-in and generates a JWT for that session.
+
- (GET) `/api/client/rooms?partyId=`
+
- This API returns information on the parties the game supports, and depending
+
on the party ID provided in the URL, information for each room that party
+
changes in some way. If the party does not change the room in any way, the
+
default version of that room will be returned. All the data is gathered by
+
the server reading the `/public/media/rooms/` directory of the game - and
+
cached for future requests.
-
### (GET) `/api/client/rooms?partyId=`
+
## Contributors
-
This API returns information on the parties the game supports, and depending on the party ID provided in the URL, information for each room that party changes in some way. If the party does not change the room in any way, the default version of that room will be returned. All the data is gathered by the server reading the `/public/media/rooms/` directory of the game - and cached for future requests.
+
- [jonastisell](https://github.com/jonastisell) - spritesheet extraction help &
+
moral support
+
- [Boo0](https://github.com/Boo6447) - provided archived assets of party room
+
versions & moral support
+
- [@boxcrittersmods/BCArchive](https://github.com/boxcrittersmods/BCArchive) -
+
provided a lot of archived assets of early rooms
-10
chalk.d.ts
···
-
/*
-
Typescript doesn't seem to recognize the functions of the chalk_deno module, so some are specified here to avoid type warnings.
-
*/
-
-
declare module "https://deno.land/x/chalk_deno@v4.1.1-deno/source/index.js" {
-
export function red(text: string): string;
-
export function green(text: string): string;
-
export function blue(text: string): string;
-
export function gray(text: string): string;
-
}
+133 -133
constants/items.ts
···
export const shop = {
-
lastItem: { itemId: "beard2_brown", cost: 20 },
-
freeItem: { itemId: "goggles_pink", cost: 0},
-
nextItem: { itemId: "scout_uniform", cost: 0},
-
collection: [
-
{ itemId: "santa_hat", cost: 100 }
-
]
-
}
+
lastItem: { itemId: "beard2_brown", cost: 20 },
+
freeItem: { itemId: "goggles_pink", cost: 0 },
+
nextItem: { itemId: "scout_uniform", cost: 0 },
+
collection: [
+
{ itemId: "santa_hat", cost: 100 },
+
],
+
};
export const codes = {
-
rocketsnail: ["viking", "rocket_red"],
-
andybulletin: "propeller",
-
cute: "toque_pink",
-
oommgames: "space_red",
-
boxcritters3d: ["3d_white", "3d_black"],
-
goodnight: "sleeping",
-
madeincanada: "toque_white",
-
thekeeper: "party_green",
-
bunnyhug: "hoodie_blue",
-
greenplumber: "ballcap_green",
-
duckhunter: "float_pink",
-
piratepack: "pirate_patch",
-
pickle: "pickle",
-
oscarproductions: "guitar_blue",
-
livestream: "headphones_black",
-
creative: "headphones_black",
-
fun: "propeller_pink",
-
marco: "keytar_red",
-
snowball: "hoodie_white",
-
critbits: "ballcap_pink",
-
esporte: "kit_yellow",
-
squeeze: "hoodie_orange",
-
imagination: "box_brown",
-
sparkle: "propeller_silver",
-
adventure: "snorkel_blue",
-
tamago: "sun_square",
-
redcross: "australian_hat",
-
discordcritterspt: "headphones_green",
-
scarletraven: "wings_black",
-
boxcritterswiki: "paperhat_colour",
-
wikicritters: "brain_green",
-
boxcrittersguild: "cardboard_sword_silver",
-
staysafe: "doctor_mask_blue",
-
glitter: "pirate_capt_pink"
-
}
+
rocketsnail: ["viking", "rocket_red"],
+
andybulletin: "propeller",
+
cute: "toque_pink",
+
oommgames: "space_red",
+
boxcritters3d: ["3d_white", "3d_black"],
+
goodnight: "sleeping",
+
madeincanada: "toque_white",
+
thekeeper: "party_green",
+
bunnyhug: "hoodie_blue",
+
greenplumber: "ballcap_green",
+
duckhunter: "float_pink",
+
piratepack: "pirate_patch",
+
pickle: "pickle",
+
oscarproductions: "guitar_blue",
+
livestream: "headphones_black",
+
creative: "headphones_black",
+
fun: "propeller_pink",
+
marco: "keytar_red",
+
snowball: "hoodie_white",
+
critbits: "ballcap_pink",
+
esporte: "kit_yellow",
+
squeeze: "hoodie_orange",
+
imagination: "box_brown",
+
sparkle: "propeller_silver",
+
adventure: "snorkel_blue",
+
tamago: "sun_square",
+
redcross: "australian_hat",
+
discordcritterspt: "headphones_green",
+
scarletraven: "wings_black",
+
boxcritterswiki: "paperhat_colour",
+
wikicritters: "brain_green",
+
boxcrittersguild: "cardboard_sword_silver",
+
staysafe: "doctor_mask_blue",
+
glitter: "pirate_capt_pink",
+
};
export const throwback = [
-
"goggles_black",
-
"pot",
-
"sombrero_yellow",
-
"cone",
-
"toque_purple",
-
"sun_orange",
-
"pirate_capt_black",
-
"lifejacket_red",
-
"ballcap_black",
-
"super_cape_red",
-
"tshirt_white",
-
"lei_red",
-
"ballcap_blue",
-
"viking_blue",
-
"overalls_orange",
-
"bunny_blue",
-
"messenger_brown",
-
"plaid_black",
-
"tinfoil_hat",
-
"monk",
-
"pirate_hat_black",
-
"hawaii_orange",
-
"space_black",
-
"traffic_cone",
-
"grass_yellow",
-
"ushanka",
-
"bandana_purple",
-
"ski_suit_blue",
-
"hotdog",
-
"space_blue",
-
"sweater_orange",
-
"tactical_headset",
-
"ballcap_yellow",
-
"ringmaster_suit_red",
-
"flight_helmet_red",
-
"rainhat_yellow",
-
"raincoat_yellow",
-
"ballcap_red",
-
"ballerina_pink",
-
"hawaii_blue",
-
"stripe_red_white",
-
"grass_green",
-
"plaid_blue",
-
"toque_orange",
-
"school_pack_orange",
-
"pirate_crew_blue",
-
"ringmaster_hat_black",
-
"super_mask_black",
-
"pirate_hat_pink",
-
"rainhat_red",
-
"tophat_black",
-
"scarf_red",
-
"skeleton_body",
-
"dracula_cloak",
-
"pumpkin",
-
"hotdog",
-
"beard3_black",
-
"scarf_purple",
-
"puffy_red",
-
"blockhead",
-
"hoodie_purple",
-
"winter_dress_red",
-
"bulb_blue",
-
"tacky_red",
-
"pirate_bandana_red"
-
]
+
"goggles_black",
+
"pot",
+
"sombrero_yellow",
+
"cone",
+
"toque_purple",
+
"sun_orange",
+
"pirate_capt_black",
+
"lifejacket_red",
+
"ballcap_black",
+
"super_cape_red",
+
"tshirt_white",
+
"lei_red",
+
"ballcap_blue",
+
"viking_blue",
+
"overalls_orange",
+
"bunny_blue",
+
"messenger_brown",
+
"plaid_black",
+
"tinfoil_hat",
+
"monk",
+
"pirate_hat_black",
+
"hawaii_orange",
+
"space_black",
+
"traffic_cone",
+
"grass_yellow",
+
"ushanka",
+
"bandana_purple",
+
"ski_suit_blue",
+
"hotdog",
+
"space_blue",
+
"sweater_orange",
+
"tactical_headset",
+
"ballcap_yellow",
+
"ringmaster_suit_red",
+
"flight_helmet_red",
+
"rainhat_yellow",
+
"raincoat_yellow",
+
"ballcap_red",
+
"ballerina_pink",
+
"hawaii_blue",
+
"stripe_red_white",
+
"grass_green",
+
"plaid_blue",
+
"toque_orange",
+
"school_pack_orange",
+
"pirate_crew_blue",
+
"ringmaster_hat_black",
+
"super_mask_black",
+
"pirate_hat_pink",
+
"rainhat_red",
+
"tophat_black",
+
"scarf_red",
+
"skeleton_body",
+
"dracula_cloak",
+
"pumpkin",
+
"hotdog",
+
"beard3_black",
+
"scarf_purple",
+
"puffy_red",
+
"blockhead",
+
"hoodie_purple",
+
"winter_dress_red",
+
"bulb_blue",
+
"tacky_red",
+
"pirate_bandana_red",
+
];
export const eventCodes = {
-
christmas2019: {
-
jinglebells: "elf_hat_green",
-
winter: "winter_dress",
-
joy: "bulb_yellow",
-
rudolph: "reindeer_head",
-
elf: "elf_suit_green",
-
cheer: "bulb_green",
-
glow: "bulb_red",
-
rednose: "reindeer_body",
-
blizzard: "wizard_blizzard",
-
family: "ornament_red",
-
shine: "angel_halo",
-
peace: "angel_wings",
-
jolly: "santa_beard",
-
merry: "santa_hat",
-
christmas: "santa_suit",
-
boxingday: "onsie_plaid_red",
-
warm: "sleeping_red",
-
tacky: "tacky_green",
-
snow: "goggles_white",
-
beautiful: "winter_dress_red",
-
newyear: "tuxedo_black",
-
"2020": "tophat_black"
-
}
-
}
+
christmas2019: {
+
jinglebells: "elf_hat_green",
+
winter: "winter_dress",
+
joy: "bulb_yellow",
+
rudolph: "reindeer_head",
+
elf: "elf_suit_green",
+
cheer: "bulb_green",
+
glow: "bulb_red",
+
rednose: "reindeer_body",
+
blizzard: "wizard_blizzard",
+
family: "ornament_red",
+
shine: "angel_halo",
+
peace: "angel_wings",
+
jolly: "santa_beard",
+
merry: "santa_hat",
+
christmas: "santa_suit",
+
boxingday: "onsie_plaid_red",
+
warm: "sleeping_red",
+
tacky: "tacky_green",
+
snow: "goggles_white",
+
beautiful: "winter_dress_red",
+
newyear: "tuxedo_black",
+
"2020": "tophat_black",
+
},
+
};
+14
constants/parties-old.json
···
+
[
+
"default",
+
"easter2019",
+
"christmas2019",
+
"lucky2020",
+
"easter2020",
+
"space",
+
"grad2020",
+
"summer2020",
+
"halloween2019",
+
"halloween2020",
+
"fools2021",
+
"halloween2021"
+
]
+50 -14
constants/parties.json
···
-
[
-
"default",
-
"easter2019",
-
"christmas2019",
-
"lucky2020",
-
"easter2020",
-
"space",
-
"grad2020",
-
"summer2020",
-
"halloween2019",
-
"halloween2020",
-
"fools2021",
-
"halloween2021"
-
]
+
{
+
"default": {
+
"start": null,
+
"end": null
+
},
+
"easter2019": {
+
"start": "04-18-2019",
+
"end": "04-28-2019"
+
},
+
"halloween2019": {
+
"start": "10-20-2019",
+
"end": "11-06-2019"
+
},
+
"christmas2019": {
+
"start": "12-05-2019",
+
"end": "01-05-2020"
+
},
+
"lucky2020": {
+
"start": "03-11-2020",
+
"end": "04-01-2020"
+
},
+
"easter2020": {
+
"start": "04-10-2020",
+
"end": "04-19-2020"
+
},
+
"grad2020": {
+
"start": "06-03-2020",
+
"end": "06-20-2020"
+
},
+
"summer2020": {
+
"start": "07-15-2020",
+
"end": "08-16-2020"
+
},
+
"space": {
+
"start": "08-16-2020",
+
"end": "09-04-2020"
+
},
+
"halloween2020": {
+
"start": "10-07-2020",
+
"end": "11-08-2020"
+
},
+
"christmas2020": {
+
"start": "12-02-2020",
+
"end": "01-01-2021"
+
},
+
"fools2021": {
+
"start": "03-30-2021",
+
"end": "04-02-2021"
+
}
+
}
+82 -81
constants/world.ts
···
-
import { PlayerCrumb, Room } from "../types.ts"
-
import { indexRoomData } from "../utils.ts";
+
import { PlayerCrumb, Room } from "../src/types.ts";
+
import { indexRoomData } from "../src/utils.ts";
-
export const rooms: Record<string, Record<string, Room>> = await indexRoomData()
+
export const rooms: Record<string, Record<string, Room>> =
+
await indexRoomData();
export const spawnRoom = "tavern";
export const players: Record<string, PlayerCrumb> = {
-
"0": {
-
"i": "0",
-
"n": "Huggable",
-
"c": "huggable",
-
"x": 1670,
-
"y": 323,
-
"r": 180,
-
"g": [],
-
"m": "",
-
"e": "",
-
"_roomId": "crash_site"
-
}
-
}
-
export const queue: Array<string> = []
+
"0": {
+
"i": "0",
+
"n": "Huggable",
+
"c": "huggable",
+
"x": 1670,
+
"y": 323,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
"_roomId": "crash_site",
+
},
+
};
+
export const queue: Array<string> = [];
export const roomExits = {
-
"cellar->tavern": { x: 360, y: 410, r: 0 },
-
"crash_site->cellar": { x: 615, y: 400, r: 0 },
-
"shack->port": { x: 550, y: 235, r: 0 },
-
"jungle->port": { x: 650, y: 230, r: 0 },
-
"snowman_village->tavern": { x: 563, y: 368, r: 0 }
-
}
+
"cellar->tavern": { x: 360, y: 410, r: 0 },
+
"crash_site->cellar": { x: 615, y: 400, r: 0 },
+
"shack->port": { x: 550, y: 235, r: 0 },
+
"jungle->port": { x: 650, y: 230, r: 0 },
+
"snowman_village->tavern": { x: 563, y: 368, r: 0 },
+
};
// deno-lint-ignore no-explicit-any
export const npcs: { [key: string]: any } = {
-
snowman_village: [
-
{
-
"i": "NPC0",
-
"n": "Snow Girl",
-
"c": "snowgirl",
-
"x": 1289,
-
"y": 228,
-
"r": 180,
-
"g": [],
-
"m": "",
-
"e": ""
-
},
-
{
-
"i": "NPC1",
-
"n": "Snow Patrol",
-
"c": "snow_patrol",
-
"x": 1644,
-
"y": 221,
-
"r": 180,
-
"g": [],
-
"m": "",
-
"e": ""
-
},
-
{
-
"i": "NPC2",
-
"n": "Snow Greeter",
-
"c": "snow_greeter",
-
"x": 443,
-
"y": 317,
-
"r": 180,
-
"g": [],
-
"m": "",
-
"e": ""
-
},
-
{
-
"i": "NPC3",
-
"n": "Snow Grandma",
-
"c": "snowgrandma",
-
"x": 1938,
-
"y": 251,
-
"r": 180,
-
"g": [],
-
"m": "",
-
"e": ""
-
},
-
{
-
"i": "NPC4",
-
"n": "Snow Keeper",
-
"c": "snowkeeper",
-
"x": 893,
-
"y": 216,
-
"r": 180,
-
"g": [],
-
"m": "",
-
"e": ""
-
}
-
]
-
}
+
snowman_village: [
+
{
+
"i": "NPC0",
+
"n": "Snow Girl",
+
"c": "snowgirl",
+
"x": 1289,
+
"y": 228,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
},
+
{
+
"i": "NPC1",
+
"n": "Snow Patrol",
+
"c": "snow_patrol",
+
"x": 1644,
+
"y": 221,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
},
+
{
+
"i": "NPC2",
+
"n": "Snow Greeter",
+
"c": "snow_greeter",
+
"x": 443,
+
"y": 317,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
},
+
{
+
"i": "NPC3",
+
"n": "Snow Grandma",
+
"c": "snowgrandma",
+
"x": 1938,
+
"y": 251,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
},
+
{
+
"i": "NPC4",
+
"n": "Snow Keeper",
+
"c": "snowkeeper",
+
"x": 893,
+
"y": 216,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
},
+
],
+
};
+5 -2
deno.json
···
{
"imports": {
-
"hono": "jsr:@hono/hono@^4.6.11"
+
"@/": "./src/",
+
"@std/fs": "jsr:@std/fs@^1.0.14"
},
"tasks": {
-
"start": "deno run --watch --allow-net --allow-read --allow-env --env-file=.env --allow-write main.ts"
+
"start": "deno run --allow-net --allow-read --allow-env --env-file=.env --allow-write src/main.ts",
+
"dev": "deno run --watch --allow-net --allow-read --allow-env --env-file=.env --allow-write src/main.ts",
+
"build": "deno compile --allow-net --allow-read --allow-env --env-file=.env.build --allow-write --output Localbox src/main.ts"
},
"compilerOptions": {
"jsx": "precompile",
+47 -131
deno.lock
···
{
"version": "4",
"specifiers": {
-
"jsr:@hono/hono@^4.6.11": "4.6.11",
-
"jsr:@std/encoding@*": "1.0.5",
+
"jsr:@std/cli@*": "1.0.15",
+
"jsr:@std/fs@*": "1.0.15",
+
"jsr:@std/fs@^1.0.14": "1.0.15",
+
"jsr:@std/path@^1.0.8": "1.0.8",
"npm:fs@^0.0.1-security": "0.0.1-security",
"npm:http@^0.0.1-security": "0.0.1-security",
+
"npm:jose@5.9.6": "5.9.6",
"npm:nodemon@^3.1.7": "3.1.9",
"npm:path@~0.12.7": "0.12.7",
"npm:socket.io@^4.8.1": "4.8.1",
"npm:zod@^3.24.1": "3.24.2"
},
"jsr": {
-
"@hono/hono@4.6.11": {
-
"integrity": "07399d911f09e94b7dc1e0e0a0577d35fe66578af20163d513958364c4e9e702"
+
"@std/cli@1.0.15": {
+
"integrity": "e79ba3272ec710ca44d8342a7688e6288b0b88802703f3264184b52893d5e93f"
+
},
+
"@std/fs@1.0.15": {
+
"integrity": "c083fb479889d6440d768e498195c3fc499d426fbf9a6592f98f53884d1d3f41",
+
"dependencies": [
+
"jsr:@std/path"
+
]
},
-
"@std/encoding@1.0.5": {
-
"integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04"
+
"@std/path@1.0.8": {
+
"integrity": "548fa456bb6a04d3c1a1e7477986b6cffbce95102d0bb447c67c4ee70e0364be"
}
},
"npm": {
···
"@types/node@22.12.0": {
"integrity": "sha512-Fll2FZ1riMjNmlmJOdAyY5pUbkftXslB5DgEzlIuNaiWhXd00FhWxVC/r4yV/4wBb9JfImTu+jiSvXTkJ7F/gA==",
"dependencies": [
-
"undici-types"
+
"undici-types@6.20.0"
]
},
-
"@types/node@22.13.8": {
-
"integrity": "sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==",
+
"@types/node@22.14.0": {
+
"integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==",
"dependencies": [
-
"undici-types"
+
"undici-types@6.21.0"
]
},
"accepts@1.3.8": {
···
"integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
"dependencies": [
"@types/cors",
-
"@types/node@22.13.8",
+
"@types/node@22.14.0",
"accepts",
"base64id",
"cookie",
···
},
"is-number@7.0.0": {
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
+
},
+
"jose@5.9.6": {
+
"integrity": "sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ=="
},
"mime-db@1.52.0": {
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
···
"undici-types@6.20.0": {
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="
},
+
"undici-types@6.21.0": {
+
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
+
},
"util@0.10.4": {
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
"dependencies": [
···
"zod@3.24.2": {
"integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="
}
-
},
-
"redirects": {
-
"https://deno.land/std/path/mod.ts": "https://deno.land/std@0.224.0/path/mod.ts"
},
"remote": {
"https://deno.land/std@0.150.0/_util/assert.ts": "e94f2eb37cebd7f199952e242c77654e43333c1ac4c5c700e929ea3aa5489f74",
···
"https://deno.land/std@0.162.0/async/pool.ts": "ef9eb97b388543acbf0ac32647121e4dbe629236899586c4d4311a8770fbb239",
"https://deno.land/std@0.162.0/async/tee.ts": "9af3a3e7612af75861308b52249e167f5ebc3dcfc8a1a4d45462d96606ee2b70",
"https://deno.land/std@0.162.0/http/server.ts": "e99c1bee8a3f6571ee4cdeb2966efad465b8f6fe62bec1bdb59c1f007cc4d155",
-
"https://deno.land/std@0.177.0/_util/asserts.ts": "178dfc49a464aee693a7e285567b3d0b555dc805ff490505a8aae34f9cfb1462",
-
"https://deno.land/std@0.177.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3",
-
"https://deno.land/std@0.177.0/bytes/index_of_needle.ts": "65c939607df609374c4415598fa4dad04a2f14c4d98cd15775216f0aaf597f24",
-
"https://deno.land/std@0.177.0/encoding/base64.ts": "7de04c2f8aeeb41453b09b186480be90f2ff357613b988e99fabb91d2eeceba1",
-
"https://deno.land/std@0.177.0/encoding/base64url.ts": "3f1178f6446834457b16bfde8b559c1cd3481727fe384d3385e4a9995dc2d851",
-
"https://deno.land/std@0.177.0/node/_core.ts": "9a58c0ef98ee77e9b8fcc405511d1b37a003a705eb6a9b6e95f75434d8009adc",
-
"https://deno.land/std@0.177.0/node/_utils.ts": "7fd55872a0cf9275e3c080a60e2fa6d45b8de9e956ebcde9053e72a344185884",
-
"https://deno.land/std@0.177.0/node/buffer.ts": "85617be2063eccaf177dbb84c7580d1e32023724ed14bd9df4e453b152a26167",
-
"https://deno.land/std@0.177.0/node/internal/buffer.mjs": "e92303a3cc6d9aaabcd270a937ad9319825d9ba08cb332650944df4562029b27",
-
"https://deno.land/std@0.177.0/node/internal/crypto/_keys.ts": "8f3c3b5a141aa0331a53c205e9338655f1b3b307a08085fd6ff6dda6f7c4190b",
-
"https://deno.land/std@0.177.0/node/internal/crypto/constants.ts": "544d605703053218499b08214f2e25cf4310651d535b7ab995891c4b7a217693",
-
"https://deno.land/std@0.177.0/node/internal/error_codes.ts": "8495e33f448a484518d76fa3d41d34fc20fe03c14b30130ad8e936b0035d4b8b",
-
"https://deno.land/std@0.177.0/node/internal/errors.ts": "1c699b8a3cb93174f697a348c004b1c6d576b66688eac8a48ebb78e65c720aae",
-
"https://deno.land/std@0.177.0/node/internal/hide_stack_frames.ts": "9dd1bad0a6e62a1042ce3a51eb1b1ecee2f246907bff44835f86e8f021de679a",
-
"https://deno.land/std@0.177.0/node/internal/normalize_encoding.mjs": "fd1d9df61c44d7196432f6e8244621468715131d18cc79cd299fc78ac549f707",
-
"https://deno.land/std@0.177.0/node/internal/primordials.mjs": "a72d86b5aa55d3d50b8e916b6a59b7cc0dc5a31da8937114b4a113ad5aa08c74",
-
"https://deno.land/std@0.177.0/node/internal/util.mjs": "f7fe2e1ca5e66f550ad0856b9f5ee4d666f0c071fe212ea7fc7f37cfa81f97a5",
-
"https://deno.land/std@0.177.0/node/internal/util/inspect.mjs": "11d7c9cab514b8e485acc3978c74b837263ff9c08ae4537fa18ad56bae633259",
-
"https://deno.land/std@0.177.0/node/internal/util/types.ts": "0e587b44ec5e017cf228589fc5ce9983b75beece6c39409c34170cfad49d6417",
-
"https://deno.land/std@0.177.0/node/internal/validators.mjs": "e02f2b02dd072a5d623970292588d541204dc82207b4c58985d933a5f4b382e6",
-
"https://deno.land/std@0.177.0/node/internal_binding/_libuv_winerror.ts": "30c9569603d4b97a1f1a034d88a3f74800d5ea1f12fcc3d225c9899d4e1a518b",
-
"https://deno.land/std@0.177.0/node/internal_binding/_node.ts": "cb2389b0eab121df99853eb6a5e3a684e4537e065fb8bf2cca0cbf219ce4e32e",
-
"https://deno.land/std@0.177.0/node/internal_binding/_utils.ts": "7c58a2fbb031a204dee9583ba211cf9c67922112fe77e7f0b3226112469e9fe1",
-
"https://deno.land/std@0.177.0/node/internal_binding/_winerror.ts": "3e8cfdfe22e89f13d2b28529bab35155e6b1730c0221ec5a6fc7077dc037be13",
-
"https://deno.land/std@0.177.0/node/internal_binding/buffer.ts": "31729e0537921d6c730ad0afea44a7e8a0a1044d070ade8368226cb6f7390c8b",
-
"https://deno.land/std@0.177.0/node/internal_binding/constants.ts": "21ff9d1ee71d0a2086541083a7711842fc6ae25e264dbf45c73815aadce06f4c",
-
"https://deno.land/std@0.177.0/node/internal_binding/string_decoder.ts": "54c3c1cbd5a9254881be58bf22637965dc69535483014dab60487e299cb95445",
-
"https://deno.land/std@0.177.0/node/internal_binding/types.ts": "2187595a58d2cf0134f4db6cc2a12bf777f452f52b15b6c3aed73fa072aa5fc3",
-
"https://deno.land/std@0.177.0/node/internal_binding/util.ts": "808ff3b92740284184ab824adfc420e75398c88c8bccf5111f0c24ac18c48f10",
-
"https://deno.land/std@0.177.0/node/internal_binding/uv.ts": "eb0048e30af4db407fb3f95563e30d70efd6187051c033713b0a5b768593a3a3",
+
"https://deno.land/std@0.212.0/path/_common/assert_path.ts": "2ca275f36ac1788b2acb60fb2b79cb06027198bc2ba6fb7e163efaedde98c297",
+
"https://deno.land/std@0.212.0/path/_common/constants.ts": "dc5f8057159f4b48cd304eb3027e42f1148cf4df1fb4240774d3492b5d12ac0c",
+
"https://deno.land/std@0.212.0/path/_os.ts": "8fb9b90fb6b753bd8c77cfd8a33c2ff6c5f5bc185f50de8ca4ac6a05710b2c15",
+
"https://deno.land/std@0.212.0/path/extname.ts": "593303db8ae8c865cbd9ceec6e55d4b9ac5410c1e276bfd3131916591b954441",
+
"https://deno.land/std@0.212.0/path/posix/_util.ts": "1e3937da30f080bfc99fe45d7ed23c47dd8585c5e473b2d771380d3a6937cf9d",
+
"https://deno.land/std@0.212.0/path/posix/extname.ts": "8d36ae0082063c5e1191639699e6f77d3acf501600a3d87b74943f0ae5327427",
+
"https://deno.land/std@0.212.0/path/windows/_util.ts": "d5f47363e5293fced22c984550d5e70e98e266cc3f31769e1710511803d04808",
+
"https://deno.land/std@0.212.0/path/windows/extname.ts": "165a61b00d781257fda1e9606a48c78b06815385e7d703232548dbfc95346bef",
"https://deno.land/std@0.224.0/assert/assert.ts": "09d30564c09de846855b7b071e62b5974b001bb72a4b797958fe0660e7849834",
"https://deno.land/std@0.224.0/assert/assertion_error.ts": "ba8752bd27ebc51f723702fac2f54d3e94447598f54264a6653d6413738a8917",
+
"https://deno.land/std@0.224.0/media_types/_db.ts": "19563a2491cd81b53b9c1c6ffd1a9145c355042d4a854c52f6e1424f73ff3923",
+
"https://deno.land/std@0.224.0/media_types/_util.ts": "e0b8da0c7d8ad2015cf27ac16ddf0809ac984b2f3ec79f7fa4206659d4f10deb",
+
"https://deno.land/std@0.224.0/media_types/content_type.ts": "ed3f2e1f243b418ad3f441edc95fd92efbadb0f9bde36219c7564c67f9639513",
+
"https://deno.land/std@0.224.0/media_types/extension.ts": "ec91e1818864cb84f8053ecafb270eaca702412c15c2086929ae34132e11c56a",
+
"https://deno.land/std@0.224.0/media_types/extensions_by_type.ts": "9db10797e09421815688c8f7a2fbfd5dcb040fa5c488278f1b9e04359369bd0b",
+
"https://deno.land/std@0.224.0/media_types/format_media_type.ts": "ffef4718afa2489530cb94021bb865a466eb02037609f7e82899c017959d288a",
+
"https://deno.land/std@0.224.0/media_types/get_charset.ts": "277ebfceb205bd34e616fe6764ef03fb277b77f040706272bea8680806ae3f11",
+
"https://deno.land/std@0.224.0/media_types/mod.ts": "c8acfa43ce3993e99f4d8aa60fb828a4eee3ab6920aaeb90f6a3d63f6f4f3435",
+
"https://deno.land/std@0.224.0/media_types/parse_media_type.ts": "487f000a38c230ccbac25420a50f600862e06796d0eee19d19631b9e84ee9654",
+
"https://deno.land/std@0.224.0/media_types/type_by_extension.ts": "bf4e3f5d6b58b624d5daa01cbb8b1e86d9939940a77e7c26e796a075b60ec73b",
+
"https://deno.land/std@0.224.0/media_types/vendor/mime-db.v1.52.0.ts": "0218d2c7d900e8cd6fa4a866e0c387712af4af9a1bae55d6b2546c73d273a1e6",
"https://deno.land/std@0.224.0/path/_common/assert_path.ts": "dbdd757a465b690b2cc72fc5fb7698c51507dec6bfafce4ca500c46b76ff7bd8",
"https://deno.land/std@0.224.0/path/_common/basename.ts": "569744855bc8445f3a56087fd2aed56bdad39da971a8d92b138c9913aecc5fa2",
"https://deno.land/std@0.224.0/path/_common/common.ts": "ef73c2860694775fe8ffcbcdd387f9f97c7a656febf0daa8c73b56f4d8a7bd4c",
···
"https://deno.land/std@0.224.0/path/windows/resolve.ts": "8dae1dadfed9d46ff46cc337c9525c0c7d959fb400a6308f34595c45bdca1972",
"https://deno.land/std@0.224.0/path/windows/to_file_url.ts": "40e560ee4854fe5a3d4d12976cef2f4e8914125c81b11f1108e127934ced502e",
"https://deno.land/std@0.224.0/path/windows/to_namespaced_path.ts": "4ffa4fb6fae321448d5fe810b3ca741d84df4d7897e61ee29be961a6aac89a4c",
-
"https://deno.land/std@0.94.0/node/tty.ts": "9fa7f7b461759774b4eeab00334ac5d25b69bf0de003c02814be01e65150da79",
-
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/ansi-styles/index.js": "7cc96ab93d1c9cfc0746e9dffb40be872e42ee242906f48e68df0d2c9669f737",
-
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/has-flag/index.js": "aed21e4eba656057e7b8c6024305f5354d2ebee2adc857a1d8cd5207923de7e5",
-
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/index.js": "6339123f32f7eb4b17c5c9c926ecdf3dbc353fd4fda7811ad2d3c1d4b98a7420",
-
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/supports-color/index.js": "4d7f2d216b6ac9013d9ec7e004de21f5a7d00bf2be4075bab2d82638d0d41a86",
-
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/templates.js": "f2e12be18cb84710e341e5499528280278052909fa74a12cefc9e2cc26a597ac",
-
"https://deno.land/x/chalk_deno@v4.1.1-deno/source/util.js": "cd08297ec411dcee91826ad01a00d3427235d4548ba605a59e64f0da83af8306",
-
"https://deno.land/x/hono@v3.0.0/adapter/deno/serve-static.ts": "d1c21498ced39849fa0bb23b372bf5d30677916fdbc875902735700ca1e789e3",
-
"https://deno.land/x/hono@v3.0.0/client/client.ts": "c720020a167139dfe8d6af7b91c0c5db38186722b380fb0bb251bae685a2103e",
-
"https://deno.land/x/hono@v3.0.0/client/index.ts": "7ad089b121f2613a0eaedd3d8aa8307a48bf3bf48f6c38281d6e73ee35f0d7ea",
-
"https://deno.land/x/hono@v3.0.0/client/types.ts": "47b83ab3dea4d6ef60a7c5ed11bfdb5113df7ded05403869e029f5317e44c827",
-
"https://deno.land/x/hono@v3.0.0/client/utils.ts": "781ec703f3e685cf02a201cfa717be111820ef89bebd7cb5968c6823310aebaa",
-
"https://deno.land/x/hono@v3.0.0/compose.ts": "9bc97f737857da061b2294eb9812b7e0ca773acc2e913c11bf93d5732c0469f0",
-
"https://deno.land/x/hono@v3.0.0/context.ts": "b053da7ec49c7a32f10572f951ae68347260ffab62fcce76cbc2ce544fe44d23",
-
"https://deno.land/x/hono@v3.0.0/hono.ts": "4929cefc738a4bd5efea2371f0c5338d27e9de91045b1c223ab98eda5a0364d5",
-
"https://deno.land/x/hono@v3.0.0/http-exception.ts": "e74a5e8504ecf795e9babdc2bab9d4052e1806a7396efc89f8e1ed83e813e3be",
-
"https://deno.land/x/hono@v3.0.0/middleware.ts": "d2c886e2f63b91b17f05a11f598ca101912c8fbfd66a25aef345b934ccfcd603",
-
"https://deno.land/x/hono@v3.0.0/middleware/basic-auth/index.ts": "0664ddf00c9f08a5109f92e93264678de55e90c341955c746bafa8f99ecab1eb",
-
"https://deno.land/x/hono@v3.0.0/middleware/bearer-auth/index.ts": "11d4ead9b57f5bcb2b6b4bf27076871f15da0e1e8828b2b79d90c15423357b47",
-
"https://deno.land/x/hono@v3.0.0/middleware/cache/index.ts": "2e86e089ebce01611ab5e231a6fbf7953ebabee3ab68362fcacea752ef48a0ab",
-
"https://deno.land/x/hono@v3.0.0/middleware/compress/index.ts": "0b8ddbd70688361d5ef4e9418afa28affe2500a7f41a231a87873dad26ef5548",
-
"https://deno.land/x/hono@v3.0.0/middleware/cors/index.ts": "10a743dcc793204835a5299070e2afaabb7e81cb742262faa9f1181f5e5d65ac",
-
"https://deno.land/x/hono@v3.0.0/middleware/etag/index.ts": "e679c30ddd2600087521a7c1dac3798c2319c09d203bb627b54357b984fc72e7",
-
"https://deno.land/x/hono@v3.0.0/middleware/html/index.ts": "a5028d8170dcc030d003749e743213e6532ff65798b741b81220207abc9af82d",
-
"https://deno.land/x/hono@v3.0.0/middleware/jsx/index.ts": "1925e4bf01ef1252b9bda6b0c4f79520b138ee319c41df3218cb3bf10c0ed248",
-
"https://deno.land/x/hono@v3.0.0/middleware/jwt/index.ts": "4af4649d9ae8ff2e767e53692d1974c956f523ab47a2560b869083cb493441ca",
-
"https://deno.land/x/hono@v3.0.0/middleware/logger/index.ts": "281b0fe431183a5d7b8d576645370efbd2737aeefaac7dc989d1c90dc03c52c0",
-
"https://deno.land/x/hono@v3.0.0/middleware/powered-by/index.ts": "7ec561885ac0410786f78aeb9789ed7869edb2d43c615cbc3d14f21baee87359",
-
"https://deno.land/x/hono@v3.0.0/middleware/pretty-json/index.ts": "f4a4b2fa2ecb73e23da6f0ef716fe7d6a7f05c69f64dc7f89fc68cbcb204a87f",
-
"https://deno.land/x/hono@v3.0.0/mod.ts": "e771d1c9f711b78f7540134e44a1ceda308fe0af58cb4c83ce7434c56c224670",
-
"https://deno.land/x/hono@v3.0.0/request.ts": "e3b38e76f7d13596266e644c8598d324c56ae73af263c3bf029f6ffeafdb221b",
-
"https://deno.land/x/hono@v3.0.0/router.ts": "21448bc2e6019574c10fae11237da4367037fa107e68bf3d049cd2fd0efd2adb",
-
"https://deno.land/x/hono@v3.0.0/router/reg-exp-router/index.ts": "52755829213941756159b7a963097bafde5cc4fc22b13b1c7c9184dc0512d1db",
-
"https://deno.land/x/hono@v3.0.0/router/reg-exp-router/node.ts": "8006b5bccb83d9fc98e0562a5545f6dd0be639ce445b089a6171c9c617aa8693",
-
"https://deno.land/x/hono@v3.0.0/router/reg-exp-router/router.ts": "35a405c855cf6d10c350a651169c2c728fd19aaf3d3a0c416e5a06752914e6bd",
-
"https://deno.land/x/hono@v3.0.0/router/reg-exp-router/trie.ts": "567493b301c44174f0895aedb8d055bbecf88f8a25626fa8ca61333bbd0c882c",
-
"https://deno.land/x/hono@v3.0.0/router/smart-router/index.ts": "74f9b4fe15ea535900f2b9b048581915f12fe94e531dd2b0032f5610e61c3bef",
-
"https://deno.land/x/hono@v3.0.0/router/smart-router/router.ts": "1d54f5c87875d856ed5fc2d22a100e1ff31debe3e9d8e9b1cc18d8e5706239f2",
-
"https://deno.land/x/hono@v3.0.0/router/trie-router/index.ts": "3eb75e7f71ba81801631b30de6b1f5cefb2c7239c03797e2b2cbab5085911b41",
-
"https://deno.land/x/hono@v3.0.0/router/trie-router/node.ts": "ca5b6a1ce6b6dc01003809bfa9cb93a323b37b27feee14c64655015deff7d2a9",
-
"https://deno.land/x/hono@v3.0.0/router/trie-router/router.ts": "0a969528a0c1680b552b20f0ca90e484e968ac279be9d5fd952b61a804d680e7",
-
"https://deno.land/x/hono@v3.0.0/types.ts": "a96ee9693b0de61b42016b80d3cbd958fabb795d8e4becb455845d70f66e7c87",
-
"https://deno.land/x/hono@v3.0.0/utils/body.ts": "b6b5ed679122968a74845df4c5454c677f09adc4f3466d822f3b1397884e540e",
-
"https://deno.land/x/hono@v3.0.0/utils/buffer.ts": "d28ab08d2571e890ec2ad7ce4c0318a503094f8403eac3d5eb18a8e5c23b29b2",
-
"https://deno.land/x/hono@v3.0.0/utils/cookie.ts": "545872bd7af3b455c24fd386ecbccfd161e7d4a0038d6b09b1bb22723602f90a",
-
"https://deno.land/x/hono@v3.0.0/utils/crypto.ts": "bda0e141bbe46d3a4a20f8fbcb6380d473b617123d9fdfa93e4499410b537acc",
-
"https://deno.land/x/hono@v3.0.0/utils/encode.ts": "b628be2de7ab48cc806b1e5b93b3baf6886b7656d6d65dfa80f4fcd9c0f63a5f",
-
"https://deno.land/x/hono@v3.0.0/utils/filepath.ts": "5f708bb6a2f0b8e83a3333868ac86abdfba15e52b46acb5e13018fa2138f0826",
-
"https://deno.land/x/hono@v3.0.0/utils/html.ts": "636c4a04eaea1c52c14a37cd28d83cba66b293d1e31420a4475e951268901ae4",
-
"https://deno.land/x/hono@v3.0.0/utils/http-status.ts": "2d6003e352c1fe918db663fa4bd2b20bf0b9d4e1699ba5e163f317f00b29d938",
-
"https://deno.land/x/hono@v3.0.0/utils/jwt/index.ts": "5e4b82a42eb3603351dfce726cd781ca41cb57437395409d227131aec348d2d5",
-
"https://deno.land/x/hono@v3.0.0/utils/jwt/jwt.ts": "4fefadfb914ad88282949d0cce2be16bdbd0b0614d15ecbb340c4fc47a9929b9",
-
"https://deno.land/x/hono@v3.0.0/utils/jwt/types.ts": "715514fe59f0c37048b2940528bd4f4ec5795e04f0bc1d6d7e33f8d8bb1fe9de",
-
"https://deno.land/x/hono@v3.0.0/utils/mime.ts": "e17bdbac85b97c3d223c48874c2abe867e0720461e9f7e0c340141c080b9c6d6",
-
"https://deno.land/x/hono@v3.0.0/utils/types.ts": "173dedfe018b447cc6b067d2b6968c1f1dccba67ad50526d356b79e0465a5753",
-
"https://deno.land/x/hono@v3.0.0/utils/url.ts": "2cf0f38d976761296ccaf2622b6999b7c0c1d3a1f63f066a6f832288ea7c28d9",
-
"https://deno.land/x/hono@v3.0.0/validator/index.ts": "3dc2c9418dee74333ea8b98642ce112b6d449042bbcefe1e835047fcf2458170",
-
"https://deno.land/x/hono@v3.0.0/validator/validator.ts": "9b2c9983b6b8a31cbc1f687096e232fb159004c15421e39cb050da2ac0f78c95",
-
"https://deno.land/x/hono@v4.3.11/adapter/deno/serve-static.ts": "db226d30f08f1a8bb77653ead42a911357b2f8710d653e43c01eccebb424b295",
-
"https://deno.land/x/hono@v4.3.11/compose.ts": "37d6e33b7db80e4c43a0629b12fd3a1e3406e7d9e62a4bfad4b30426ea7ae4f1",
-
"https://deno.land/x/hono@v4.3.11/context.ts": "facfd749d823a645039571d66d9d228f5ae6836818b65d3b6c4c6891adfe071e",
-
"https://deno.land/x/hono@v4.3.11/hono-base.ts": "fd7e9c1bba1e13119e95158270011784da3a7c3014c149ba0700e700f840ae0d",
-
"https://deno.land/x/hono@v4.3.11/hono.ts": "23edd0140bf0bd5a68c14ae96e5856a5cec6b844277e853b91025e91ea74f416",
-
"https://deno.land/x/hono@v4.3.11/http-exception.ts": "f5dd375e61aa4b764eb9b99dd45a7160f8317fd36d3f79ae22585b9a5e8ad7c5",
-
"https://deno.land/x/hono@v4.3.11/middleware/serve-static/index.ts": "14b760bbbc4478cc3a7fb9728730bc6300581c890365b7101b80c16e70e4b21e",
-
"https://deno.land/x/hono@v4.3.11/request.ts": "7b08602858e642d1626c3106c0bedc2aa8d97e30691a079351d9acef7c5955e6",
-
"https://deno.land/x/hono@v4.3.11/router.ts": "880316f561918fc197481755aac2165fdbe2f530b925c5357a9f98d6e2cc85c7",
-
"https://deno.land/x/hono@v4.3.11/router/reg-exp-router/index.ts": "52755829213941756159b7a963097bafde5cc4fc22b13b1c7c9184dc0512d1db",
-
"https://deno.land/x/hono@v4.3.11/router/reg-exp-router/node.ts": "7efaa6f4301efc2aad0519c84973061be8555da02e5868409293a1fd98536aaf",
-
"https://deno.land/x/hono@v4.3.11/router/reg-exp-router/router.ts": "632f2fa426b3e45a66aeed03f7205dad6d13e8081bed6f8d1d987b6cad8fb455",
-
"https://deno.land/x/hono@v4.3.11/router/reg-exp-router/trie.ts": "852ce7207e6701e47fa30889a0d2b8bfcd56d8862c97e7bc9831e0a64bd8835f",
-
"https://deno.land/x/hono@v4.3.11/router/smart-router/index.ts": "74f9b4fe15ea535900f2b9b048581915f12fe94e531dd2b0032f5610e61c3bef",
-
"https://deno.land/x/hono@v4.3.11/router/smart-router/router.ts": "dc22a8505a0f345476f07dca3054c0c50a64d7b81c9af5a904476490dfd5cbb4",
-
"https://deno.land/x/hono@v4.3.11/router/trie-router/index.ts": "3eb75e7f71ba81801631b30de6b1f5cefb2c7239c03797e2b2cbab5085911b41",
-
"https://deno.land/x/hono@v4.3.11/router/trie-router/node.ts": "d3e00e8f1ba7fb26896459d5bba882356891a07793387c4655d1864c519a91de",
-
"https://deno.land/x/hono@v4.3.11/router/trie-router/router.ts": "54ced78d35676302c8fcdda4204f7bdf5a7cc907fbf9967c75674b1e394f830d",
-
"https://deno.land/x/hono@v4.3.11/types.ts": "b561c3ee846121b33c2d81331246cdedf7781636ed72dad7406677105b4275de",
-
"https://deno.land/x/hono@v4.3.11/utils/body.ts": "774cb319dfbe886a9d39f12c43dea15a39f9d01e45de0323167cdd5d0aad14d4",
-
"https://deno.land/x/hono@v4.3.11/utils/filepath.ts": "a83e5fe87396bb291a6c5c28e13356fcbea0b5547bad2c3ba9660100ff964000",
-
"https://deno.land/x/hono@v4.3.11/utils/html.ts": "6ea4f6bf41587a51607dff7a6d2865ef4d5001e4203b07e5c8a45b63a098e871",
-
"https://deno.land/x/hono@v4.3.11/utils/http-status.ts": "f5b820f2793e45209f34deddf147b23e3133a89eb4c57dc643759a504706636b",
-
"https://deno.land/x/hono@v4.3.11/utils/mime.ts": "d1fc2c047191ccb01d736c6acf90df731324536298181dba0ecc2259e5f7d661",
-
"https://deno.land/x/hono@v4.3.11/utils/types.ts": "050bfa9dc6d0cc4b7c5069944a8bd60066c2f9f95ee69833623ad104f11f92bf",
-
"https://deno.land/x/hono@v4.3.11/utils/url.ts": "855169632c61d03703bd08cafb27664ba3fdb352892f01687d5cce8fd49e3cb1",
"https://deno.land/x/imagescript@1.3.0/ImageScript.js": "cf90773c966031edd781ed176c598f7ed495e7694cd9b86c986d2d97f783cca0",
"https://deno.land/x/imagescript@1.3.0/mod.ts": "18a6cb83c55e690c873505f6fe867364c678afb64934fe7aef593a6b92f79995",
"https://deno.land/x/imagescript@1.3.0/png/src/crc.mjs": "5cf50de181d61dd00e66a240d811018ba5070afa8bba302f393604404604de84",
···
"https://deno.land/x/socket_io@0.2.0/packages/socket.io/mod.ts": "dfd465bdcf23161af0c4d79fb8fc8912418c46a20d15e8b314cec6d9fb508196",
"https://deno.land/x/socket_io@0.2.0/test_deps.ts": "1f9dfa07a1e806ccddc9fa5f7255338d9dff67c40d7e83795f4f0f7bd710bde9",
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/backoff.ts": "33e4a6e245f8743fbae0ce583993a671a3ac2ecee433a3e7f0bd77b5dd541d84",
-
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/command.ts": "802df3a1f49f6c49fe3e8fcf13fd0cc360b8a02369de0310a72d7f0c8e4ceaab",
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/connection.ts": "c31d2e0cb360bc641e7286f1d53cf58790fbcda025c06887f84a821f39d0fdff",
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/errors.ts": "bc8f7091cb9f36cdd31229660e0139350b02c26851e3ac69d592c066745feb27",
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/executor.ts": "03e5f43df4e0c9c62b0e1be778811d45b6a1966ddf406e21ed5a227af70b7183",
···
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/protocol/command.ts": "b1efd3b62fe5d1230e6d96b5c65ba7de1592a1eda2cc927161e5997a15f404ac",
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/protocol/mod.ts": "f2601df31d8adc71785b5d19f6a7e43dfce94adbb6735c4dafc1fb129169d11a",
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/protocol/reply.ts": "beac2061b03190bada179aef1a5d92b47a5104d9835e8c7468a55c24812ae9e4",
-
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/protocol/types.ts": "40b0a568cb7fd4dc9107997062584d24e5c6ffa1f21acb6410aa19c92f89e9e1",
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/pubsub.ts": "324b87dae0700e4cb350780ce3ae5bc02780f79f3de35e01366b894668b016c6",
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/redis.ts": "a5c2cf8c72e7c92c9c8c6911f98227062649f6cba966938428c5414200f3aa54",
"https://deno.land/x/socket_io@0.2.0/vendor/deno.land/x/redis@v0.27.1/stream.ts": "f116d73cfe04590ff9fa8a3c08be8ff85219d902ef2f6929b8c1e88d92a07810",
···
},
"workspace": {
"dependencies": [
-
"jsr:@hono/hono@^4.6.11"
+
"jsr:@std/fs@^1.0.14"
],
"packageJson": {
"dependencies": [
"npm:fs@^0.0.1-security",
"npm:http@^0.0.1-security",
+
"npm:jose@5.9.6",
"npm:nodemon@^3.1.7",
"npm:path@~0.12.7",
"npm:socket.io@^4.8.1",
-475
io.ts
···
-
// deno-lint-ignore-file no-explicit-any
-
import { Server } from "https://deno.land/x/socket_io@0.2.0/mod.ts";
-
import { decode } from 'hono/jwt';
-
import chalk from "https://deno.land/x/chalk_deno@v4.1.1-deno/source/index.js";
-
import { z } from "zod";
-
-
import * as world from "./constants/world.ts";
-
import * as items from "./constants/items.ts";
-
import * as utils from "./utils.ts";
-
import { LocalPlayer, PlayerCrumb, ShopData, CritterId } from "./types.ts";
-
-
import parties from "./constants/parties.json" with { type: "json" };
-
import itemsJSON from "./public/base/items.json" with { type: "json" };
-
-
export const io = new Server();
-
io.on("connection", (socket) => {
-
let localPlayer: LocalPlayer;
-
-
/** Condensed player data that is sufficient enough for other clients */
-
let localCrumb: PlayerCrumb;
-
-
// TODO: implement checking PlayFab API with ticket
-
socket.once("login", async (ticket: string) => {
-
if (z.object({
-
ticket: z.string()
-
}).safeParse({ ticket: ticket }).success == false) return;
-
-
let playerData;
-
try {
-
playerData = decode(ticket);
-
} catch(_e) {
-
socket.disconnect(true);
-
return
-
}
-
-
// TODO: make this just an inline function instead of having an onPropertyChange function, I'm just really lazy right now lol -index
-
function onPropertyChange(property: string, value: any) {
-
utils.updateAccount(localPlayer.nickname, property, value);
-
}
-
-
const createArrayHandler = (propertyName: string) => ({
-
get(target: any, property: string) {
-
if (typeof target[property] === 'function') {
-
return function (...args: any[]) {
-
const result = target[property].apply(target, args);
-
onPropertyChange(propertyName, target);
-
return result;
-
};
-
}
-
return target[property];
-
}
-
});
-
-
const handler = {
-
set(target: any, property: string, value: any) {
-
if (Array.isArray(value)) {
-
target[property] = new Proxy(value, createArrayHandler(property));
-
onPropertyChange(property, target[property]);
-
} else {
-
target[property] = value;
-
onPropertyChange(property, value);
-
}
-
return true;
-
},
-
get(target: any, property: string) {
-
if (Array.isArray(target[property])) {
-
return new Proxy(target[property], createArrayHandler(property));
-
}
-
return target[property];
-
}
-
};
-
-
const payload = playerData.payload;
-
const sub = payload.sub as {
-
playerId: string,
-
nickname: string,
-
critterId: CritterId,
-
partyId: string,
-
persistent: boolean,
-
mods: Array<string>
-
};
-
const persistentAccount = await utils.getAccount(sub.nickname);
-
if (!sub.persistent || persistentAccount.individual == null) {
-
localPlayer = {
-
playerId: sub.playerId,
-
nickname: sub.nickname,
-
critterId: sub.critterId,
-
ignore: [],
-
friends: [],
-
inventory: [],
-
gear: [],
-
eggs: [],
-
coins: 150,
-
isMember: false,
-
isGuest: false,
-
isTeam: false,
-
x: 0,
-
y: 0,
-
rotation: 0,
-
mutes: [],
-
-
_partyId: sub.partyId, // This key is replaced down the line anyway
-
_mods: []
-
};
-
-
if (sub.persistent) {
-
utils.createAccount(localPlayer);
-
localPlayer = new Proxy<LocalPlayer>(utils.expandAccount(localPlayer), handler);
-
};
-
} else {
-
persistentAccount.individual.critterId = sub.critterId || "hamster";
-
persistentAccount.individual._partyId = sub.partyId || "default";
-
persistentAccount.individual._mods = sub.mods || [];
-
-
localPlayer = new Proxy<LocalPlayer>(utils.expandAccount(persistentAccount.individual), handler);
-
}
-
-
localPlayer._partyId = socket.handshake.query.get('partyId') || 'default';
-
world.queue.splice(world.queue.indexOf(localPlayer.nickname), 1);
-
-
localCrumb = utils.makeCrumb(localPlayer, world.spawnRoom);
-
socket.join(world.spawnRoom);
-
-
world.players[localPlayer.playerId] = localCrumb;
-
socket.emit("login", {
-
player: localPlayer,
-
spawnRoom: world.spawnRoom,
-
});
-
});
-
-
socket.on("joinRoom", (roomId: string) => {
-
if (z.object({
-
roomId: z.enum(Object.keys(world.rooms) as any)
-
}).safeParse({ roomId: roomId }).success == false) return;
-
-
const _room = (world.rooms[roomId] || { default: null }).default;
-
if (!_room) return;
-
-
socket.leave(localCrumb._roomId);
-
socket.broadcast.in(localCrumb._roomId).emit("R", localCrumb);
-
-
const modEnabled = (localPlayer._mods || []).includes('roomExits');
-
//@ts-ignore: Index type is correct
-
const correctExit = world.roomExits[localCrumb._roomId + "->" + roomId]
-
if (modEnabled && correctExit) {
-
localPlayer.x = correctExit.x;
-
localPlayer.y = correctExit.y;
-
localPlayer.rotation = correctExit.r;
-
};
-
-
if (!modEnabled || !correctExit) {
-
localPlayer.x = _room.startX;
-
localPlayer.y = _room.startY;
-
localPlayer.rotation = _room.startR | 180;
-
};
-
-
localCrumb = utils.makeCrumb(localPlayer, roomId);
-
world.players[localPlayer.playerId] = localCrumb;
-
-
console.log(chalk.green('> ' + localPlayer.nickname + ' joined "' + roomId + '"!'));
-
socket.join(roomId);
-
-
let playerCrumbs = Object.values(world.players).filter((crumb) => crumb._roomId == roomId);
-
if (world.npcs[roomId]) {
-
playerCrumbs = [
-
...playerCrumbs,
-
...world.npcs[roomId]
-
];
-
};
-
socket.emit("joinRoom", {
-
name: _room.name,
-
roomId: roomId,
-
playerCrumbs: playerCrumbs
-
});
-
-
socket.broadcast.in(localCrumb._roomId).emit("A", localCrumb);
-
});
-
-
socket.on("moveTo", (x: number, y: number) => {
-
const roomData = world.rooms[localCrumb._roomId][localPlayer._partyId];
-
if (z.object({
-
x: z.number().min(0).max(roomData.width),
-
y: z.number().min(0).max(roomData.height)
-
}).safeParse({ x: x, y: y }).success == false) return;
-
-
const newDirection = utils.getDirection(localPlayer.x, localPlayer.y, x, y);
-
-
localPlayer.x = x;
-
localPlayer.y = y;
-
localPlayer.rotation = newDirection;
-
-
localCrumb.x = x;
-
localCrumb.y = y;
-
localCrumb.r = newDirection;
-
-
io.in(localCrumb._roomId).volatile.emit("X", {
-
i: localPlayer.playerId,
-
x: x,
-
y: y,
-
r: newDirection
-
});
-
});
-
-
socket.on("message", (text: string) => {
-
if (z.object({
-
text: z.string().nonempty()
-
}).safeParse({ text: text }).success == false) return;
-
-
console.log(chalk.gray(`> ${localPlayer.nickname} sent message: "%s"`), text);
-
localCrumb.m = text;
-
-
socket.broadcast.in(localCrumb._roomId).emit("M", {
-
i: localPlayer.playerId,
-
m: text
-
});
-
-
setTimeout(() => {
-
if (localCrumb.m != text) return;
-
localCrumb.m = "";
-
}, 5e3);
-
});
-
-
socket.on("emote", (emote: string) => {
-
if (z.object({
-
emote: z.string().nonempty() // TODO: make this an enum
-
}).safeParse({ emote: emote }).success == false) return;
-
-
console.log(chalk.gray(`> ${localPlayer.nickname} sent emote: %s`), emote);
-
localCrumb.e = emote;
-
-
socket.broadcast.in(localCrumb._roomId).emit("E", {
-
i: localPlayer.playerId,
-
e: emote
-
});
-
-
setTimeout(() => {
-
if (localCrumb.e != emote) return;
-
localCrumb.e = "";
-
}, 5e3);
-
});
-
-
// ? Options is specified just because sometimes it is sent, but its always an empty string
-
socket.on("code", (code: string, _options?: string) => {
-
if (z.object({
-
command: z.enum([
-
"pop",
-
"freeitem",
-
"tbt",
-
"darkmode",
-
"spydar",
-
"allitems"
-
])
-
}).safeParse({
-
command: code
-
}).success == false) return;
-
-
console.log(chalk.gray(`> ${localPlayer.nickname} sent code: %s`), code);
-
-
const addItem = function(id: string, showGUI: boolean = false) {
-
if (!localPlayer.inventory.includes(id)) {
-
socket.emit("addItem", { itemId: id, showGUI: showGUI });
-
localPlayer.inventory.push(id);
-
}
-
}
-
-
// Misc. Codes
-
switch(code) {
-
case 'pop': {
-
socket.emit("pop", Object.values(world.players).filter((critter) => critter.c != "huggable").length);
-
break;
-
}
-
case 'freeitem': {
-
addItem(items.shop.freeItem.itemId, true);
-
break;
-
}
-
case 'tbt': {
-
const _throwbackItem = utils.getNewCodeItem(localPlayer, items.throwback);
-
if (_throwbackItem) addItem(_throwbackItem, true);
-
break;
-
}
-
case 'darkmode': {
-
addItem("3d_black", true);
-
break;
-
}
-
case 'spydar': {
-
localPlayer.gear = [
-
"sun_orange",
-
"super_mask_black",
-
"toque_blue",
-
"dracula_cloak",
-
"headphones_black",
-
"hoodie_black"
-
];
-
-
if (localCrumb._roomId == "tavern") {
-
localPlayer.x = 216;
-
localPlayer.y = 118;
-
-
localCrumb.x = 216;
-
localCrumb.y = 118;
-
-
io.in(localCrumb._roomId).volatile.emit("X", {
-
i: localPlayer.playerId,
-
x: 216,
-
y: 118
-
});
-
}
-
-
io.in(localCrumb._roomId).emit("G", {
-
i: localPlayer.playerId,
-
g: localPlayer.gear
-
});
-
-
socket.emit("updateGear", localPlayer.gear);
-
break;
-
}
-
case 'allitems': {
-
for (const item of itemsJSON) {
-
addItem(item.itemId, false);
-
}
-
break;
-
}
-
};
-
-
// Item Codes
-
const _itemCodes = items.codes as Record<string, string|Array<string>>
-
const item = _itemCodes[code];
-
-
if (typeof(item) == "string") {
-
addItem(item, true);
-
} else if (typeof(item) == "object") {
-
for (const _ of item) {
-
addItem(_, true);
-
}
-
}
-
-
// Event Codes (eg. Christmas 2019)
-
const _eventItemCodes = items.eventCodes as Record<string, Record<string, string>>;
-
const eventItem = (_eventItemCodes[localPlayer._partyId] || {})[code];
-
if (eventItem) addItem(eventItem);
-
});
-
-
socket.on("updateGear", (gear: Array<string>) => {
-
if (z.object({
-
gear: z.array(z.string().nonempty()).default([])
-
}).strict().safeParse({ gear: gear }).success == false) return;
-
-
const _gear = [];
-
for (const itemId of gear) {
-
if (localPlayer.inventory.includes(itemId)) {
-
_gear.push(itemId)
-
}
-
}
-
localPlayer.gear = _gear;
-
-
io.in(localCrumb._roomId).emit("G", {
-
i: localPlayer.playerId,
-
g: localPlayer.gear
-
});
-
-
socket.emit("updateGear", localPlayer.gear);
-
});
-
-
socket.on("getShop", () => {
-
const _shopItems = items.shop as unknown as ShopData;
-
socket.emit("getShop", {
-
lastItem: _shopItems.lastItem.itemId,
-
freeItem: _shopItems.freeItem.itemId,
-
nextItem: _shopItems.nextItem.itemId,
-
collection: _shopItems.collection.map((item) => item.itemId)
-
})
-
});
-
-
socket.on("buyItem", (itemId: string) => {
-
if (z.object({
-
itemId: z.string().nonempty()
-
}).strict().safeParse({ itemId: itemId }).success == false) return;
-
-
// ? Free item is excluded from this list because the game just sends the "/freeitem" code
-
const currentShop = items.shop;
-
const _shopItems = [currentShop.lastItem, currentShop.nextItem, ...currentShop.collection]
-
-
const target = _shopItems.find((item) => item.itemId == itemId)!;
-
if (!target) {
-
console.log(chalk.red("> There is no item in this week's shop with itemId: %s"), itemId);
-
return;
-
};
-
-
if (localPlayer.coins >= target.cost && !localPlayer.inventory.includes(itemId)) {
-
console.log(chalk.green("[+] Bought item: %s for %d coins"), itemId, target.cost);
-
localPlayer.coins -= target.cost;
-
localPlayer.inventory.push(itemId);
-
-
socket.emit("buyItem", { itemId: itemId });
-
socket.emit("updateCoins", { balance: localPlayer.coins });
-
}
-
});
-
-
socket.on("trigger", async () => {
-
const activatedTrigger = await utils.getTrigger(localPlayer, localCrumb._roomId, localPlayer._partyId);
-
if (!activatedTrigger) return;
-
-
if (activatedTrigger.hasItems) {
-
for (const item of activatedTrigger.hasItems) {
-
if (!localPlayer.inventory.includes(item)) return;
-
}
-
}
-
-
if (activatedTrigger.grantItem) {
-
let items = activatedTrigger.grantItem;
-
if (typeof(items) == 'string') items = [items];
-
-
for (const item of items) {
-
if (!localPlayer.inventory.includes(item)) {
-
socket.emit("addItem", { itemId: item, showGUI: true });
-
localPlayer.inventory.push(item);
-
}
-
}
-
}
-
-
if (activatedTrigger.addEgg) {
-
const egg = activatedTrigger.addEgg;
-
socket.emit("addEgg", egg);
-
localPlayer.eggs.push(egg);
-
}
-
});
-
-
socket.on("addIgnore", (playerId: string) => {
-
if (z.object({
-
playerId: z.enum(Object.keys(world.players) as any)
-
}).strict().safeParse({ playerId: playerId }).success == false) return;
-
-
if (Object.keys(world.players).includes(playerId) && !localPlayer.ignore.includes(playerId)) {
-
localPlayer.ignore.push(playerId);
-
};
-
});
-
-
socket.on("attack", (playerId: string) => {
-
if (z.object({
-
playerId: z.enum(Object.keys(world.players) as any)
-
}).strict().safeParse({ playerId: playerId }).success == false) return;
-
-
if (!localPlayer.gear.includes('bb_beebee')) return;
-
const monster = Object.values(world.players).find((player) => player.i == playerId && player.c == "huggable");
-
-
if (monster) {
-
io.in(localCrumb._roomId).emit("R", monster);
-
-
localPlayer.coins += 10;
-
socket.emit("updateCoins", { balance: localPlayer.coins });
-
-
delete world.players[playerId];
-
};
-
});
-
-
socket.on("switchParty", (partyId: string) => {
-
if (z.object({
-
partyId: z.enum(parties as any)
-
}).strict().safeParse({ partyId: partyId }).success == false) return;
-
-
localPlayer._partyId = partyId;
-
socket.emit("switchParty");
-
});
-
-
socket.on("beep", () => socket.emit("beep"));
-
-
socket.on("disconnect", (reason) => {
-
if (reason == "server namespace disconnect") return;
-
-
if (localPlayer && localCrumb) {
-
io.in(localCrumb._roomId).emit("R", localCrumb);
-
delete world.players[localPlayer.playerId];
-
};
-
});
-
});
-144
main.ts
···
-
import { serve } from "https://deno.land/std@0.162.0/http/server.ts";
-
-
import { sign } from 'hono/jwt';
-
import { Hono } from "https://deno.land/x/hono@v3.0.0/mod.ts";
-
import { serveStatic } from "https://deno.land/x/hono@v3.0.0/middleware.ts";
-
import { env } from 'hono/adapter';
-
import { validator } from 'hono/validator';
-
-
import { io } from "./io.ts";
-
import * as world from "./constants/world.ts";
-
import { Room } from "./types.ts";
-
import * as schemas from "./schema.ts";
-
import { getAccount } from "./utils.ts";
-
import parties from "./constants/parties.json" with { type: 'json' };
-
-
const app = new Hono();
-
app.get('/*', serveStatic({ root: './public' }));
-
-
// APIs for debugging and other purposes
-
app.get('/api/server/players', (c) => c.json({ players: world.players }));
-
-
app.get('/api/server/rooms', (c) => c.json(world.rooms));
-
-
app.get('/api/server/persistence', async (c) => {
-
const account = await getAccount();
-
return c.json({
-
success: true,
-
data: account
-
});
-
})
-
-
// APIs for use by the client
-
app.post('/api/client/login', validator('json', async (_value, c) => {
-
try {
-
const body = await c.req.json();
-
const parsed = schemas.login.safeParse(body);
-
if (!parsed.success) {
-
return c.json({
-
success: false,
-
message: "Validation failure",
-
error: parsed.error
-
}, 400);
-
};
-
return parsed.data;
-
} catch(_e) {
-
return c.json({
-
success: false,
-
message: "Bad request"
-
}, 400);
-
}
-
// deno-lint-ignore no-explicit-any
-
}) as any, async (c) => {
-
const body = c.req.valid('json') as {
-
nickname: string,
-
critterId: string,
-
partyId: string,
-
persistent: boolean,
-
mods: Array<string>
-
};
-
-
const _players = Object.values(world.players);
-
if (_players.find((player) => player.n == body.nickname) || world.queue.includes(body.nickname)) {
-
return c.json({
-
success: false,
-
message: "There is already a player with this nickname online."
-
});
-
}
-
-
const JWT_CONTENT = {
-
sub: {
-
playerId: crypto.randomUUID(),
-
...body // ZOD validator is set to make the body strict, so this expansion should be fine
-
},
-
exp: Math.floor(Date.now() / 1000) + 60 * 5 // 5 mins expiry
-
};
-
-
//@ts-ignore: Deno lint
-
const { JWT_TOKEN } = env<{ JWT_TOKEN: string }>(c);
-
const token = await sign(JWT_CONTENT, JWT_TOKEN);
-
-
world.queue.push(body.nickname);
-
return c.json({
-
success: true,
-
playerId: JWT_CONTENT.sub.playerId,
-
token: token
-
});
-
});
-
-
app.get('/api/client/rooms', (c) => {
-
const partyId = c.req.query('partyId') || 'default';
-
if (!parties.includes(partyId)) {
-
return c.json({
-
success: false,
-
message: "Invalid partyId hash provided."
-
});
-
}
-
-
let missing = 0;
-
const roomResponse = Object.keys(world.rooms).reduce((res: Array<Room>, roomId) => {
-
const room = world.rooms[roomId];
-
-
if (room[partyId]) {
-
if (!room[partyId].partyExclusive || room[partyId].partyExclusive.includes(partyId)) {
-
res.push(room[partyId]);
-
} else {
-
missing++;
-
}
-
} else {
-
if (!room.default.partyExclusive || room.default.partyExclusive.includes(partyId)) {
-
res.push(room.default);
-
} else {
-
missing++;
-
}
-
}
-
return res;
-
}, []);
-
-
if (missing == Object.keys(world.rooms).length) {
-
return c.json({
-
success: false,
-
message: "No rooms were fetched while indexxing using the specified partyId hash."
-
});
-
}
-
-
const res = roomResponse.filter((room) => room != null);
-
if (c.req.query('debug')) {
-
const roomNames = res.map((room) => room.name);
-
return c.json({
-
parties: parties,
-
data: roomNames
-
});
-
}
-
-
return c.json({
-
parties: parties,
-
data: res
-
});
-
});
-
-
const handler = io.handler(async (req) => {
-
return await app.fetch(req);
-
});
-
-
await serve(handler, { port: 3257 });
+2 -2
package-lock.json
···
{
-
"name": "box-critters-revival",
+
"name": "localbox",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
-
"name": "box-critters-revival",
+
"name": "localbox",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
+12 -10
package.json
···
{
-
"name": "box-critters-revival",
+
"name": "localbox",
"version": "1.0.0",
-
"description": "",
-
"main": "main.js",
-
"scripts": {
-
"test": "echo \"Error: no test specified\" && exit 1",
-
"start": "nodemon main.js",
-
"dev": "nodemon main.js"
+
"description": "A Typescript server emulator for Box Critters, a defunct virtual world by RocketSnail games.",
+
"author": {
+
"name": "Index",
+
"url": "https://github.com/indexxing/"
},
-
"author": "",
-
"license": "ISC",
+
"license": "MIT",
"dependencies": {
"fs": "^0.0.1-security",
"http": "^0.0.1-security",
+
"jose": "5.9.6",
"nodemon": "^3.1.7",
"path": "^0.12.7",
"socket.io": "^4.8.1",
"zod": "^3.24.1"
-
}
+
},
+
"repository": "github:Box-Critters-Localbox/Localbox",
+
"contributors": [
+
"https://github.com/jonastisell"
+
]
}
-25
schema.ts
···
-
import { z } from 'zod';
-
import parties from './constants/parties.json' with { type: 'json' };
-
-
/*
-
LOGIN API
-
*/
-
export const login = z.object({
-
nickname: z.string().nonempty().max(25),
-
critterId: z.enum([
-
"hamster",
-
"beaver",
-
"lizard",
-
"raccoon",
-
"penguin",
-
"snail",
-
"snow_greeter",
-
"snowkeeper",
-
"snowgirl",
-
"snow_patrol",
-
"snowgrandma"
-
]).default("hamster"),
-
partyId: z.enum(parties as [string, ...string[]]).default("default"),
-
persistent: z.boolean().default(false),
-
mods: z.array(z.enum(["roomExits"])).default([])
-
}).strict(); // Strict to disallow extra keys
+6530
src/constants/items.json
···
+
[
+
{
+
"itemId": "3d_black",
+
"name": "Black 3D Glasses",
+
"cost": 100,
+
"slot": "eyes",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/3d_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/3d_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/3d_black/sprites.png"
+
},
+
{
+
"itemId": "viking",
+
"name": "Red Viking Helmet",
+
"cost": 10,
+
"slot": "head",
+
"theme": "viking",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/viking/icon.png",
+
"bits": { "front": "https://media.boxcritters.com/items/viking/front.png" },
+
"sprites": "https://media.boxcritters.com/items/viking/sprites.png"
+
},
+
{
+
"itemId": "viking_blue",
+
"name": "Blue Viking Helmet",
+
"cost": 100,
+
"slot": "head",
+
"theme": "viking",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/viking_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/viking_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/viking_blue/sprites.png"
+
},
+
{
+
"itemId": "3d_white",
+
"name": "White 3D Glasses",
+
"cost": 100,
+
"slot": "eyes",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/3d_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/3d_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/3d_white/sprites.png"
+
},
+
{
+
"itemId": "headphones_black",
+
"name": "Black Headphones",
+
"cost": 100,
+
"slot": "ears",
+
"theme": "gamer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/headphones_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/headphones_black/front.png",
+
"back": "https://media.boxcritters.com/items/headphones_black/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/headphones_black/sprites.png"
+
},
+
{
+
"itemId": "headphones_white",
+
"name": "White Headphones",
+
"cost": 100,
+
"slot": "ears",
+
"theme": "gamer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/headphones_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/headphones_white/front.png",
+
"back": "https://media.boxcritters.com/items/headphones_white/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/headphones_white/sprites.png"
+
},
+
{
+
"itemId": "toque_pink",
+
"name": "Pink Toque",
+
"cost": 100,
+
"slot": "head",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/toque_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/toque_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/toque_pink/sprites.png"
+
},
+
{
+
"itemId": "toque_purple",
+
"name": "Purple Toque",
+
"cost": 100,
+
"slot": "head",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/toque_purple/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/toque_purple/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/toque_purple/sprites.png"
+
},
+
{
+
"itemId": "toque_white",
+
"name": "White Toque",
+
"cost": 100,
+
"slot": "head",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/toque_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/toque_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/toque_white/sprites.png"
+
},
+
{
+
"itemId": "toque_blue",
+
"name": "Blue Toque",
+
"cost": 100,
+
"slot": "head",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/toque_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/toque_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/toque_blue/sprites.png"
+
},
+
{
+
"itemId": "plaid_red",
+
"name": "Red Plaid",
+
"cost": 400,
+
"slot": "body",
+
"theme": "explore",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/plaid_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/plaid_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/plaid_red/sprites.png"
+
},
+
{
+
"itemId": "plaid_blue",
+
"name": "Blue Plaid",
+
"cost": 400,
+
"slot": "body",
+
"theme": "explore",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/plaid_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/plaid_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/plaid_blue/sprites.png"
+
},
+
{
+
"itemId": "propeller",
+
"name": "Propeller Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/propeller/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/propeller/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/propeller/sprites.png"
+
},
+
{
+
"itemId": "propeller_blue",
+
"name": "Blue Propeller Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/propeller_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/propeller_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/propeller_blue/sprites.png"
+
},
+
{
+
"itemId": "propeller_pink",
+
"name": "Pink Propeller Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/propeller_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/propeller_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/propeller_pink/sprites.png"
+
},
+
{
+
"itemId": "propeller_gold",
+
"name": "Gold Propeller Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/propeller_gold/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/propeller_gold/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/propeller_gold/sprites.png"
+
},
+
{
+
"itemId": "hoodie_black",
+
"name": "Black Hoodie",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hoodie_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hoodie_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hoodie_black/sprites.png"
+
},
+
{
+
"itemId": "hoodie_blue",
+
"name": "Blue Hoodie",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hoodie_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hoodie_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hoodie_blue/sprites.png"
+
},
+
{
+
"itemId": "hoodie_green",
+
"name": "Green Hoddie",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hoodie_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hoodie_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hoodie_green/sprites.png"
+
},
+
{
+
"itemId": "hoodie_pink",
+
"name": "Pink Hoodie",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hoodie_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hoodie_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hoodie_pink/sprites.png"
+
},
+
{
+
"itemId": "hoodie_purple",
+
"name": "Purple Hoodie",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hoodie_purple/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hoodie_purple/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hoodie_purple/sprites.png"
+
},
+
{
+
"itemId": "hoodie_white",
+
"name": "White Hoodie",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hoodie_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hoodie_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hoodie_white/sprites.png"
+
},
+
{
+
"itemId": "sleeping",
+
"name": "Sleeping Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sleeping/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sleeping/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sleeping/sprites.png"
+
},
+
{
+
"itemId": "party_green",
+
"name": "Green and Yellow Party Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "party",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/party_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/party_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/party_green/sprites.png"
+
},
+
{
+
"itemId": "paperhat",
+
"name": "Newspaper Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/paperhat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/paperhat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/paperhat/sprites.png"
+
},
+
{
+
"itemId": "space_black",
+
"name": "Space Commander",
+
"cost": 100,
+
"slot": "body",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/space_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/space_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_black/sprites.png"
+
},
+
{
+
"itemId": "space_blue",
+
"name": "Science Officer",
+
"cost": 100,
+
"slot": "body",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/space_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/space_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_blue/sprites.png"
+
},
+
{
+
"itemId": "space_orange",
+
"name": "Space Engineer",
+
"cost": 100,
+
"slot": "body",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/space_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/space_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_orange/sprites.png"
+
},
+
{
+
"itemId": "space_red",
+
"name": "Security Officer",
+
"cost": 100,
+
"slot": "body",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/space_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/space_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_red/sprites.png"
+
},
+
{
+
"itemId": "ballcap_black",
+
"name": "Black Ball Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ballcap_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ballcap_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ballcap_black/sprites.png"
+
},
+
{
+
"itemId": "ballcap_blue",
+
"name": "Blue Ball Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ballcap_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ballcap_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ballcap_blue/sprites.png"
+
},
+
{
+
"itemId": "ballcap_green",
+
"name": "Green Ball Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ballcap_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ballcap_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ballcap_green/sprites.png"
+
},
+
{
+
"itemId": "ballcap_pink",
+
"name": "Pink Ball Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ballcap_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ballcap_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ballcap_pink/sprites.png"
+
},
+
{
+
"itemId": "ballcap_red",
+
"name": "Red Ball Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ballcap_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ballcap_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ballcap_red/sprites.png"
+
},
+
{
+
"itemId": "ballcap_yellow",
+
"name": "Yellow Ball Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ballcap_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ballcap_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ballcap_yellow/sprites.png"
+
},
+
{
+
"itemId": "bandana_red",
+
"name": "Red Bandana",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bandana_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bandana_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bandana_red/sprites.png"
+
},
+
{
+
"itemId": "bandana_blue",
+
"name": "Blue Bandana",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bandana_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bandana_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bandana_blue/sprites.png"
+
},
+
{
+
"itemId": "tshirt_black",
+
"name": "Black T-Shirt",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tshirt_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tshirt_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tshirt_black/sprites.png"
+
},
+
{
+
"itemId": "tshirt_white",
+
"name": "Inside Out White T-Shirt",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tshirt_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tshirt_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tshirt_white/sprites.png"
+
},
+
{
+
"itemId": "float_yellow",
+
"name": "Yellow Duck Floatie",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/float_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/float_yellow/front.png",
+
"back": "https://media.boxcritters.com/items/float_yellow/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/float_yellow/sprites.png"
+
},
+
{
+
"itemId": "pirate_patch",
+
"name": "Pirate Patch",
+
"cost": 100,
+
"slot": "mask",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_patch/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_patch/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_patch/sprites.png"
+
},
+
{
+
"itemId": "pirate_crew_blue",
+
"name": "Pirate Crew Blue",
+
"cost": 100,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_crew_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_crew_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_crew_blue/sprites.png"
+
},
+
{
+
"itemId": "pirate_capt_black",
+
"name": "Black Pirate Captain",
+
"cost": 100,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_capt_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_capt_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_capt_black/sprites.png"
+
},
+
{
+
"itemId": "pirate_capt_red",
+
"name": "Red Pirate Captian",
+
"cost": 100,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_capt_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_capt_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_capt_red/sprites.png"
+
},
+
{
+
"itemId": "pirate_hat_black",
+
"name": "Black Pirate Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_hat_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_hat_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_hat_black/sprites.png"
+
},
+
{
+
"itemId": "pirate_hat_blue",
+
"name": "Blue Pirate Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_hat_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_hat_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_hat_blue/sprites.png"
+
},
+
{
+
"itemId": "guitar_blue",
+
"name": "Blue Electric Guitar",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "music",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/guitar_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/guitar_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/guitar_blue/sprites.png"
+
},
+
{
+
"itemId": "pickle",
+
"name": "Pickle",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pickle/icon.png",
+
"bits": { "front": "https://media.boxcritters.com/items/pickle/front.png" },
+
"sprites": "https://media.boxcritters.com/items/pickle/sprites.png"
+
},
+
{
+
"itemId": "grass_yellow",
+
"name": "Yellow Grass Skirt",
+
"cost": 100,
+
"slot": "belt",
+
"theme": "hawaiian",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/grass_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/grass_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/grass_yellow/sprites.png"
+
},
+
{
+
"itemId": "grass_green",
+
"name": "Green Grass Skirt",
+
"cost": 100,
+
"slot": "belt",
+
"theme": "hawaiian",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/grass_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/grass_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/grass_green/sprites.png"
+
},
+
{
+
"itemId": "hawaii_blue",
+
"name": "Blue Hawaiian Shirt",
+
"cost": 100,
+
"slot": "body",
+
"theme": "hawaiian",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hawaii_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hawaii_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hawaii_blue/sprites.png"
+
},
+
{
+
"itemId": "hawaii_orange",
+
"name": "Orange Hawaiian Shirt",
+
"cost": 100,
+
"slot": "body",
+
"theme": "hawaiian",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hawaii_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hawaii_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hawaii_orange/sprites.png"
+
},
+
{
+
"itemId": "fishing_green",
+
"name": "Fishing Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "explore",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/fishing_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/fishing_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/fishing_green/sprites.png"
+
},
+
{
+
"itemId": "unclesam",
+
"name": "Uncle Sam Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "party",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/unclesam/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/unclesam/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/unclesam/sprites.png"
+
},
+
{
+
"itemId": "box_hat_brown",
+
"name": "Cardboard Box Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/box_hat_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/box_hat_brown/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/box_hat_brown/sprites.png"
+
},
+
{
+
"itemId": "box_hat_purple",
+
"name": "Purple Cardboard Box Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/box_hat_purple/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/box_hat_purple/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/box_hat_purple/sprites.png"
+
},
+
{
+
"itemId": "monk",
+
"name": "Monk Robe",
+
"cost": 100,
+
"slot": "body",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/monk/icon.png",
+
"bits": { "front": "https://media.boxcritters.com/items/monk/front.png" },
+
"sprites": "https://media.boxcritters.com/items/monk/sprites.png"
+
},
+
{
+
"itemId": "pot",
+
"name": "Pot Helm",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pot/icon.png",
+
"bits": { "front": "https://media.boxcritters.com/items/pot/front.png" },
+
"sprites": "https://media.boxcritters.com/items/pot/sprites.png"
+
},
+
{
+
"itemId": "goggles_black",
+
"name": "Black Goggles",
+
"cost": 100,
+
"slot": "eyes",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/goggles_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/goggles_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/goggles_black/sprites.png"
+
},
+
{
+
"itemId": "goggles_green",
+
"name": "Green Goggles",
+
"cost": 20,
+
"slot": "eyes",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/goggles_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/goggles_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/goggles_green/sprites.png"
+
},
+
{
+
"itemId": "goggles_white",
+
"name": "White Goggles",
+
"cost": 100,
+
"slot": "eyes",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/goggles_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/goggles_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/goggles_white/sprites.png"
+
},
+
{
+
"itemId": "goggles_pink",
+
"name": "Pink Googles",
+
"cost": 100,
+
"slot": "eyes",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/goggles_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/goggles_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/goggles_pink/sprites.png"
+
},
+
{
+
"itemId": "lifejacket_red",
+
"name": "Red Lifejacket",
+
"cost": 100,
+
"slot": "body",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/lifejacket_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/lifejacket_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/lifejacket_red/sprites.png"
+
},
+
{
+
"itemId": "cone",
+
"name": "Critter Cone",
+
"cost": 100,
+
"slot": "neck",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/cone/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/cone/front.png",
+
"back": "https://media.boxcritters.com/items/cone/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/cone/sprites.png"
+
},
+
{
+
"itemId": "lei_red",
+
"name": "Red Hawaiian Lei",
+
"cost": 100,
+
"slot": "neck",
+
"theme": "hawaiian",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/lei_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/lei_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/lei_red/sprites.png"
+
},
+
{
+
"itemId": "backpack_green",
+
"name": "Green Camping Backpack",
+
"cost": 1000,
+
"slot": "pack",
+
"theme": "explore",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/backpack_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/backpack_green/front.png",
+
"back": "https://media.boxcritters.com/items/backpack_green/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/backpack_green/sprites.png"
+
},
+
{
+
"itemId": "bunny_blue",
+
"name": "Blue Bunny Ears",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bunny_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bunny_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bunny_blue/sprites.png"
+
},
+
{
+
"itemId": "bunny_pink",
+
"name": "Pink Bunny Ears",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bunny_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bunny_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bunny_pink/sprites.png"
+
},
+
{
+
"itemId": "bunny_white",
+
"name": "White Bunny Ears",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bunny_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bunny_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bunny_white/sprites.png"
+
},
+
{
+
"itemId": "easteregg_a",
+
"name": "Easter Egg",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/easteregg_a/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/easteregg_a/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/easteregg_a/sprites.png"
+
},
+
{
+
"itemId": "easteregg_b",
+
"name": "Easter Egg",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/easteregg_b/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/easteregg_b/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/easteregg_b/sprites.png"
+
},
+
{
+
"itemId": "easteregg_c",
+
"name": "Easter Egg",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/easteregg_c/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/easteregg_c/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/easteregg_c/sprites.png"
+
},
+
{
+
"itemId": "moose",
+
"name": "Moose Antlers",
+
"cost": 100,
+
"slot": "ears",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/moose/icon.png",
+
"bits": { "front": "https://media.boxcritters.com/items/moose/front.png" },
+
"sprites": "https://media.boxcritters.com/items/moose/sprites.png"
+
},
+
{
+
"itemId": "ninja_swords",
+
"name": "Ninja Swords",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ninja_swords/icon.png",
+
"bits": {
+
"back": "https://media.boxcritters.com/items/ninja_swords/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ninja_swords/sprites.png"
+
},
+
{
+
"itemId": "overalls_orange",
+
"name": "Orange Overalls",
+
"cost": 100,
+
"slot": "body",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/overalls_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/overalls_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/overalls_orange/sprites.png"
+
},
+
{
+
"itemId": "space_helmet_white",
+
"name": "Space Helmet",
+
"cost": 100,
+
"slot": "head",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/space_helmet_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/space_helmet_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_helmet_white/sprites.png"
+
},
+
{
+
"itemId": "space_suit_white",
+
"name": "Space Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/space_suit_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/space_suit_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_suit_white/sprites.png"
+
},
+
{
+
"itemId": "space_pack_white",
+
"name": "Space Backpack",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/space_pack_white/icon.png",
+
"bits": {
+
"back": "https://media.boxcritters.com/items/space_pack_white/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_pack_white/sprites.png"
+
},
+
{
+
"itemId": "suit_dark",
+
"name": "Dark Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/suit_dark/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/suit_dark/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/suit_dark/sprites.png"
+
},
+
{
+
"itemId": "suit_gold",
+
"name": "Gold Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/suit_gold/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/suit_gold/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/suit_gold/sprites.png"
+
},
+
{
+
"itemId": "sun_orange",
+
"name": "Orange Sunglasses",
+
"cost": 100,
+
"slot": "eyes",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sun_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sun_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sun_orange/sprites.png"
+
},
+
{
+
"itemId": "sun_square",
+
"name": "Tamago's Shades",
+
"cost": 100,
+
"slot": "eyes",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sun_square/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sun_square/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sun_square/sprites.png"
+
},
+
{
+
"itemId": "super_cape_red",
+
"name": "Superhero Cape",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/super_cape_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/super_cape_red/front.png",
+
"back": "https://media.boxcritters.com/items/super_cape_red/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/super_cape_red/sprites.png"
+
},
+
{
+
"itemId": "super_mask_black",
+
"name": "Superhero Mask",
+
"cost": 100,
+
"slot": "mask",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/super_mask_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/super_mask_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/super_mask_black/sprites.png"
+
},
+
{
+
"itemId": "tophat_dark",
+
"name": "Dark Tophat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tophat_dark/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tophat_dark/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tophat_dark/sprites.png"
+
},
+
{
+
"itemId": "tophat_gold",
+
"name": "Gold Tophat",
+
"cost": 1000000,
+
"slot": "head",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tophat_gold/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tophat_gold/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tophat_gold/sprites.png"
+
},
+
{
+
"itemId": "tophat_black",
+
"name": "Black Tophat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tophat_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tophat_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tophat_black/sprites.png"
+
},
+
{
+
"itemId": "snorkel_blue",
+
"name": "Blue Snorkel",
+
"cost": 100,
+
"slot": "eyes",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/snorkel_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/snorkel_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/snorkel_blue/sprites.png"
+
},
+
{
+
"itemId": "keytar_red",
+
"name": "Red Keytar",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "music",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/keytar_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/keytar_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/keytar_red/sprites.png"
+
},
+
{
+
"itemId": "aviator_hat_brown",
+
"name": "Aviator Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "history",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/aviator_hat_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/aviator_hat_brown/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/aviator_hat_brown/sprites.png"
+
},
+
{
+
"itemId": "aviator_goggles_brown",
+
"name": "Aviator Goggles",
+
"cost": 100,
+
"slot": "eyes",
+
"theme": "history",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/aviator_goggles_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/aviator_goggles_brown/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/aviator_goggles_brown/sprites.png"
+
},
+
{
+
"itemId": "watergun_yellow",
+
"name": "Watergun",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/watergun_yellow/icon.png",
+
"bits": {},
+
"sprites": "https://media.boxcritters.com/items/watergun_yellow/sprites.png"
+
},
+
{
+
"itemId": "pot_black",
+
"name": "Black Pot",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pot_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pot_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pot_black/sprites.png"
+
},
+
{
+
"itemId": "ballcap_orange",
+
"name": "Orange Ballcap",
+
"cost": 40,
+
"slot": "head",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ballcap_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ballcap_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ballcap_orange/sprites.png"
+
},
+
{
+
"itemId": "hoodie_orange",
+
"name": "Orange Hoodie",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hoodie_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hoodie_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hoodie_orange/sprites.png"
+
},
+
{
+
"itemId": "school_pack_orange",
+
"name": "Orange Backpack",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "school",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/school_pack_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/school_pack_orange/front.png",
+
"back": "https://media.boxcritters.com/items/school_pack_orange/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/school_pack_orange/sprites.png"
+
},
+
{
+
"itemId": "toque_orange",
+
"name": "Orange Toque",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/toque_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/toque_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/toque_orange/sprites.png"
+
},
+
{
+
"itemId": "kit_yellow",
+
"name": "Yellow and Green Kit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/kit_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/kit_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/kit_yellow/sprites.png"
+
},
+
{
+
"itemId": "plaid_black",
+
"name": "Black Plaid Shirt",
+
"cost": 400,
+
"slot": "body",
+
"theme": "explore",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/plaid_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/plaid_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/plaid_black/sprites.png"
+
},
+
{
+
"itemId": "propeller_silver",
+
"name": "Silver Propeller Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/propeller_silver/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/propeller_silver/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/propeller_silver/sprites.png"
+
},
+
{
+
"itemId": "school_pack_pink",
+
"name": "School Backpack with Rainbows",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "school",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/school_pack_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/school_pack_pink/front.png",
+
"back": "https://media.boxcritters.com/items/school_pack_pink/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/school_pack_pink/sprites.png"
+
},
+
{
+
"itemId": "school_pack_blue",
+
"name": "School Backpack with Stars",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "school",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/school_pack_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/school_pack_blue/front.png",
+
"back": "https://media.boxcritters.com/items/school_pack_blue/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/school_pack_blue/sprites.png"
+
},
+
{
+
"itemId": "tshirt_orange",
+
"name": "Orange T-Shirt",
+
"cost": 50,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tshirt_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tshirt_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tshirt_orange/sprites.png"
+
},
+
{
+
"itemId": "tinfoil_hat",
+
"name": "Tinfoil Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tinfoil_hat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tinfoil_hat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tinfoil_hat/sprites.png"
+
},
+
{
+
"itemId": "sombrero_yellow",
+
"name": "Yellow Sombrero",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sombrero_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sombrero_yellow/front.png",
+
"back": "https://media.boxcritters.com/items/sombrero_yellow/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sombrero_yellow/sprites.png"
+
},
+
{
+
"itemId": "box_brown",
+
"name": "Cardboard Box",
+
"cost": 100,
+
"slot": "ride",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/box_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/box_brown/front.png",
+
"back": "https://media.boxcritters.com/items/box_brown/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/box_brown/sprites.png"
+
},
+
{
+
"itemId": "ghost_white",
+
"name": "Ghost",
+
"cost": 100,
+
"slot": "body",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/ghost_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ghost_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ghost_white/sprites.png"
+
},
+
{
+
"itemId": "army_helmet_green",
+
"name": "Army Helmet",
+
"cost": 400,
+
"slot": "head",
+
"theme": "army",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/army_helmet_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/army_helmet_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/army_helmet_green/sprites.png"
+
},
+
{
+
"itemId": "super_cape_pink",
+
"name": "Pink Superhero Cape",
+
"cost": 50,
+
"slot": "pack",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/super_cape_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/super_cape_pink/front.png",
+
"back": "https://media.boxcritters.com/items/super_cape_pink/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/super_cape_pink/sprites.png"
+
},
+
{
+
"itemId": "super_cape_blue",
+
"name": "Blue Superhero Cape",
+
"cost": 50,
+
"slot": "pack",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/super_cape_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/super_cape_blue/front.png",
+
"back": "https://media.boxcritters.com/items/super_cape_blue/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/super_cape_blue/sprites.png"
+
},
+
{
+
"itemId": "super_cape_white",
+
"name": "White Superhero Cape",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/super_cape_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/super_cape_white/front.png",
+
"back": "https://media.boxcritters.com/items/super_cape_white/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/super_cape_white/sprites.png"
+
},
+
{
+
"itemId": "school_navy_a",
+
"name": "School Navy Uniform",
+
"cost": 100,
+
"slot": "body",
+
"theme": "school",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/school_navy_a/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/school_navy_a/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/school_navy_a/sprites.png"
+
},
+
{
+
"itemId": "school_navy_b",
+
"name": "School Navy Uniform",
+
"cost": 100,
+
"slot": "body",
+
"theme": "school",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/school_navy_b/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/school_navy_b/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/school_navy_b/sprites.png"
+
},
+
{
+
"itemId": "ball_blue",
+
"name": "Running Ball",
+
"cost": 100,
+
"slot": "ride",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ball_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ball_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ball_blue/sprites.png"
+
},
+
{
+
"itemId": "scarf_red",
+
"name": "Red Scarf",
+
"cost": 100,
+
"slot": "neck",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scarf_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scarf_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scarf_red/sprites.png"
+
},
+
{
+
"itemId": "fairy_wings",
+
"name": "Fairy Wings",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "fantasy",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/fairy_wings/icon.png",
+
"bits": {
+
"back": "https://media.boxcritters.com/items/fairy_wings/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/fairy_wings/sprites.png"
+
},
+
{
+
"itemId": "rocket_red",
+
"name": "Rocket",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "mascot",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/rocket_red/icon.png",
+
"bits": {
+
"back": "https://media.boxcritters.com/items/rocket_red/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/rocket_red/sprites.png"
+
},
+
{
+
"itemId": "roman_helmet",
+
"name": "Roman Helmet",
+
"cost": 100,
+
"slot": "head",
+
"theme": "history",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/roman_helmet/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/roman_helmet/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/roman_helmet/sprites.png"
+
},
+
{
+
"itemId": "bandana_black",
+
"name": "Black Bandana",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bandana_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bandana_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bandana_black/sprites.png"
+
},
+
{
+
"itemId": "bandana_pink",
+
"name": "Pink Bandana",
+
"cost": 20,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bandana_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bandana_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bandana_pink/sprites.png"
+
},
+
{
+
"itemId": "clown_nose",
+
"name": "Clown Nose",
+
"cost": 100,
+
"slot": "mask",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/clown_nose/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/clown_nose/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/clown_nose/sprites.png"
+
},
+
{
+
"itemId": "frank",
+
"name": "Frank",
+
"cost": 100,
+
"slot": "head",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/frank/icon.png",
+
"bits": { "front": "https://media.boxcritters.com/items/frank/front.png" },
+
"sprites": "https://media.boxcritters.com/items/frank/sprites.png"
+
},
+
{
+
"itemId": "hotdog",
+
"name": "Hotdog",
+
"cost": 100,
+
"slot": "body",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hotdog/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hotdog/front.png",
+
"back": "https://media.boxcritters.com/items/hotdog/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hotdog/sprites.png"
+
},
+
{
+
"itemId": "mummy_head",
+
"name": "Mummy Head",
+
"cost": 20,
+
"slot": "head",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/mummy_head/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/mummy_head/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/mummy_head/sprites.png"
+
},
+
{
+
"itemId": "pirate_capt_pink",
+
"name": "Pink Pirate Captian",
+
"cost": 100,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_capt_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_capt_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_capt_pink/sprites.png"
+
},
+
{
+
"itemId": "pirate_hat_pink",
+
"name": "Pink Pirate Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_hat_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_hat_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_hat_pink/sprites.png"
+
},
+
{
+
"itemId": "pumpkin",
+
"name": "Pumpkin",
+
"cost": 100,
+
"slot": "head",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/pumpkin/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pumpkin/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pumpkin/sprites.png"
+
},
+
{
+
"itemId": "shark_blue",
+
"name": "Blue Shark",
+
"cost": 400,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/shark_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/shark_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/shark_blue/sprites.png"
+
},
+
{
+
"itemId": "viking_pink",
+
"name": "Pink Viking Helmet",
+
"cost": 100,
+
"slot": "head",
+
"theme": "viking",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/viking_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/viking_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/viking_pink/sprites.png"
+
},
+
{
+
"itemId": "witch",
+
"name": "Witch Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/witch/icon.png",
+
"bits": { "front": "https://media.boxcritters.com/items/witch/front.png" },
+
"sprites": "https://media.boxcritters.com/items/witch/sprites.png"
+
},
+
{
+
"itemId": "messenger_brown",
+
"name": "Messenger Bag",
+
"cost": 100,
+
"slot": "belt",
+
"theme": "school",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/messenger_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/messenger_brown/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/messenger_brown/sprites.png"
+
},
+
{
+
"itemId": "princess_dress_pink",
+
"name": "Pink Princess Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "fantasy",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/princess_dress_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/princess_dress_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/princess_dress_pink/sprites.png"
+
},
+
{
+
"itemId": "scarf_black",
+
"name": "Black Scarf",
+
"cost": 100,
+
"slot": "neck",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scarf_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scarf_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scarf_black/sprites.png"
+
},
+
{
+
"itemId": "scarf_blue",
+
"name": "Blue Scarf",
+
"cost": 100,
+
"slot": "neck",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scarf_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scarf_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scarf_blue/sprites.png"
+
},
+
{
+
"itemId": "scarf_pink",
+
"name": "Pink Scarf",
+
"cost": 100,
+
"slot": "neck",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scarf_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scarf_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scarf_pink/sprites.png"
+
},
+
{
+
"itemId": "scarf_purple",
+
"name": "Purple Scarf",
+
"cost": 100,
+
"slot": "neck",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scarf_purple/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scarf_purple/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scarf_purple/sprites.png"
+
},
+
{
+
"itemId": "scarf_white",
+
"name": "White Scarf",
+
"cost": 100,
+
"slot": "neck",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scarf_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scarf_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scarf_white/sprites.png"
+
},
+
{
+
"itemId": "ballerina_pink",
+
"name": "Pink Ballerina",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ballerina_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ballerina_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ballerina_pink/sprites.png"
+
},
+
{
+
"itemId": "headphones_pink",
+
"name": "Pink Headphones",
+
"cost": 100,
+
"slot": "ears",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/headphones_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/headphones_pink/front.png",
+
"back": "https://media.boxcritters.com/items/headphones_pink/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/headphones_pink/sprites.png"
+
},
+
{
+
"itemId": "goggles_orange",
+
"name": "Orange Goggles",
+
"cost": 50,
+
"slot": "eyes",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/goggles_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/goggles_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/goggles_orange/sprites.png"
+
},
+
{
+
"itemId": "goggles_blue",
+
"name": "Blue Goggles",
+
"cost": 20,
+
"slot": "eyes",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/goggles_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/goggles_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/goggles_blue/sprites.png"
+
},
+
{
+
"itemId": "dracula_cloak",
+
"name": "Dracula Cloak",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/dracula_cloak/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/dracula_cloak/front.png",
+
"back": "https://media.boxcritters.com/items/dracula_cloak/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/dracula_cloak/sprites.png"
+
},
+
{
+
"itemId": "plaid_green",
+
"name": "Green Plaid",
+
"cost": 400,
+
"slot": "body",
+
"theme": "explore",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/plaid_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/plaid_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/plaid_green/sprites.png"
+
},
+
{
+
"itemId": "plaid_pink",
+
"name": "Pink Plaid",
+
"cost": 400,
+
"slot": "body",
+
"theme": "explore",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/plaid_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/plaid_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/plaid_pink/sprites.png"
+
},
+
{
+
"itemId": "stripe_red_white",
+
"name": "Red and White Striped Shirt",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/stripe_red_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/stripe_red_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/stripe_red_white/sprites.png"
+
},
+
{
+
"itemId": "princess_hat_pink",
+
"name": "Pink Princess Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "fantasy",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/princess_hat_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/princess_hat_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/princess_hat_pink/sprites.png"
+
},
+
{
+
"itemId": "bandana_orange",
+
"name": "Orange Bandana",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bandana_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bandana_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bandana_orange/sprites.png"
+
},
+
{
+
"itemId": "bandana_purple",
+
"name": "Purple Bandana",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bandana_purple/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bandana_purple/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bandana_purple/sprites.png"
+
},
+
{
+
"itemId": "propeller_black",
+
"name": "Black Propeller Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/propeller_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/propeller_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/propeller_black/sprites.png"
+
},
+
{
+
"itemId": "super_cape_black",
+
"name": "Black Super Cape",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/super_cape_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/super_cape_black/front.png",
+
"back": "https://media.boxcritters.com/items/super_cape_black/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/super_cape_black/sprites.png"
+
},
+
{
+
"itemId": "skeleton_body",
+
"name": "Skeleton Body",
+
"cost": 100,
+
"slot": "body",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/skeleton_body/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/skeleton_body/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/skeleton_body/sprites.png"
+
},
+
{
+
"itemId": "skeleton_head",
+
"name": "Skeleton Head",
+
"cost": 100,
+
"slot": "mask",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/skeleton_head/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/skeleton_head/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/skeleton_head/sprites.png"
+
},
+
{
+
"itemId": "mummy_body",
+
"name": "Mummy",
+
"cost": 40,
+
"slot": "body",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/mummy_body/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/mummy_body/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/mummy_body/sprites.png"
+
},
+
{
+
"itemId": "party_hat",
+
"name": "The Party Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "party",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/party_hat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/party_hat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/party_hat/sprites.png"
+
},
+
{
+
"itemId": "moustache",
+
"name": "Movember Moustache",
+
"cost": 100,
+
"slot": "fuzz",
+
"theme": "charity",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/moustache/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/moustache/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/moustache/sprites.png"
+
},
+
{
+
"itemId": "bb_headband",
+
"name": "Wil's Headband",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bb_headband/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bb_headband/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bb_headband/sprites.png"
+
},
+
{
+
"itemId": "bb_will",
+
"name": "Wil's Outfit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bb_will/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bb_will/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bb_will/sprites.png"
+
},
+
{
+
"itemId": "bb_huggable",
+
"name": "Huggable Head",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/bb_huggable/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bb_huggable/front.png",
+
"back": "https://media.boxcritters.com/items/bb_huggable/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bb_huggable/sprites.png"
+
},
+
{
+
"itemId": "bb_oliver",
+
"name": "Oliver's Outfit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bb_oliver/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bb_oliver/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bb_oliver/sprites.png"
+
},
+
{
+
"itemId": "bb_astoria",
+
"name": "Astoria's Outfit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bb_astoria/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bb_astoria/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bb_astoria/sprites.png"
+
},
+
{
+
"itemId": "bb_ballcap",
+
"name": "Battle Bears Ballcap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bb_ballcap/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bb_ballcap/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bb_ballcap/sprites.png"
+
},
+
{
+
"itemId": "bb_riggs",
+
"name": "Rigg's Outfit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bb_riggs/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bb_riggs/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bb_riggs/sprites.png"
+
},
+
{
+
"itemId": "bb_backpack",
+
"name": "Battle Bears Backpack",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bb_backpack/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bb_backpack/front.png",
+
"back": "https://media.boxcritters.com/items/bb_backpack/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bb_backpack/sprites.png"
+
},
+
{
+
"itemId": "headband_blue",
+
"name": "Blue Headband",
+
"cost": 100,
+
"slot": "head",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/headband_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/headband_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/headband_blue/sprites.png"
+
},
+
{
+
"itemId": "bb_beebee",
+
"name": "Bee Bee",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bb_beebee/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bb_beebee/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bb_beebee/sprites.png"
+
},
+
{
+
"itemId": "candy_corn",
+
"name": "Candy Corn",
+
"cost": 100,
+
"slot": "body",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/candy_corn/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/candy_corn/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/candy_corn/sprites.png"
+
},
+
{
+
"itemId": "turkey",
+
"name": "Turkey Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/turkey/icon.png",
+
"bits": { "front": "https://media.boxcritters.com/items/turkey/front.png" },
+
"sprites": "https://media.boxcritters.com/items/turkey/sprites.png"
+
},
+
{
+
"itemId": "bonnet_white",
+
"name": "Bonnet",
+
"cost": 100,
+
"slot": "head",
+
"theme": "history",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bonnet_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bonnet_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bonnet_white/sprites.png"
+
},
+
{
+
"itemId": "pilgram_suit",
+
"name": "Pilgrim Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "history",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pilgram_suit/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pilgram_suit/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pilgram_suit/sprites.png"
+
},
+
{
+
"itemId": "pilgram_dress",
+
"name": "Pilgrim Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "history",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pilgram_dress/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pilgram_dress/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pilgram_dress/sprites.png"
+
},
+
{
+
"itemId": "pilgram_hat",
+
"name": "Pilgrim Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "history",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pilgram_hat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pilgram_hat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pilgram_hat/sprites.png"
+
},
+
{
+
"itemId": "bb_tillman",
+
"name": "Tillman",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bb_tillman/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bb_tillman/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bb_tillman/sprites.png"
+
},
+
{
+
"itemId": "jetpack",
+
"name": "Jetpack",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/jetpack/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/jetpack/front.png",
+
"back": "https://media.boxcritters.com/items/jetpack/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/jetpack/sprites.png"
+
},
+
{
+
"itemId": "santa_hat",
+
"name": "Santa Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/santa_hat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/santa_hat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/santa_hat/sprites.png"
+
},
+
{
+
"itemId": "santa_beard",
+
"name": "Santa Beard",
+
"cost": 100,
+
"slot": "fuzz",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/santa_beard/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/santa_beard/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/santa_beard/sprites.png"
+
},
+
{
+
"itemId": "santa_suit",
+
"name": "Santa Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/santa_suit/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/santa_suit/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/santa_suit/sprites.png"
+
},
+
{
+
"itemId": "elf_suit_green",
+
"name": "Green Elf",
+
"cost": 100,
+
"slot": "body",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/elf_suit_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/elf_suit_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/elf_suit_green/sprites.png"
+
},
+
{
+
"itemId": "elf_hat_green",
+
"name": "Green Elf Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/elf_hat_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/elf_hat_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/elf_hat_green/sprites.png"
+
},
+
{
+
"itemId": "winter_dress",
+
"name": "Winter Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/winter_dress/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/winter_dress/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/winter_dress/sprites.png"
+
},
+
{
+
"itemId": "bow_white",
+
"name": "White Bow",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bow_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bow_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bow_white/sprites.png"
+
},
+
{
+
"itemId": "jumpsuit_red",
+
"name": "Red Jumpsuit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/jumpsuit_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/jumpsuit_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/jumpsuit_red/sprites.png"
+
},
+
{
+
"itemId": "gelatinous_green",
+
"name": "Gelatinous Cube",
+
"cost": 100,
+
"slot": "ride",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/gelatinous_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/gelatinous_green/front.png",
+
"back": "https://media.boxcritters.com/items/gelatinous_green/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/gelatinous_green/sprites.png"
+
},
+
{
+
"itemId": "bulb_blue",
+
"name": "Blue Bulb",
+
"cost": 100,
+
"slot": "head",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/bulb_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bulb_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bulb_blue/sprites.png"
+
},
+
{
+
"itemId": "bulb_green",
+
"name": "Green Bulb",
+
"cost": 100,
+
"slot": "head",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/bulb_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bulb_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bulb_green/sprites.png"
+
},
+
{
+
"itemId": "bulb_pink",
+
"name": "Pink Bulb",
+
"cost": 100,
+
"slot": "head",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/bulb_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bulb_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bulb_pink/sprites.png"
+
},
+
{
+
"itemId": "bulb_red",
+
"name": "Red Bulb",
+
"cost": 100,
+
"slot": "head",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/bulb_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bulb_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bulb_red/sprites.png"
+
},
+
{
+
"itemId": "bulb_white",
+
"name": "White Bulb",
+
"cost": 100,
+
"slot": "head",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/bulb_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bulb_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bulb_white/sprites.png"
+
},
+
{
+
"itemId": "bulb_yellow",
+
"name": "Yellow Bulb",
+
"cost": 100,
+
"slot": "head",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/bulb_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bulb_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bulb_yellow/sprites.png"
+
},
+
{
+
"itemId": "train_blue",
+
"name": "Train Conductor",
+
"cost": 100,
+
"slot": "body",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/train_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/train_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/train_blue/sprites.png"
+
},
+
{
+
"itemId": "box_hat_red",
+
"name": "Red Cardboard Box Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/box_hat_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/box_hat_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/box_hat_red/sprites.png"
+
},
+
{
+
"itemId": "train_hat_blue",
+
"name": "Train Conductor Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/train_hat_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/train_hat_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/train_hat_blue/sprites.png"
+
},
+
{
+
"itemId": "reindeer_body",
+
"name": "Reindeer",
+
"cost": 100,
+
"slot": "body",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/reindeer_body/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/reindeer_body/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/reindeer_body/sprites.png"
+
},
+
{
+
"itemId": "reindeer_head",
+
"name": "Reindeer Head",
+
"cost": 100,
+
"slot": "head",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/reindeer_head/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/reindeer_head/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/reindeer_head/sprites.png"
+
},
+
{
+
"itemId": "gift_hat_green",
+
"name": "Green Gift",
+
"cost": 100,
+
"slot": "head",
+
"theme": "charity",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/gift_hat_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/gift_hat_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/gift_hat_green/sprites.png"
+
},
+
{
+
"itemId": "gift_hat_red",
+
"name": "Red Gift",
+
"cost": 100,
+
"slot": "head",
+
"theme": "charity",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/gift_hat_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/gift_hat_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/gift_hat_red/sprites.png"
+
},
+
{
+
"itemId": "angel_halo",
+
"name": "Angel Halo",
+
"cost": 100,
+
"slot": "head",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/angel_halo/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/angel_halo/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/angel_halo/sprites.png"
+
},
+
{
+
"itemId": "angel_wings",
+
"name": "Angel Wings",
+
"cost": 100,
+
"slot": "body",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/angel_wings/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/angel_wings/front.png",
+
"back": "https://media.boxcritters.com/items/angel_wings/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/angel_wings/sprites.png"
+
},
+
{
+
"itemId": "ushanka",
+
"name": "Ushanka",
+
"cost": 100,
+
"slot": "head",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ushanka/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ushanka/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ushanka/sprites.png"
+
},
+
{
+
"itemId": "bandana_yellow",
+
"name": "Yellow Bandana",
+
"cost": 40,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bandana_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bandana_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bandana_yellow/sprites.png"
+
},
+
{
+
"itemId": "puffy_white",
+
"name": "White Puffy Jacket",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/puffy_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/puffy_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/puffy_white/sprites.png"
+
},
+
{
+
"itemId": "puffy_red",
+
"name": "Red Puffy Jacket",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/puffy_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/puffy_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/puffy_red/sprites.png"
+
},
+
{
+
"itemId": "puffy_pink",
+
"name": "Pink Puffy Jacket",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/puffy_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/puffy_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/puffy_pink/sprites.png"
+
},
+
{
+
"itemId": "puffy_blue",
+
"name": "Blue Puffy Jacket",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/puffy_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/puffy_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/puffy_blue/sprites.png"
+
},
+
{
+
"itemId": "puffy_black",
+
"name": "Black Puffy Jacket",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/puffy_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/puffy_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/puffy_black/sprites.png"
+
},
+
{
+
"itemId": "winter_dress_red",
+
"name": "Red Winter Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/winter_dress_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/winter_dress_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/winter_dress_red/sprites.png"
+
},
+
{
+
"itemId": "tacky_green",
+
"name": "Tacky Green Sweater",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tacky_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tacky_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tacky_green/sprites.png"
+
},
+
{
+
"itemId": "tacky_red",
+
"name": "Tacky Red Sweater",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tacky_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tacky_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tacky_red/sprites.png"
+
},
+
{
+
"itemId": "ornament_blue",
+
"name": "Blue Ornament",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ornament_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ornament_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ornament_blue/sprites.png"
+
},
+
{
+
"itemId": "ornament_gold",
+
"name": "Gold Ornament",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ornament_gold/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ornament_gold/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ornament_gold/sprites.png"
+
},
+
{
+
"itemId": "ornament_green",
+
"name": "Green Ornament",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ornament_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ornament_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ornament_green/sprites.png"
+
},
+
{
+
"itemId": "ornament_pink",
+
"name": "Pink Ornament",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ornament_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ornament_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ornament_pink/sprites.png"
+
},
+
{
+
"itemId": "ornament_red",
+
"name": "Red Ornament",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ornament_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ornament_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ornament_red/sprites.png"
+
},
+
{
+
"itemId": "ski_suit_blue",
+
"name": "Blue Ski Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ski_suit_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ski_suit_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ski_suit_blue/sprites.png"
+
},
+
{
+
"itemId": "ski_poles",
+
"name": "Ski Poles",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ski_poles/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ski_poles/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ski_poles/sprites.png"
+
},
+
{
+
"itemId": "toque_black",
+
"name": "Black Toque",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/toque_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/toque_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/toque_black/sprites.png"
+
},
+
{
+
"itemId": "onesie_plaid_red",
+
"name": "Onesie Red Plaid",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/onesie_plaid_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/onesie_plaid_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/onesie_plaid_red/sprites.png"
+
},
+
{
+
"itemId": "sleeping_red",
+
"name": "Red Sleeping Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sleeping_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sleeping_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sleeping_red/sprites.png"
+
},
+
{
+
"itemId": "sweater_black",
+
"name": "Black Sweater",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sweater_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sweater_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sweater_black/sprites.png"
+
},
+
{
+
"itemId": "sweater_blue",
+
"name": "Blue Sweater",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sweater_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sweater_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sweater_blue/sprites.png"
+
},
+
{
+
"itemId": "sweater_green",
+
"name": "Green Sweater",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sweater_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sweater_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sweater_green/sprites.png"
+
},
+
{
+
"itemId": "sweater_orange",
+
"name": "Orange Sweater",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sweater_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sweater_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sweater_orange/sprites.png"
+
},
+
{
+
"itemId": "sweater_pink",
+
"name": "Pink Sweater",
+
"cost": 20,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sweater_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sweater_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sweater_pink/sprites.png"
+
},
+
{
+
"itemId": "sweater_red",
+
"name": "Red Sweater",
+
"cost": 100,
+
"slot": "body",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sweater_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sweater_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sweater_red/sprites.png"
+
},
+
{
+
"itemId": "blockhead",
+
"name": "Blockhead Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/blockhead/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/blockhead/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/blockhead/sprites.png"
+
},
+
{
+
"itemId": "tuxedo_black",
+
"name": "Tuxedo",
+
"cost": 100,
+
"slot": "body",
+
"theme": "party",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tuxedo_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tuxedo_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tuxedo_black/sprites.png"
+
},
+
{
+
"itemId": "australian_hat",
+
"name": "Australian Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "charity",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/australian_hat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/australian_hat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/australian_hat/sprites.png"
+
},
+
{
+
"itemId": "headphones_blue",
+
"name": "Blue Headphones",
+
"cost": 100,
+
"slot": "ears",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/headphones_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/headphones_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/headphones_blue/sprites.png"
+
},
+
{
+
"itemId": "ski_helmet_blue",
+
"name": "Blue Ski Helmet",
+
"cost": 100,
+
"slot": "head",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/ski_helmet_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ski_helmet_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ski_helmet_blue/sprites.png"
+
},
+
{
+
"itemId": "hockey_jersey_red",
+
"name": "Red Hockey Jersey",
+
"cost": 100,
+
"slot": "body",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hockey_jersey_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hockey_jersey_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hockey_jersey_red/sprites.png"
+
},
+
{
+
"itemId": "hockey_helmet_blue",
+
"name": "Blue Hockey Helmet",
+
"cost": 100,
+
"slot": "head",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/hockey_helmet_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hockey_helmet_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hockey_helmet_blue/sprites.png"
+
},
+
{
+
"itemId": "hockey_helmet_red",
+
"name": "Red Hockey Helmet",
+
"cost": 100,
+
"slot": "head",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/hockey_helmet_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hockey_helmet_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hockey_helmet_red/sprites.png"
+
},
+
{
+
"itemId": "hockey_jersey_blue",
+
"name": "Blue Hockey Jersey",
+
"cost": 100,
+
"slot": "body",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hockey_jersey_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hockey_jersey_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hockey_jersey_blue/sprites.png"
+
},
+
{
+
"itemId": "traffic_cone",
+
"name": "Traffic Cone",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/traffic_cone/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/traffic_cone/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/traffic_cone/sprites.png"
+
},
+
{
+
"itemId": "ringmaster_suit_red",
+
"name": "Ringmaster Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ringmaster_suit_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ringmaster_suit_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ringmaster_suit_red/sprites.png"
+
},
+
{
+
"itemId": "ringmaster_hat_black",
+
"name": "Ringmaster Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ringmaster_hat_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ringmaster_hat_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ringmaster_hat_black/sprites.png"
+
},
+
{
+
"itemId": "football_jersey_red",
+
"name": "Red Football Jersey",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/football_jersey_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/football_jersey_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/football_jersey_red/sprites.png"
+
},
+
{
+
"itemId": "clown_suit_red",
+
"name": "Clown Suit",
+
"cost": 200,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/clown_suit_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/clown_suit_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/clown_suit_red/sprites.png"
+
},
+
{
+
"itemId": "clown_wig",
+
"name": "Clown Wig",
+
"cost": 200,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/clown_wig/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/clown_wig/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/clown_wig/sprites.png"
+
},
+
{
+
"itemId": "clown_face_white",
+
"name": "Clown Face Paint",
+
"cost": 100,
+
"slot": "face",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/clown_face_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/clown_face_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/clown_face_white/sprites.png"
+
},
+
{
+
"itemId": "potato_brown",
+
"name": "Potato Sack",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/potato_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/potato_brown/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/potato_brown/sprites.png"
+
},
+
{
+
"itemId": "football_face_black",
+
"name": "Football Face Paint",
+
"cost": 100,
+
"slot": "face",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/football_face_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/football_face_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/football_face_black/sprites.png"
+
},
+
{
+
"itemId": "float_pink",
+
"name": "Pink Duck Floatie",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/float_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/float_pink/front.png",
+
"back": "https://media.boxcritters.com/items/float_pink/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/float_pink/sprites.png"
+
},
+
{
+
"itemId": "cake_suit_pink",
+
"name": "1 Year Anniversary Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/cake_suit_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/cake_suit_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/cake_suit_pink/sprites.png"
+
},
+
{
+
"itemId": "cake_hat_pink",
+
"name": "1 Year Anniversary Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/cake_hat_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/cake_hat_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/cake_hat_pink/sprites.png"
+
},
+
{
+
"itemId": "headphones_green",
+
"name": "Green Headphones",
+
"cost": 100,
+
"slot": "ears",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/headphones_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/headphones_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/headphones_green/sprites.png"
+
},
+
{
+
"itemId": "tactical_headset",
+
"name": "Tactical Headset",
+
"cost": 100,
+
"slot": "ears",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tactical_headset/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tactical_headset/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tactical_headset/sprites.png"
+
},
+
{
+
"itemId": "night_vision",
+
"name": "Night Vision",
+
"cost": 100,
+
"slot": "eyes",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/night_vision/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/night_vision/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/night_vision/sprites.png"
+
},
+
{
+
"itemId": "tactical_vest",
+
"name": "Tactical Vest",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tactical_vest/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tactical_vest/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tactical_vest/sprites.png"
+
},
+
{
+
"itemId": "wings_black",
+
"name": "Raven Wings",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/wings_black/icon.png",
+
"bits": {
+
"back": "https://media.boxcritters.com/items/wings_black/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/wings_black/sprites.png"
+
},
+
{
+
"itemId": "school_pack_black",
+
"name": "Black School Backpack",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/school_pack_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/school_pack_black/front.png",
+
"back": "https://media.boxcritters.com/items/school_pack_black/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/school_pack_black/sprites.png"
+
},
+
{
+
"itemId": "toque_green",
+
"name": "Green Toque",
+
"cost": 100,
+
"slot": "head",
+
"theme": "winter",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/toque_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/toque_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/toque_green/sprites.png"
+
},
+
{
+
"itemId": "wings_yellow",
+
"name": "Angel Wings",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/wings_yellow/icon.png",
+
"bits": {
+
"back": "https://media.boxcritters.com/items/wings_yellow/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/wings_yellow/sprites.png"
+
},
+
{
+
"itemId": "tomb_fedora",
+
"name": "Movie Fedora",
+
"cost": 100,
+
"slot": "head",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tomb_fedora/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tomb_fedora/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tomb_fedora/sprites.png"
+
},
+
{
+
"itemId": "tomb_jacket",
+
"name": "Movie Jacket",
+
"cost": 100,
+
"slot": "body",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/tomb_jacket/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/tomb_jacket/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/tomb_jacket/sprites.png"
+
},
+
{
+
"itemId": "leprechaun_hat",
+
"name": "Leprechaun Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/leprechaun_hat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/leprechaun_hat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/leprechaun_hat/sprites.png"
+
},
+
{
+
"itemId": "leprechaun_suit",
+
"name": "Leprechaun Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/leprechaun_suit/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/leprechaun_suit/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/leprechaun_suit/sprites.png"
+
},
+
{
+
"itemId": "leprechaun_dress",
+
"name": "Leprechaun Lady",
+
"cost": 100,
+
"slot": "body",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/leprechaun_dress/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/leprechaun_dress/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/leprechaun_dress/sprites.png"
+
},
+
{
+
"itemId": "cardboard_sword",
+
"name": "Cardboard Sword",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "fantasy",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/cardboard_sword/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/cardboard_sword/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/cardboard_sword/sprites.png"
+
},
+
{
+
"itemId": "buster_pack",
+
"name": "Buster Pack",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/buster_pack/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/buster_pack/front.png",
+
"back": "https://media.boxcritters.com/items/buster_pack/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/buster_pack/sprites.png"
+
},
+
{
+
"itemId": "buster_suit",
+
"name": "Buster Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/buster_suit/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/buster_suit/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/buster_suit/sprites.png"
+
},
+
{
+
"itemId": "scout_scarf_blue",
+
"name": "Blue Scout Scarf",
+
"cost": 100,
+
"slot": "neck",
+
"theme": "explore",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scout_scarf_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scout_scarf_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scout_scarf_blue/sprites.png"
+
},
+
{
+
"itemId": "scout_uniform",
+
"name": "Scout Uniform",
+
"cost": 100,
+
"slot": "body",
+
"theme": "explore",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scout_uniform/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scout_uniform/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scout_uniform/sprites.png"
+
},
+
{
+
"itemId": "scuba_suit_blue",
+
"name": "Blue Scuba Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scuba_suit_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scuba_suit_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scuba_suit_blue/sprites.png"
+
},
+
{
+
"itemId": "scuba_tank_yellow",
+
"name": "Yellow Scuba Tank",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scuba_tank_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scuba_tank_yellow/front.png",
+
"back": "https://media.boxcritters.com/items/scuba_tank_yellow/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scuba_tank_yellow/sprites.png"
+
},
+
{
+
"itemId": "scuba_tank_green",
+
"name": "Green Scuba Tank",
+
"cost": 100,
+
"slot": "pack",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scuba_tank_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scuba_tank_green/front.png",
+
"back": "https://media.boxcritters.com/items/scuba_tank_green/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scuba_tank_green/sprites.png"
+
},
+
{
+
"itemId": "scarecrow",
+
"name": "Scarecrow",
+
"cost": 100,
+
"slot": "body",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scarecrow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scarecrow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scarecrow/sprites.png"
+
},
+
{
+
"itemId": "scarecrow_hat",
+
"name": "Scarecrow Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/scarecrow_hat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/scarecrow_hat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/scarecrow_hat/sprites.png"
+
},
+
{
+
"itemId": "leprechaun_hat2",
+
"name": "Leprechaun Hat with Hair",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/leprechaun_hat2/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/leprechaun_hat2/front.png",
+
"back": "https://media.boxcritters.com/items/leprechaun_hat2/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/leprechaun_hat2/sprites.png"
+
},
+
{
+
"itemId": "potofgold",
+
"name": "Pot of Gold",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/potofgold/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/potofgold/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/potofgold/sprites.png"
+
},
+
{
+
"itemId": "fiddle",
+
"name": "Fiddle",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/fiddle/icon.png",
+
"bits": { "front": "https://media.boxcritters.com/items/fiddle/front.png" },
+
"sprites": "https://media.boxcritters.com/items/fiddle/sprites.png"
+
},
+
{
+
"itemId": "chef_hat_white",
+
"name": "White Chef Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/chef_hat_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/chef_hat_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/chef_hat_white/sprites.png"
+
},
+
{
+
"itemId": "rainhat_yellow",
+
"name": "Rain Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/rainhat_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/rainhat_yellow/front.png",
+
"back": "https://media.boxcritters.com/items/rainhat_yellow/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/rainhat_yellow/sprites.png"
+
},
+
{
+
"itemId": "raincoat_yellow",
+
"name": "Raincoat",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/raincoat_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/raincoat_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/raincoat_yellow/sprites.png"
+
},
+
{
+
"itemId": "gift_hat_blue",
+
"name": "Blue Gift Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/gift_hat_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/gift_hat_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/gift_hat_blue/sprites.png"
+
},
+
{
+
"itemId": "doctor_cap_blue",
+
"name": "Doctor Cap",
+
"cost": 100,
+
"slot": "head",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/doctor_cap_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/doctor_cap_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/doctor_cap_blue/sprites.png"
+
},
+
{
+
"itemId": "doctor_coat_white",
+
"name": "Doctor Coat",
+
"cost": 200,
+
"slot": "body",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/doctor_coat_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/doctor_coat_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/doctor_coat_white/sprites.png"
+
},
+
{
+
"itemId": "doctor_mask_blue",
+
"name": "Doctor Mask",
+
"cost": 100,
+
"slot": "mask",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/doctor_mask_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/doctor_mask_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/doctor_mask_blue/sprites.png"
+
},
+
{
+
"itemId": "stethoscope",
+
"name": "Stethoscope",
+
"cost": 100,
+
"slot": "neck",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/stethoscope/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/stethoscope/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/stethoscope/sprites.png"
+
},
+
{
+
"itemId": "paperhat_colour",
+
"name": "Coloured Paper Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/paperhat_colour/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/paperhat_colour/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/paperhat_colour/sprites.png"
+
},
+
{
+
"itemId": "jester_hat",
+
"name": "Jester Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "fantasy",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/jester_hat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/jester_hat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/jester_hat/sprites.png"
+
},
+
{
+
"itemId": "peacoat_red",
+
"name": "Red Peacoat",
+
"cost": 50,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/peacoat_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/peacoat_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/peacoat_red/sprites.png"
+
},
+
{
+
"itemId": "beret_red",
+
"name": "Beret RED",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beret_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beret_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beret_red/sprites.png"
+
},
+
{
+
"itemId": "flight_suit_red",
+
"name": "Flight Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/flight_suit_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/flight_suit_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/flight_suit_red/sprites.png"
+
},
+
{
+
"itemId": "flight_helmet_red",
+
"name": "Flight Helmet",
+
"cost": 100,
+
"slot": "head",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/flight_helmet_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/flight_helmet_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/flight_helmet_red/sprites.png"
+
},
+
{
+
"itemId": "waistcoat_red",
+
"name": "Red Waistcoat",
+
"cost": 100,
+
"slot": "body",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/waistcoat_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/waistcoat_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/waistcoat_red/sprites.png"
+
},
+
{
+
"itemId": "peacoat_pink",
+
"name": "Pink Peacoat",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/peacoat_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/peacoat_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/peacoat_pink/sprites.png"
+
},
+
{
+
"itemId": "peacoat_yellow",
+
"name": "Yellow Peacoat",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/peacoat_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/peacoat_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/peacoat_yellow/sprites.png"
+
},
+
{
+
"itemId": "beret_pink",
+
"name": "Pink Beret",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beret_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beret_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beret_pink/sprites.png"
+
},
+
{
+
"itemId": "beret_blue",
+
"name": "Blue Beret",
+
"cost": 50,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beret_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beret_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beret_blue/sprites.png"
+
},
+
{
+
"itemId": "beret_yellow",
+
"name": "Yellow Beret",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beret_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beret_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beret_yellow/sprites.png"
+
},
+
{
+
"itemId": "peacoat_blue",
+
"name": "Blue Peacoat",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/peacoat_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/peacoat_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/peacoat_blue/sprites.png"
+
},
+
{
+
"itemId": "raincoat_green",
+
"name": "Green Raincoat",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/raincoat_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/raincoat_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/raincoat_green/sprites.png"
+
},
+
{
+
"itemId": "rainhat_green",
+
"name": "Green Rainhat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/rainhat_green/icon.png",
+
"bits": {},
+
"sprites": "https://media.boxcritters.com/items/rainhat_green/sprites.png"
+
},
+
{
+
"itemId": "raincoat_blue",
+
"name": "Blue Raincoat",
+
"cost": 50,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/raincoat_blue/icon.png",
+
"bits": {},
+
"sprites": "https://media.boxcritters.com/items/raincoat_blue/sprites.png"
+
},
+
{
+
"itemId": "rainhat_blue",
+
"name": "Blue Rainhat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/rainhat_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/rainhat_blue/front.png",
+
"back": "https://media.boxcritters.com/items/rainhat_blue/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/rainhat_blue/sprites.png"
+
},
+
{
+
"itemId": "raincoat_red",
+
"name": "Red Raincoat",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/raincoat_red/icon.png",
+
"bits": {},
+
"sprites": "https://media.boxcritters.com/items/raincoat_red/sprites.png"
+
},
+
{
+
"itemId": "rainhat_red",
+
"name": "Red Rainhat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/rainhat_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/rainhat_red/front.png",
+
"back": "https://media.boxcritters.com/items/rainhat_red/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/rainhat_red/sprites.png"
+
},
+
{
+
"itemId": "chef_jacket_white",
+
"name": "White Chef Jacket",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/chef_jacket_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/chef_jacket_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/chef_jacket_white/sprites.png"
+
},
+
{
+
"itemId": "chef_jacket_black",
+
"name": "Black Chef Jacket",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/chef_jacket_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/chef_jacket_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/chef_jacket_black/sprites.png"
+
},
+
{
+
"itemId": "chef_hat_black",
+
"name": "Black Chef Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/chef_hat_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/chef_hat_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/chef_hat_black/sprites.png"
+
},
+
{
+
"itemId": "easteregg_d",
+
"name": "Easter Egg",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/easteregg_d/icon.png",
+
"bits": {},
+
"sprites": "https://media.boxcritters.com/items/easteregg_d/sprites.png"
+
},
+
{
+
"itemId": "easteregg_e",
+
"name": "Easter Egg",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/easteregg_e/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/easteregg_e/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/easteregg_e/sprites.png"
+
},
+
{
+
"itemId": "easteregg_f",
+
"name": "Easter Egg",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/easteregg_f/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/easteregg_f/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/easteregg_f/sprites.png"
+
},
+
{
+
"itemId": "fuzzy_head_yellow",
+
"name": "Yellow Fuzzy Bunny Ears",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/fuzzy_head_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/fuzzy_head_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/fuzzy_head_yellow/sprites.png"
+
},
+
{
+
"itemId": "fuzzy_body_yellow",
+
"name": "Yellow Fuzzy Bunny Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/fuzzy_body_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/fuzzy_body_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/fuzzy_body_yellow/sprites.png"
+
},
+
{
+
"itemId": "fuzzy_feet_yellow",
+
"name": "Yellow Fuzzy Bunny Feet",
+
"cost": 100,
+
"slot": "feet",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/fuzzy_feet_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/fuzzy_feet_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/fuzzy_feet_yellow/sprites.png"
+
},
+
{
+
"itemId": "bunny_black",
+
"name": "Black Bunny",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bunny_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bunny_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bunny_black/sprites.png"
+
},
+
{
+
"itemId": "bunny_robot",
+
"name": "Robot Bunny",
+
"cost": 100,
+
"slot": "head",
+
"theme": "event",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/bunny_robot/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/bunny_robot/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/bunny_robot/sprites.png"
+
},
+
{
+
"itemId": "brain_green",
+
"name": "Green Brain",
+
"cost": 100,
+
"slot": "head",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/brain_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/brain_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/brain_green/sprites.png"
+
},
+
{
+
"itemId": "pirate_dress_blue",
+
"name": "Blue Pirate Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "holiday",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_dress_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_dress_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_dress_blue/sprites.png"
+
},
+
{
+
"itemId": "pirate_dress_green",
+
"name": "Green Pirate Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_dress_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_dress_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_dress_green/sprites.png"
+
},
+
{
+
"itemId": "pirate_dress_pink",
+
"name": "Pink Pirate Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_dress_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_dress_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_dress_pink/sprites.png"
+
},
+
{
+
"itemId": "pirate_dress_tan",
+
"name": "Tan Pirate Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_dress_tan/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_dress_tan/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_dress_tan/sprites.png"
+
},
+
{
+
"itemId": "belt_black",
+
"name": "Black belt",
+
"cost": 100,
+
"slot": "belt",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/belt_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/belt_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/belt_black/sprites.png"
+
},
+
{
+
"itemId": "pirate_hair_red",
+
"name": "Red Pirate Hat with Hair",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_hair_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_hair_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_hair_red/sprites.png"
+
},
+
{
+
"itemId": "pirate_hair_blue",
+
"name": "Blue Pirate Hat with Hair",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_hair_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_hair_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_hair_blue/sprites.png"
+
},
+
{
+
"itemId": "pirate_hair_pink",
+
"name": "Pink Pirate Hat with Hair",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_hair_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_hair_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_hair_pink/sprites.png"
+
},
+
{
+
"itemId": "pirate_hair_purple",
+
"name": "Purple Pirate Hat with Hair",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_hair_purple/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_hair_purple/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_hair_purple/sprites.png"
+
},
+
{
+
"itemId": "doll_hat_white",
+
"name": "White Doll Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/doll_hat_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/doll_hat_white/front.png",
+
"back": "https://media.boxcritters.com/items/doll_hat_white/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/doll_hat_white/sprites.png"
+
},
+
{
+
"itemId": "doll_dress_white",
+
"name": "White Doll Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "movie",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/doll_dress_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/doll_dress_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/doll_dress_white/sprites.png"
+
},
+
{
+
"itemId": "pirate_bandana_blue",
+
"name": "Blue Pirate Bandana ",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_bandana_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_bandana_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_bandana_blue/sprites.png"
+
},
+
{
+
"itemId": "pirate_bandana_red",
+
"name": "Red Pirate Bandana",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_bandana_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_bandana_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_bandana_red/sprites.png"
+
},
+
{
+
"itemId": "pirate_bandana_purple",
+
"name": "Purple Pirate Bandana",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_bandana_purple/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_bandana_purple/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_bandana_purple/sprites.png"
+
},
+
{
+
"itemId": "pirate_bandana_pink",
+
"name": "Pink Pirate Bandana",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_bandana_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_bandana_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_bandana_pink/sprites.png"
+
},
+
{
+
"itemId": "pirate_bandana_darkblack",
+
"name": "Dark Mode Pirate Bandana",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_bandana_darkblack/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_bandana_darkblack/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_bandana_darkblack/sprites.png"
+
},
+
{
+
"itemId": "pirate_bandana_tan",
+
"name": "Tan Pirate Bandana",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_bandana_tan/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_bandana_tan/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_bandana_tan/sprites.png"
+
},
+
{
+
"itemId": "pirate_bandana_black",
+
"name": "Black Bandana",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_bandana_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_bandana_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_bandana_black/sprites.png"
+
},
+
{
+
"itemId": "pirate_vest_white",
+
"name": "Pirate Vest",
+
"cost": 100,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_vest_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_vest_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_vest_white/sprites.png"
+
},
+
{
+
"itemId": "pirate_crew_pink",
+
"name": "Pink Pirate Crew",
+
"cost": 200,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_crew_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_crew_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_crew_pink/sprites.png"
+
},
+
{
+
"itemId": "pirate_crew_dark",
+
"name": "Dark Pirate Crew",
+
"cost": 200,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_crew_dark/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_crew_dark/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_crew_dark/sprites.png"
+
},
+
{
+
"itemId": "pirate_crew_green",
+
"name": "Green Pirate Crew",
+
"cost": 200,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_crew_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_crew_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_crew_green/sprites.png"
+
},
+
{
+
"itemId": "pirate_crew_red",
+
"name": "Red Pirate Crew",
+
"cost": 200,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_crew_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_crew_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_crew_red/sprites.png"
+
},
+
{
+
"itemId": "pirate_crew_grey",
+
"name": "Grey Pirate Crew",
+
"cost": 200,
+
"slot": "body",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_crew_grey/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_crew_grey/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_crew_grey/sprites.png"
+
},
+
{
+
"itemId": "beard1_black",
+
"name": "Black Circle Beard",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beard1_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beard1_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beard1_black/sprites.png"
+
},
+
{
+
"itemId": "beard1_red",
+
"name": "Red Circle Beard",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beard1_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beard1_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beard1_red/sprites.png"
+
},
+
{
+
"itemId": "beard1_brown",
+
"name": "Brown Circle Beard",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beard1_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beard1_brown/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beard1_brown/sprites.png"
+
},
+
{
+
"itemId": "beard2_black",
+
"name": "Black Dutch Beard",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beard2_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beard2_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beard2_black/sprites.png"
+
},
+
{
+
"itemId": "beard2_red",
+
"name": "Red Dutch Beard",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beard2_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beard2_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beard2_red/sprites.png"
+
},
+
{
+
"itemId": "beard2_brown",
+
"name": "Brown Dutch Beard",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beard2_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beard2_brown/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beard2_brown/sprites.png"
+
},
+
{
+
"itemId": "beard3_black",
+
"name": "Black Full Beard",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beard3_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beard3_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beard3_black/sprites.png"
+
},
+
{
+
"itemId": "beard3_red",
+
"name": "Red Full Beard",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beard3_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beard3_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beard3_red/sprites.png"
+
},
+
{
+
"itemId": "beard3_brown",
+
"name": "Brown Full Beard",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/beard3_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/beard3_brown/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/beard3_brown/sprites.png"
+
},
+
{
+
"itemId": "goatee_black",
+
"name": "Black Goatee",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/goatee_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/goatee_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/goatee_black/sprites.png"
+
},
+
{
+
"itemId": "goatee_red",
+
"name": "Red Goatee",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/goatee_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/goatee_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/goatee_red/sprites.png"
+
},
+
{
+
"itemId": "goatee_brown",
+
"name": "Brown Goatee",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/goatee_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/goatee_brown/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/goatee_brown/sprites.png"
+
},
+
{
+
"itemId": "moustache_black",
+
"name": "Black Moustache",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/moustache_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/moustache_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/moustache_black/sprites.png"
+
},
+
{
+
"itemId": "moustache_red",
+
"name": "Red Moustache",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/moustache_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/moustache_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/moustache_red/sprites.png"
+
},
+
{
+
"itemId": "moustache_brown",
+
"name": " Brown Moustache",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/moustache_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/moustache_brown/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/moustache_brown/sprites.png"
+
},
+
{
+
"itemId": "stubble_black",
+
"name": "Face Stubble",
+
"cost": 20,
+
"slot": "fuzz",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/stubble_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/stubble_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/stubble_black/sprites.png"
+
},
+
{
+
"itemId": "pirate_hat_hook",
+
"name": "Pirate Hook Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "pirate",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_hat_hook/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_hat_hook/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_hat_hook/sprites.png"
+
},
+
{
+
"itemId": "fedora_black",
+
"name": "Black Fedora Hat",
+
"cost": 50,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/fedora_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/fedora_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/fedora_black/sprites.png"
+
},
+
{
+
"itemId": "fedora_white",
+
"name": "White Fedora Hat",
+
"cost": 50,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/fedora_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/fedora_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/fedora_white/sprites.png"
+
},
+
{
+
"itemId": "grad_hat",
+
"name": "Grad Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "school",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/grad_hat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/grad_hat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/grad_hat/sprites.png"
+
},
+
{
+
"itemId": "grad_robe",
+
"name": "Grad Robe",
+
"cost": 100,
+
"slot": "body",
+
"theme": "school",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/grad_robe/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/grad_robe/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/grad_robe/sprites.png"
+
},
+
{
+
"itemId": "cardboard_sword_gold",
+
"name": "Golden Cardboard Sword",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "fantasy",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/cardboard_sword_gold/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/cardboard_sword_gold/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/cardboard_sword_gold/sprites.png"
+
},
+
{
+
"itemId": "cardboard_sword_silver",
+
"name": "Silver Cardboard Sword",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "fantasy",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/cardboard_sword_silver/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/cardboard_sword_silver/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/cardboard_sword_silver/sprites.png"
+
},
+
{
+
"itemId": "ninja_black",
+
"name": "Black Ninja",
+
"cost": 200,
+
"slot": "body",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ninja_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ninja_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ninja_black/sprites.png"
+
},
+
{
+
"itemId": "ninja_gi_gray",
+
"name": "Gray Ninja Gi",
+
"cost": 100,
+
"slot": "body",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ninja_gi_gray/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ninja_gi_gray/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ninja_gi_gray/sprites.png"
+
},
+
{
+
"itemId": "ninja_orange",
+
"name": "Orange Ninja Trainer",
+
"cost": 200,
+
"slot": "body",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ninja_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ninja_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ninja_orange/sprites.png"
+
},
+
{
+
"itemId": "ninja_gi_pink",
+
"name": "Pink Ninja Gi",
+
"cost": 100,
+
"slot": "body",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ninja_gi_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ninja_gi_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ninja_gi_pink/sprites.png"
+
},
+
{
+
"itemId": "water_wings_blue",
+
"name": "Blue Water Wings",
+
"cost": 20,
+
"slot": "hand",
+
"theme": "sports",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/water_wings_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/water_wings_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/water_wings_blue/sprites.png"
+
},
+
{
+
"itemId": "pie_purple",
+
"name": "Purple Pie",
+
"cost": 100,
+
"slot": "head",
+
"theme": "silly",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pie_purple/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pie_purple/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pie_purple/sprites.png"
+
},
+
{
+
"itemId": "ninja_gi_red",
+
"name": "Red Ninja Gi",
+
"cost": 100,
+
"slot": "body",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ninja_gi_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ninja_gi_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ninja_gi_red/sprites.png"
+
},
+
{
+
"itemId": "ninja_rogue_black",
+
"name": "Rogue Ninja Gi",
+
"cost": 100,
+
"slot": "body",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ninja_rogue_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ninja_rogue_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ninja_rogue_black/sprites.png"
+
},
+
{
+
"itemId": "hachimaki_black",
+
"name": "Black Hachimaki",
+
"cost": 100,
+
"slot": "head",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hachimaki_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hachimaki_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hachimaki_black/sprites.png"
+
},
+
{
+
"itemId": "ninja_hood_pink",
+
"name": "Pink Ninja Hood",
+
"cost": 100,
+
"slot": "head",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ninja_hood_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ninja_hood_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ninja_hood_pink/sprites.png"
+
},
+
{
+
"itemId": "ninja_hood_gray",
+
"name": "Gray Ninja Hood",
+
"cost": 100,
+
"slot": "head",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ninja_hood_gray/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ninja_hood_gray/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ninja_hood_gray/sprites.png"
+
},
+
{
+
"itemId": "ninja",
+
"name": "Fullbody Ninja",
+
"cost": 100,
+
"slot": "body",
+
"theme": "ninja",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ninja/icon.png",
+
"bits": { "front": "https://media.boxcritters.com/items/ninja/front.png" },
+
"sprites": "https://media.boxcritters.com/items/ninja/sprites.png"
+
},
+
{
+
"itemId": "mermaid_blue",
+
"name": "Mermaid Body Blue",
+
"cost": 100,
+
"slot": "body",
+
"theme": "fantasy",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/mermaid_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/mermaid_blue/front.png",
+
"back": "https://media.boxcritters.com/items/mermaid_blue/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/mermaid_blue/sprites.png"
+
},
+
{
+
"itemId": "mermaid_pink",
+
"name": "Mermaid Body Pink",
+
"cost": 100,
+
"slot": "body",
+
"theme": "fantasy",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/mermaid_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/mermaid_pink/front.png",
+
"back": "https://media.boxcritters.com/items/mermaid_pink/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/mermaid_pink/sprites.png"
+
},
+
{
+
"itemId": "mermaid_black",
+
"name": "Mermaid Body Black",
+
"cost": 100,
+
"slot": "body",
+
"theme": "fantasy",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/mermaid_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/mermaid_black/front.png",
+
"back": "https://media.boxcritters.com/items/mermaid_black/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/mermaid_black/sprites.png"
+
},
+
{
+
"itemId": "mermaid_hair_yellow",
+
"name": "Mermaid Hair Yellow",
+
"cost": 100,
+
"slot": "head",
+
"theme": "fantasy",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/mermaid_hair_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/mermaid_hair_yellow/front.png",
+
"back": "https://media.boxcritters.com/items/mermaid_hair_yellow/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/mermaid_hair_yellow/sprites.png"
+
},
+
{
+
"itemId": "mermaid_hair_blue",
+
"name": "Mermaid Hair Blue",
+
"cost": 100,
+
"slot": "head",
+
"theme": "fantasy",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/mermaid_hair_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/mermaid_hair_blue/front.png",
+
"back": "https://media.boxcritters.com/items/mermaid_hair_blue/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/mermaid_hair_blue/sprites.png"
+
},
+
{
+
"itemId": "mermaid_hair_red",
+
"name": "Mermaid Hair Red",
+
"cost": 100,
+
"slot": "head",
+
"theme": "fantasy",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/mermaid_hair_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/mermaid_hair_red/front.png",
+
"back": "https://media.boxcritters.com/items/mermaid_hair_red/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/mermaid_hair_red/sprites.png"
+
},
+
{
+
"itemId": "mermaid_hair_pink",
+
"name": "Mermaid Hair Pink",
+
"cost": 20,
+
"slot": "head",
+
"theme": "fantasy",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/mermaid_hair_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/mermaid_hair_pink/front.png",
+
"back": "https://media.boxcritters.com/items/mermaid_hair_pink/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/mermaid_hair_pink/sprites.png"
+
},
+
{
+
"itemId": "mermaid_hair_black",
+
"name": "Mermaid Hair Black",
+
"cost": 100,
+
"slot": "head",
+
"theme": "fantasy",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/mermaid_hair_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/mermaid_hair_black/front.png",
+
"back": "https://media.boxcritters.com/items/mermaid_hair_black/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/mermaid_hair_black/sprites.png"
+
},
+
{
+
"itemId": "lashes_black",
+
"name": "Black Eyelashes",
+
"cost": 100,
+
"slot": "fuzz",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/lashes_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/lashes_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/lashes_black/sprites.png"
+
},
+
{
+
"itemId": "pirate_parrot",
+
"name": "Red Pirate Parrot",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pirate_parrot/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pirate_parrot/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pirate_parrot/sprites.png"
+
},
+
{
+
"itemId": "robot_head",
+
"name": "Robot Head",
+
"cost": 150,
+
"slot": "head",
+
"theme": "space",
+
"hideEars": true,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/robot_head/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/robot_head/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/robot_head/sprites.png"
+
},
+
{
+
"itemId": "robot_body",
+
"name": "Robot Body",
+
"cost": 100,
+
"slot": "body",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/robot_body/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/robot_body/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/robot_body/sprites.png"
+
},
+
{
+
"itemId": "sun_green",
+
"name": "Green Rectangle Sunglasses",
+
"cost": 20,
+
"slot": "eyes",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/sun_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/sun_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/sun_green/sprites.png"
+
},
+
{
+
"itemId": "trunks_yellow",
+
"name": "Neon Yellow Swimming Trunks",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/trunks_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/trunks_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/trunks_yellow/sprites.png"
+
},
+
{
+
"itemId": "swimsuit_blue",
+
"name": "Blue Swimsuit",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/swimsuit_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/swimsuit_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/swimsuit_blue/sprites.png"
+
},
+
{
+
"itemId": "trunks_red",
+
"name": "Red/Orange Wave Swimming Trunks",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/trunks_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/trunks_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/trunks_red/sprites.png"
+
},
+
{
+
"itemId": "trunks_blue",
+
"name": "Neon Blue Swimming Trunks",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/trunks_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/trunks_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/trunks_blue/sprites.png"
+
},
+
{
+
"itemId": "trunks_green",
+
"name": "Neon Green Swimming Trunks",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/trunks_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/trunks_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/trunks_green/sprites.png"
+
},
+
{
+
"itemId": "trunks_purple",
+
"name": "Neon Purple Swimming Trunks",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/trunks_purple/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/trunks_purple/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/trunks_purple/sprites.png"
+
},
+
{
+
"itemId": "lifeguard_trunks",
+
"name": "Red Lifeguard Swimming Trunks",
+
"cost": 100,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/lifeguard_trunks/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/lifeguard_trunks/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/lifeguard_trunks/sprites.png"
+
},
+
{
+
"itemId": "trunks_teal",
+
"name": "Blue White Teal Swimming Trunks",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/trunks_teal/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/trunks_teal/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/trunks_teal/sprites.png"
+
},
+
{
+
"itemId": "trunks_black",
+
"name": "Black Swimming Trunks",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/trunks_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/trunks_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/trunks_black/sprites.png"
+
},
+
{
+
"itemId": "lifeguard_suit",
+
"name": "Red Lifeguard Swimsuit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/lifeguard_suit/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/lifeguard_suit/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/lifeguard_suit/sprites.png"
+
},
+
{
+
"itemId": "swimsuit_pink",
+
"name": "Pink Swimsuit",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/swimsuit_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/swimsuit_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/swimsuit_pink/sprites.png"
+
},
+
{
+
"itemId": "swimsuit_black",
+
"name": "Black Swimsuit",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/swimsuit_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/swimsuit_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/swimsuit_black/sprites.png"
+
},
+
{
+
"itemId": "swimsuit_white",
+
"name": "White Swimsuit",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/swimsuit_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/swimsuit_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/swimsuit_white/sprites.png"
+
},
+
{
+
"itemId": "swimsuit_green",
+
"name": "Neon Green Swimsuit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/swimsuit_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/swimsuit_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/swimsuit_green/sprites.png"
+
},
+
{
+
"itemId": "swimsuit_yellow",
+
"name": "Neon Yellow Swimsuit",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/swimsuit_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/swimsuit_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/swimsuit_yellow/sprites.png"
+
},
+
{
+
"itemId": "swimsuit_purple",
+
"name": "Neon Purple Swimsuit",
+
"cost": 20,
+
"slot": "body",
+
"theme": "summer",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/swimsuit_purple/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/swimsuit_purple/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/swimsuit_purple/sprites.png"
+
},
+
{
+
"itemId": "porkpie_grey",
+
"name": "Grey Porkpie Hat",
+
"cost": 20,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/porkpie_grey/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/porkpie_grey/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/porkpie_grey/sprites.png"
+
},
+
{
+
"itemId": "porkpie_straw",
+
"name": "Straw Porkpie Hat",
+
"cost": 20,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/porkpie_straw/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/porkpie_straw/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/porkpie_straw/sprites.png"
+
},
+
{
+
"itemId": "porkpie_black",
+
"name": "Black Porkpie Hat",
+
"cost": 20,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/porkpie_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/porkpie_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/porkpie_black/sprites.png"
+
},
+
{
+
"itemId": "porkpie_pink",
+
"name": "Pink Porkpie Hat",
+
"cost": 20,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/porkpie_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/porkpie_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/porkpie_pink/sprites.png"
+
},
+
{
+
"itemId": "straw_sun_hat",
+
"name": "Floppy Straw Sun Hat",
+
"cost": 40,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/straw_sun_hat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/straw_sun_hat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/straw_sun_hat/sprites.png"
+
},
+
{
+
"itemId": "wooden_barrel",
+
"name": "Wooden Barrel",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/wooden_barrel/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/wooden_barrel/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/wooden_barrel/sprites.png"
+
},
+
{
+
"itemId": "hero_cape_black",
+
"name": "Black Cape",
+
"cost": 50,
+
"slot": "neck",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hero_cape_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hero_cape_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hero_cape_black/sprites.png"
+
},
+
{
+
"itemId": "hero_suit_black",
+
"name": "Black Super Suit",
+
"cost": 80,
+
"slot": "body",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hero_suit_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hero_suit_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hero_suit_black/sprites.png"
+
},
+
{
+
"itemId": "hero_mask_black",
+
"name": "Black Comic Mask",
+
"cost": 20,
+
"slot": "mask",
+
"theme": "super",
+
"hideEars": true,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/hero_mask_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hero_mask_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hero_mask_black/sprites.png"
+
},
+
{
+
"itemId": "hero_cape_blue",
+
"name": "Blue Cape",
+
"cost": 50,
+
"slot": "neck",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hero_cape_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hero_cape_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hero_cape_blue/sprites.png"
+
},
+
{
+
"itemId": "hero_mask_blue",
+
"name": "Blue Comic Mask",
+
"cost": 20,
+
"slot": "mask",
+
"theme": "super",
+
"hideEars": true,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/hero_mask_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hero_mask_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hero_mask_blue/sprites.png"
+
},
+
{
+
"itemId": "hero_suit_blue",
+
"name": "Blue Super Suit",
+
"cost": 80,
+
"slot": "body",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hero_suit_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hero_suit_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hero_suit_blue/sprites.png"
+
},
+
{
+
"itemId": "hero_suit_pink",
+
"name": "Pink Super Suit",
+
"cost": 80,
+
"slot": "body",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hero_suit_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hero_suit_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hero_suit_pink/sprites.png"
+
},
+
{
+
"itemId": "hero_mask_pink",
+
"name": "Pink Comic Mask",
+
"cost": 20,
+
"slot": "mask",
+
"theme": "super",
+
"hideEars": true,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/hero_mask_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hero_mask_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hero_mask_pink/sprites.png"
+
},
+
{
+
"itemId": "hero_cape_pink",
+
"name": "Pink Cape",
+
"cost": 50,
+
"slot": "neck",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hero_cape_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hero_cape_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hero_cape_pink/sprites.png"
+
},
+
{
+
"itemId": "space_shirt_red",
+
"name": "Red Space Shirt",
+
"cost": 50,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/space_shirt_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/space_shirt_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_shirt_red/sprites.png"
+
},
+
{
+
"itemId": "box_helmet",
+
"name": "Box Helmet",
+
"cost": 20,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/box_helmet/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/box_helmet/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/box_helmet/sprites.png"
+
},
+
{
+
"itemId": "ufo_green",
+
"name": "Green UFO",
+
"cost": 100,
+
"slot": "ride",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ufo_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ufo_green/front.png",
+
"back": "https://media.boxcritters.com/items/ufo_green/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ufo_green/sprites.png"
+
},
+
{
+
"itemId": "ufo_pink",
+
"name": "Pink UFO",
+
"cost": 100,
+
"slot": "ride",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ufo_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ufo_pink/front.png",
+
"back": "https://media.boxcritters.com/items/ufo_pink/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ufo_pink/sprites.png"
+
},
+
{
+
"itemId": "ufo_black",
+
"name": "Black UFO",
+
"cost": 100,
+
"slot": "ride",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ufo_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ufo_black/front.png",
+
"back": "https://media.boxcritters.com/items/ufo_black/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ufo_black/sprites.png"
+
},
+
{
+
"itemId": "ufo_white",
+
"name": "White UFO",
+
"cost": 100,
+
"slot": "ride",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ufo_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ufo_white/front.png",
+
"back": "https://media.boxcritters.com/items/ufo_white/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ufo_white/sprites.png"
+
},
+
{
+
"itemId": "ufo_grey",
+
"name": "Grey UFO",
+
"cost": 100,
+
"slot": "ride",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ufo_grey/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ufo_grey/front.png",
+
"back": "https://media.boxcritters.com/items/ufo_grey/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ufo_grey/sprites.png"
+
},
+
{
+
"itemId": "apron_green",
+
"name": "Green Bakery Apron",
+
"cost": 20,
+
"slot": "body",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/apron_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/apron_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/apron_green/sprites.png"
+
},
+
{
+
"itemId": "space_suit",
+
"name": "NEW Space Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/space_suit/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/space_suit/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_suit/sprites.png"
+
},
+
{
+
"itemId": "space_helmet",
+
"name": "NEW Space Helmet",
+
"cost": 50,
+
"slot": "head",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/space_helmet/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/space_helmet/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_helmet/sprites.png"
+
},
+
{
+
"itemId": "glasses_red",
+
"name": "Red Square Glasses",
+
"cost": 20,
+
"slot": "eyes",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/glasses_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/glasses_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/glasses_red/sprites.png"
+
},
+
{
+
"itemId": "glasses_black",
+
"name": "Black Square Glasses",
+
"cost": 20,
+
"slot": "eyes",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/glasses_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/glasses_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/glasses_black/sprites.png"
+
},
+
{
+
"itemId": "glasses_blue",
+
"name": "Blue Square Glasses",
+
"cost": 20,
+
"slot": "eyes",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/glasses_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/glasses_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/glasses_blue/sprites.png"
+
},
+
{
+
"itemId": "glasses_brown",
+
"name": "Brown Square Glasses",
+
"cost": 20,
+
"slot": "eyes",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/glasses_brown/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/glasses_brown/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/glasses_brown/sprites.png"
+
},
+
{
+
"itemId": "space_boots_white",
+
"name": "Space Boots",
+
"cost": 50,
+
"slot": "feet",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/space_boots_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/space_boots_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_boots_white/sprites.png"
+
},
+
{
+
"itemId": "space_boots",
+
"name": "Dark Grey Space Boots",
+
"cost": 50,
+
"slot": "feet",
+
"theme": "space",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/space_boots/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/space_boots/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/space_boots/sprites.png"
+
},
+
{
+
"itemId": "ringmaster_boots",
+
"name": "Black Ringmaster Boots",
+
"cost": 50,
+
"slot": "feet",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/ringmaster_boots/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/ringmaster_boots/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/ringmaster_boots/sprites.png"
+
},
+
{
+
"itemId": "super_girl_suit",
+
"name": "Super Hero Girl",
+
"cost": 80,
+
"slot": "body",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/super_girl_suit/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/super_girl_suit/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/super_girl_suit/sprites.png"
+
},
+
{
+
"itemId": "super_boy_suit",
+
"name": "Super Hero Boy",
+
"cost": 80,
+
"slot": "body",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/super_boy_suit/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/super_boy_suit/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/super_boy_suit/sprites.png"
+
},
+
{
+
"itemId": "onsie_plaid_feet_red",
+
"name": "Red Plaid Onsie Feet",
+
"cost": 20,
+
"slot": "feet",
+
"theme": "plaid",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/onsie_plaid_feet_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/onsie_plaid_feet_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/onsie_plaid_feet_red/sprites.png"
+
},
+
{
+
"itemId": "climb_rope_yellow",
+
"name": "Yellow Climbing Rope with Grappling Hook",
+
"cost": 20,
+
"slot": "belt",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_rope_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_rope_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_rope_yellow/sprites.png"
+
},
+
{
+
"itemId": "climb_rope_dark",
+
"name": "Dark Grey Climbing Rope with Grappling Hook",
+
"cost": 20,
+
"slot": "belt",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_rope_dark/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_rope_dark/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_rope_dark/sprites.png"
+
},
+
{
+
"itemId": "climb_rope_orange",
+
"name": "Orange Climbing Rope with Grappling Hook",
+
"cost": 20,
+
"slot": "belt",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_rope_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_rope_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_rope_orange/sprites.png"
+
},
+
{
+
"itemId": "climb_rope_green",
+
"name": "Green Climbing Rope with Grappling Hook",
+
"cost": 20,
+
"slot": "belt",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_rope_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_rope_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_rope_green/sprites.png"
+
},
+
{
+
"itemId": "climb_rope_blue",
+
"name": "Blue Climbing Rope with Grappling Hook",
+
"cost": 20,
+
"slot": "belt",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_rope_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_rope_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_rope_blue/sprites.png"
+
},
+
{
+
"itemId": "climb_rope_pink",
+
"name": "Pink Climbing Rope with Grappling Hook",
+
"cost": 20,
+
"slot": "belt",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_rope_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_rope_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_rope_pink/sprites.png"
+
},
+
{
+
"itemId": "climb_helmet_yellow",
+
"name": "Yellow Climbing Helmet",
+
"cost": 40,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_helmet_yellow/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_helmet_yellow/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_helmet_yellow/sprites.png"
+
},
+
{
+
"itemId": "climb_helmet_dark",
+
"name": " Dark Grey Climbing Helmet",
+
"cost": 40,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_helmet_dark/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_helmet_dark/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_helmet_dark/sprites.png"
+
},
+
{
+
"itemId": "climb_helmet_orange",
+
"name": "Orange Climbing Helmet",
+
"cost": 40,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_helmet_orange/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_helmet_orange/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_helmet_orange/sprites.png"
+
},
+
{
+
"itemId": "climb_helmet_green",
+
"name": "Green Climbing Helmet",
+
"cost": 40,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_helmet_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_helmet_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_helmet_green/sprites.png"
+
},
+
{
+
"itemId": "climb_helmet_blue",
+
"name": "Blue Climbing Helmet",
+
"cost": 40,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_helmet_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_helmet_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_helmet_blue/sprites.png"
+
},
+
{
+
"itemId": "climb_helmet_pink",
+
"name": "Pink Climbing Helmet",
+
"cost": 40,
+
"slot": "head",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/climb_helmet_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/climb_helmet_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/climb_helmet_pink/sprites.png"
+
},
+
{
+
"itemId": "hazmat_suit",
+
"name": "Hazmat Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hazmat_suit/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hazmat_suit/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hazmat_suit/sprites.png"
+
},
+
{
+
"itemId": "hazmat_hood",
+
"name": "Hazmat Hood",
+
"cost": 100,
+
"slot": "head",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/hazmat_hood/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hazmat_hood/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hazmat_hood/sprites.png"
+
},
+
{
+
"itemId": "hazmat_boots",
+
"name": "Hazmat Boots",
+
"cost": 100,
+
"slot": "feet",
+
"theme": "work",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hazmat_boots/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hazmat_boots/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hazmat_boots/sprites.png"
+
},
+
{
+
"itemId": "pantomime_face",
+
"name": "Pantomime Face",
+
"cost": 100,
+
"slot": "face",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pantomime_face/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pantomime_face/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pantomime_face/sprites.png"
+
},
+
{
+
"itemId": "spider",
+
"name": "Spider Costume",
+
"cost": 100,
+
"slot": "body",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/spider/icon.png",
+
"bits": { "front": "https://media.boxcritters.com/items/spider/front.png" },
+
"sprites": "https://media.boxcritters.com/items/spider/sprites.png"
+
},
+
{
+
"itemId": "pantomime_suit",
+
"name": "Pantomime Suit",
+
"cost": 100,
+
"slot": "body",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pantomime_suit/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pantomime_suit/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pantomime_suit/sprites.png"
+
},
+
{
+
"itemId": "pantomime_hat",
+
"name": "Pantomime Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/pantomime_hat/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/pantomime_hat/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/pantomime_hat/sprites.png"
+
},
+
{
+
"itemId": "witch_dress_white",
+
"name": "White Witch Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/witch_dress_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/witch_dress_white/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/witch_dress_white/sprites.png"
+
},
+
{
+
"itemId": "witch_hat_black",
+
"name": "Black Witch Hat with Red Hair",
+
"cost": 100,
+
"slot": "head",
+
"theme": "halloween",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/witch_hat_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/witch_hat_black/front.png",
+
"back": "https://media.boxcritters.com/items/witch_hat_black/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/witch_hat_black/sprites.png"
+
},
+
{
+
"itemId": "witch_hat_burgundy",
+
"name": "Burgundy Witch Hat with Blue Hair",
+
"cost": 100,
+
"slot": "head",
+
"theme": "halloween",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/witch_hat_burgundy/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/witch_hat_burgundy/front.png",
+
"back": "https://media.boxcritters.com/items/witch_hat_burgundy/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/witch_hat_burgundy/sprites.png"
+
},
+
{
+
"itemId": "witch_hat_white",
+
"name": "White Witch Hat with Pink Hair",
+
"cost": 100,
+
"slot": "head",
+
"theme": "halloween",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/witch_hat_white/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/witch_hat_white/front.png",
+
"back": "https://media.boxcritters.com/items/witch_hat_white/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/witch_hat_white/sprites.png"
+
},
+
{
+
"itemId": "witch_dress_black",
+
"name": "Black Witch Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/witch_dress_black/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/witch_dress_black/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/witch_dress_black/sprites.png"
+
},
+
{
+
"itemId": "witch_dress_burgundy",
+
"name": "Burgundy Witch Dress",
+
"cost": 100,
+
"slot": "body",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/witch_dress_burgundy/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/witch_dress_burgundy/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/witch_dress_burgundy/sprites.png"
+
},
+
{
+
"itemId": "knight_armour",
+
"name": "Knight Armor",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/knight_armour/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/knight_armour/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/knight_armour/sprites.png"
+
},
+
{
+
"itemId": "robot_head_pink",
+
"name": "Pink Robot Head",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": true,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/robot_head_pink/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/robot_head_pink/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/robot_head_pink/sprites.png"
+
},
+
{
+
"itemId": "chocolate_bar",
+
"name": "Chocolate Costume",
+
"cost": 100,
+
"slot": "body",
+
"theme": "",
+
"hideEars": true,
+
"hideNose": true,
+
"icon": "https://media.boxcritters.com/items/chocolate_bar/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/chocolate_bar/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/chocolate_bar/sprites.png"
+
},
+
{
+
"itemId": "paint_green",
+
"name": "Green Face Paint with Warts",
+
"cost": 100,
+
"slot": "face",
+
"theme": "halloween",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/paint_green/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/paint_green/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/paint_green/sprites.png"
+
},
+
{
+
"itemId": "wizard_blizzard",
+
"name": "Blizzard Wizard Hat",
+
"cost": 100,
+
"slot": "head",
+
"theme": "",
+
"hideEars": true,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/wizard_blizzard/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/wizard_blizzard/front.png",
+
"back": "https://media.boxcritters.com/items/wizard_blizzard/back.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/wizard_blizzard/sprites.png"
+
},
+
{
+
"itemId": "knight_shield_blue",
+
"name": "Blue Shield",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "fantasy",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/knight_shield_blue/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/knight_shield_blue/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/knight_shield_blue/sprites.png"
+
},
+
{
+
"itemId": "hero_cape_red",
+
"name": "Red Super Cape",
+
"cost": 100,
+
"slot": "neck",
+
"theme": "super",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/hero_cape_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/hero_cape_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/hero_cape_red/sprites.png"
+
},
+
{
+
"itemId": "knight_shield_red",
+
"name": "Red Shield",
+
"cost": 100,
+
"slot": "hand",
+
"theme": "fantasy",
+
"hideEars": null,
+
"hideNose": null,
+
"icon": "https://media.boxcritters.com/items/knight_shield_red/icon.png",
+
"bits": {
+
"front": "https://media.boxcritters.com/items/knight_shield_red/front.png"
+
},
+
"sprites": "https://media.boxcritters.com/items/knight_shield_red/sprites.png"
+
}
+
]
+140
src/constants/items.ts
···
+
export const shop = {
+
lastItem: { itemId: "beard2_brown", cost: 20 },
+
freeItem: { itemId: "goggles_pink", cost: 0 },
+
nextItem: { itemId: "scout_uniform", cost: 0 },
+
collection: [
+
{ itemId: "santa_hat", cost: 100 },
+
],
+
};
+
+
export const codes = {
+
rocketsnail: ["viking", "rocket_red"],
+
andybulletin: "propeller",
+
cute: "toque_pink",
+
oommgames: "space_red",
+
boxcritters3d: ["3d_white", "3d_black"],
+
goodnight: "sleeping",
+
madeincanada: "toque_white",
+
thekeeper: "party_green",
+
bunnyhug: "hoodie_blue",
+
greenplumber: "ballcap_green",
+
duckhunter: "float_pink",
+
piratepack: "pirate_patch",
+
pickle: "pickle",
+
oscarproductions: "guitar_blue",
+
livestream: "headphones_black",
+
creative: "headphones_black",
+
fun: "propeller_pink",
+
marco: "keytar_red",
+
snowball: "hoodie_white",
+
critbits: "ballcap_pink",
+
esporte: "kit_yellow",
+
squeeze: "hoodie_orange",
+
imagination: "box_brown",
+
sparkle: "propeller_silver",
+
adventure: "snorkel_blue",
+
tamago: "sun_square",
+
redcross: "australian_hat",
+
discordcritterspt: "headphones_green",
+
scarletraven: "wings_black",
+
boxcritterswiki: "paperhat_colour",
+
wikicritters: "brain_green",
+
boxcrittersguild: "cardboard_sword_silver",
+
staysafe: "doctor_mask_blue",
+
glitter: "pirate_capt_pink",
+
};
+
+
export const throwback = [
+
"goggles_black",
+
"pot",
+
"sombrero_yellow",
+
"cone",
+
"toque_purple",
+
"sun_orange",
+
"pirate_capt_black",
+
"lifejacket_red",
+
"ballcap_black",
+
"super_cape_red",
+
"tshirt_white",
+
"lei_red",
+
"ballcap_blue",
+
"viking_blue",
+
"overalls_orange",
+
"bunny_blue",
+
"messenger_brown",
+
"plaid_black",
+
"tinfoil_hat",
+
"monk",
+
"pirate_hat_black",
+
"hawaii_orange",
+
"space_black",
+
"traffic_cone",
+
"grass_yellow",
+
"ushanka",
+
"bandana_purple",
+
"ski_suit_blue",
+
"hotdog",
+
"space_blue",
+
"sweater_orange",
+
"tactical_headset",
+
"ballcap_yellow",
+
"ringmaster_suit_red",
+
"flight_helmet_red",
+
"rainhat_yellow",
+
"raincoat_yellow",
+
"ballcap_red",
+
"ballerina_pink",
+
"hawaii_blue",
+
"stripe_red_white",
+
"grass_green",
+
"plaid_blue",
+
"toque_orange",
+
"school_pack_orange",
+
"pirate_crew_blue",
+
"ringmaster_hat_black",
+
"super_mask_black",
+
"pirate_hat_pink",
+
"rainhat_red",
+
"tophat_black",
+
"scarf_red",
+
"skeleton_body",
+
"dracula_cloak",
+
"pumpkin",
+
"hotdog",
+
"beard3_black",
+
"scarf_purple",
+
"puffy_red",
+
"blockhead",
+
"hoodie_purple",
+
"winter_dress_red",
+
"bulb_blue",
+
"tacky_red",
+
"pirate_bandana_red",
+
];
+
+
export const eventCodes = {
+
christmas2019: {
+
jinglebells: "elf_hat_green",
+
winter: "winter_dress",
+
joy: "bulb_yellow",
+
rudolph: "reindeer_head",
+
elf: "elf_suit_green",
+
cheer: "bulb_green",
+
glow: "bulb_red",
+
rednose: "reindeer_body",
+
blizzard: "wizard_blizzard",
+
family: "ornament_red",
+
shine: "angel_halo",
+
peace: "angel_wings",
+
jolly: "santa_beard",
+
merry: "santa_hat",
+
christmas: "santa_suit",
+
boxingday: "onsie_plaid_red",
+
warm: "sleeping_red",
+
tacky: "tacky_green",
+
snow: "goggles_white",
+
beautiful: "winter_dress_red",
+
newyear: "tuxedo_black",
+
"2020": "tophat_black",
+
},
+
};
+50
src/constants/parties.json
···
+
{
+
"default": {
+
"start": null,
+
"end": null
+
},
+
"easter2019": {
+
"start": "04-18-2019",
+
"end": "04-28-2019"
+
},
+
"halloween2019": {
+
"start": "10-20-2019",
+
"end": "11-06-2019"
+
},
+
"christmas2019": {
+
"start": "12-05-2019",
+
"end": "01-05-2020"
+
},
+
"lucky2020": {
+
"start": "03-11-2020",
+
"end": "04-01-2020"
+
},
+
"easter2020": {
+
"start": "04-10-2020",
+
"end": "04-19-2020"
+
},
+
"grad2020": {
+
"start": "06-03-2020",
+
"end": "06-20-2020"
+
},
+
"summer2020": {
+
"start": "07-15-2020",
+
"end": "08-16-2020"
+
},
+
"space": {
+
"start": "08-16-2020",
+
"end": "09-04-2020"
+
},
+
"halloween2020": {
+
"start": "10-07-2020",
+
"end": "11-08-2020"
+
},
+
"christmas2020": {
+
"start": "12-02-2020",
+
"end": "01-01-2021"
+
},
+
"fools2021": {
+
"start": "03-30-2021",
+
"end": "04-02-2021"
+
}
+
}
+91
src/constants/world.ts
···
+
import { PlayerCrumb, Room } from "@/types.ts";
+
import { indexRoomData } from "@/utils.ts";
+
+
export const rooms: Record<string, Record<string, Room>> =
+
await indexRoomData();
+
export const spawnRoom = "tavern";
+
+
export const players: Record<string, PlayerCrumb> = {
+
"0": {
+
"i": "0",
+
"n": "Huggable",
+
"c": "huggable",
+
"x": 1670,
+
"y": 323,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
"_roomId": "crash_site",
+
},
+
};
+
export const queue: Array<string> = [];
+
+
export const roomExits = {
+
"cellar->tavern": { x: 360, y: 410, r: 0 },
+
"crash_site->cellar": { x: 615, y: 400, r: 0 },
+
"shack->port": { x: 550, y: 235, r: 0 },
+
"jungle->port": { x: 650, y: 230, r: 0 },
+
"snowman_village->tavern": { x: 563, y: 368, r: 0 },
+
};
+
+
// deno-lint-ignore no-explicit-any
+
export const npcs: { [key: string]: any } = {
+
snowman_village: [
+
{
+
"i": "NPC0",
+
"n": "Snow Girl",
+
"c": "snowgirl",
+
"x": 1289,
+
"y": 228,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
},
+
{
+
"i": "NPC1",
+
"n": "Snow Patrol",
+
"c": "snow_patrol",
+
"x": 1644,
+
"y": 221,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
},
+
{
+
"i": "NPC2",
+
"n": "Snow Greeter",
+
"c": "snow_greeter",
+
"x": 443,
+
"y": 317,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
},
+
{
+
"i": "NPC3",
+
"n": "Snow Grandma",
+
"c": "snowgrandma",
+
"x": 1938,
+
"y": 251,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
},
+
{
+
"i": "NPC4",
+
"n": "Snow Keeper",
+
"c": "snowkeeper",
+
"x": 893,
+
"y": 216,
+
"r": 180,
+
"g": [],
+
"m": "",
+
"e": "",
+
},
+
],
+
};
+237
src/main.ts
···
+
import { serve } from "https://deno.land/std@0.162.0/http/server.ts";
+
import { contentType } from "https://deno.land/std@0.224.0/media_types/mod.ts";
+
import {
+
dirname,
+
fromFileUrl,
+
join,
+
normalize,
+
} from "https://deno.land/std@0.224.0/path/mod.ts";
+
import { exists } from "jsr:@std/fs/exists";
+
+
import { io } from "@/socket/index.ts";
+
import * as world from "@/constants/world.ts";
+
import { getAccount } from "@/utils.ts";
+
import * as schemas from "@/schema.ts";
+
import * as utils from "@/utils.ts";
+
import parties from "@/constants/parties.json" with { type: "json" };
+
import { extname } from "https://deno.land/std@0.212.0/path/extname.ts";
+
import { parseArgs } from "jsr:@std/cli/parse-args";
+
+
const EXECUTABLE = Deno.env.get("EXECUTABLE") == "true";
+
const BASE_DIR = EXECUTABLE
+
? dirname(Deno.execPath())
+
: dirname(dirname(fromFileUrl(Deno.mainModule)));
+
const PUBLIC_DIR = join(BASE_DIR, "public");
+
+
if (!EXECUTABLE) {
+
if (!await exists("./public") || !await exists(".env")) {
+
console.error("Missing files. Make sure you have `public/` and `.env`");
+
Deno.exit();
+
}
+
}
+
+
async function serveStatic(req: Request): Promise<Response> {
+
const url = new URL(req.url);
+
let pathname = url.pathname;
+
pathname = decodeURIComponent(pathname);
+
pathname = pathname.endsWith("/") ? pathname + "index.html" : pathname;
+
+
const fsPath = normalize(join(PUBLIC_DIR, pathname));
+
+
// Prevent directory traversal
+
if (!fsPath.startsWith(PUBLIC_DIR)) {
+
return new Response("Forbidden", { status: 403 });
+
}
+
+
try {
+
const file = await Deno.readFile(fsPath);
+
const mime = contentType(extname(fsPath)) || "application/octet-stream";
+
return new Response(file, {
+
headers: { "Content-Type": mime },
+
});
+
} catch {
+
return new Response("Not Found", { status: 404 });
+
}
+
}
+
+
async function handler(
+
req: Request,
+
connInfo: Deno.ServeHandlerInfo,
+
): Promise<Response> {
+
const url = new URL(req.url);
+
const pathname = url.pathname;
+
+
if (req.headers.get("upgrade") === "websocket") {
+
//@ts-ignore: The websocket successfully upgrades
+
return io.handler()(req, connInfo);
+
}
+
+
if (req.method == "POST" && pathname == "/api/client/login") {
+
try {
+
const body = await req.json();
+
const parsed = schemas.login.safeParse(body);
+
if (!parsed.success) {
+
return Response.json({
+
success: false,
+
message: "Validation failure",
+
error: parsed.error,
+
}, { status: 400 });
+
}
+
+
const data = parsed.data;
+
const _players = Object.values(world.players);
+
const nameInUse = _players.find((p) => p.n === data.nickname) ||
+
world.queue.includes(data.nickname);
+
+
if (nameInUse) {
+
return Response.json({
+
success: false,
+
message: "There is already a player with this nickname online.",
+
});
+
}
+
+
const JWT_CONTENT = {
+
playerId: crypto.randomUUID(),
+
...data,
+
};
+
+
const JWT_TOKEN = Deno.env.get("JWT_TOKEN");
+
if (!JWT_TOKEN) {
+
return new Response("JWT_TOKEN not set in env", { status: 500 });
+
}
+
+
const token = await utils.createJWT(JWT_CONTENT);
+
+
world.queue.push(data.nickname);
+
+
return Response.json({
+
success: true,
+
playerId: JWT_CONTENT.playerId,
+
token,
+
});
+
} catch {
+
return Response.json({
+
success: false,
+
message: "Bad request",
+
}, { status: 400 });
+
}
+
}
+
+
if (req.method == "GET") {
+
switch (pathname) {
+
case "/api/server/players": {
+
return Response.json({ players: world.players });
+
}
+
+
case "/api/server/rooms": {
+
return Response.json(world.rooms);
+
}
+
+
case "/api/server/persistence": {
+
const account = await getAccount();
+
return Response.json({
+
success: true,
+
data: account,
+
});
+
}
+
+
case "/api/client/rooms": {
+
const url = new URL(req.url);
+
let partyId = url.searchParams.get("partyId") || "default";
+
const debug = url.searchParams.has("debug");
+
+
if (
+
[
+
"today2019",
+
"today2020",
+
"today2021",
+
].includes(partyId)
+
) {
+
partyId = utils.getCurrentEvent(
+
parseInt(partyId.replace("today", "")),
+
);
+
}
+
+
if (!Object.keys(parties).includes(partyId)) {
+
return Response.json({
+
success: false,
+
message: "Invalid partyId hash provided.",
+
});
+
}
+
+
let missing = 0;
+
const roomResponse = Object.keys(world.rooms).reduce(
+
(res, roomId) => {
+
const room = world.rooms[roomId];
+
+
if (room[partyId]) {
+
if (
+
!room[partyId].partyExclusive ||
+
room[partyId]?.partyExclusive?.includes(partyId)
+
) {
+
res.push(room[partyId]);
+
} else {
+
missing++;
+
}
+
} else {
+
if (
+
!room.default.partyExclusive ||
+
room.default.partyExclusive.includes(partyId)
+
) {
+
res.push(room.default);
+
} else {
+
missing++;
+
}
+
}
+
+
return res;
+
},
+
[] as Array<typeof world.rooms[string]["default"]>,
+
);
+
+
if (missing === Object.keys(world.rooms).length) {
+
return Response.json({
+
success: false,
+
message:
+
"No rooms were fetched while indexxing using the specified partyId hash.",
+
});
+
}
+
+
const partyIds = Object.keys(parties);
+
if (debug) {
+
const roomNames = roomResponse.map((room) => room.name);
+
return Response.json({
+
parties: partyIds,
+
data: roomNames,
+
});
+
}
+
+
return Response.json({
+
parties: partyIds,
+
data: roomResponse,
+
});
+
}
+
+
default: {
+
return serveStatic(req);
+
}
+
}
+
}
+
+
return new Response("Not Found", { status: 404 });
+
}
+
+
const args = parseArgs(Deno.args, {
+
string: ["port"],
+
default: {
+
port: "3257",
+
},
+
});
+
+
if (isNaN(Number(args.port))) {
+
console.log("Port provided is not valid.");
+
Deno.exit();
+
}
+
+
//@ts-ignore: Type issues occuring from upgrading websocket requests to Socket.io
+
await serve(handler, { port: args.port });
+37
src/schema.ts
···
+
import { z } from "zod";
+
import parties from "@/constants/parties.json" with { type: "json" };
+
+
/*
+
LOGIN API
+
*/
+
export const login = z.object({
+
nickname: z.string()
+
.transform((s) => s.trim())
+
.pipe(
+
z.string()
+
.min(3, "The nickname must be at least 3 characters long.")
+
.max(25, "The nickname must be less than 25 characters long."),
+
),
+
critterId: z.enum([
+
"hamster",
+
"beaver",
+
"lizard",
+
"raccoon",
+
"penguin",
+
"snail",
+
"snow_greeter",
+
"snowkeeper",
+
"snowgirl",
+
"snow_patrol",
+
"snowgrandma",
+
"alpha_hamster",
+
]).default("hamster"),
+
partyId: z.enum([
+
...Object.keys(parties) as [string, ...string[]],
+
"today2019",
+
"today2020",
+
"today2021",
+
]).default("default"),
+
persistent: z.boolean().default(false),
+
mods: z.array(z.enum(["roomExits"])).default([]),
+
}).strict(); // Strict to disallow extra keys
+155
src/socket/handlers/connection.ts
···
+
// deno-lint-ignore-file no-explicit-any
+
import { Server, Socket } from "https://deno.land/x/socket_io@0.2.0/mod.ts";
+
import z from "zod";
+
+
import * as world from "@/constants/world.ts";
+
import * as utils from "@/utils.ts";
+
import * as types from "@/types.ts";
+
+
export function listen(
+
io: Server,
+
socket: Socket,
+
ctx: types.SocketHandlerContext,
+
) {
+
socket.once("login", async (ticket: string) => {
+
if (
+
z.object({
+
ticket: z.string(),
+
}).safeParse({ ticket: ticket }).success == false
+
) return;
+
+
let playerData;
+
try {
+
playerData = await utils.verifyJWT(ticket);
+
} catch (_e) {
+
socket.disconnect(true);
+
return;
+
}
+
+
// TODO: make this just an inline function instead of having an onPropertyChange function, I'm just really lazy right now lol -index
+
function onPropertyChange(property: string, value: any) {
+
utils.updateAccount(ctx.localPlayer!.nickname, property, value);
+
}
+
+
const createArrayHandler = (propertyName: string) => ({
+
get(target: any, property: string) {
+
if (typeof target[property] === "function") {
+
return function (...args: any[]) {
+
const result = target[property].apply(target, args);
+
onPropertyChange(propertyName, target);
+
return result;
+
};
+
}
+
return target[property];
+
},
+
});
+
+
const handler = {
+
set(target: any, property: string, value: any) {
+
if (Array.isArray(value)) {
+
target[property] = new Proxy(value, createArrayHandler(property));
+
onPropertyChange(property, target[property]);
+
} else {
+
target[property] = value;
+
onPropertyChange(property, value);
+
}
+
return true;
+
},
+
get(target: any, property: string) {
+
if (Array.isArray(target[property])) {
+
return new Proxy(target[property], createArrayHandler(property));
+
}
+
return target[property];
+
},
+
};
+
+
//@ts-ignore: I will fix the type errors with using a different JWT library eventually
+
const sub = playerData as {
+
playerId: string;
+
nickname: string;
+
critterId: types.CritterId;
+
partyId: string;
+
persistent: boolean;
+
mods: Array<string>;
+
};
+
+
if (
+
[
+
"today2019",
+
"today2020",
+
"today2021",
+
].includes(sub.partyId)
+
) {
+
console.log("target year:", parseInt(sub.partyId.replace("today", "")));
+
sub.partyId = utils.getCurrentEvent(
+
parseInt(sub.partyId.replace("today", "")),
+
);
+
}
+
+
const persistentAccount = await utils.getAccount(sub.nickname);
+
if (!sub.persistent || persistentAccount.individual == null) {
+
ctx.localPlayer = {
+
playerId: sub.playerId,
+
nickname: sub.nickname,
+
critterId: sub.critterId,
+
ignore: [],
+
friends: [],
+
inventory: [],
+
gear: [],
+
eggs: [],
+
coins: 150,
+
isMember: false,
+
isGuest: false,
+
isTeam: false,
+
x: 0,
+
y: 0,
+
rotation: 0,
+
mutes: [],
+
+
_partyId: sub.partyId, // This key is replaced down the line anyway
+
_mods: sub.mods,
+
};
+
+
if (sub.persistent) {
+
utils.createAccount(ctx.localPlayer);
+
ctx.localPlayer = new Proxy<types.LocalPlayer>(
+
utils.expandAccount(ctx.localPlayer),
+
handler,
+
);
+
}
+
} else {
+
persistentAccount.individual.critterId = sub.critterId || "hamster";
+
persistentAccount.individual._partyId = sub.partyId || "default";
+
persistentAccount.individual._mods = sub.mods || [];
+
+
ctx.localPlayer = new Proxy<types.LocalPlayer>(
+
utils.expandAccount(persistentAccount.individual),
+
handler,
+
);
+
}
+
+
ctx.localPlayer._partyId = socket.handshake.query.get("partyId") ||
+
"default";
+
world.queue.splice(world.queue.indexOf(ctx.localPlayer.nickname), 1);
+
+
ctx.localCrumb = utils.makeCrumb(ctx.localPlayer, world.spawnRoom);
+
socket.join(world.spawnRoom);
+
+
world.players[ctx.localPlayer.playerId] = ctx.localCrumb;
+
socket.emit("login", {
+
player: ctx.localPlayer,
+
spawnRoom: world.spawnRoom,
+
});
+
});
+
+
socket.on("beep", () => socket.emit("beep"));
+
+
socket.on("disconnect", (reason) => {
+
if (reason == "server namespace disconnect") return;
+
+
if (ctx.localPlayer && ctx.localCrumb) {
+
io.in(ctx.localCrumb._roomId).emit("R", ctx.localCrumb);
+
delete world.players[ctx.localPlayer.playerId];
+
}
+
});
+
}
+62
src/socket/handlers/economy.ts
···
+
import { Server, Socket } from "https://deno.land/x/socket_io@0.2.0/mod.ts";
+
import z from "zod";
+
+
import * as items from "@/constants/items.ts";
+
import * as types from "@/types.ts";
+
+
export function listen(
+
_io: Server,
+
socket: Socket,
+
ctx: types.SocketHandlerContext,
+
) {
+
socket.on("getShop", () => {
+
const _shopItems = items.shop as unknown as types.ShopData;
+
socket.emit("getShop", {
+
lastItem: _shopItems.lastItem.itemId,
+
freeItem: _shopItems.freeItem.itemId,
+
nextItem: _shopItems.nextItem.itemId,
+
collection: _shopItems.collection.map((item) => item.itemId),
+
});
+
});
+
+
socket.on("buyItem", (itemId: string) => {
+
if (!ctx.localPlayer || !ctx.localCrumb) return;
+
+
if (
+
z.object({
+
itemId: z.string().nonempty(),
+
}).strict().safeParse({ itemId: itemId }).success == false
+
) return;
+
+
// ? Free item is excluded from this list because the game just sends the "/freeitem" code
+
const currentShop = items.shop;
+
const _shopItems = [
+
currentShop.lastItem,
+
currentShop.nextItem,
+
...currentShop.collection,
+
];
+
+
const target = _shopItems.find((item) => item.itemId == itemId)!;
+
if (!target) {
+
console.log(
+
"> There is no item in this week's shop with itemId:",
+
itemId,
+
);
+
return;
+
}
+
+
if (
+
ctx.localPlayer.coins >= target.cost &&
+
!ctx.localPlayer.inventory.includes(itemId)
+
) {
+
console.log(
+
"[+] Bought item: " + itemId + " for " + target.cost + " coins",
+
);
+
ctx.localPlayer.coins -= target.cost;
+
ctx.localPlayer.inventory.push(itemId);
+
+
socket.emit("buyItem", { itemId: itemId });
+
socket.emit("updateCoins", { balance: ctx.localPlayer.coins });
+
}
+
});
+
}
+73
src/socket/handlers/player.ts
···
+
import { Server, Socket } from "https://deno.land/x/socket_io@0.2.0/mod.ts";
+
import z from "zod";
+
+
import * as world from "@/constants/world.ts";
+
import * as utils from "@/utils.ts";
+
import * as types from "@/types.ts";
+
+
export function listen(
+
io: Server,
+
socket: Socket,
+
ctx: types.SocketHandlerContext,
+
) {
+
socket.on("moveTo", (x: number, y: number) => {
+
if (!ctx.localPlayer || !ctx.localCrumb) return;
+
+
const roomData =
+
world.rooms[ctx.localCrumb._roomId][ctx.localPlayer._partyId] ||
+
world.rooms[ctx.localCrumb._roomId].default;
+
if (
+
z.object({
+
x: z.number().min(0).max(roomData.width),
+
y: z.number().min(0).max(roomData.height),
+
}).safeParse({ x: x, y: y }).success == false
+
) return;
+
+
const newDirection = utils.getDirection(
+
ctx.localPlayer.x,
+
ctx.localPlayer.y,
+
x,
+
y,
+
);
+
+
ctx.localPlayer.x = x;
+
ctx.localPlayer.y = y;
+
ctx.localPlayer.rotation = newDirection;
+
+
ctx.localCrumb.x = x;
+
ctx.localCrumb.y = y;
+
ctx.localCrumb.r = newDirection;
+
+
io.in(ctx.localCrumb._roomId).volatile.emit("X", {
+
i: ctx.localPlayer.playerId,
+
x: x,
+
y: y,
+
r: newDirection,
+
});
+
});
+
+
socket.on("updateGear", (gear: Array<string>) => {
+
if (!ctx.localPlayer || !ctx.localCrumb) return;
+
+
if (
+
z.object({
+
gear: z.array(z.string().nonempty()).default([]),
+
}).strict().safeParse({ gear: gear }).success == false
+
) return;
+
+
const _gear = [];
+
for (const itemId of gear) {
+
if (ctx.localPlayer.inventory.includes(itemId)) {
+
_gear.push(itemId);
+
}
+
}
+
ctx.localPlayer.gear = _gear;
+
+
io.in(ctx.localCrumb._roomId).emit("G", {
+
i: ctx.localPlayer.playerId,
+
g: ctx.localPlayer.gear,
+
});
+
+
socket.emit("updateGear", ctx.localPlayer.gear);
+
});
+
}
+219
src/socket/handlers/social.ts
···
+
// deno-lint-ignore-file no-explicit-any
+
import { Server, Socket } from "https://deno.land/x/socket_io@0.2.0/mod.ts";
+
import z from "zod";
+
+
import * as world from "@/constants/world.ts";
+
import * as items from "@/constants/items.ts";
+
import * as utils from "@/utils.ts";
+
import * as types from "@/types.ts";
+
+
import itemsJSON from "@/constants/items.json" with { type: "json" };
+
+
export function listen(
+
io: Server,
+
socket: Socket,
+
ctx: types.SocketHandlerContext,
+
) {
+
socket.on("message", (text: string) => {
+
if (!ctx.localPlayer || !ctx.localCrumb) return;
+
+
if (
+
z.object({
+
text: z.string().nonempty(),
+
}).safeParse({ text: text }).success == false
+
) return;
+
+
console.log(`> ${ctx.localPlayer.nickname} sent message:`, text);
+
ctx.localCrumb.m = text;
+
+
socket.broadcast.in(ctx.localCrumb._roomId).emit("M", {
+
i: ctx.localPlayer.playerId,
+
m: text,
+
});
+
+
setTimeout(() => {
+
if (ctx.localCrumb!.m != text) return;
+
ctx.localCrumb!.m = "";
+
}, 5e3);
+
});
+
+
socket.on("emote", (emote: string) => {
+
if (!ctx.localPlayer || !ctx.localCrumb) return;
+
+
if (
+
z.object({
+
emote: z.string().nonempty(), // TODO: make this an enum
+
}).safeParse({ emote: emote }).success == false
+
) return;
+
+
console.log(`> ${ctx.localPlayer.nickname} sent emote:`, emote);
+
ctx.localCrumb.e = emote;
+
+
socket.broadcast.in(ctx.localCrumb._roomId).emit("E", {
+
i: ctx.localPlayer.playerId,
+
e: emote,
+
});
+
+
setTimeout(() => {
+
if (ctx.localCrumb!.e != emote) return;
+
ctx.localCrumb!.e = "";
+
}, 5e3);
+
});
+
+
// ? Options is specified just because sometimes it is sent, but its always an empty string
+
socket.on("code", (code: string, _options?: string) => {
+
if (!ctx.localPlayer || !ctx.localCrumb) return;
+
+
if (
+
z.object({
+
command: z.enum([
+
"pop",
+
"freeitem",
+
"tbt",
+
"darkmode",
+
"spydar",
+
"allitems",
+
]),
+
}).safeParse({
+
command: code,
+
}).success == false
+
) return;
+
+
console.log(`> ${ctx.localPlayer.nickname} sent code:`, code);
+
+
const addItem = function (id: string, showGUI: boolean = false) {
+
if (!ctx.localPlayer!.inventory.includes(id)) {
+
socket.emit("addItem", { itemId: id, showGUI: showGUI });
+
ctx.localPlayer!.inventory.push(id);
+
}
+
};
+
+
// Misc. Codes
+
switch (code) {
+
case "pop": {
+
socket.emit(
+
"pop",
+
Object.values(world.players).filter((critter) =>
+
critter.c != "huggable"
+
).length,
+
);
+
break;
+
}
+
case "freeitem": {
+
addItem(items.shop.freeItem.itemId, true);
+
break;
+
}
+
case "tbt": {
+
const _throwbackItem = utils.getNewCodeItem(
+
ctx.localPlayer,
+
items.throwback,
+
);
+
if (_throwbackItem) addItem(_throwbackItem, true);
+
break;
+
}
+
case "darkmode": {
+
addItem("3d_black", true);
+
break;
+
}
+
case "spydar": {
+
ctx.localPlayer.gear = [
+
"sun_orange",
+
"super_mask_black",
+
"toque_blue",
+
"dracula_cloak",
+
"headphones_black",
+
"hoodie_black",
+
];
+
+
if (ctx.localCrumb._roomId == "tavern") {
+
ctx.localPlayer.x = 216;
+
ctx.localPlayer.y = 118;
+
+
ctx.localCrumb.x = 216;
+
ctx.localCrumb.y = 118;
+
+
io.in(ctx.localCrumb._roomId).volatile.emit("X", {
+
i: ctx.localPlayer.playerId,
+
x: 216,
+
y: 118,
+
});
+
}
+
+
io.in(ctx.localCrumb._roomId).emit("G", {
+
i: ctx.localPlayer.playerId,
+
g: ctx.localPlayer.gear,
+
});
+
+
socket.emit("updateGear", ctx.localPlayer.gear);
+
break;
+
}
+
case "allitems": {
+
for (const item of itemsJSON) {
+
addItem(item.itemId, false);
+
}
+
break;
+
}
+
}
+
+
// Item Codes
+
const _itemCodes = items.codes as Record<string, string | Array<string>>;
+
const item = _itemCodes[code];
+
+
if (typeof item == "string") {
+
addItem(item, true);
+
} else if (typeof item == "object") {
+
for (const _ of item) {
+
addItem(_, true);
+
}
+
}
+
+
// Event Codes (eg. Christmas 2019)
+
const _eventItemCodes = items.eventCodes as Record<
+
string,
+
Record<string, string>
+
>;
+
const eventItem = (_eventItemCodes[ctx.localPlayer._partyId] || {})[code];
+
if (eventItem) addItem(eventItem);
+
});
+
+
socket.on("addIgnore", (playerId: string) => {
+
if (!ctx.localPlayer || !ctx.localCrumb) return;
+
+
if (
+
z.object({
+
playerId: z.enum(Object.keys(world.players) as any),
+
}).strict().safeParse({ playerId: playerId }).success == false
+
) return;
+
+
if (
+
Object.keys(world.players).includes(playerId) &&
+
!ctx.localPlayer.ignore.includes(playerId)
+
) {
+
ctx.localPlayer.ignore.push(playerId);
+
}
+
});
+
+
socket.on("attack", (playerId: string) => {
+
if (!ctx.localPlayer || !ctx.localCrumb) return;
+
+
if (
+
z.object({
+
playerId: z.enum(Object.keys(world.players) as any),
+
}).strict().safeParse({ playerId: playerId }).success == false
+
) return;
+
+
if (!ctx.localPlayer.gear.includes("bb_beebee")) return;
+
const monster = Object.values(world.players).find((player) =>
+
player.i == playerId && player.c == "huggable"
+
);
+
+
if (monster) {
+
io.in(ctx.localCrumb._roomId).emit("R", monster);
+
+
ctx.localPlayer.coins += 10;
+
socket.emit("updateCoins", { balance: ctx.localPlayer.coins });
+
+
delete world.players[playerId];
+
}
+
});
+
}
+116
src/socket/handlers/world.ts
···
+
import { Server, Socket } from "https://deno.land/x/socket_io@0.2.0/mod.ts";
+
import z from "zod";
+
+
import * as world from "@/constants/world.ts";
+
import * as utils from "@/utils.ts";
+
import * as types from "@/types.ts";
+
+
import parties from "@/constants/parties.json" with { type: "json" };
+
+
export function listen(
+
_io: Server,
+
socket: Socket,
+
ctx: types.SocketHandlerContext,
+
) {
+
socket.on("joinRoom", (roomId: string) => {
+
if (!ctx.localPlayer || !ctx.localCrumb) return;
+
+
if (
+
z.object({
+
roomId: z.enum(Object.keys(world.rooms) as [string, ...string[]]),
+
}).safeParse({ roomId: roomId }).success == false
+
) return;
+
+
const _room = (world.rooms[roomId] || { default: null }).default;
+
if (!_room) return;
+
+
socket.leave(ctx.localCrumb._roomId);
+
socket.broadcast.in(ctx.localCrumb._roomId).emit("R", ctx.localCrumb);
+
+
const modEnabled = (ctx.localPlayer._mods || []).includes("roomExits");
+
//@ts-ignore: Index type is correct
+
const correctExit = world.roomExits[ctx.localCrumb._roomId + "->" + roomId];
+
if (modEnabled && correctExit) {
+
ctx.localPlayer.x = correctExit.x;
+
ctx.localPlayer.y = correctExit.y;
+
ctx.localPlayer.rotation = correctExit.r;
+
}
+
+
if (!modEnabled || !correctExit) {
+
ctx.localPlayer.x = _room.startX;
+
ctx.localPlayer.y = _room.startY;
+
ctx.localPlayer.rotation = _room.startR | 180;
+
}
+
+
ctx.localCrumb = utils.makeCrumb(ctx.localPlayer, roomId);
+
world.players[ctx.localPlayer.playerId] = ctx.localCrumb;
+
+
console.log("> " + ctx.localPlayer.nickname + ' joined "' + roomId + '"!');
+
socket.join(roomId);
+
+
let playerCrumbs = Object.values(world.players).filter((crumb) =>
+
crumb._roomId == roomId
+
);
+
if (world.npcs[roomId]) {
+
playerCrumbs = [
+
...playerCrumbs,
+
...world.npcs[roomId],
+
];
+
}
+
socket.emit("joinRoom", {
+
name: _room.name,
+
roomId: roomId,
+
playerCrumbs: playerCrumbs,
+
});
+
+
socket.broadcast.in(ctx.localCrumb._roomId).emit("A", ctx.localCrumb);
+
});
+
+
socket.on("switchParty", (partyId: string) => {
+
if (!ctx.localPlayer || !ctx.localCrumb) return;
+
+
if (
+
z.object({
+
partyId: z.enum(Object.keys(parties) as [string, ...string[]]),
+
}).strict().safeParse({ partyId: partyId }).success == false
+
) return;
+
+
ctx.localPlayer._partyId = partyId;
+
socket.emit("switchParty");
+
});
+
+
socket.on("trigger", async () => {
+
if (!ctx.localPlayer || !ctx.localCrumb) return;
+
+
const activatedTrigger = await utils.getTrigger(
+
ctx.localPlayer,
+
ctx.localCrumb._roomId,
+
ctx.localPlayer._partyId,
+
);
+
if (!activatedTrigger) return;
+
+
if (activatedTrigger.hasItems) {
+
for (const item of activatedTrigger.hasItems) {
+
if (!ctx.localPlayer.inventory.includes(item)) return;
+
}
+
}
+
+
if (activatedTrigger.grantItem) {
+
let items = activatedTrigger.grantItem;
+
if (typeof items == "string") items = [items];
+
+
for (const item of items) {
+
if (!ctx.localPlayer.inventory.includes(item)) {
+
socket.emit("addItem", { itemId: item, showGUI: true });
+
ctx.localPlayer.inventory.push(item);
+
}
+
}
+
}
+
+
if (activatedTrigger.addEgg) {
+
const egg = activatedTrigger.addEgg;
+
socket.emit("addEgg", egg);
+
ctx.localPlayer.eggs.push(egg);
+
}
+
});
+
}
+22
src/socket/index.ts
···
+
import { Server } from "https://deno.land/x/socket_io@0.2.0/mod.ts";
+
+
import * as connection from "./handlers/connection.ts";
+
import * as player from "./handlers/player.ts";
+
import * as world from "./handlers/world.ts";
+
import * as social from "./handlers/social.ts";
+
import * as economy from "./handlers/economy.ts";
+
+
export const io = new Server();
+
+
io.on("connection", (socket) => {
+
const context = {
+
localPlayer: null,
+
localCrumb: null,
+
};
+
+
connection.listen(io, socket, context);
+
player.listen(io, socket, context);
+
world.listen(io, socket, context);
+
social.listen(io, socket, context);
+
economy.listen(io, socket, context);
+
});
+109
src/types.ts
···
+
export type CritterId =
+
| "hamster"
+
| "snail"
+
| "lizard"
+
| "beaver"
+
| "raccoon"
+
| "penguin"
+
| "huggable";
+
+
export type Trigger = {
+
hex: string;
+
world?: { joinRoom: string };
+
room?: { hide: Array<string> };
+
server?: {
+
grantItem?: string | Array<string>;
+
hasItems?: Array<string>;
+
joinGame?: string;
+
addEgg?: string;
+
};
+
};
+
+
export type Room = {
+
roomId: string;
+
name: string;
+
width: number;
+
height: number;
+
startX: number;
+
startY: number;
+
startR: number;
+
media: {
+
background: string;
+
foreground?: string;
+
treasure?: string;
+
navMesh: string;
+
music?: string;
+
video?: string;
+
};
+
layout: string;
+
triggers: Array<Trigger>;
+
spriteSheet: string;
+
extra: null;
+
partyExclusive?: Array<string>;
+
};
+
+
export type LocalPlayer = {
+
playerId: string;
+
nickname: string;
+
critterId: CritterId;
+
ignore: Array<string>;
+
friends: Array<string>;
+
inventory: Array<string>;
+
gear: Array<string>;
+
/** Eggs is the term used to describe any object used in a scavenger hunt. Any prop name found in that list will be hidden and replaced with it's "_found" suffix prop counterpart */
+
eggs: Array<string>;
+
coins: number;
+
isMember: boolean | false;
+
isGuest: boolean | false;
+
isTeam: boolean | false;
+
x: number | 440;
+
y: number | 210;
+
rotation: number | 180;
+
mutes: Array<unknown>;
+
+
_partyId: string;
+
_mods: Array<string>;
+
+
// deno-lint-ignore no-explicit-any
+
[key: string]: any;
+
};
+
+
export type PlayerCrumb = {
+
/** Player ID */
+
i: string;
+
/** Player Nickname */
+
n: string;
+
/** Critter (Hamster, Beaver, Lizard, Snail, etc) */
+
c: CritterId;
+
x: number;
+
y: number;
+
r: number;
+
/** Gear (equipped items) */
+
g: Array<string>;
+
+
/** Message */
+
m: string;
+
/** Emote */
+
e: string;
+
+
_roomId: string;
+
};
+
+
export type ShopData = {
+
lastItem: { itemId: string; cost: number };
+
freeItem: { itemId: string; cost: number };
+
nextItem: { itemId: string; cost: number };
+
collection: Array<{ itemId: string; cost: number }>;
+
};
+
+
export type PartySchedule = {
+
[key: string]: {
+
start: string | null;
+
end: string | null;
+
};
+
};
+
+
export type SocketHandlerContext = {
+
localPlayer: null | LocalPlayer;
+
localCrumb: null | PlayerCrumb;
+
};
+266
src/utils.ts
···
+
import { Image } from "https://deno.land/x/imagescript@1.3.0/mod.ts";
+
import {
+
dirname,
+
fromFileUrl,
+
join,
+
} from "https://deno.land/std@0.224.0/path/mod.ts";
+
import { jwtVerify, SignJWT } from "npm:jose@5.9.6";
+
+
import { rooms, spawnRoom } from "@/constants/world.ts";
+
import { LocalPlayer, PlayerCrumb, Room } from "@/types.ts";
+
import parties from "@/constants/parties.json" with { type: "json" };
+
+
const EXECUTABLE = Deno.env.get("EXECUTABLE") == "true";
+
const BASE_DIR = EXECUTABLE
+
? dirname(Deno.execPath())
+
: dirname(dirname(fromFileUrl(Deno.mainModule)));
+
const PUBLIC_DIR = join(BASE_DIR, "public");
+
+
// deno-lint-ignore no-explicit-any
+
export async function createJWT(payload: any): Promise<string> {
+
const jwt = await new SignJWT(payload)
+
.setProtectedHeader({ alg: "HS256" })
+
.setIssuedAt()
+
.setExpirationTime("1h")
+
.sign(new TextEncoder().encode(Deno.env.get("JWT_TOKEN")));
+
+
return jwt;
+
}
+
+
// deno-lint-ignore no-explicit-any
+
export async function verifyJWT(token: string): Promise<any | null> {
+
try {
+
const { payload } = await jwtVerify(
+
token,
+
new TextEncoder().encode(Deno.env.get("JWT_TOKEN")),
+
);
+
return payload;
+
} catch (_e) {
+
return null;
+
}
+
}
+
+
/** Condenses the local player variable into data that is sufficient enough for other clients */
+
export function makeCrumb(player: LocalPlayer, roomId: string): PlayerCrumb {
+
return {
+
i: player.playerId,
+
n: player.nickname,
+
c: player.critterId,
+
x: player.x,
+
y: player.y,
+
r: player.rotation,
+
g: player.gear,
+
+
// message & emote
+
m: "",
+
e: "",
+
+
_roomId: roomId,
+
};
+
}
+
+
// TODO: use the correct triggers for the active party
+
export async function getTrigger(
+
player: LocalPlayer,
+
roomId: string,
+
partyId: string,
+
) {
+
const room = rooms[roomId][partyId];
+
if (!room) {
+
console.log(`[!] Cannot find room: "${roomId}@${partyId}"!`);
+
return;
+
}
+
+
try {
+
const treasureBuffer = await Deno.readFile(
+
//@ts-ignore: Deno lint
+
room.media.treasure?.replace("..", "public"),
+
);
+
const treasure = await Image.decode(treasureBuffer);
+
if (!treasure) {
+
throw new Error('Missing map server for room "' + roomId + '"!');
+
}
+
+
const pixel = treasure.getPixelAt(player.x, player.y);
+
const r = (pixel >> 24) & 0xFF,
+
g = (pixel >> 16) & 0xFF,
+
b = (pixel >> 8) & 0xFF;
+
const hexCode = ((r << 16) | (g << 8) | b).toString(16).padStart(6, "0");
+
+
const trigger = room.triggers.find((trigger) => trigger.hex == hexCode);
+
if (trigger) {
+
return trigger.server;
+
} else {
+
return null;
+
}
+
} catch (e) {
+
console.warn("[!] Caught error while checking for activated trigger.", e);
+
}
+
}
+
+
export function getNewCodeItem(player: LocalPlayer, items: Array<string>) {
+
const itemsSet = new Set(player.inventory);
+
const available = items.filter((item) => !itemsSet.has(item));
+
return available.length === 0
+
? null
+
: available[Math.floor(Math.random() * available.length)];
+
}
+
+
/**
+
* Indexes the /media/rooms directory for all versions of all rooms
+
* @returns All versions of every room
+
*/
+
export async function indexRoomData() {
+
const _roomData: Record<string, Record<string, Room>> = {};
+
+
const basePath = join(PUBLIC_DIR, "media", "rooms");
+
const _rooms = Deno.readDir(basePath);
+
+
for await (const room of _rooms) {
+
if (room.isDirectory) {
+
_roomData[room.name] = {};
+
const roomPath = join(basePath, room.name);
+
const versions = Deno.readDir(roomPath);
+
for await (const version of versions) {
+
if (version.isDirectory) {
+
const versionPath = join(roomPath, version.name, "data.json");
+
try {
+
const data = await Deno.readTextFile(versionPath);
+
_roomData[room.name][version.name] = JSON.parse(data);
+
} catch (_) {
+
console.log(
+
`[!] "${room.name}@${version.name}" is missing a data.json file`,
+
);
+
}
+
}
+
}
+
}
+
}
+
+
return _roomData;
+
}
+
+
export async function getAccount(nickname?: string) {
+
let accounts = [];
+
try {
+
const data = await Deno.readTextFile("accounts.json");
+
accounts = JSON.parse(data);
+
} catch (error) {
+
if (error instanceof Deno.errors.NotFound) {
+
console.log("Persistent login JSON is missing, using blank JSON array..");
+
accounts = [];
+
} else {
+
console.log(
+
"[!] Failure to fetch persistent login data with nickname: ",
+
nickname,
+
);
+
throw error;
+
}
+
}
+
+
if (nickname) {
+
const existingAccount = accounts.find((player: { nickname: string }) =>
+
player.nickname == nickname
+
);
+
if (existingAccount) {
+
return {
+
all: accounts,
+
individual: existingAccount,
+
};
+
} else {
+
return {
+
all: accounts,
+
individual: null,
+
};
+
}
+
} else {
+
return accounts;
+
}
+
}
+
+
export async function updateAccount(
+
nickname: string,
+
property: string,
+
value: unknown,
+
) {
+
if (["x", "y", "rotation", "_partyId", "_mods"].includes(property)) return;
+
const accounts = await getAccount(nickname);
+
+
accounts.individual[property] = value;
+
await Deno.writeTextFile(
+
"accounts.json",
+
JSON.stringify(accounts.all, null, 2),
+
);
+
}
+
+
export function trimAccount(player: LocalPlayer) {
+
for (
+
const key of [
+
"critterId",
+
"x",
+
"y",
+
"rotation",
+
"_partyId",
+
"_mods",
+
]
+
) {
+
delete player[key];
+
}
+
return player;
+
}
+
+
export function expandAccount(player: LocalPlayer) {
+
const defaultPos = rooms[spawnRoom].default;
+
player.x = defaultPos.startX;
+
player.y = defaultPos.startY;
+
player.rotation = defaultPos.startR;
+
return player;
+
}
+
+
export function getDirection(
+
x: number,
+
y: number,
+
targetX: number,
+
targetY: number,
+
) {
+
const a = Math.floor((180 * Math.atan2(targetX - x, y - targetY)) / Math.PI);
+
return a < 0 ? a + 360 : a;
+
}
+
+
export async function createAccount(player: LocalPlayer) {
+
const accounts = await getAccount();
+
accounts.push(trimAccount(player));
+
+
await Deno.writeTextFile("accounts.json", JSON.stringify(accounts, null, 2));
+
}
+
+
export function getCurrentEvent(year: number): string {
+
const today = new Date();
+
const testDate = new Date(year, today.getMonth(), today.getDate());
+
+
//@ts-ignore: Types are bugging out here for absolutely no reason
+
for (const [eventId, { start, end }] of Object.entries(parties)) {
+
if (!start || !end) continue;
+
+
const originalStart = new Date(start);
+
const originalEnd = new Date(end);
+
+
const adjustedStart = new Date(
+
year,
+
originalStart.getMonth(),
+
originalStart.getDate(),
+
);
+
const adjustedEnd = new Date(
+
// Handle roll over
+
originalEnd.getFullYear() > originalStart.getFullYear() ? year + 1 : year,
+
originalEnd.getMonth(),
+
originalEnd.getDate(),
+
);
+
+
if (testDate >= adjustedStart && testDate <= adjustedEnd) {
+
return eventId;
+
}
+
}
+
+
return "default";
+
}
-124
types.ts
···
-
export type CritterId = "hamster" | "snail" | "lizard" | "beaver" | "raccoon" | "penguin" | "huggable";
-
-
export type Trigger = {
-
hex: string,
-
world?: { joinRoom: string },
-
room?: { hide: Array<string> },
-
server?: {
-
grantItem?: string | Array<string>,
-
hasItems?: Array<string>,
-
joinGame?: string,
-
addEgg?: string
-
}
-
}
-
-
export type Room = {
-
roomId: string,
-
name: string,
-
width: number,
-
height: number,
-
startX: number,
-
startY: number,
-
startR: number,
-
media: {
-
background: string,
-
foreground?: string,
-
treasure?: string,
-
navMesh: string,
-
music?: string,
-
video?: string,
-
},
-
layout: string,
-
triggers: Array<Trigger>,
-
spriteSheet: string,
-
extra: null,
-
partyExclusive?: Array<string>
-
}
-
-
export type LocalPlayer = {
-
playerId: string,
-
nickname: string,
-
critterId: CritterId,
-
ignore: Array<string>,
-
friends: Array<string>,
-
inventory: Array<string>,
-
gear: Array<string>,
-
/** Eggs is the term used to describe any object used in a scavenger hunt. Any prop name found in that list will be hidden and replaced with it's "_found" suffix prop counterpart */
-
eggs: Array<string>,
-
coins: number,
-
isMember: boolean | false,
-
isGuest: boolean | false,
-
isTeam: boolean | false,
-
x: number | 440,
-
y: number | 210,
-
rotation: number | 180,
-
mutes: Array<unknown>,
-
-
_partyId: string,
-
_mods: Array<string>,
-
-
// deno-lint-ignore no-explicit-any
-
[key: string]: any
-
}
-
-
export type PlayerCrumb = {
-
/** Player ID */
-
i: string,
-
/** Player Nickname */
-
n: string,
-
/** Critter (Hamster, Beaver, Lizard, Snail, etc) */
-
c: CritterId,
-
x: number,
-
y: number,
-
r: number,
-
/** Gear (equipped items) */
-
g: Array<string>,
-
-
/** Message */
-
m: string,
-
/** Emote */
-
e: string,
-
-
_roomId: string
-
}
-
-
export type ShopData = {
-
lastItem: { itemId: string, cost: number },
-
freeItem: { itemId: string, cost: number },
-
nextItem: { itemId: string, cost: number },
-
collection: Array<{ itemId: string, cost: number }>
-
}
-
-
/*
-
Socket.io
-
*/
-
export interface ServerToClientEvents {
-
login: () => {player: LocalPlayer};
-
updateGear: () => { i: number, g: Array<string> };
-
updateCoins: () => { balance: number };
-
addItem: () => { itemId: string };
-
addEgg: () => string;
-
A: () => PlayerCrumb;
-
R: () => PlayerCrumb;
-
X: () => { i: number, x: number, y: number };
-
M: () => { i: number, m: string };
-
E: () => { i: number, e: string };
-
G: () => { i: number, g: Array<string> };
-
}
-
-
export interface ClientToServerEvents {
-
login: (ticket: string) => void;
-
joinLobby: () => unknown; // Unsure what this is for, I don't think the game had several servers
-
joinRoom: (roomId: string) => { name: string, roomId: string, playerCrumbs: Array<PlayerCrumb> };
-
message: (text: string) => void;
-
emote: (emote: string) => void;
-
code: (code: string, options?: string) => void;
-
addIgnore: (id: number) => void;
-
addFriend: (id: number) => void;
-
moveTo: (x: number, y: number) => void;
-
updateCritter: () => unknown; // Unsure of what this is, maybe settings?
-
updateGear: (gear: Array<string>) => void;
-
getShop: () => ShopData;
-
buyItem: (itemId: string) => void;
-
trigger: () => void;
-
}
-167
utils.ts
···
-
import { Image } from 'https://deno.land/x/imagescript@1.3.0/mod.ts';
-
import chalk from "https://deno.land/x/chalk_deno@v4.1.1-deno/source/index.js"
-
import { join } from "https://deno.land/std@0.224.0/path/mod.ts";
-
-
import { rooms, spawnRoom } from "./constants/world.ts";
-
import { Room, LocalPlayer, PlayerCrumb } from "./types.ts";
-
-
/** Condenses the local player variable into data that is sufficient enough for other clients */
-
export function makeCrumb(player: LocalPlayer, roomId: string): PlayerCrumb {
-
return {
-
i: player.playerId,
-
n: player.nickname,
-
c: player.critterId,
-
x: player.x,
-
y: player.y,
-
r: player.rotation,
-
g: player.gear,
-
-
// message & emote
-
m: "",
-
e: "",
-
-
_roomId: roomId
-
}
-
}
-
-
// TODO: use the correct triggers for the active party
-
export async function getTrigger(player: LocalPlayer, roomId: string, partyId: string) {
-
const room = rooms[roomId][partyId];
-
if (!room) {
-
console.log(chalk.red(`[!] Cannot find room: "${roomId}@${partyId}"!`));
-
return;
-
}
-
-
try {
-
//@ts-ignore: Deno lint
-
const treasureBuffer = await Deno.readFile(room.media.treasure?.replace('..','public'));
-
const treasure = await Image.decode(treasureBuffer);
-
if (!treasure) throw new Error('Missing map server for room "' + roomId + '"!');
-
-
const pixel = treasure.getPixelAt(player.x, player.y);
-
const r = (pixel >> 24) & 0xFF, g = (pixel >> 16) & 0xFF, b = (pixel >> 8) & 0xFF;
-
const hexCode = ((r << 16) | (g << 8) | b).toString(16).padStart(6, '0');
-
-
const trigger = room.triggers.find((trigger) => trigger.hex == hexCode);
-
if (trigger) {
-
return trigger.server;
-
} else {
-
return null;
-
}
-
} catch(e) {
-
console.warn(chalk.red('[!] Caught error while checking for activated trigger.'), e);
-
}
-
}
-
-
export function getNewCodeItem(player: LocalPlayer, items: Array<string>) {
-
const itemsSet = new Set(player.inventory);
-
const available = items.filter(item => !itemsSet.has(item));
-
return available.length === 0 ? null : available[Math.floor(Math.random() * available.length)];
-
}
-
-
/**
-
* Indexes the /media/rooms directory for all versions of all rooms
-
* @returns All versions of every room
-
*/
-
export async function indexRoomData() {
-
const _roomData: Record<string, Record<string, Room>> = {};
-
-
const basePath = join(Deno.cwd(), 'public', 'media', 'rooms');
-
const _rooms = Deno.readDir(basePath);
-
-
for await (const room of _rooms) {
-
if (room.isDirectory) {
-
_roomData[room.name] = {};
-
const roomPath = join(basePath, room.name);
-
const versions = Deno.readDir(roomPath);
-
for await (const version of versions) {
-
if (version.isDirectory) {
-
const versionPath = join(roomPath, version.name, 'data.json');
-
try {
-
const data = await Deno.readTextFile(versionPath);
-
_roomData[room.name][version.name] = JSON.parse(data);
-
} catch(_) {
-
console.log(chalk.red('[!] "%s@%s" is missing a data.json file'), room.name, version.name);
-
};
-
}
-
}
-
}
-
}
-
-
return _roomData
-
}
-
-
export async function getAccount(nickname?: string) {
-
let accounts = [];
-
try {
-
const data = await Deno.readTextFile('accounts.json');
-
accounts = JSON.parse(data);
-
} catch (error) {
-
if (error instanceof Deno.errors.NotFound) {
-
console.log(chalk.gray('Persistent login JSON is missing, using blank JSON array..'));
-
accounts = [];
-
} else {
-
console.log(chalk.red('[!] Failure to fetch persistent login data with nickname: '), nickname);
-
throw error;
-
};
-
}
-
-
if (nickname) {
-
const existingAccount = accounts.find((player: { nickname: string }) => player.nickname == nickname);
-
if (existingAccount) {
-
return {
-
all: accounts,
-
individual: existingAccount,
-
}
-
} else {
-
return {
-
all: accounts,
-
individual: null
-
}
-
}
-
} else {
-
return accounts;
-
}
-
}
-
-
export async function updateAccount(nickname: string, property: string, value: unknown) {
-
if (["x", "y", "rotation", "_partyId"].includes(property)) return;
-
const accounts = await getAccount(nickname);
-
-
accounts.individual[property] = value;
-
await Deno.writeTextFile('accounts.json', JSON.stringify(accounts.all, null, 2));
-
}
-
-
export function trimAccount(player: LocalPlayer) {
-
for (const key of [
-
"critterId",
-
"x",
-
"y",
-
"rotation",
-
"_partyId",
-
"_mods"
-
]) {
-
delete player[key];
-
}
-
return player;
-
}
-
-
export function expandAccount(player: LocalPlayer) {
-
const defaultPos = rooms[spawnRoom].default;
-
player.x = defaultPos.startX;
-
player.y = defaultPos.startY;
-
player.rotation = defaultPos.startR;
-
return player;
-
}
-
-
export function getDirection(x: number, y: number, targetX: number, targetY: number) {
-
const a = Math.floor((180 * Math.atan2(targetX - x, y - targetY)) / Math.PI);
-
return a < 0 ? a + 360 : a;
-
}
-
-
export async function createAccount(player: LocalPlayer) {
-
const accounts = await getAccount();
-
accounts.push(trimAccount(player));
-
-
await Deno.writeTextFile('accounts.json', JSON.stringify(accounts, null, 2));
-
}