A cosmetic is a CosmeticDefinition: looks only, no stats, bought with meta points on the CUSTOMIZE screen and
gated by account level. Add a Cosmetic(...) line in DefaultContent. A new shape also needs a CosmeticStyle
value and a case in CosmeticBuilder, or a real prefab. A bat needs a model entry in BatModelLibrary.
Steps
Add the line in Assets/_Project/Scripts/Data/DefaultContent.cs, block // ---------- Cosmetics.
Cosmetic(id, name, slot, style, description, unlockedByDefault, cost, primaryColor, secondaryColor):
db.cosmetics.Add(Cosmetic("hat_cowboy", "Cowboy Hat", CosmeticSlot.Hat, CosmeticStyle.CowboyHat, "Yeehaw.", false, 45, new Color(0.72f, 0.5f, 0.25f), new Color(0.35f, 0.2f, 0.1f)));
Prefix the id with its slot (hat_, glasses_, shoes_, bat_).
Gate it by account level (paid items only) in the RequireLevel(...) lines below the cosmetics:
RequireLevel(db, 6, "hat_cowboy");
The item card then shows LV 6 and the button REACH LV 6 until the account level is high enough
(MetaProgression.CanBuy); after that the tile shows the points gem and the price, and the button reads BUY.
See XP and Levels.
A new code-built look (hat, glasses or shoes):
add a value at the end of enum CosmeticStyle in Data/CosmeticDefinition.cs (after Model; see Gotchas),
add a case CosmeticStyle.X: in the right builder in Batting/CosmeticBuilder.cs (BuildHat, BuildGlasses
or BuildShoe), built from Visuals.Primitive shapes sized as fractions of the head diameter or foot length.
Unknown styles fall to each builder’s default (Top Hat, Sunglasses, Sneakers).
Or a real model: set prefab on the definition. Author it at the contact point (hat: underside of the band,
glasses: bridge, shoe: heel at the ankle), +Y up, +Z forward, in metres, for a head/foot of referenceSize
(default 0.3). The socket supplies position and scale; the code-built shapes are skipped.
A bat (CosmeticSlot.Bat, CosmeticStyle.Model): add an entry to Resources/BatModelLibrary.asset whose
upgradeId is the cosmetic’s id (for example bat_03) and whose prefab is the model. The bat cosmetic overrides
the look of the shop bat; the shop bat keeps its stats.
Fit it. Nudge position, rotation and size per item with Fit Mode (CUSTOMIZE → FIT, or Ctrl+Shift+F), which
writes the item’s offset / rotation / scale (code-built items go to FitOverrides.json). See Fit Mode.
Check it works
Tools → Home Run Derby → Run Progression XP Test: checks level gates and that every free cosmetic needs no
level.
Press Play, open CUSTOMIZE, pick the tab (HATS, GLASSES, SHOES, BATS), buy and equip it.
F1 → unlock every item and set the account level to skip the grind (Dev Menu and Shortcuts).
Gotchas
Free items must have no level. The XP test fails if an unlockedByDefault item has requiredLevel above 1.
One id namespace. Cosmetic ids share SaveData.unlockedIds with batters and upgrades; keep them unique.
The worn item per slot is saved by id in SaveData.cosmetics; an id that disappears just shows nothing.
Append enum values: style is stored as a number in exported assets.
Co-op: each player’s worn ids travel in the roster (PlayerInfo.cosmeticIds) and others look them up in their own
content, skipping unknown ids. A friend on an older build simply won’t see the new item.
Cosmetics follow the batter’s sockets. A new character model needs a fit profile first
(Tools → Home Run Derby → Generate Fit Profile For Batter).
The tile picture (UI/ThumbnailRenderer) is rendered the frame after it is first asked for (#45), so a new item’s
tile is blank for one frame. A cosmetic with nothing to render gets no picture and is not retried.
Cosmetics are not in the main menu INDEX; nothing to add there.