#pragma once // Player look: base body, skin tone, one item per slot and a color per slot. // The layer art and the list of valid items come from // assets/characters/manifest.json (written by tools/character_art.py). #include #include #include #include #include namespace bs { enum Slot : int { Shoes = 0, Bottom, Top, Hair, Hat, kSlotCount }; const char* slotName(int slot); // manifest slot keys: shoes bottom top hair hat int slotFromName(const std::string& s); // -1 if unknown struct Appearance { std::string body = "male"; uint8_t skin = 0; // index into manifest skinTones std::array items; // "" = nothing in that slot std::array colors{}; // 0xRRGGBB per slot }; struct ItemDef { std::string id, label; int slot = 0; uint32_t color = 0; // default color bool fromBody = false; // carved out of the base body (tee, shorts) }; struct BodyDef { std::string id, label; int anchorRow = 63; // lowest opaque row of a 64 px frame std::vector items; }; struct NpcArt { std::string sheet, portrait; int anchorRow = 63; }; // assets/characters/manifest.json struct CharacterManifest { int cell = 64, framesPerDirection = 5, walkFrames = 4, shades = 4; std::vector directions; // sheet rows: south, east, north (west = east mirrored) std::array slotZ{}; std::vector skinTones; std::vector palette; std::map bodies; std::map items; std::map npcs; bool load(const std::string& path, std::string* error = nullptr); const ItemDef* item(const std::string& id) const; bool bodyHas(const std::string& body, const std::string& item) const; Appearance defaultLook(const std::string& body = "male") const; // Fixes anything invalid (unknown body/item, wrong slot, missing top/bottom, skin out of // range). Returns false if something had to be changed. bool sanitize(Appearance& a) const; }; uint32_t parseHexColor(const std::string& s, uint32_t fallback = 0xffffff); std::string hexColor(uint32_t rgb); } // namespace bs