Tomatoes, match tabs, shared pings, and a round of fixes

- On the results, everyone but the winner splats tomatoes where they
  click (bots aim at the winner), with a new splat sound.
- During a match the menu has the item tabs (choosing when the
  countdown allows, and otherwise levelling the sandbox's levels for
  when you're back), Play (the game's rules) and Settings.
- Pings are shared through the server (protocol 4), so other players'
  connection bars show over their heads and on their windows.
- PvP comes into lobbies with your profile and is saved.
- The choice menu opens by itself however the countdown began.
- Knocked-out fighters keep their buttons current, so Ready on the
  results toggles once per press.
- The weapon wheel wraps both ways. Readying with empty slots plays
  the equip sound for your own fighter.
- Shock Absorber: up to 50% less weapon movement and 33% less
  knockback. Only the catalog previews the next level.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shweetandClaude Opus 5.5 committed 2026-11-08 18:11:06 -05:00
1 parent cdb0845da2
commit 9a41474069
29 files changed
+295 -79

No files matched your search

+10 -5
View File
@@ -41,6 +41,7 @@ constexpr const char* KEY_HUD_SPEED = "hud_speed";
constexpr const char* KEY_VSYNC = "vsync";
constexpr const char* KEY_WEAPON_SLOTS = "weapon_slots";
constexpr const char* SETTINGS_FILE = "settings.txt";
constexpr const char* KEY_SANDBOX_PVP = "sandbox_pvp";
constexpr const char* KEY_SANDBOX_SLOT = "sandbox_slot";
constexpr const char* KEY_SANDBOX_TRINKET_LEVELS = "sandbox_trinket_levels";
constexpr const char* KEY_SANDBOX_TRINKETS = "sandbox_trinkets";
@@ -201,6 +202,8 @@ static void sandbox_read(SandboxSave& sandbox, const char* key, char* value)
sandbox.feet.y = SDL_atof(value);
else if (SDL_strcmp(key, KEY_SANDBOX_SLOT) == 0)
sandbox.slot = SDL_atoi(value);
else if (SDL_strcmp(key, KEY_SANDBOX_PVP) == 0)
sandbox.isPvp = SDL_atoi(value) != 0;
else if (SDL_strcmp(key, KEY_SANDBOX_WEAPONS) == 0)
list_read(value, false, sandbox.weaponLoadout, sandbox.weaponSlotLevels);
else if (SDL_strcmp(key, KEY_SANDBOX_TRINKETS) == 0)
@@ -216,8 +219,9 @@ static void sandbox_read(SandboxSave& sandbox, const char* key, char* value)
static int sandbox_write(Fighter& fighter, char* text, int size)
{
int length = SDL_snprintf(text, size, "%s=%g\n%s=%g\n%s=%d\n", KEY_SANDBOX_X, fighter.center_get().x, KEY_SANDBOX_Y,
fighter.body.box.y + fighter.body.box.h, KEY_SANDBOX_SLOT, fighter.slot);
int length = SDL_snprintf(text, size, "%s=%g\n%s=%g\n%s=%d\n%s=%d\n", KEY_SANDBOX_X, fighter.center_get().x,
KEY_SANDBOX_Y, fighter.body.box.y + fighter.body.box.h, KEY_SANDBOX_SLOT, fighter.slot,
KEY_SANDBOX_PVP, (int)fighter.isPvp);
for (int kind = 0; kind < 2; kind++)
{
bool isTrinket = kind == 1;
@@ -621,9 +625,9 @@ void Settings::customize_draw(Game& game, Rect area)
save(game);
}
// Puts the sandbox back as it was saved, once the world and fighter exist: loadouts, levels, the slot held, and where
// the fighter stood (unless that spot is now blocked). With nothing saved yet, the fighter starts with a sword in its
// first slot.
// Puts the sandbox back as it was saved, once the world and fighter exist: loadouts, levels, the slot held, PvP, and
// where the fighter stood (unless that spot is now blocked). With nothing saved yet, the fighter starts with a sword in
// its first slot.
void Settings::restore(Game& game)
{
Fighter& fighter = game.local_get();
@@ -636,6 +640,7 @@ void Settings::restore(Game& game)
std::copy(std::begin(sandbox.trinketLoadout), std::end(sandbox.trinketLoadout), fighter.trinketLoadout);
std::copy(std::begin(sandbox.weaponLevels), std::end(sandbox.weaponLevels), fighter.weaponItemLevels);
std::copy(std::begin(sandbox.trinketLevels), std::end(sandbox.trinketLevels), fighter.trinketItemLevels);
fighter.isPvp = sandbox.isPvp;
// Saves from before copies had their own levels give each copy its item's level.
for (int i = 0; i < SLOT_MAX; i++)
{