Add a Map

In short

A map is a BallparkDefinition built with Park(...) in DefaultContent and added to db.ballparks. Its place in that list is its unlock order and sets how high its goals are: every goal is x1.15 per step along the list. Practice-only parks go in db.practiceParks instead.

Steps

  1. Create the park in Assets/_Project/Scripts/Data/DefaultContent.cs, block // ---------- Ballparks ----------. Park(id, name, description, leftFieldFt, centerFieldFt, rightFieldFt, wallHeightFt, grassColor, wallColor):
    var windy = Park("windy", "Windy City Yard", "The wind is blowing out to left.", 330f, 400f, 340f, 12f,
        new Color(0.3f, 0.66f, 0.26f), new Color(0.14f, 0.42f, 0.24f));
  2. Add targets (score zones past the wall). A multiplier zone uses Target(label, sprayDeg, distanceFt, radiusFt, multiplier, color); a flat-points bonus location outside the stadium uses Bonus(...), optionally up in the air:
    windy.targets.Add(Target("Rooftop", -30f, 430f, 35f, 2f, Color.yellow));
    moon.targets.Add(Bonus("Space Station", 6f, 500f, 38f, 150, new Color(0.5f, 0.9f, 1f), heightM: 32f));
    Spray: -45 = left field line, 0 = center, 45 = right field line. See Scoring.
  3. Set the modifiers and look as fields on the object: windMph, windDirectionDeg, gravityScale, isNight, batterModifiers, sky (Add a Sky), ground (GroundStyle), wallStyle, seatColor, crowdDensity, stadium = false for an open field, dressing (a Resources prefab path). New kinds of modifier: Add a Ballpark Modifier.
  4. Give it a special ball (optional): windy.balls.Add(Ball(BallEffect.SuperFast, 1f / 3f, "fastball", "slider")); A ball with a pitch list also makes every pitcher here lean on those pitches (about 70% of pitches). See Add a Special Ball.
  5. Give it its own final boss: create a boss and set finalBossId (windy.finalBossId = "gale";). Every run map has one since #41; without one, inning 9 is drawn from the roaming boss pool, which is only The Ace and Knuckle Sandwich, both already used in innings 3 and 6, so inning 9 repeats one of them. See Add a Final Boss.
  6. Put it in the unlock order. Add it to the db.ballparks.AddRange(new[] { sandlot, farm, windy, midnight, moon }); line at the position where it should unlock. A batting-cage-only park goes in db.practiceParks.Add(...) instead (never a run map, never unlocked, not in the co-op lobby).
  7. Props (optional). An open field can be decorated by a code layout in Ballparks/ParkLayouts.cs (today only farm and moon have one, keyed by park id in ParkLayouts.Has) and baked into a prefab with Tools → Home Run Derby → Bake Park Dressing. See Ballpark Building.
  8. Nothing else to register. These read the park list or the definition on their own:
    • the map select carousel (UI/MapSelectView) and its card picture, which UI/ParkSnapshots builds and photographs once per session for every park in db.ballparks (#39, #45);
    • the match intro (#40): the stadium, tour and map shots use the walls and targets, and the card lists the park’s conditions, special balls and targets with what each pays (Cutscenes);
    • the main menu INDEX’s BALLPARKS tab (Index Screen);
    • the batting cage park list (Batting Cage).

Numbers

Goal of an inning (RunState.InningGoal), rounded to the nearest 25, at least 25:

inningGoal x inningGoalGrowth^inning x (bossGoalMultiplier in a boss inning) x mapGoalGrowth^order x difficulty multiplier

WhatValueWhere to tune
First inning goal, first map, Normal500RunConfig.inningGoal
Growth per map along the listx1.15RunConfig.mapGoalGrowth
Growth per inningx1.25RunConfig.inningGoalGrowth
Boss inningx1.25RunConfig.bossGoalMultiplier
Medium / Hard goalsx1.4 / x1.9Difficulties.GoalMultiplier

Unlock rule

The first map is always open. Beating a map on Normal opens its Medium; beating Medium opens its Hard and the next map in the list (MetaProgression.IsMapUnlocked, RecordMap). See Map Select and Unlocks.

Check it works

  • Tools → Home Run Derby → Run Run Structure Test: checks innings, goals and the unlock order, and that the dev insta-home-run bat clears every park’s wall (including practice parks).
  • Tools → Home Run Derby → Run Scoreboard Smash Test: checks that an ordinary home run never reaches the scoreboard in a run park, and logs how hard you must hit it in each park.
  • Tools → Home Run Derby → Run Ball Effect Test: EveryParkHasItsOwnBoss uses a fixed park → boss table; add the new pair to it.
  • Press Play, start a run on the new park, then Tools → Home Run Derby → Preview Match Intro (or I on the run map, Editor only) replays the intro on that inning to check its shots.
  • Press Play: the map should appear on the map select screen. F1 → MAPS can unlock every map to try it. The batting cage lists every park too (Batting Cage).

Gotchas

  • Inserting a map in the middle changes the goals of every map after it and moves who unlocks whom. Players who had beaten the map before the new one on Medium will find the new map open, and the map that used to follow is locked again for them until they beat the new one on Medium (unlocks are worked out from the list each time, not saved per map).
  • db.ballparks[0] is also the backdrop of the title, lobby and Customize screens (GameRoot.GoToMainMenu).
  • The daily challenge picks parks[Derive(today, 0xDA11) % parks.Count], so adding a map changes which map the daily is on (and builds with different map lists disagree on the daily).
  • Saves store the park id (MapProgress.parkId, RunSaveData.parkId). Never rename an id.
  • Co-op: the host sends the park id in StartRun; every player needs the same content. Since the build version is always 1.0, only NetSession.Protocol catches mismatched builds (see Protocol Versions).
  • Tools → Home Run Derby → Export Default Content To Assets does not export practiceParks.