void DisplayFlag( const struct vec2i pos, const char *s, const bool isOn, const bool isHighlighted) { color_t labelMask = isHighlighted ? colorRed : colorWhite; struct vec2i p = FontStrMask(s, pos, labelMask); p = FontChMask(':', p, labelMask); FontStrMask(isOn ? "On" : "Off", p, isOn ? colorPurple : colorWhite); }
static void DeathmatchFinalScoresDraw(void *data) { UNUSED(data); // This will only draw once const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; GraphicsBlitBkg(&gGraphicsDevice); // Work out the highest kills int maxKills = 0; for (int i = 0; i < (int)gPlayerDatas.size; i++) { const PlayerData *p = CArrayGet(&gPlayerDatas, i); if (p->kills > maxKills) { maxKills = p->kills; } } // Draw players and their names spread evenly around the screen. CASSERT( gPlayerDatas.size >= 2 && gPlayerDatas.size <= 4, "Unimplemented number of players for deathmatch"); #define LAST_MAN_TEXT "Last man standing!" for (int i = 0; i < (int)gPlayerDatas.size; i++) { const Vec2i pos = Vec2iNew( w / 4 + (i & 1) * w / 2, gPlayerDatas.size == 2 ? h / 2 : h / 4 + (i / 2) * h / 2); const PlayerData *p = CArrayGet(&gPlayerDatas, i); DisplayCharacterAndName(pos, &p->Char, p->name); // Kills char s[16]; sprintf(s, "Kills: %d", p->kills); FontStrMask( s, Vec2iNew(pos.x - FontStrW(s) / 2, pos.y + 20), p->kills == maxKills ? colorGreen : colorWhite); // Last man standing? if (p->Lives > 0) { FontStrMask( LAST_MAN_TEXT, Vec2iNew(pos.x - FontStrW(LAST_MAN_TEXT) / 2, pos.y + 30), colorGreen); } } }
static void DogfightFinalScoresDraw(void *data) { UNUSED(data); // This will only draw once const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; GraphicsBlitBkg(&gGraphicsDevice); // Work out who's the winner, or if it's a tie int maxScore = 0; int playersWithMaxScore = 0; for (int i = 0; i < (int)gPlayerDatas.size; i++) { const PlayerData *p = CArrayGet(&gPlayerDatas, i); if (p->RoundsWon > maxScore) { maxScore = p->RoundsWon; playersWithMaxScore = 1; } else if (p->RoundsWon == maxScore) { playersWithMaxScore++; } } const bool isTie = playersWithMaxScore == (int)gPlayerDatas.size; // Draw players and their names spread evenly around the screen. // If it's a tie, display the message in the centre, // otherwise display the winner just below the winning player #define DRAW_TEXT "It's a draw!" #define WINNER_TEXT "Winner!" CASSERT( gPlayerDatas.size >= 2 && gPlayerDatas.size <= 4, "Unimplemented number of players for dogfight"); for (int i = 0; i < (int)gPlayerDatas.size; i++) { const Vec2i pos = Vec2iNew( w / 4 + (i & 1) * w / 2, gPlayerDatas.size == 2 ? h / 2 : h / 4 + (i / 2) * h / 2); const PlayerData *p = CArrayGet(&gPlayerDatas, i); DisplayCharacterAndName(pos, &p->Char, p->name); ShowPlayerScore(pos, p->RoundsWon); if (!isTie && maxScore == p->RoundsWon) { FontStrMask( WINNER_TEXT, Vec2iNew(pos.x - FontStrW(WINNER_TEXT) / 2, pos.y + 30), colorGreen); } } if (isTie) { FontStrCenter(DRAW_TEXT); } }
Vec2i DisplayMenuItem( Vec2i pos, const char *s, int selected, int isDisabled, color_t color) { if (selected) { return FontStrMask(s, pos, colorRed); } else if (isDisabled) { color_t dark = { 64, 64, 64, 255 }; return FontStrMask(s, pos, dark); } else if (!ColorEquals(color, colorBlack)) { return FontStrMask(s, pos, color); } else { return FontStr(s, pos); } }
static void VictoryDraw(void *data) { const VictoryData *vd = data; const int w = gGraphicsDevice.cachedConfig.Res.x; FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_CENTER; opts.Area = gGraphicsDevice.cachedConfig.Res; int y = 30; // Congratulations text #define CONGRATULATIONS "Congratulations, you have completed " FontStrOpt(CONGRATULATIONS, svec2i(0, y), opts); y += 15; opts.Mask = colorRed; FontStrOpt(vd->Campaign->Setting.Title, svec2i(0, y), opts); y += 15; // Final words struct vec2i pos = svec2i((w - FontStrW(vd->FinalWords)) / 2, y); pos = FontChMask('"', pos, colorDarker); pos = FontStrMask(vd->FinalWords, pos, colorPurple); FontChMask('"', pos, colorDarker); }
static void VictoryDraw(void *data) { // This will only draw once const CampaignOptions *c = data; GraphicsBlitBkg(&gGraphicsDevice); const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_CENTER; opts.Area = gGraphicsDevice.cachedConfig.Res; int y = 100; // Congratulations text #define CONGRATULATIONS "Congratulations, you have completed " FontStrOpt(CONGRATULATIONS, Vec2iNew(0, y), opts); y += 15; opts.Mask = colorRed; FontStrOpt(c->Setting.Title, Vec2iNew(0, y), opts); // Display players switch (gPlayerDatas.size) { case 1: { const PlayerData *p = CArrayGet(&gPlayerDatas, 0); DisplayCharacterAndName(Vec2iNew(w / 4, h / 4), &p->Char, p->name); } break; case 2: { // side by side const PlayerData *p1 = CArrayGet(&gPlayerDatas, 0); DisplayCharacterAndName( Vec2iNew(w / 8, h / 4), &p1->Char, p1->name); const PlayerData *p2 = CArrayGet(&gPlayerDatas, 1); DisplayCharacterAndName( Vec2iNew(w / 8 + w / 2, h / 4), &p2->Char, p2->name); } break; case 3: // fallthrough case 4: { // 2x2 const PlayerData *p1 = CArrayGet(&gPlayerDatas, 0); DisplayCharacterAndName( Vec2iNew(w / 8, h / 8), &p1->Char, p1->name); const PlayerData *p2 = CArrayGet(&gPlayerDatas, 1); DisplayCharacterAndName( Vec2iNew(w / 8 + w / 2, h / 8), &p2->Char, p2->name); const PlayerData *p3 = CArrayGet(&gPlayerDatas, 2); DisplayCharacterAndName( Vec2iNew(w / 8, h / 8 + h / 4), &p3->Char, p3->name); if (gPlayerDatas.size == 4) { const PlayerData *p4 = CArrayGet(&gPlayerDatas, 3); DisplayCharacterAndName( Vec2iNew(w / 8 + w / 2, h / 8 + h / 4), &p4->Char, p4->name); } } break; default: CASSERT(false, "not implemented"); break; } // Final words const char *finalWordsSingle[] = { "Ha, next time I'll use my good hand", "Over already? I was just warming up...", "There's just no good opposition to be found these days!", "Well, maybe I'll just do my monthly reload then", "Woof woof", "I'll just bury the bones in the back yard, he-he", "I just wish they'd let me try bare-handed", "Rambo? Who's Rambo?", "<in Austrian accent:> I'll be back", "Gee, my trigger finger is sore", "I need more practice. I think I missed a few shots at times" }; const char *finalWordsMulti[] = { "United we stand, divided we conquer", "Nothing like good teamwork, is there?", "Which way is the camera?", "We eat bullets for breakfast and have grenades as dessert", "We're so cool we have to wear mittens", }; const char *finalWords; if (gPlayerDatas.size == 1) { const int numWords = sizeof finalWordsSingle / sizeof(char *); finalWords = finalWordsSingle[rand() % numWords]; } else { const int numWords = sizeof finalWordsMulti / sizeof(char *); finalWords = finalWordsMulti[rand() % numWords]; } Vec2i pos = Vec2iNew((w - FontStrW(finalWords)) / 2, h / 2 + 20); pos = FontChMask('"', pos, colorDarker); pos = FontStrMask(finalWords, pos, colorPurple); FontChMask('"', pos, colorDarker); }
// Display compact player summary, with player on left half and score summaries // on right half static void DrawPlayerSummary( const Vec2i pos, const Vec2i size, const PlayerData *data) { char s[50]; const int totalTextHeight = FontH() * 7; // display text on right half Vec2i textPos = Vec2iNew( pos.x + size.x / 2, CENTER_Y(pos, size, totalTextHeight)); DisplayCharacterAndName( Vec2iAdd(pos, Vec2iNew(size.x / 4, size.y / 2)), &data->Char, data->name); if (data->survived) { FontStr("Completed mission", textPos); } else { FontStrMask("Failed mission", textPos, colorRed); } textPos.y += 2 * FontH(); sprintf(s, "Score: %d", data->score); FontStr(s, textPos); textPos.y += FontH(); sprintf(s, "Total: %d", data->totalScore); FontStr(s, textPos); textPos.y += FontH(); sprintf(s, "Missions: %d", data->missions + (data->survived ? 1 : 0)); FontStr(s, textPos); textPos.y += FontH(); // Display bonuses if the player has survived if (data->survived) { const int healthBonus = GetHealthBonus(data); if (healthBonus != 0) { sprintf(s, "Health bonus: %d", healthBonus); } const int resurrectionFee = GetResurrectionFee(data); if (resurrectionFee != 0) { sprintf(s, "Resurrection fee: %d", resurrectionFee); } if (healthBonus != 0 || resurrectionFee != 0) { FontStr(s, textPos); textPos.y += FontH(); } const int butcherPenalty = GetButcherPenalty(data); if (butcherPenalty != 0) { sprintf(s, "Butcher penalty: %d", butcherPenalty); } const int ninjaBonus = GetNinjaBonus(data); if (ninjaBonus != 0) { sprintf(s, "Ninja bonus: %d", ninjaBonus); } const int friendlyBonus = GetFriendlyBonus(data); if (friendlyBonus != 0) { sprintf(s, "Friendly bonus: %d", friendlyBonus); } if (butcherPenalty != 0 || ninjaBonus != 0 || friendlyBonus != 0) { FontStr(s, textPos); } } }
void HUDDraw(HUD *hud, const input_device_e pausingDevice) { char s[50]; int flags = 0; const int numPlayersAlive = GetNumPlayers(PLAYER_ALIVE_OR_DYING, false, false); const int numLocalPlayers = GetNumPlayers(PLAYER_ANY, false, true); const int numLocalPlayersAlive = GetNumPlayers(PLAYER_ALIVE_OR_DYING, false, true); Rect2i r; r.Size = Vec2iNew( hud->device->cachedConfig.Res.x, hud->device->cachedConfig.Res.y); if (numLocalPlayersAlive <= 1) { flags = 0; } else if ( ConfigGetEnum(&gConfig, "Interface.Splitscreen") == SPLITSCREEN_NEVER) { flags |= HUDFLAGS_SHARE_SCREEN; } else if (numLocalPlayers == 2) { r.Size.x /= 2; flags |= HUDFLAGS_HALF_SCREEN; } else if (numLocalPlayers == 3 || numLocalPlayers == 4) { r.Size.x /= 2; r.Size.y /= 2; flags |= HUDFLAGS_QUARTER_SCREEN; } else { assert(0 && "not implemented"); } int idx = 0; for (int i = 0; i < (int)gPlayerDatas.size; i++, idx++) { const PlayerData *p = CArrayGet(&gPlayerDatas, i); if (!p->IsLocal) { idx--; continue; } int drawFlags = flags; r.Pos = Vec2iZero(); if (idx & 1) { r.Pos.x = r.Size.x; drawFlags |= HUDFLAGS_PLACE_RIGHT; } if (idx >= 2) { r.Pos.y = r.Size.y; drawFlags |= HUDFLAGS_PLACE_BOTTOM; } TActor *player = NULL; if (IsPlayerAlive(p)) { player = ActorGetByUID(p->ActorUID); } DrawPlayerStatus(hud, p, player, drawFlags, r); DrawScoreUpdate(&hud->scoreUpdates[idx], drawFlags); DrawHealthUpdate(&hud->healthUpdates[idx], drawFlags); DrawAmmoUpdate(&hud->ammoUpdates[idx], drawFlags); } // Only draw radar once if shared if (ConfigGetBool(&gConfig, "Interface.ShowHUDMap") && (flags & HUDFLAGS_SHARE_SCREEN) && IsAutoMapEnabled(gCampaign.Entry.Mode)) { DrawSharedRadar(hud->device, RADAR_SCALE, hud->showExit); } if (numPlayersAlive == 0) { if (AreAllPlayersDeadAndNoLives()) { if (!IsPVP(gCampaign.Entry.Mode)) { FontStrCenter("Game Over!"); } else { FontStrCenter("All Kill!"); } } } else if (hud->mission->state == MISSION_STATE_PICKUP) { int timeLeft = gMission.pickupTime + PICKUP_LIMIT - gMission.time; sprintf(s, "Pickup in %d seconds\n", (timeLeft + (FPS_FRAMELIMIT - 1)) / FPS_FRAMELIMIT); FontStrCenter(s); } if (pausingDevice != INPUT_DEVICE_UNSET) { Vec2i pos = Vec2iScaleDiv(Vec2iMinus( gGraphicsDevice.cachedConfig.Res, FontStrSize("Foo\nPress foo or bar to unpause\nBaz")), 2); const int x = pos.x; FontStr("<Paused>", pos); pos.y += FontH(); pos = FontStr("Press ", pos); color_t c = colorWhite; const char *buttonName = InputGetButtonNameColor(pausingDevice, 0, CMD_ESC, &c); pos = FontStrMask(buttonName, pos, c); FontStr(" again to quit", pos); pos.x = x; pos.y += FontH(); pos = FontStr("Press ", pos); buttonName = InputGetButtonNameColor( pausingDevice, 0, CMD_BUTTON1, &c); pos = FontStrMask(buttonName, pos, c); pos = FontStr(" or ", pos); buttonName = InputGetButtonNameColor( pausingDevice, 0, CMD_BUTTON2, &c); pos = FontStrMask(buttonName, pos, c); FontStr(" to unpause", pos); } if (hud->messageTicks > 0 || hud->messageTicks == -1) { // Draw the message centered, and just below the automap Vec2i pos = Vec2iNew( (hud->device->cachedConfig.Res.x - FontStrW(hud->message)) / 2, AUTOMAP_SIZE + AUTOMAP_PADDING + AUTOMAP_PADDING); FontStrMask(hud->message, pos, colorCyan); } if (ConfigGetBool(&gConfig, "Interface.ShowFPS")) { FPSCounterDraw(&hud->fpsCounter); } if (ConfigGetBool(&gConfig, "Interface.ShowTime")) { WallClockDraw(&hud->clock); } DrawKeycards(hud); // Draw elapsed mission time as MM:SS int missionTimeSeconds = gMission.time / FPS_FRAMELIMIT; sprintf(s, "%d:%02d", missionTimeSeconds / 60, missionTimeSeconds % 60); FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_CENTER; opts.Area = hud->device->cachedConfig.Res; opts.Pad.y = 5; FontStrOpt(s, Vec2iZero(), opts); if (HasObjectives(gCampaign.Entry.Mode)) { DrawObjectiveCounts(hud); } }
static void DrawCompassArrow( GraphicsDevice *g, Rect2i r, Vec2i pos, Vec2i playerPos, color_t mask, const char *label) { Vec2i compassV = Vec2iMinus(pos, playerPos); // Don't draw if objective is on screen if (abs(pos.x - playerPos.x) < r.Size.x / 2 && abs(pos.y - playerPos.y) < r.Size.y / 2) { return; } Vec2i textPos = Vec2iZero(); // Find which edge of screen is the best bool hasDrawn = false; if (compassV.x != 0) { double sx = r.Size.x / 2.0 / compassV.x; int yInt = (int)floor(fabs(sx) * compassV.y + 0.5); if (yInt >= -r.Size.y / 2 && yInt <= r.Size.y / 2) { // Intercepts either left or right side hasDrawn = true; if (compassV.x > 0) { // right edge textPos = Vec2iNew( r.Pos.x + r.Size.x, r.Pos.y + r.Size.y / 2 + yInt); const Pic *p = PicManagerGetPic(&gPicManager, "arrow_right"); Vec2i drawPos = Vec2iNew( textPos.x - p->size.x, textPos.y - p->size.y / 2); BlitMasked(g, p, drawPos, mask, true); } else if (compassV.x < 0) { // left edge textPos = Vec2iNew(r.Pos.x, r.Pos.y + r.Size.y / 2 + yInt); const Pic *p = PicManagerGetPic(&gPicManager, "arrow_left"); Vec2i drawPos = Vec2iNew(textPos.x, textPos.y - p->size.y / 2); BlitMasked(g, p, drawPos, mask, true); } } } if (!hasDrawn && compassV.y != 0) { double sy = r.Size.y / 2.0 / compassV.y; int xInt = (int)floor(fabs(sy) * compassV.x + 0.5); if (xInt >= -r.Size.x / 2 && xInt <= r.Size.x / 2) { // Intercepts either top or bottom side if (compassV.y > 0) { // bottom edge textPos = Vec2iNew( r.Pos.x + r.Size.x / 2 + xInt, r.Pos.y + r.Size.y); const Pic *p = PicManagerGetPic(&gPicManager, "arrow_down"); Vec2i drawPos = Vec2iNew( textPos.x - p->size.x / 2, textPos.y - p->size.y); BlitMasked(g, p, drawPos, mask, true); } else if (compassV.y < 0) { // top edge textPos = Vec2iNew(r.Pos.x + r.Size.x / 2 + xInt, r.Pos.y); const Pic *p = PicManagerGetPic(&gPicManager, "arrow_up"); Vec2i drawPos = Vec2iNew(textPos.x - p->size.x / 2, textPos.y); BlitMasked(g, p, drawPos, mask, true); } } } if (label && strlen(label) > 0) { Vec2i textSize = FontStrSize(label); // Center the text around the target position textPos.x -= textSize.x / 2; textPos.y -= textSize.y / 2; // Make sure the text is inside the screen int padding = 8; textPos.x = MAX(textPos.x, r.Pos.x + padding); textPos.x = MIN(textPos.x, r.Pos.x + r.Size.x - textSize.x - padding); textPos.y = MAX(textPos.y, r.Pos.y + padding); textPos.y = MIN(textPos.y, r.Pos.y + r.Size.y - textSize.y - padding); FontStrMask(label, textPos, mask); } }
static void MenuDisplaySubmenus(const MenuSystem *ms) { int x = 0, yStart = 0; const menu_t *menu = ms->current; switch (menu->type) { // TODO: refactor the three menu types (normal, options, campaign) into one case MENU_TYPE_NORMAL: case MENU_TYPE_OPTIONS: { int iStart = 0; int iEnd = (int)menu->u.normal.subMenus.size; int numMenuLines = 0; int maxIEnd = (int)menu->u.normal.subMenus.size; if (menu->u.normal.maxItems > 0) { // Calculate first/last indices if (menu->u.normal.scroll != 0) { iStart = menu->u.normal.scroll; } maxIEnd = iStart + menu->u.normal.maxItems; } // Count the number of menu items that can fit // This is to account for multi-line items for (iEnd = iStart; iEnd < maxIEnd && iEnd < (int)menu->u.normal.subMenus.size; iEnd++) { const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, iEnd); const int numLines = FontStrNumLines(subMenu->name); if (menu->u.normal.maxItems > 0 && numMenuLines + numLines > menu->u.normal.maxItems) { break; } numMenuLines += numLines; } int maxWidth = 0; for (int i = 0; i < (int)menu->u.normal.subMenus.size; i++) { const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, i); const int width = FontStrW(subMenu->name); if (width > maxWidth) { maxWidth = width; } } // Limit max width if it is larger than the menu system size maxWidth = MIN(ms->size.x, maxWidth); const bool isCentered = menu->type == MENU_TYPE_NORMAL; switch (ms->align) { case MENU_ALIGN_CENTER: x = MS_CENTER_X(*ms, maxWidth); if (!isCentered) { x -= 20; } break; case MENU_ALIGN_LEFT: x = ms->pos.x; break; default: assert(0 && "unknown alignment"); break; } yStart = MS_CENTER_Y(*ms, numMenuLines * FontH()); if (menu->u.normal.maxItems > 0) { // Display scroll arrows if (menu->u.normal.scroll != 0) { DisplayMenuItem( Vec2iNew( MS_CENTER_X(*ms, FontW('^')), yStart - 2 - FontH()), "^", 0, 0, colorBlack); } if (iEnd < (int)menu->u.normal.subMenus.size - 1) { DisplayMenuItem( Vec2iNew( MS_CENTER_X(*ms, FontW('v')), yStart + numMenuLines*FontH() + 2), "v", 0, 0, colorBlack); } } const int xOptions = x + maxWidth + 10; // Display normal menu items Vec2i pos = Vec2iNew(x, yStart); for (int i = iStart; i < iEnd; i++) { const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, i); char *nameBuf; CMALLOC(nameBuf, strlen(subMenu->name) + 3); if (subMenu->type == MENU_TYPE_NORMAL && subMenu->u.normal.isSubmenusAlt) { sprintf(nameBuf, "%s >", subMenu->name); } else { strcpy(nameBuf, subMenu->name); } switch (menu->u.normal.align) { case MENU_ALIGN_CENTER: pos.x = MS_CENTER_X(*ms, FontStrW(nameBuf)); break; case MENU_ALIGN_LEFT: // Do nothing break; default: assert(0 && "unknown alignment"); break; } const int yNext = DisplayMenuItem( pos, nameBuf, i == menu->u.normal.index, subMenu->isDisabled, subMenu->color).y + FontH(); // display option value if (subMenu->type == MENU_TYPE_SET_OPTION_TOGGLE || subMenu->type == MENU_TYPE_SET_OPTION_RANGE || subMenu->type == MENU_TYPE_SET_OPTION_SEED || subMenu->type == MENU_TYPE_SET_OPTION_UP_DOWN_VOID_FUNC_VOID || subMenu->type == MENU_TYPE_SET_OPTION_RANGE_GET_SET) { const int optionInt = MenuOptionGetIntValue(subMenu); const Vec2i value_pos = Vec2iNew(xOptions, pos.y); switch (subMenu->u.option.displayStyle) { case MENU_OPTION_DISPLAY_STYLE_INT: { char buf[32]; sprintf(buf, "%d", optionInt); FontStr(buf, value_pos); } break; case MENU_OPTION_DISPLAY_STYLE_YES_NO: FontStr(optionInt ? "Yes" : "No", value_pos); break; case MENU_OPTION_DISPLAY_STYLE_ON_OFF: FontStr(optionInt ? "On" : "Off", value_pos); break; case MENU_OPTION_DISPLAY_STYLE_STR_FUNC: FontStr(subMenu->u.option.uFunc.str(), value_pos); break; case MENU_OPTION_DISPLAY_STYLE_INT_TO_STR_FUNC: FontStr( subMenu->u.option.uFunc.intToStr(optionInt), value_pos); break; default: break; } } pos.y = yNext; } } break; case MENU_TYPE_KEYS: { int xKeys; x = MS_CENTER_X(*ms, (FontW('a') * 10)) / 2; xKeys = x * 3; yStart = (gGraphicsDevice.cachedConfig.Res.y / 2) - (FontH() * 10); for (int i = 0; i < (int)menu->u.normal.subMenus.size; i++) { int y = yStart + i * FontH(); int isSelected = i == menu->u.normal.index; const menu_t *subMenu = CArrayGet(&menu->u.normal.subMenus, i); const char *name = subMenu->name; if (isSelected && subMenu->type != MENU_TYPE_SET_OPTION_CHANGE_KEY) { FontStrMask(name, Vec2iNew(x, y), colorRed); } else { FontStr(name, Vec2iNew(x, y)); } if (subMenu->type == MENU_TYPE_SET_OPTION_CHANGE_KEY) { const char *keyName; if (menu->u.normal.changeKeyMenu == subMenu) { keyName = "Press a key"; } else if (subMenu->u.changeKey.code == KEY_CODE_MAP) { keyName = SDL_GetKeyName(gConfig.Input.PlayerKeys[0].Keys.map); } else { keyName = SDL_GetKeyName(InputGetKey( subMenu->u.changeKey.keys, subMenu->u.changeKey.code)); } DisplayMenuItem( Vec2iNew(xKeys, y), keyName, isSelected, 0, colorBlack); } } } break; default: // No submenus, don't display anything break; } }
static void PlayerListCustomDraw( const menu_t *menu, GraphicsDevice *g, const struct vec2i pos, const struct vec2i size, const void *data) { UNUSED(menu); UNUSED(g); // Draw players starting from the index // TODO: custom columns const PlayerList *pl = data; // First draw the headers const int xStart = pos.x + 80 + (size.x - 320) / 2; int x = xStart; int y = pos.y; FontStrMask("Player", svec2i(x, y), colorPurple); x += 100; FontStrMask("Score", svec2i(x, y), colorPurple); x += 32; FontStrMask("Kills", svec2i(x, y), colorPurple); y += FontH() * 2 + PLAYER_LIST_ROW_HEIGHT + 4; // Then draw the player list int maxScore = -1; for (int i = pl->scroll; i < MIN((int)pl->playerUIDs.size, pl->scroll + PlayerListMaxRows(pl)); i++) { const int *playerUID = CArrayGet(&pl->playerUIDs, i); PlayerData *p = PlayerDataGetByUID(*playerUID); if (p == NULL) { continue; } if (maxScore < GetModeScore(p)) { maxScore = GetModeScore(p); } x = xStart; // Highlight local players using different coloured text const color_t textColor = p->IsLocal ? colorPurple : colorWhite; // Draw the players offset on alternate rows DisplayCharacterAndName( svec2i(x + (i & 1) * 16, y + 4), &p->Char, DIRECTION_DOWN, p->name, textColor); // Draw score x += 100; char buf[256]; sprintf(buf, "%d", p->Totals.Score); FontStrMask(buf, svec2i(x, y), textColor); // Draw kills x += 32; sprintf(buf, "%d", p->Totals.Kills); FontStrMask(buf, svec2i(x, y), textColor); // Draw winner/award text x += 32; if (pl->showWinners && GetModeScore(p) == maxScore) { FontStrMask("Winner!", svec2i(x, y), colorGreen); } else if (pl->showLastMan && p->Lives > 0 && gCampaign.Entry.Mode == GAME_MODE_DEATHMATCH) { // Only show last man standing on deathmatch mode FontStrMask("Last man standing!", svec2i(x, y), colorGreen); } y += PLAYER_LIST_ROW_HEIGHT; } // Draw indicator arrows if there's enough to scroll if (pl->scroll > 0) { FontStr("^", svec2i( CENTER_X(pos, size, FontStrW("^")), pos.y + FontH())); } if (pl->scroll < PlayerListMaxScroll(pl)) { FontStr("v", svec2i( CENTER_X(pos, size, FontStrW("v")), pos.y + size.y - FontH())); } // Finally draw any custom stuff if (pl->drawFunc) { pl->drawFunc(pl->data); } }
static void DisplayAt(int x, int y, const char *s, int hilite) { color_t mask = hilite ? colorRed : colorWhite; FontStrMask(s, Vec2iNew(x, y), mask); }