Add a Cosmetic

In short

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

  1. 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_).
  2. 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.
  3. 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).
  4. 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.
  5. 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.
  6. 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.