Add a Batter

In short

A batter is a BatterDefinition: stat modifiers, a batting style (trajectory, hitDirection, curve, since #46), a starting kit of upgrades, a model, a meta-point price, an account level needed to buy them (requiredLevel) and trait perks earned with their own character level (levelPerks, at levels 2, 5 and 10). Add it in the Batters block of DefaultContent.

Steps

  1. Create the batter in Assets/_Project/Scripts/Data/DefaultContent.cs, block // ---------- Batters ----------. Batter(id, name, description, unlocked, cost, color, accent, shape, model, startingUpgrades, params modifiers), usually wrapped in RiggedBean(batter, modelPath, texturePath, eyeHeight, eyeSpacing):
    db.batters.Add(RiggedBean(Batter("slugger", "Baked Bean", "Heavy hitter. High power, slow but strong.", false, 40,
        new Color(0.98f, 0.72f, 0.26f), new Color(0.85f, 0.2f, 0.2f), BeanShape.Baked, BatterModel.Human, new string[0],
        StatModifier.Percent(StatType.Power, 0.2f), StatModifier.Percent(StatType.TimingWindow, -0.15f)),
        "Characters/BakedBean", "Characters/baked_bean_albedo", 0.86f, 0.26f));
    • unlocked true = free from the start (only Peapod today); otherwise cost is the meta-point price.
    • startingUpgrades are upgrade ids given at the start of every run (Peapod: maple_bat).
    • Trade-offs read well: a strength plus a weakness, like Baked Bean above.
  2. Pick the model.
    • BatterModel.Human with a rigged model: RiggedBean sets riggedModel / riggedTexture (Resources paths) and the eye line used to fit glasses. The model gets a Humanoid avatar at runtime and plays the same clips (Batting/HumanBatter.SetBatter). If the path fails to load, the library batter is used and a warning logged. The texture may be null for a model with flat colours (String Bean since #44: RiggedBean(..., "Characters/StringBean", null, 1.25f, 0.109f)).
    • Bone names (Batting/BeanRig): either the bean rig’s (hips, spine, chest, head, upper_arm.L, forearm.L, hand.L, thigh.L, shin.L, foot.L and the .R side) or game-engine style (pelvis, spine_01, spine_02, spine_03, clavicle_l/r, upperarm_l, lowerarm_l, hand_l, thigh_l, calf_l, foot_l…). Chest, upper chest and shoulders are optional; any other missing bone logs a warning and the model can’t be animated. The hat socket is hat_socket or SOCKET_Head (else the head bone).
    • BatterModel.Human without riggedModel: the CharacterLibrary batter prefab.
    • BatterModel.Bean: the code-built bean (Batting/BeanBatter) in the given BeanShape. CharacterLibrary.useBeans forces the code-built bean for everyone. See Characters and Rigs.
  3. Account level to buy them (#37). After the batters are added:
    db.GetBatter("slugger").requiredLevel = 2;
    0 or 1 = any level. Until the account reaches it, the main menu card says LOCKED - ACCOUNT LV 2 and the button REACH LV 2; then it becomes UNLOCK with the points gem and the price, 40 (MetaProgression.CanBuy / TryBuy). Owning a batter always counts, whatever the level. Today: Baked Bean 2, String Bean 4, Pinto 6. See XP and Levels.
  4. Character-level perks (#37). One small boost to the batter’s own trait at each of levels 2, 5 and 10. The Perks helper adds the same modifier at all three:
    Perks(db.GetBatter("slugger"), StatModifier.Percent(StatType.Power, 0.01f));
    or add them one by one, as Pinto does (coins first, a whole extra out at level 10):
    pinto.levelPerks.Add(Perk(PerkLevels[0], StatModifier.Add(StatType.CoinBonus, 1f)));
  5. Batting style (#46). Next to the other Style(...) lines:
    Style(db.GetBatter("slugger"), HitTrajectory.High, HitDirection.Pull, 1f);
    • HitTrajectory Low / Mid / High shifts every launch-angle range down or up 4°.
    • HitDirection Pull / Mid / Push: up to x1.05 exit velocity toward their side, down to x0.85 at the far line.
    • curve scales how much hits hook or slice: 1 normal, about 1.6 a super-curve (String Bean). Unset, a batter is Mid / Mid / 1. The PLAY screen prints it under the description, e.g. “HIGH TRAJECTORY - PULL HITTER”, adding “SUPER CURVE” from a curve of 1.3 (MainMenuView.StyleLabel). See Batters and Batting Explained.

Nothing else to register: the INDEX’s BATTERS tab lists them from the content (“Starter”, “Account level n” or “Unlockable”), and the match intro’s player card shows their name, bat and stats. See Index Screen.

Batter XP (Progression/Leveling.cs)

  • Every finished run gives the same XP to the account and to the batter who played it (MetaProgression.AwardXp): (innings cleared x 25 + home runs x 5 + 150 for a win) x the difficulty's meta-point multiplier (1 / 1.5 / 2.5 for Normal / Medium / Hard), rounded.
  • Only XP is saved (SaveData.batterProgress, one BatterProgress { batterId, xp } per batter); levels are worked out from the curve, so retuning the curve never breaks a save.
  • Perks apply from the next run: RunState takes MetaProgression.LevelBonus(batter) (every perk at or below their level) when the run starts, and keeps it fixed for the run. The batting cage uses them too.

Numbers

WhatValueWhere to tune
Character level XP (total)L2 300, L3 650, L4 1050, L5 1500, L6 2000, L7 2550, L8 3150, L9 3800, L10 4500Leveling.CharacterXp
Max character level10Leveling.MaxCharacterLevel
Perk levels2, 5, 10DefaultContent.PerkLevels
XP per inning cleared / home run / win25 / 5 / 150Leveling.XpPerInning, XpPerHomeRun, XpForWin
Account step from level L to L+1100 + 75 x (L - 1)Leveling.FirstStep, StepGrowth

Check it works

  • Tools → Home Run Derby → Run Progression XP Test: curves, run XP, that every batter’s perks add up at levels 2 / 5 / 10, that runs include perks, level-gated buying, and old saves.
  • Press Play: the batter appears on the title screen’s batter strip. F1 → BATTER LEVELS sets their level (LEVEL UP, MAX, RESET), the account level can be raised the same way, and BATTERS, SHOP ITEMS, COSMETICS unlocks everyone (Dev Menu and Shortcuts).

Gotchas

  • Exactly one perk at each of 2, 5 and 10. ProgressionXpTest.PerksAddUp checks that every batter has 0, 1, 2 and 3 perks at levels 1, 2, 5 and 10. A batter with no perks, or two at one level, fails it.
  • One id namespace. Batter ids share SaveData.unlockedIds with upgrades and cosmetics. Saves also store selectedBatterId, batterProgress and the saved run’s batterId, so never rename an id.
  • The results screen announces “NEW BATTER TO BUY: NAME” when an account level-up reaches their requiredLevel.
  • Co-op: a player’s batter id travels in the roster; a peer that doesn’t know it shows batters[0] instead (CoopRun.BatterOf).
  • A new body shape for the code-built bean needs a BeanShape value and its build in Batting/BeanBatter.
  • Everyone bats right-handed: HitDirection.Pull always favours left field (spray -45°).