Room and lobby chat, /host, /fashion, player-colored lines, context tips, and American spelling
- Chat goes to your room (the sandbox, or your game) by default, and to the whole lobby with /all or a leading !. Lines from elsewhere say where they came from ([Sandbox], [Game 2]), lobby-wide ones from your room say [All]. Typing shows only to your room. Protocol 8. - Hosting: everyone is told who hosts now, and the host can hand it over with /host "player". - /fashion <item> wears a fashion locally, like /color. - Lines that mention a player show in that player's color, looked up as they draw. - Tips are tagged with the item and situations they fit, and those that fit are 1.5x as likely. They turn over every 10 seconds on one continuous run, show under the menu too, and can be turned off with Tips in HUD / Gameplay. The HUD hotbars hide while the menu is up (except on its item tabs). - American spelling throughout: color, center, gray, mustache, and so on.
This commit is contained in:
1 parent
f4e7784f3c
commit
93cdf46d5a
38 files changed
+603
-325
No files matched your search
+229
-141
@@ -10,7 +10,7 @@ constexpr float LOADOUT_FADE = 0.25f;
|
||||
constexpr float LOADOUT_GAP = 4;
|
||||
constexpr float LOADOUT_DIVIDER = 2;
|
||||
constexpr float ICON_SIZE = 24;
|
||||
// How big a fashion item's cell is (its art is centred in it).
|
||||
// How big a fashion item's cell is (its art is centerd in it).
|
||||
constexpr float FASHION_CELL = 32;
|
||||
constexpr float RIVAL_BAR_MIN = 16;
|
||||
constexpr float RIVAL_DIVIDER = 1;
|
||||
@@ -116,6 +116,10 @@ constexpr float FIELD_INSET = 4;
|
||||
constexpr float SLIDER_INSET = 3;
|
||||
constexpr float TEXT_INSET = (WIDGET_HEIGHT - LINE_HEIGHT) / 2;
|
||||
constexpr float HINT_FADE = 0.5f;
|
||||
// How much likelier a tip that fits what's at hand is to be picked than one that doesn't.
|
||||
constexpr float TIP_FIT_WEIGHT = 1.5f;
|
||||
// How long tips take to fade in and out.
|
||||
constexpr float TIP_FADE = 0.4f;
|
||||
constexpr const char* HINT_SPACING = " ";
|
||||
// How wide a row of first-run hints may run, clear of the hotbars in the corners.
|
||||
constexpr float HINT_ROW_WIDTH = 360;
|
||||
@@ -142,103 +146,123 @@ constexpr Vec2 LABEL_OFFSETS[ICON_TINY] = {{4, 2}, {3, 2}};
|
||||
constexpr Vec2 ICON_LABEL_OFFSET = {LABEL_OFFSETS[ICON_FULL].x - (SLOT_SIZE - ICON_SIZE) / 2,
|
||||
LABEL_OFFSETS[ICON_FULL].y - (SLOT_SIZE - ICON_SIZE) / 2};
|
||||
|
||||
// The situations a tip fits, combined as flags: playing online or offline, and the menu being open.
|
||||
enum TipContext
|
||||
{
|
||||
TIP_ONLINE = 1,
|
||||
TIP_OFFLINE = 2,
|
||||
TIP_MENU = 4
|
||||
};
|
||||
|
||||
// A tip for the ready screen, and what it's about: a weapon or trinket by name, and the situations it fits (see
|
||||
// TipContext). One that fits what's at hand (that item equipped, or one of those situations) is TIP_FIT_WEIGHT times as
|
||||
// likely to be picked (see tip_pick).
|
||||
struct Tip
|
||||
{
|
||||
const char* text;
|
||||
const char* item = nullptr;
|
||||
int contexts = 0;
|
||||
};
|
||||
|
||||
// Tips shown under the ready button, one at random: movement and knockback, cooldowns and Charge, projectiles, each
|
||||
// Weapon and Trinket in turn, then the menus.
|
||||
constexpr const char* TIPS[] = {
|
||||
"Getting knocked into walls hurts! Do your best to reduce your speed when attacked!",
|
||||
"Don't get cornered! Knockback damage can add up fast!",
|
||||
"Crouch to reduce knockback!",
|
||||
"Crawl around by crouching and moving!",
|
||||
"Roll around on sloped surfaces with Down!",
|
||||
"Platforms block shots. Use them as cover!",
|
||||
constexpr Tip TIPS[] = {
|
||||
{"Getting knocked into walls hurts! Do your best to reduce your speed when attacked!"},
|
||||
{"Don't get cornered! Knockback damage can add up fast!"},
|
||||
{"Crouch to reduce knockback!"},
|
||||
{"Crawl around by crouching and moving!"},
|
||||
{"Roll around on sloped surfaces with Down!"},
|
||||
{"Platforms block shots. Use them as cover!"},
|
||||
|
||||
"Weapons don't share cooldowns! Switch Weapons to maximize damage!",
|
||||
"When a Weapon enters cooldown, it will fade and return to normal when it's done!",
|
||||
"Your cursor's color and vibration can show you your Weapon's cooldown or Charge!",
|
||||
"Let go of a Charge early for a Basic Attack that hits harder!",
|
||||
"Your Charge carries over when you swap Weapons!",
|
||||
"Higher damaging attacks will glow and vibrate!",
|
||||
"Going faster makes you glow and vibrate, showing that your attacks deal more damage!",
|
||||
{"Weapons don't share cooldowns! Switch Weapons to maximize damage!"},
|
||||
{"When a Weapon enters cooldown, it will fade and return to normal when it's done!"},
|
||||
{"Your cursor's color and vibration can show you your Weapon's cooldown or Charge!"},
|
||||
{"Let go of a Charge early for a Basic Attack that hits harder!"},
|
||||
{"Your Charge carries over when you swap Weapons!"},
|
||||
{"Higher damaging attacks will glow and vibrate!"},
|
||||
{"Going faster makes you glow and vibrate, showing that your attacks deal more damage!"},
|
||||
|
||||
"Almost every attack counts as a projectile! See how you can change their traits with Trinkets!",
|
||||
"Projectiles inherit your velocity. Go fast for big damage!",
|
||||
"Projectiles lose damage the longer they're out. Get close!",
|
||||
"Projectiles will fade with time; this fade shows their damage potential!",
|
||||
"Increasing a projectile's lifetime or speed can both increase its range!",
|
||||
"Explosions hit hardest at their center!",
|
||||
"Your explosions don't hurt, and only push you. Try rocket jumping!",
|
||||
"Recoil is movement. Fire away from where you want to go!",
|
||||
{"Almost every attack counts as a projectile! See how you can change their traits with Trinkets!"},
|
||||
{"Projectiles inherit your velocity. Go fast for big damage!"},
|
||||
{"Projectiles lose damage the longer they're out. Get close!"},
|
||||
{"Projectiles will fade with time; this fade shows their damage potential!"},
|
||||
{"Increasing a projectile's lifetime or speed can both increase its range!"},
|
||||
{"Explosions hit hardest at their center!"},
|
||||
{"Your explosions don't hurt, and only push you. Try rocket jumping!"},
|
||||
{"Recoil is movement. Fire away from where you want to go!"},
|
||||
|
||||
"Mix and match Weapons and Trinkets for nigh-infinite amounts of combos!",
|
||||
"The Fist's Charged Attack doesn't always send you straight up! Change your aim!",
|
||||
"Slam a Hammer into surfaces to fly far!",
|
||||
"The Lance's Charged Attack lunges you forward!",
|
||||
"Crack the Whip against a wall to be pulled toward it!",
|
||||
"The Whip's Charged Attack only blasts at the very tip, but does big damage!",
|
||||
"Charge a Flail to throw its ball, and Charge again to reel it back!",
|
||||
"You can have the Flail's ball extended and another Weapon equipped for a dangerous combo!",
|
||||
"Charged Boomerangs are platforms only you can ride!",
|
||||
"Charge recalls your Boomerangs!",
|
||||
"A Grapple's Charged Attack yanks you to your target!",
|
||||
"Swing around on or release a latched Grapple for big speed!",
|
||||
"You can have a Grapple latched and another Weapon equipped for an acrobatic fight!",
|
||||
"A Bow's arrows stuck in walls are semi-solid platforms!",
|
||||
"The Bow's arrows fly faster and further with Charge!",
|
||||
"Fire a wide volley with the Uzi's Charged Attack!",
|
||||
"Use a Shotgun's recoil for great movement!",
|
||||
"The Bolter's projectiles can cling onto surfaces, perfect for area denial!",
|
||||
"Cook a Grenade Launcher's grenade via Charging to explode it sooner!",
|
||||
"Fully Charging a Grenade Launcher will cause an explosion in your face!",
|
||||
"Hit the Grenade Launcher's projectile and explosion at the same time for big damage! Double Donk!",
|
||||
"Mines need a moment to arm before they'll go off!",
|
||||
"The Flamethrower's Charged Attack detonates mid-air! Space an explosion!",
|
||||
"The Vacuum's Charged Attack deals big knockback! Send foes flying!",
|
||||
"The Vacuum's Charged Attack turns enemy shots into your own!",
|
||||
"Hold your own projectiles in the Vacuum's suction to keep them fresh!",
|
||||
"The Vacuum's suction and Charged Attack can move projectiles around. Manipulate the battlefield!",
|
||||
"Use the Grimoire's wisps as a close-range attack or to pepper from afar!",
|
||||
"Use the Grimoire's teleporting Charged Attack to escape danger!",
|
||||
{"Mix and match Weapons and Trinkets for nigh-infinite amounts of combos!", nullptr, TIP_MENU},
|
||||
{"The Fist's Charged Attack doesn't always send you straight up! Change your aim!", "Fist"},
|
||||
{"Slam a Hammer into surfaces to fly far!", "Hammer"},
|
||||
{"The Lance's Charged Attack lunges you forward!", "Lance"},
|
||||
{"Crack the Whip against a wall to be pulled toward it!", "Whip"},
|
||||
{"The Whip's Charged Attack only blasts at the very tip, but does big damage!", "Whip"},
|
||||
{"Charge a Flail to throw its ball, and Charge again to reel it back!", "Flail"},
|
||||
{"You can have the Flail's ball extended and another Weapon equipped for a dangerous combo!", "Flail"},
|
||||
{"Charged Boomerangs are platforms only you can ride!", "Boomerang"},
|
||||
{"Charge recalls your Boomerangs!", "Boomerang"},
|
||||
{"A Grapple's Charged Attack yanks you to your target!", "Grapple"},
|
||||
{"Swing around on or release a latched Grapple for big speed!", "Grapple"},
|
||||
{"You can have a Grapple latched and another Weapon equipped for an acrobatic fight!", "Grapple"},
|
||||
{"A Bow's arrows stuck in walls are semi-solid platforms!", "Bow"},
|
||||
{"The Bow's arrows fly faster and further with Charge!", "Bow"},
|
||||
{"Fire a wide volley with the Uzi's Charged Attack!", "Uzi"},
|
||||
{"Use a Shotgun's recoil for great movement!", "Shotgun"},
|
||||
{"The Bolter's projectiles can cling onto surfaces, perfect for area denial!", "Bolter"},
|
||||
{"Cook a Grenade Launcher's grenade via Charging to explode it sooner!", "Grenade Launcher"},
|
||||
{"Fully Charging a Grenade Launcher will cause an explosion in your face!", "Grenade Launcher"},
|
||||
{"Hit the Grenade Launcher's projectile and explosion at the same time for big damage! Double Donk!",
|
||||
"Grenade Launcher"},
|
||||
{"Mines need a moment to arm before they'll go off!", "Mine"},
|
||||
{"The Flamethrower's Charged Attack detonates mid-air! Space an explosion!", "Flamethrower"},
|
||||
{"The Vacuum's Charged Attack deals big knockback! Send foes flying!", "Vacuum"},
|
||||
{"The Vacuum's Charged Attack turns enemy shots into your own!", "Vacuum"},
|
||||
{"Hold your own projectiles in the Vacuum's suction to keep them fresh!", "Vacuum"},
|
||||
{"The Vacuum's suction and Charged Attack can move projectiles around. Manipulate the battlefield!", "Vacuum"},
|
||||
{"Use the Grimoire's wisps as a close-range attack or to pepper from afar!", "Grimoire"},
|
||||
{"Use the Grimoire's teleporting Charged Attack to escape danger!", "Grimoire"},
|
||||
|
||||
"Diazepam reduces Weapon spread, perfect for concentrated attacks!",
|
||||
"Splitter copies fan wider with each level!",
|
||||
"The off-hand Weapon from Dual Wield is the one you last switched from!",
|
||||
"Ricochet makes projectiles deal more damage the more they've bounced!",
|
||||
"Ricochet bounces reset a projectile's lifetime!",
|
||||
"Tesla Coil arcs electricity between your projectiles. Make traps or wide danger zones!",
|
||||
"The kickback the Recoil Coil gives is perfect for movement!",
|
||||
"Stay in place with a Shock Absorber!",
|
||||
"Embiggener makes you a bigger target, but gives a solid damage buff!",
|
||||
"Make yourself a living hazard with a Pebble Helmet!",
|
||||
"With a Speedometer, moving fast and attacking deals bigger damage!",
|
||||
"A pair of Wings will make you dash to where you're aiming when you jump mid-air!",
|
||||
"With the Feather, hold Up in the air to float!",
|
||||
"With Iron Weights, faster falls produce bigger blasts!",
|
||||
"Cling, slide, and kick off walls with a Climb Claw!",
|
||||
{"Diazepam reduces Weapon spread, perfect for concentrated attacks!", "Diazepam"},
|
||||
{"Splitter copies fan wider with each level!", "Splitter"},
|
||||
{"The off-hand Weapon from Dual Wield is the one you last switched from!", "Dual Wield"},
|
||||
{"Ricochet makes projectiles deal more damage the more they've bounced!", "Ricochet"},
|
||||
{"Ricochet bounces reset a projectile's lifetime!", "Ricochet"},
|
||||
{"Tesla Coil arcs electricity between your projectiles. Make traps or wide danger zones!", "Tesla Coil"},
|
||||
{"The kickback the Recoil Coil gives is perfect for movement!", "Recoil Coil"},
|
||||
{"Stay in place with a Shock Absorber!", "Shock Absorber"},
|
||||
{"Embiggener makes you a bigger target, but gives a solid damage buff!", "Embiggener"},
|
||||
{"Make yourself a living hazard with a Pebble Helmet!", "Pebble Helmet"},
|
||||
{"With a Speedometer, moving fast and attacking deals bigger damage!", "Speedometer"},
|
||||
{"A pair of Wings will make you dash to where you're aiming when you jump mid-air!", "Wings"},
|
||||
{"With the Feather, hold Up in the air to float!", "Feather"},
|
||||
{"With Iron Weights, faster falls produce bigger blasts!", "Iron Weights"},
|
||||
{"Cling, slide, and kick off walls with a Climb Claw!", "Climb Claw"},
|
||||
|
||||
"Create a Lobby and share its link or code with friends for multiplayer fun!",
|
||||
"When in a Lobby, queue up a game in the menu!",
|
||||
"Queuing takes place in the Sandbox! Holler fighters over to your queued area and Ready up!",
|
||||
"Change a game's rules underneath the Rules option!",
|
||||
"If a game has a time limit, the winner will be decided based on who has the most health!",
|
||||
"Is your name \"Fighter\"? Change it in Profile!",
|
||||
"Change your fighter and HUD color in Profile!",
|
||||
"Feeling naked and unsexy? Wear some Fashion within your Profile!",
|
||||
"Set and load custom gear Loadouts in your Profile!",
|
||||
"Save a gear Loadout and use Shift + a number to equip it!",
|
||||
"Turn on DPS in Settings to see the damage you deal each second!",
|
||||
"Turn on Speed in Settings to see how fast you're going!",
|
||||
"Turn on FPS in Settings to see how smoothly the game runs!",
|
||||
"Is previewing your aim too intrusive? Toggle Aim Preview off in Settings!",
|
||||
"Want to see how things actually collide? Toggle Display Collision in Settings!",
|
||||
"Game too loud? Turn down the Volume or Mute it in Settings!",
|
||||
"Don't like the Controls? Tweak them to your liking in Settings!",
|
||||
"Want to experiment with more Weapon or Trinket slots in the Sandbox? Change them in Settings!",
|
||||
"Want to fight in the Sandbox with others? Check PvP in Settings or use the /pvp command!",
|
||||
"The Dummy in the Sandbox doesn't feel pain! Beat it as much as you'd like, it enjoys it!",
|
||||
"Throw tomatoes to mock a game's winner in between rounds by clicking anywhere on screen!",
|
||||
"Sling slurs and trashtalk all you wish with a Chat message!",
|
||||
"A number of useful / commands are present in Chat! Type /help to see!",
|
||||
"Want to see the sexy person who made this game? Check About in the menu!",
|
||||
{"Create a Lobby and share its link or code with friends for multiplayer fun!", nullptr, TIP_OFFLINE},
|
||||
{"When in a Lobby, queue up a game in the menu!", nullptr, TIP_ONLINE},
|
||||
{"Queuing takes place in the Sandbox! Holler fighters over to your queued area and Ready up!", nullptr, TIP_ONLINE},
|
||||
{"Change a game's rules underneath the Rules option!", nullptr, TIP_MENU},
|
||||
{"If a game has a time limit, the winner will be decided based on who has the most health!"},
|
||||
{"Is your name \"Fighter\"? Change it in Profile!", nullptr, TIP_MENU},
|
||||
{"Change your fighter and HUD color in Profile!", nullptr, TIP_MENU},
|
||||
{"Feeling naked and unsexy? Wear some Fashion within your Profile!", nullptr, TIP_MENU},
|
||||
{"Set and load custom gear Loadouts in your Profile!", nullptr, TIP_MENU},
|
||||
{"Save a gear Loadout and use Shift + a number to equip it!", nullptr, TIP_MENU},
|
||||
{"Turn on DPS in Settings to see the damage you deal each second!", nullptr, TIP_MENU},
|
||||
{"Turn on Speed in Settings to see how fast you're going!", nullptr, TIP_MENU},
|
||||
{"Turn on FPS in Settings to see how smoothly the game runs!", nullptr, TIP_MENU},
|
||||
{"Is previewing your aim too intrusive? Toggle Aim Preview off in Settings!", nullptr, TIP_MENU},
|
||||
{"Want to see how things actually collide? Toggle Display Collision in Settings!", nullptr, TIP_MENU},
|
||||
{"Game too loud? Turn down the Volume or Mute it in Settings!", nullptr, TIP_MENU},
|
||||
{"Don't like the Controls? Tweak them to your liking in Settings!", nullptr, TIP_MENU},
|
||||
{"Want to experiment with more Weapon or Trinket slots in the Sandbox? Change them in Settings!", nullptr,
|
||||
TIP_MENU},
|
||||
{"Want to fight in the Sandbox with others? Check PvP in Settings or use the /pvp command!", nullptr, TIP_ONLINE},
|
||||
{"The Dummy in the Sandbox doesn't feel pain! Beat it as much as you'd like, it enjoys it!"},
|
||||
{"Throw tomatoes to mock a game's winner in between rounds by clicking anywhere on screen!"},
|
||||
{"Sling slurs and trashtalk all you wish with a Chat message!", nullptr, TIP_ONLINE},
|
||||
{"A number of useful / commands are present in Chat! Type /help to see!", nullptr, TIP_ONLINE},
|
||||
{"Want to see the sexy person who made this game? Check About in the menu!", nullptr, TIP_MENU},
|
||||
};
|
||||
|
||||
static CountdownInfo countdown_info_get(Game& game);
|
||||
@@ -287,8 +311,9 @@ void Ui::draw(Game& game)
|
||||
shade = color_shade(tint, SECONDARY_SHADE);
|
||||
Vec2 mouse = game.input.mouse;
|
||||
|
||||
// The menu covers the match overlays. Hotbars sit under them, except in the sandbox menu they slide into. With
|
||||
// several players on this screen in a match, each gets a compact panel along the bottom instead.
|
||||
// The menu covers the match overlays. Hotbars sit under them (hidden while the menu is up, except on its item tabs,
|
||||
// which they slide under). With several players on this screen in a match, each gets a compact panel along the
|
||||
// bottom instead.
|
||||
statusRect = {};
|
||||
queues_draw(game);
|
||||
bars_draw(game);
|
||||
@@ -298,7 +323,7 @@ void Ui::draw(Game& game)
|
||||
offscreen_draw(game, camera);
|
||||
bool isBarsRaised = game.menu.is_bars_open();
|
||||
bool isShared = game.sim.match.state != MATCH_PRACTICE && game.local_count() > 1;
|
||||
if (!isBarsRaised && !isShared && !isWatching)
|
||||
if (!game.menu.isOpen && !isShared && !isWatching)
|
||||
{
|
||||
cooldowns_draw(game);
|
||||
hotbar_draw(game);
|
||||
@@ -317,6 +342,8 @@ void Ui::draw(Game& game)
|
||||
game.menu.draw(game);
|
||||
if (game.sim.match.state == MATCH_COUNTDOWN && game.menu.isOpen && !isShared)
|
||||
countdown_aside_draw(game);
|
||||
else if (game.menu.isOpen)
|
||||
tip_draw(game, tip_low_get());
|
||||
if (isBarsRaised)
|
||||
hotbar_draw(game);
|
||||
crosshairs_draw(game, camera);
|
||||
@@ -327,7 +354,7 @@ void Ui::draw(Game& game)
|
||||
}
|
||||
|
||||
// Stacked above each living fighter: in a deathmatch its health bar, then (in a match, or a lobby's sandbox with others
|
||||
// in it) its name in its colour, then the local fighter's charge bar while charging, which blinks once full.
|
||||
// in it) its name in its color, then the local fighter's charge bar while charging, which blinks once full.
|
||||
void Ui::bars_draw(Game& game)
|
||||
{
|
||||
Match& match = game.sim.match;
|
||||
@@ -356,7 +383,7 @@ void Ui::bars_draw(Game& game)
|
||||
bool isNamed = isMatch || (game.session.isActive && game.sim.fighterCount > 1);
|
||||
if (isNamed)
|
||||
{
|
||||
// Online, the player's connection beside it, the two centred together.
|
||||
// Online, the player's connection beside it, the two centerd together.
|
||||
const char* name = name_get(game, i);
|
||||
Vec2 size = game.text.size_get(name);
|
||||
bottom -= size.y;
|
||||
@@ -394,11 +421,11 @@ void Ui::match_draw(Game& game)
|
||||
{
|
||||
Match& match = game.sim.match;
|
||||
char text[TOOLTIP_LENGTH];
|
||||
isTipChosen &= match.state == MATCH_COUNTDOWN;
|
||||
Input& input = game.input;
|
||||
if (match.state == MATCH_PRACTICE)
|
||||
{
|
||||
hints_draw(game);
|
||||
if (!game.menu.isOpen)
|
||||
hints_draw(game);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -407,7 +434,7 @@ void Ui::match_draw(Game& game)
|
||||
race_draw(game);
|
||||
else
|
||||
{
|
||||
// The score: each fighter's wins in its own colour, the dashes between them white. It's centred along the top,
|
||||
// The score: each fighter's wins in its own color, the dashes between them white. It's centerd along the top,
|
||||
// unless a long weapon hotbar reaches it in the HUD, when it moves right to clear the hotbar (the clock under
|
||||
// it moves with it). It stays put while the menu moves the hotbar.
|
||||
Renderer& renderer = game.renderer;
|
||||
@@ -460,9 +487,6 @@ void Ui::match_draw(Game& game)
|
||||
CountdownInfo info = countdown_info_get(game);
|
||||
bool isWatching = game.session.is_watching(game);
|
||||
bool isLocalReady = match.isReady[game.local] || isWatching;
|
||||
if (!isTipChosen)
|
||||
tip = random_get(0, std::size(TIPS));
|
||||
isTipChosen = true;
|
||||
if (game.menu.isOpen)
|
||||
break;
|
||||
|
||||
@@ -490,8 +514,7 @@ void Ui::match_draw(Game& game)
|
||||
widgets_enable(true);
|
||||
y += READY_BUTTON_HEIGHT + READY_GAP;
|
||||
|
||||
SDL_snprintf(text, sizeof(text), "Tip: %s", TIPS[tip]);
|
||||
text_center_draw(game, text, y, 1);
|
||||
tip_draw(game, y);
|
||||
y += LINE_HEIGHT + TIP_GAP;
|
||||
loadoutTop = y;
|
||||
if (!game.menu.isOpen && game.local_count() == 1)
|
||||
@@ -520,7 +543,7 @@ void Ui::match_draw(Game& game)
|
||||
SDL_snprintf(text, sizeof(text), "%s Wins!", name_get(game, match.winner));
|
||||
else
|
||||
SDL_strlcpy(text, "Draw", sizeof(text));
|
||||
// A win is announced in the winner's colour.
|
||||
// A win is announced in the winner's color.
|
||||
text_center_draw(game, text, BANNER_Y, ROUND_SCALE,
|
||||
match.winner >= 0 ? game.color_get(match.winner) : tint);
|
||||
break;
|
||||
@@ -579,7 +602,7 @@ static void time_format(char* text, int size, float seconds)
|
||||
SDL_snprintf(text, size, "%d:%05.2f", minutes, seconds - minutes * SECONDS_PER_MINUTE);
|
||||
}
|
||||
|
||||
// Along the top of a race: the clock, and a track with a dot in each racer's colour showing how far along the course
|
||||
// Along the top of a race: the clock, and a track with a dot in each racer's color showing how far along the course
|
||||
// it is.
|
||||
void Ui::race_draw(Game& game)
|
||||
{
|
||||
@@ -602,7 +625,7 @@ void Ui::race_draw(Game& game)
|
||||
}
|
||||
}
|
||||
|
||||
// The race's finishing order from y down, one line per racer in its colour, with its time (or DNF). Returns the y
|
||||
// The race's finishing order from y down, one line per racer in its color, with its time (or DNF). Returns the y
|
||||
// below the last line.
|
||||
float Ui::results_draw(Game& game, float y)
|
||||
{
|
||||
@@ -736,7 +759,7 @@ static float beside_icon_get(Game& game, Tooltip& tooltip, Font font)
|
||||
return width;
|
||||
}
|
||||
|
||||
// The local fighter's loadout laid out in a centred grid between top and bottom: weapons and trinkets side by side
|
||||
// The local fighter's loadout laid out in a centerd grid between top and bottom: weapons and trinkets side by side
|
||||
// when they all fit in one row, otherwise weapons above trinkets, a kind's panels wrapping onto more rows as needed,
|
||||
// with a line between the two kinds (down between them, or across). Every panel is as wide as the widest so they line
|
||||
// up, and a row as tall as its kind's tallest. It never moves up over what's above it: it takes the fullest style that
|
||||
@@ -880,7 +903,7 @@ void Ui::loadout_draw(Game& game, float alpha, float top)
|
||||
}
|
||||
}
|
||||
|
||||
// Text centred across the canvas with its top at y.
|
||||
// Text centerd across the canvas with its top at y.
|
||||
// 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)
|
||||
@@ -903,6 +926,72 @@ static CountdownInfo countdown_info_get(Game& game)
|
||||
return info;
|
||||
}
|
||||
|
||||
// Tips show on the ready screen and while the menu is up, from one continuous run that turns over every TIP_DURATION
|
||||
// whether or not they're showing. Each fits what's at hand when it's picked (the first, on the first tick).
|
||||
void Ui::tips_update(Game& game)
|
||||
{
|
||||
tipAge += TICK;
|
||||
if (tipAge < TIP_DURATION)
|
||||
return;
|
||||
tip = tip_pick(game);
|
||||
tipAge = 0;
|
||||
}
|
||||
|
||||
// The tip showing (unless tips are turned off), centered at y in small text, fading in as it comes and out before the
|
||||
// next.
|
||||
void Ui::tip_draw(Game& game, float y)
|
||||
{
|
||||
if (!game.settings.isTipsShown)
|
||||
return;
|
||||
char text[TOOLTIP_LENGTH];
|
||||
SDL_snprintf(text, sizeof(text), "Tip: %s", TIPS[tip].text);
|
||||
float alpha = std::clamp(std::min(tipAge, TIP_DURATION - tipAge) / TIP_FADE, 0.0f, 1.0f);
|
||||
text_center_draw(game, text, y, 1, alpha);
|
||||
}
|
||||
|
||||
// Where a tip goes under the menu: below the hotbars' row (and the gamepad hints, when they show).
|
||||
float Ui::tip_low_get()
|
||||
{
|
||||
return BAR_Y + SLOT_SIZE + HINT_GAP + (isFocusShown ? LINE_HEIGHT : 0);
|
||||
}
|
||||
|
||||
// Picks the next tip (never the one showing), at random but TIP_FIT_WEIGHT times as likely for one that fits what's at
|
||||
// hand: about a weapon or trinket this screen's fighter has equipped, or fitting how it's playing (online or offline)
|
||||
// or the menu being open.
|
||||
int Ui::tip_pick(Game& game)
|
||||
{
|
||||
Fighter& local = game.local_get();
|
||||
int contexts = (game.session.isActive ? TIP_ONLINE : TIP_OFFLINE) | (game.menu.isOpen ? TIP_MENU : 0);
|
||||
auto is_equipped = [&](const char* name) {
|
||||
for (ItemKind kind : ITEM_KINDS)
|
||||
{
|
||||
int item = item_find(kind, name);
|
||||
int* loadout = local.loadout_get(kind);
|
||||
int slots = local.slots_get(kind);
|
||||
if (item >= 0 && std::find(loadout, loadout + slots, item) != loadout + slots)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
float weights[std::size(TIPS)];
|
||||
float total = 0;
|
||||
for (size_t i = 0; i < std::size(TIPS); i++)
|
||||
{
|
||||
const Tip& candidate = TIPS[i];
|
||||
bool isFit = (candidate.contexts & contexts) || (candidate.item && is_equipped(candidate.item));
|
||||
weights[i] = (int)i == tip ? 0 : isFit ? TIP_FIT_WEIGHT : 1;
|
||||
total += weights[i];
|
||||
}
|
||||
float roll = random_get(0, total);
|
||||
for (size_t i = 0; i < std::size(TIPS); i++)
|
||||
{
|
||||
roll -= weights[i];
|
||||
if (roll < 0)
|
||||
return i;
|
||||
}
|
||||
return std::size(TIPS) - 1;
|
||||
}
|
||||
|
||||
// Marks a first-run hint learned once this screen's fighter has done what it says (a full charge for the charged
|
||||
// attack), keeping it in the settings across launches, and fades learned ones out.
|
||||
void Ui::hints_update(Game& game)
|
||||
@@ -931,7 +1020,7 @@ void Ui::hints_update(Game& game)
|
||||
}
|
||||
}
|
||||
|
||||
// The first-run hints still showing, each with its key, centred in rows along the bottom of the sandbox, a row going up
|
||||
// The first-run hints still showing, each with its key, centerd in rows along the bottom of the sandbox, a row going up
|
||||
// when they'd run too wide for one.
|
||||
void Ui::hints_draw(Game& game)
|
||||
{
|
||||
@@ -1052,9 +1141,7 @@ void Ui::countdown_aside_draw(Game& game)
|
||||
line_draw("to Ready Up", rightX, y, 1);
|
||||
}
|
||||
|
||||
y = BAR_Y + SLOT_SIZE + HINT_GAP + (isFocusShown ? LINE_HEIGHT : 0);
|
||||
SDL_snprintf(text, sizeof(text), "Tip: %s", TIPS[tip]);
|
||||
text_center_draw(game, text, y, 1);
|
||||
tip_draw(game, tip_low_get());
|
||||
}
|
||||
|
||||
void Ui::text_center_draw(Game& game, const char* text, float y, float scale, float alpha)
|
||||
@@ -1462,7 +1549,7 @@ Vec2 Ui::tooltip_size(Game& game, Tooltip& tooltip, Font font)
|
||||
name.y + stats.y + table.y + description.y + flavor.y};
|
||||
}
|
||||
|
||||
// Stacks the name (in the tooltip's own colour, if it has one), gray stats, description and any gray flavor quote from
|
||||
// Stacks the name (in the tooltip's own color, if it has one), gray stats, description and any gray flavor quote from
|
||||
// draw.position down, faded by draw.color's alpha.
|
||||
void Ui::tooltip_text_draw(Game& game, Tooltip& tooltip, Draw draw, Font font)
|
||||
{
|
||||
@@ -1564,11 +1651,11 @@ void Ui::tooltip_draw(Game& game, Tooltip& tooltip)
|
||||
panel.y + TOOLTIP_PADDING + ICON_SIZE / 2}});
|
||||
}
|
||||
|
||||
// A tooltip's item icon (or fashion item's art), centred on the placement, with the hotbar slot it's in numbered over
|
||||
// A tooltip's item icon (or fashion item's art), centerd on the placement, with the hotbar slot it's in numbered over
|
||||
// its top left corner as on the hotbar.
|
||||
void Ui::tooltip_icon_draw(Game& game, Tooltip& tooltip, Draw placement)
|
||||
{
|
||||
// A fashion item's art sits centred in its cell, which is centred on the placement.
|
||||
// A fashion item's art sits centerd in its cell, which is centerd on the placement.
|
||||
float half = FASHION_CELL / 2;
|
||||
if (tooltip.fashion > 0)
|
||||
game.sprites.fashion_icon_draw(
|
||||
@@ -1630,7 +1717,7 @@ void Ui::label_draw(Game& game, const char* text, Vec2 position)
|
||||
{.color = widget_color(false), .position = {position.x, position.y + TEXT_INSET}});
|
||||
}
|
||||
|
||||
// Notes what the option in box does, to show (see hint_draw) while the mouse is over it, even greyed out.
|
||||
// Notes what the option in box does, to show (see hint_draw) while the mouse is over it, even grayed out.
|
||||
void Ui::hint_set(Game& game, Rect box, const char* text)
|
||||
{
|
||||
if (isWidgetLive && !isFocusShown && is_point_in_rect(game.input.mouse, box))
|
||||
@@ -1722,7 +1809,7 @@ bool Ui::radio_draw(Game& game, Vec2 position, const char* label, int& value, in
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
// A box with its label centred, lighting up under the mouse like a toggle. Returns whether it was clicked. Its focus
|
||||
// A box with its label centerd, lighting up under the mouse like a toggle. Returns whether it was clicked. Its focus
|
||||
// id is its label unless given one (for labels that change or repeat).
|
||||
bool Ui::button_draw(Game& game, Rect box, const char* label, const void* id)
|
||||
{
|
||||
@@ -1756,7 +1843,7 @@ bool Ui::choice_draw(Game& game, Rect box, int& value, int option)
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
// A colour to pick (see choice_draw), filling its slot, more of it once picked.
|
||||
// A color to pick (see choice_draw), filling its slot, more of it once picked.
|
||||
bool Ui::swatch_draw(Game& game, Rect box, Color color, int& value, int option)
|
||||
{
|
||||
bool isChanged = choice_draw(game, box, value, option);
|
||||
@@ -1948,7 +2035,7 @@ bool Ui::number_widget(Game& game, Rect box, float& value, NumberRange range, vo
|
||||
return value != before;
|
||||
}
|
||||
|
||||
// A section title centred in a row, with a line either side of it.
|
||||
// A section title centerd in a row, with a line either side of it.
|
||||
void Ui::separator_draw(Game& game, Rect row, const char* label)
|
||||
{
|
||||
Vec2 size = game.text.size_get(label);
|
||||
@@ -1992,21 +2079,21 @@ bool Ui::fold_draw(Game& game, Rect row, const char* label, bool isOpen)
|
||||
return isClicked;
|
||||
}
|
||||
|
||||
// A fighter walking on the spot at the right of an area, in a colour and wearing worn, its name over it and a caption
|
||||
// A fighter walking on the spot at the right of an area, in a color and wearing worn, its name over it and a caption
|
||||
// under it (a line each).
|
||||
void Ui::fighter_preview_draw(Game& game, Rect area, const char* name, Color color, const char* caption, int worn)
|
||||
{
|
||||
// The name, the fighter at its tallest in what it wears, and the caption, stacked and centred in the column (by
|
||||
// The name, the fighter at its tallest in what it wears, and the caption, stacked and centerd in the column (by
|
||||
// their most, so it holds still as it walks).
|
||||
float centre = area.x + area.w - FIGHTER_PREVIEW_WIDTH / 2;
|
||||
float center = area.x + area.w - FIGHTER_PREVIEW_WIDTH / 2;
|
||||
Vec2 nameSize = game.text.size_get(name);
|
||||
float body = std::max(game.sprites.player.rise_get(PREVIEW_ANIMATION), game.sprites.fashion_height_get(worn));
|
||||
float captionHeight = caption && *caption ? game.text.size_get(caption).y : 0;
|
||||
float height = nameSize.y + PREVIEW_NAME_GAP + body * PREVIEW_SCALE + PREVIEW_CAPTION_GAP + captionHeight;
|
||||
float top = std::round(area.y + (area.h - height) / 2);
|
||||
game.text.draw(game.renderer, name, {.color = color, .position = {std::round(centre - nameSize.x / 2), top}});
|
||||
game.text.draw(game.renderer, name, {.color = color, .position = {std::round(center - nameSize.x / 2), top}});
|
||||
float feet = std::round(top + nameSize.y + PREVIEW_NAME_GAP + body * PREVIEW_SCALE);
|
||||
player_draw(game, {centre, feet}, color, PREVIEW_SCALE, worn);
|
||||
player_draw(game, {center, feet}, color, PREVIEW_SCALE, worn);
|
||||
float y = feet + PREVIEW_CAPTION_GAP;
|
||||
for (const char* start = caption; start && *start;)
|
||||
{
|
||||
@@ -2014,7 +2101,7 @@ void Ui::fighter_preview_draw(Game& game, Rect area, const char* name, Color col
|
||||
char line[CAPTION_LENGTH];
|
||||
SDL_strlcpy(line, start, std::min<int>(end ? end - start + 1 : sizeof(line), sizeof(line)));
|
||||
Vec2 size = game.text.size_get(line);
|
||||
game.text.draw(game.renderer, line, {.color = shade, .position = {std::round(centre - size.x / 2), y}});
|
||||
game.text.draw(game.renderer, line, {.color = shade, .position = {std::round(center - size.x / 2), y}});
|
||||
y += size.y;
|
||||
start = end ? end + 1 : nullptr;
|
||||
}
|
||||
@@ -2246,6 +2333,7 @@ static void bars_arrange(CooldownBar* bars)
|
||||
void Ui::update(Game& game)
|
||||
{
|
||||
hints_update(game);
|
||||
tips_update(game);
|
||||
spectate_update(game);
|
||||
|
||||
// The HUD's elements change while the mouse is over them, and change back after.
|
||||
@@ -2341,7 +2429,7 @@ void Ui::cooldowns_draw(Game& game)
|
||||
}
|
||||
}
|
||||
|
||||
// A weapon's or trinket's HUD icon, centred on the placement.
|
||||
// A weapon's or trinket's HUD icon, centerd on the placement.
|
||||
void Ui::icon_draw(Game& game, ItemKind kind, int item, Draw placement, IconSize size)
|
||||
{
|
||||
char name[TOOLTIP_LENGTH];
|
||||
@@ -2349,7 +2437,7 @@ void Ui::icon_draw(Game& game, ItemKind kind, int item, Draw placement, IconSize
|
||||
(kind == ITEM_TRINKET ? game.sprites.trinkets : game.sprites.icons).draw(game.renderer, name, 0, placement);
|
||||
}
|
||||
|
||||
// An arrow at the screen's edge for every other living fighter out of view, pointing toward it, in its colour, with
|
||||
// An arrow at the screen's edge for every other living fighter out of view, pointing toward it, in its color, with
|
||||
// its name just inside.
|
||||
void Ui::offscreen_draw(Game& game, Vec2 camera)
|
||||
{
|
||||
@@ -2381,7 +2469,7 @@ void Ui::offscreen_draw(Game& game, Vec2 camera)
|
||||
}
|
||||
}
|
||||
|
||||
// Each of this screen's fighters' crosshairs in its colour, filling in and shaking with its weapon's charge and
|
||||
// Each of this screen's fighters' crosshairs in its color, filling in and shaking with its weapon's charge and
|
||||
// cooldown, and darkened through the cooldown: at the mouse for the keyboard's (and whenever the menu is open),
|
||||
// otherwise where its stick aims. With none (a bot playing), the mouse's.
|
||||
void Ui::crosshairs_draw(Game& game, Vec2 camera)
|
||||
@@ -2409,7 +2497,7 @@ void Ui::crosshairs_draw(Game& game, Vec2 camera)
|
||||
{.color = tint, .position = {std::floor(input.mouse.x), std::floor(input.mouse.y)}});
|
||||
}
|
||||
|
||||
// With several players on this screen in a match, a compact panel each along the bottom in their colour: name and
|
||||
// With several players on this screen in a match, a compact panel each along the bottom in their color: name and
|
||||
// health, then rows of small weapon and trinket slots, the held weapon lit.
|
||||
void Ui::panels_draw(Game& game)
|
||||
{
|
||||
@@ -2532,7 +2620,7 @@ void Ui::spectate_update(Game& game)
|
||||
}
|
||||
}
|
||||
|
||||
// While spectating, who's being watched, in their colour, near the bottom, with the controls that switch.
|
||||
// While spectating, who's being watched, in their color, near the bottom, with the controls that switch.
|
||||
void Ui::spectate_draw(Game& game)
|
||||
{
|
||||
if (spectated < 0)
|
||||
@@ -2646,7 +2734,7 @@ RivalLayout Ui::rivals_layout(Game& game)
|
||||
return layout;
|
||||
}
|
||||
|
||||
// Every other fighter's window in a match, bordered in its colour: its name centred (cut short if it doesn't fit), its
|
||||
// Every other fighter's window in a match, bordered in its color: its name centerd (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, half see-through (half again for a fighter out of the
|
||||
// round), and fades by half again while the mouse is over it.
|
||||
@@ -2716,7 +2804,7 @@ void Ui::rivals_draw(Game& game)
|
||||
y += layout.line + RIVAL_PADDING;
|
||||
}
|
||||
|
||||
// Weapons then trinkets, each in its columns, centred with the divider between.
|
||||
// Weapons then trinkets, each in its columns, centerd with the divider between.
|
||||
if (!layout.isIconShown)
|
||||
{
|
||||
renderer.layer_end(base * hover_alpha_get(rivalHovers[index]));
|
||||
@@ -2734,9 +2822,9 @@ void Ui::rivals_draw(Game& game)
|
||||
if (item < 0)
|
||||
continue;
|
||||
bool isLit = kind == ITEM_TRINKET || slot == fighter.slot;
|
||||
Vec2 centre = {x + slot % layout.columns[kind] * cell + iconSize / 2,
|
||||
Vec2 center = {x + slot % layout.columns[kind] * cell + iconSize / 2,
|
||||
y + slot / layout.columns[kind] * cell + iconSize / 2};
|
||||
icon_draw(game, kind, item, {.color = isLit ? color : dim, .position = centre}, layout.size);
|
||||
icon_draw(game, kind, item, {.color = isLit ? color : dim, .position = center}, layout.size);
|
||||
}
|
||||
x += layout.columns[kind] * cell;
|
||||
if (kind == ITEM_WEAPON)
|
||||
@@ -2769,7 +2857,7 @@ void Ui::picks_draw(Game& game, float top)
|
||||
std::floor(height)});
|
||||
}
|
||||
|
||||
// One player's choosing panel in their colour: their name, which kind they're choosing (bumpers switch when both
|
||||
// One player's choosing panel in their color: their name, which kind they're choosing (bumpers switch when both
|
||||
// are), the highlighted item's name, whether they're ready, the items to choose from and the slots they fill. On a
|
||||
// gamepad the D-pad or stick moves the highlight, A puts the item in the first empty slot (or the last) and B empties
|
||||
// the last filled slot. The keyboard's player can also click items in, click a slot to fill it, right-click to empty
|
||||
@@ -2938,8 +3026,8 @@ void Ui::focus_begin(Game& game)
|
||||
: direction == NAV_DOWN ? b.y - (a.y + a.h)
|
||||
: direction == NAV_LEFT ? a.x - (b.x + b.w)
|
||||
: b.x - (a.x + a.w);
|
||||
float centre = isVertical ? (b.y + b.h / 2) - (a.y + a.h / 2) : (b.x + b.w / 2) - (a.x + a.w / 2);
|
||||
bool isAhead = (direction == NAV_UP || direction == NAV_LEFT) ? centre < -1 : centre > 1;
|
||||
float center = isVertical ? (b.y + b.h / 2) - (a.y + a.h / 2) : (b.x + b.w / 2) - (a.x + a.w / 2);
|
||||
bool isAhead = (direction == NAV_UP || direction == NAV_LEFT) ? center < -1 : center > 1;
|
||||
float aside = isVertical ? std::max({0.0f, b.x - (a.x + a.w), a.x - (b.x + b.w)})
|
||||
: std::max({0.0f, b.y - (a.y + a.h), a.y - (b.y + b.h)});
|
||||
if (!isAhead || (!isVertical && aside > 0))
|
||||
@@ -3031,8 +3119,8 @@ bool Ui::is_focused(const void* id, int sub)
|
||||
// past the rows shown. Whatever scrolls says so with scroll_mark, so the move is tried again.
|
||||
int Ui::scroll_take(Rect area)
|
||||
{
|
||||
Vec2 centre = {focusRect.x + focusRect.w / 2, focusRect.y + focusRect.h / 2};
|
||||
if (!scrollWant || !isFocusShown || !is_point_in_rect(centre, area))
|
||||
Vec2 center = {focusRect.x + focusRect.w / 2, focusRect.y + focusRect.h / 2};
|
||||
if (!scrollWant || !isFocusShown || !is_point_in_rect(center, area))
|
||||
return 0;
|
||||
return scrollWant == NAV_UP ? -1 : 1;
|
||||
}
|
||||
@@ -3042,7 +3130,7 @@ void Ui::scroll_mark()
|
||||
isScrolled = true;
|
||||
}
|
||||
|
||||
// Online, in the lobby's sandbox, each queued game: its area on the ground in the queuer's colour (a mark on the floor
|
||||
// Online, in the lobby's sandbox, each queued game: its area on the ground in the queuer's color (a mark on the floor
|
||||
// for each place in it, lit once taken), the game's mode icon and a "!" bobbing over the queuer, and under the area,
|
||||
// how many have readied up and (standing in it) the key to ready up. Hovering the queuer or its icon shows the game.
|
||||
void Ui::queues_draw(Game& game)
|
||||
|
||||
Reference in new issue
Block a user