Skip to content

02 6 Quick Start Bump N Jump

evets17 edited this page Mar 24, 2026 · 3 revisions

Quick Start: Bump N Jump

This page explains the game-level setup used for Bump N Jump.

Config file used

  • {usb_root}\boosted\Bump N Jump_boost.toml

What this example demonstrates

This example combines several Sprint Boost systems in one game:

  • 2-player game_info tracking (active player, lives, score, smashed cars)
  • play_stats persistence across game sessions
  • Session-triggered play-stats rollovers when a game-over condition is detected
  • On-screen leaderboards shown in a custom game layout (Score, Track Reached, Smashed)
  • Player-specific visual switching based on whose turn is active

1) Menu extension

Bump N Jump extends the shared Display menu with a game-specific item:

[menu.menus.display.item9]
label = "Switch to Leaderboard View"
action = { command = "switch_layout", layout = "bnj_layout" }

This keeps the base menu intact and adds one game-level action to jump into the custom leaderboard layout.

2) game_info values and derived values

Bump N Jump uses game_info for both active-player stats and per-player score tracking.

[game_info.values.active_player]
address = "0x01A0"
decode = "u16"
transform = { op = "offset", value = 1 }

[game_info.values.active_lives]
address = "0x019C"
decode = "u16"

[game_info.values.active_smashed_cars]
address = "0x019D"
decode = "u16"

[game_info.values.active_score_low]
address = "0x0314"
decode = "u16"

[game_info.values.active_score_thousands]
address = "0x0315"
decode = "u16"
transform = { op = "mul", value = 1000 }

[game_info.derived.active_score]
op = "sum"
terms = ["active_score_low", "active_score_thousands"]

[game_info.values.active_track_number]
address = "0x0167"
decode = "u16"

[game_info.values.p1_lives]
address = "0x01A3"
decode = "u16"

[game_info.values.p2_lives]
address = "0x01AD"
decode = "u16"

[game_info.values.p1_score_low]
address = "0x01A9"
decode = "lo_bytes_le"

[game_info.values.p1_score_thousands]
address = "0x01AB"
decode = "u16"
transform = { op = "mul", value = 1000 }

[game_info.derived.p1_score]
op = "sum"
terms = ["p1_score_low", "p1_score_thousands"]

[game_info.values.p2_score_low]
address = "0x01B3"
decode = "lo_bytes_le"

[game_info.values.p2_score_thousands]
address = "0x01B5"
decode = "u16"
transform = { op = "mul", value = 1000 }

[game_info.derived.p2_score]
op = "sum"
terms = ["p2_score_low", "p2_score_thousands"]

What this is doing:

  • active_player is decoded from memory, then shifted with offset = 1 so values line up cleanly as player 1 and 2.
  • decode = "u16" reads a plain 16-bit value.
  • decode = "lo_bytes_le" is used for split score values where the low bytes are stored separately in little-endian order.
  • The thousands value is normalized with transform = { op = "mul", value = 1000 }.
  • game_info.derived.* introduces computed values by combining terms. Here, each final score is rebuilt with op = "sum".

This derived-score pattern is important because Bump N Jump does not store every score as one simple memory value.

3) play_stats configuration

The play-stats section persists player totals across sessions.

[play_stats]
enabled = true
stats_file = "{gv.game_play_stats_path}"
auto_save_quit = true
auto_save_reset = true

[play_stats.session_triggers]
auto_save = true

[play_stats.session_triggers.p1_game_over]
game_info = "p1_lives"
trigger = { mode = "changes_to", value = 0 }

[[play_stats.session_triggers.p1_game_over.conditions]]
game_info = "p2_lives"
op = "eq"
value = 0

[play_stats.session_triggers.p2_game_over]
game_info = "p2_lives"
trigger = { mode = "changes_to", value = 0 }

[[play_stats.session_triggers.p2_game_over.conditions]]
game_info = "p1_lives"
op = "eq"
value = 0

[play_stats.players.p1]
label = "P1"

[[play_stats.players.p1.save_conditions]]
game_info = "p1_score"
op = "gt"
value = 0

[play_stats.players.p1.tracked.score]
game_info = "active_score"
mode = "amount_increased"
[[play_stats.players.p1.tracked.score.update_conditions]]
game_info = "active_player"
op = "eq"
value = 1

[play_stats.players.p1.tracked.smashed_cars]
game_info = "active_smashed_cars"
mode = "amount_increased"
[[play_stats.players.p1.tracked.smashed_cars.update_conditions]]
game_info = "active_player"
op = "eq"
value = 1

[play_stats.players.p1.tracked.track_number]
game_info = "active_track_number"
mode = "peak"
[[play_stats.players.p1.tracked.track_number.update_conditions]]
game_info = "active_player"
op = "eq"
value = 1

[play_stats.players.p2]
label = "P2"

[[play_stats.players.p2.save_conditions]]
game_info = "p2_score"
op = "gt"
value = 0

[play_stats.players.p2.tracked.score]
game_info = "active_score"
mode = "amount_increased"
[[play_stats.players.p2.tracked.score.update_conditions]]
game_info = "active_player"
op = "eq"
value = 2

[play_stats.players.p2.tracked.smashed_cars]
game_info = "active_smashed_cars"
mode = "amount_increased"
[[play_stats.players.p2.tracked.smashed_cars.update_conditions]]
game_info = "active_player"
op = "eq"
value = 2

[play_stats.players.p2.tracked.track_number]
game_info = "active_track_number"
mode = "peak"
[[play_stats.players.p2.tracked.track_number.update_conditions]]
game_info = "active_player"
op = "eq"
value = 2

How to read these entries:

  • enabled = true turns on stats tracking for this game.
  • stats_file points to the per-game persistent stats file path from global variables.
  • auto_save_quit and auto_save_reset save automatically on quit/reset.
  • session_triggers rolls to a new play-stats session when either player's lives change to 0 and the other player's lives are also 0.
  • session_triggers.auto_save = true saves the finished session before rollover.
  • players.p1 and players.p2 create separate tracked profiles.
  • save_conditions avoids saving empty runs (only save when score is greater than 0).
  • tracked.score, tracked.smashed_cars, and tracked.track_number define what to accumulate.
  • mode = "amount_increased" records only positive increases.
  • mode = "peak" for track_number preserves the highest track reached in that session.
  • update_conditions routes shared active values to the correct player using active_player == 1 or 2.

Result: one set of in-game counters can drive clean per-player persistent history.

4) Display layout, leaderboards, and player image switching

The custom layout is defined as the default and combines gameplay with persistent stat visuals.

[display]
default_layout = "bnj_layout"

[display.layouts.bnj_layout]

[[display.layouts.bnj_layout.elements]]
type = "leaderboard"
play_stat = "score"
order = "desc"
header = "High Scores"
include_players = ["p1", "p2"]

[[display.layouts.bnj_layout.elements]]
type = "leaderboard"
play_stat = "track_number"
order = "desc"
header = "Track Reached"
include_players = ["p1", "p2"]

[[display.layouts.bnj_layout.elements]]
type = "leaderboard"
play_stat = "smashed_cars"
order = "desc"
header = "Smashed"
include_players = ["p1", "p2"]

[[display.layouts.bnj_layout.elements]]
type = "image"
source = "{gv.assets_images}/bnj/bnj_p{gi:active_player}.png"
fallback = "none"

What appears on screen:

  • Three leaderboard panels (Score, Track Reached, and Smashed), all using persistent play_stats data.
  • A dynamic side image that switches with active_player, so each player gets their own visual when it is their turn.

Why this is useful

This pattern is a strong example of combining live memory reads, persistent tracking, and layout-driven UI in a single game profile.

Next step

Continue to the final Quick Start game page:

Clone this wiki locally