Deal items on ready, /pvp, and calmer rival windows
- Readying in a countdown deals any empty slots at random right away, and the countdown reads "Starting..." once everyone is ready. - /pvp toggles PvP in the sandbox, offline or in a lobby. - Rival windows sit at half opacity (half again when knocked out, and when hovered), show no item tooltips, and drop to small icons past two full-size rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6f112ab231
commit
5d6ebf2bba
5 files changed
+77
-68
No files matched your search
+37
-53
@@ -14,7 +14,8 @@ constexpr float RIVAL_BAR_MIN = 16;
|
||||
constexpr float RIVAL_DIVIDER = 1;
|
||||
constexpr float RIVAL_GAP = 4;
|
||||
constexpr float RIVAL_ICON_GAP = 2;
|
||||
constexpr float RIVAL_OUT_ALPHA = 0.25f;
|
||||
constexpr float RIVAL_ALPHA = 0.5f;
|
||||
constexpr float RIVAL_OUT_SHARE = 0.5f;
|
||||
constexpr float SPECTATE_GAP = 4;
|
||||
constexpr Control SPECTATE_CONTROLS[] = {CONTROL_LEFT, CONTROL_RIGHT, CONTROL_ATTACK};
|
||||
constexpr float HUD_IDLE_ALPHA = 0.5f;
|
||||
@@ -25,6 +26,8 @@ constexpr int RIVAL_COLUMNS_MAX = 3;
|
||||
// What the other fighters' windows try, from largest to smallest, until they all fit in a couple of rows: shrinking
|
||||
// icons before the font, then leaving the icons out.
|
||||
constexpr int RIVAL_ROWS_MAX = 2;
|
||||
// The most rows of full-size icons the windows can stack up between them before the icons shrink.
|
||||
constexpr int RIVAL_FULL_ICON_ROWS_MAX = 2;
|
||||
constexpr int RIVAL_STYLES = 6;
|
||||
constexpr int RIVAL_ICON_STYLES = 4;
|
||||
constexpr Font RIVAL_FONTS[RIVAL_STYLES] = {FONT_SMALL, FONT_SMALL, FONT_TINY, FONT_TINY, FONT_SMALL, FONT_TINY};
|
||||
@@ -393,7 +396,7 @@ void Ui::match_draw(Game& game)
|
||||
if (game.menu.isOpen)
|
||||
break;
|
||||
|
||||
SDL_snprintf(text, sizeof(text), "%s %d", info.round, info.seconds);
|
||||
SDL_snprintf(text, sizeof(text), "%s %s", info.round, info.time);
|
||||
text_center_draw(game, text, y, ROUND_SCALE);
|
||||
y += game.text.line_height_get(FONT_SMALL, ROUND_SCALE) + READY_GAP;
|
||||
SDL_snprintf(text, sizeof(text), isLocalReady ? "%d/%d Ready" : "%d/%d Ready - Press %s to Ready Up",
|
||||
@@ -731,7 +734,8 @@ void Ui::loadout_draw(Game& game, float alpha, float top)
|
||||
}
|
||||
|
||||
// Text centred across the canvas with its top at y.
|
||||
// What the countdown shows: the round's name, whole seconds left, how many are ready, and the key that readies up.
|
||||
// What the countdown shows: the round's name, whole seconds left (or Starting... once everyone's ready), how many are
|
||||
// ready, and the key that readies up.
|
||||
static CountdownInfo countdown_info_get(Game& game, bool isPad)
|
||||
{
|
||||
Match& match = game.sim.match;
|
||||
@@ -742,9 +746,12 @@ static CountdownInfo countdown_info_get(Game& game, bool isPad)
|
||||
SDL_strlcpy(info.round, "Final Round", sizeof(info.round));
|
||||
else
|
||||
SDL_snprintf(info.round, sizeof(info.round), "Round %d", match.round + 1);
|
||||
info.seconds = (int)std::ceil(match.timer);
|
||||
for (int i = 0; i < game.sim.fighterCount; i++)
|
||||
info.ready += match.isReady[i];
|
||||
if (info.ready == game.sim.fighterCount)
|
||||
SDL_strlcpy(info.time, "Starting...", sizeof(info.time));
|
||||
else
|
||||
SDL_snprintf(info.time, sizeof(info.time), "%d", (int)std::ceil(match.timer));
|
||||
Binding binding = game.input.bindings[CONTROL_READY];
|
||||
game.input.binding_name_get(isPad ? binding.pad : binding.key, isPad, info.key, sizeof(info.key));
|
||||
return info;
|
||||
@@ -758,7 +765,6 @@ void Ui::countdown_aside_draw(Game& game, bool isPad)
|
||||
{
|
||||
CountdownInfo info = countdown_info_get(game, isPad);
|
||||
const char* round = info.round;
|
||||
int seconds = info.seconds;
|
||||
int ready = info.ready;
|
||||
const char* key = game.sim.match.isReady[game.local] ? nullptr : info.key;
|
||||
float margin = (CANVAS_WIDTH - MENU_WIDTH) / 2;
|
||||
@@ -771,16 +777,20 @@ void Ui::countdown_aside_draw(Game& game, bool isPad)
|
||||
return game.text.line_height_get(FONT_SMALL, scale);
|
||||
};
|
||||
|
||||
float labelScale = game.text.size_get(round, FONT_SMALL, ASIDE_LABEL_SCALE).x <= margin - ASIDE_PADDING * 2
|
||||
? ASIDE_LABEL_SCALE
|
||||
: 1;
|
||||
float height =
|
||||
game.text.line_height_get(FONT_SMALL, labelScale) + game.text.line_height_get(FONT_SMALL, ROUND_SCALE);
|
||||
// Each line at the largest size that fits the margin.
|
||||
auto scale_fit = [&](const char* line, float scale) {
|
||||
for (; scale > 1; scale--)
|
||||
if (game.text.size_get(line, FONT_SMALL, scale).x <= margin - ASIDE_PADDING * 2)
|
||||
break;
|
||||
return scale;
|
||||
};
|
||||
float labelScale = scale_fit(round, ASIDE_LABEL_SCALE);
|
||||
float timeScale = scale_fit(info.time, ROUND_SCALE);
|
||||
float height = game.text.line_height_get(FONT_SMALL, labelScale) + game.text.line_height_get(FONT_SMALL, timeScale);
|
||||
float y = middle - height / 2;
|
||||
y += line_draw(round, margin / 2, y, labelScale);
|
||||
line_draw(info.time, margin / 2, y, timeScale);
|
||||
char text[TOOLTIP_LENGTH];
|
||||
SDL_snprintf(text, sizeof(text), "%d", seconds);
|
||||
line_draw(text, margin / 2, y, ROUND_SCALE);
|
||||
|
||||
float rightX = CANVAS_WIDTH - margin / 2;
|
||||
y = middle - (key ? LINE_HEIGHT * 3 : LINE_HEIGHT) / 2;
|
||||
@@ -1177,12 +1187,12 @@ static void effects_preview_get(Game& game, const char* now, const char* next, c
|
||||
|
||||
// Name, numbers at a level and description of a weapon or trinket. Trinkets say how many copies are equipped, and
|
||||
// when they stack.
|
||||
Tooltip Ui::tooltip_build(Game& game, bool isTrinket, int item, int level, int owner)
|
||||
Tooltip Ui::tooltip_build(Game& game, bool isTrinket, int item, int level)
|
||||
{
|
||||
Tooltip tooltip;
|
||||
tooltip.isTrinket = isTrinket;
|
||||
tooltip.item = item;
|
||||
Fighter& fighter = owner >= 0 ? game.sim.fighters[owner] : game.local_get();
|
||||
Fighter& fighter = game.local_get();
|
||||
int* loadout = isTrinket ? fighter.trinketLoadout : fighter.weaponLoadout;
|
||||
int slots = isTrinket ? fighter.trinketSlots : fighter.weaponSlots;
|
||||
tooltip.slot = std::find(loadout, loadout + slots, item) - loadout;
|
||||
@@ -2271,10 +2281,11 @@ static float rival_ping_get(Game& game, int fighter)
|
||||
|
||||
// Lays out the other fighters' windows in the HUD's right column, from the bottom up to the readouts above, in no more
|
||||
// than two rows: as many to a row as fit across (the rows as even as they go). A window is as wide as its icons, name
|
||||
// or health (with its connection's bars, online). Without icons, the health number can go to make room. When they don't
|
||||
// all fit, the icons shrink to small, then the font to tiny, then the icons to tiny, then the icons are left out. Each
|
||||
// kind of icon wraps onto more rows past three across, and tiny icons wrap further if they still don't fit. If even
|
||||
// names and health alone don't fit, the windows split the column evenly and the names are cut short.
|
||||
// or health (with its connection's bars, online). Without icons, the health number can go to make room. Full-size
|
||||
// icons stack no more than two rows deep across the windows. When they don't all fit, the icons shrink to small, then
|
||||
// the font to tiny, then the icons to tiny, then the icons are left out. Each kind of icon wraps onto more rows past
|
||||
// three across, and tiny icons wrap further if they still don't fit. If even names and health alone don't fit, the
|
||||
// windows split the column evenly and the names are cut short.
|
||||
RivalLayout Ui::rivals_layout(Game& game)
|
||||
{
|
||||
RivalLayout layout;
|
||||
@@ -2336,7 +2347,8 @@ RivalLayout Ui::rivals_layout(Game& game)
|
||||
perRow = std::clamp((int)((HUD_COLUMN_WIDTH + RIVAL_GAP) / (width + RIVAL_GAP)), 1, layout.count);
|
||||
int cardRows = (layout.count + perRow - 1) / perRow;
|
||||
perRow = (layout.count + cardRows - 1) / cardRows;
|
||||
isFit = width <= HUD_COLUMN_WIDTH && cardRows <= RIVAL_ROWS_MAX &&
|
||||
bool isFullTall = layout.size == ICON_FULL && cardRows * layout.rows > RIVAL_FULL_ICON_ROWS_MAX;
|
||||
isFit = width <= HUD_COLUMN_WIDTH && cardRows <= RIVAL_ROWS_MAX && !isFullTall &&
|
||||
cardRows * (height + RIVAL_GAP) - RIVAL_GAP <= room;
|
||||
}
|
||||
}
|
||||
@@ -2359,9 +2371,8 @@ RivalLayout Ui::rivals_layout(Game& game)
|
||||
|
||||
// Every other fighter's window in a match, bordered in its colour: its name centred (cut short if it doesn't fit), its
|
||||
// health (online with its connection's bars after), then (with room) its weapons, a divider, and its trinkets, the
|
||||
// weapon in hand lit. Each is laid over the game as one piece,
|
||||
// see-through for a fighter out of the round, and fades while the mouse is over it. Hovering an icon shows the item's
|
||||
// tooltip, as that fighter has it, in its colour.
|
||||
// weapon in hand lit. Each is laid over the game as one piece, half see-through (half again for a fighter out of the
|
||||
// round), and fades by half again while the mouse is over it.
|
||||
void Ui::rivals_draw(Game& game)
|
||||
{
|
||||
Match& match = game.sim.match;
|
||||
@@ -2372,15 +2383,12 @@ void Ui::rivals_draw(Game& game)
|
||||
float inset = PANEL_BORDER + RIVAL_PADDING;
|
||||
float iconSize = ICON_PIXELS[layout.size];
|
||||
float cell = iconSize + RIVAL_ICON_GAP;
|
||||
int hoveredFighter = -1;
|
||||
int hoveredKind = 0;
|
||||
int hoveredSlot = 0;
|
||||
for (int k = 0; k < layout.count; k++)
|
||||
{
|
||||
int index = layout.fighters[k];
|
||||
Fighter& fighter = game.sim.fighters[index];
|
||||
Rect card = layout.cards[k];
|
||||
float base = fighter.isAlive || match.is_racing() || index == spectated ? 1 : RIVAL_OUT_ALPHA;
|
||||
float base = RIVAL_ALPHA * (fighter.isAlive || match.is_racing() || index == spectated ? 1 : RIVAL_OUT_SHARE);
|
||||
Color color = game.color_get(index);
|
||||
Color dim = color_shade(color, SECONDARY_SHADE);
|
||||
renderer.layer_begin();
|
||||
@@ -2446,21 +2454,13 @@ void Ui::rivals_draw(Game& game)
|
||||
int* loadout = isTrinket ? fighter.trinketLoadout : fighter.weaponLoadout;
|
||||
for (int slot = 0; slot < slots; slot++)
|
||||
{
|
||||
Rect box = {x + slot % layout.columns[kind] * cell, y + slot / layout.columns[kind] * cell, iconSize,
|
||||
iconSize};
|
||||
int item = loadout[slot];
|
||||
if (item < 0)
|
||||
continue;
|
||||
bool isLit = isTrinket || slot == fighter.slot;
|
||||
icon_draw(game, isTrinket, item,
|
||||
{.color = isLit ? color : dim, .position = {box.x + box.w / 2, box.y + box.h / 2}},
|
||||
layout.size);
|
||||
if (is_point_in_rect(game.input.mouse, box))
|
||||
{
|
||||
hoveredFighter = index;
|
||||
hoveredKind = kind;
|
||||
hoveredSlot = slot;
|
||||
}
|
||||
Vec2 centre = {x + slot % layout.columns[kind] * cell + iconSize / 2,
|
||||
y + slot / layout.columns[kind] * cell + iconSize / 2};
|
||||
icon_draw(game, isTrinket, item, {.color = isLit ? color : dim, .position = centre}, layout.size);
|
||||
}
|
||||
x += layout.columns[kind] * cell;
|
||||
if (kind == 0)
|
||||
@@ -2471,22 +2471,6 @@ void Ui::rivals_draw(Game& game)
|
||||
}
|
||||
renderer.layer_end(base * hover_alpha_get(rivalHovers[index]));
|
||||
}
|
||||
|
||||
if (hoveredFighter < 0 || game.menu.isOpen)
|
||||
return;
|
||||
Fighter& fighter = game.sim.fighters[hoveredFighter];
|
||||
bool isTrinket = hoveredKind == 1;
|
||||
int item = (isTrinket ? fighter.trinketLoadout : fighter.weaponLoadout)[hoveredSlot];
|
||||
int level = (isTrinket ? fighter.trinketSlotLevels : fighter.weaponSlotLevels)[hoveredSlot];
|
||||
Tooltip tooltip = tooltip_build(game, isTrinket, item, level, hoveredFighter);
|
||||
tooltip.slot = hoveredSlot;
|
||||
// The tooltip takes that fighter's colour for the moment, so it's clear whose item it is.
|
||||
Color colors[2] = {tint, shade};
|
||||
tint = game.color_get(hoveredFighter);
|
||||
shade = color_shade(tint, SECONDARY_SHADE);
|
||||
tooltip_draw(game, tooltip);
|
||||
tint = colors[0];
|
||||
shade = colors[1];
|
||||
}
|
||||
|
||||
// Choosing items with several players on this screen: a panel each below top (side by side for two, a 2x2 grid for
|
||||
|
||||
Reference in new issue
Block a user