static void DisplayEquippedWeapons( const menu_t *menu, GraphicsDevice *g, const Vec2i pos, const Vec2i size, const void *data) { UNUSED(g); const WeaponMenuData *d = data; Vec2i weaponsPos; Vec2i maxTextSize = FontStrSize("LongestWeaponName"); UNUSED(menu); Vec2i dPos = pos; dPos.x -= size.x; // move to left half of screen weaponsPos = Vec2iNew( dPos.x + size.x * 3 / 4 - maxTextSize.x / 2, CENTER_Y(dPos, size, 0) + 14); const PlayerData *p = PlayerDataGetByUID(d->display.PlayerUID); if (p->weaponCount == 0) { FontStr("None selected...", weaponsPos); } else { for (int i = 0; i < p->weaponCount; i++) { FontStr( p->weapons[i]->name, Vec2iAdd(weaponsPos, Vec2iNew(0, i * FontH()))); } } }
static void MissionBriefingDraw(void *data) { const MissionBriefingData *mData = data; GraphicsClear(&gGraphicsDevice); // Mission title FontStrOpt(mData->Title, Vec2iZero(), mData->TitleOpts); // Display password FontStrOpt(mData->Password, Vec2iZero(), mData->PasswordOpts); // Display description with typewriter effect FontStr(mData->TypewriterBuf, mData->DescriptionPos); // Display objectives CA_FOREACH( const Objective, o, mData->MissionOptions->missionData->Objectives) // Do not brief optional objectives if (o->Required == 0) { continue; } Vec2i offset = Vec2iNew(0, _ca_index * mData->ObjectiveHeight); FontStr(o->Description, Vec2iAdd(mData->ObjectiveDescPos, offset)); // Draw the icons slightly offset so that tall icons don't overlap each // other offset.x = -16 * (_ca_index & 1); DrawObjectiveInfo(o, Vec2iAdd(mData->ObjectiveInfoPos, offset)); CA_FOREACH_END() }
static void MissionBriefingDraw(void *data) { const MissionBriefingData *mData = data; GraphicsBlitBkg(&gGraphicsDevice); // Mission title FontStrOpt(mData->Title, Vec2iZero(), mData->TitleOpts); // Display password FontStrOpt(mData->Password, Vec2iZero(), mData->PasswordOpts); // Display description with typewriter effect FontStr(mData->TypewriterBuf, mData->DescriptionPos); // Display objectives for (int i = 0; i < (int)mData->MissionOptions->missionData->Objectives.size; i++) { const MissionObjective *o = CArrayGet(&mData->MissionOptions->missionData->Objectives, i); // Do not brief optional objectives if (o->Required == 0) { continue; } const Vec2i yInc = Vec2iNew(0, i * mData->ObjectiveHeight); FontStr(o->Description, Vec2iAdd(mData->ObjectiveDescPos, yInc)); DrawObjectiveInfo( mData->MissionOptions, i, Vec2iAdd(mData->ObjectiveInfoPos, yInc)); } }
bool ConfirmScreen(const char *info, const char *msg) { int w = gGraphicsDevice.cachedConfig.Res.x; int h = gGraphicsDevice.cachedConfig.Res.y; ClearScreen(&gGraphicsDevice); FontStr(info, Vec2iNew((w - FontStrW(info)) / 2, (h - FontH()) / 2)); FontStr(msg, Vec2iNew((w - FontStrW(msg)) / 2, (h + FontH()) / 2)); BlitFlip(&gGraphicsDevice); SDL_Keycode k = SDL_GetKeyFromScancode(GetKey(&gEventHandlers)); return k == SDLK_y; }
bool ConfirmScreen(const char *info, const char *msg) { int w = gGraphicsDevice.cachedConfig.Res.x; int h = gGraphicsDevice.cachedConfig.Res.y; ClearScreen(&gGraphicsDevice); FontStr(info, Vec2iNew((w - FontStrW(info)) / 2, (h - FontH()) / 2)); FontStr(msg, Vec2iNew((w - FontStrW(msg)) / 2, (h + FontH()) / 2)); BlitFlip(&gGraphicsDevice, &gConfig.Graphics); int c = GetKey(&gEventHandlers); return (c == 'Y' || c == 'y'); }
bool ConfirmScreen(const char *info, const char *msg) { int w = gGraphicsDevice.cachedConfig.Res.x; int h = gGraphicsDevice.cachedConfig.Res.y; ClearScreen(&gGraphicsDevice); FontStr(info, svec2i((w - FontStrW(info)) / 2, (h - FontH()) / 2)); FontStr(msg, svec2i((w - FontStrW(msg)) / 2, (h + FontH()) / 2)); WindowContextPreRender(&gGraphicsDevice.gameWindow); BlitUpdateFromBuf(&gGraphicsDevice, gGraphicsDevice.screen); WindowContextPostRender(&gGraphicsDevice.gameWindow); SDL_Keycode k = SDL_GetKeyFromScancode(GetKey(&gEventHandlers)); return k == SDLK_y; }
static void CampaignIntroDraw(void *data) { // This will only draw once const CampaignSetting *c = data; GraphicsBlitBkg(&gGraphicsDevice); const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; const int y = h / 4; // Display title + author char *buf; CMALLOC(buf, strlen(c->Title) + strlen(c->Author) + 16); sprintf(buf, "%s by %s", c->Title, c->Author); FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_CENTER; opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad.y = y - 25; FontStrOpt(buf, Vec2iZero(), opts); CFREE(buf); // Display campaign description // allow some slack for newlines if (strlen(c->Description) > 0) { CMALLOC(buf, strlen(c->Description) * 2); // Pad about 1/6th of the screen width total (1/12th left and right) FontSplitLines(c->Description, buf, w * 5 / 6); FontStr(buf, Vec2iNew(w / 12, y)); CFREE(buf); } }
static int DisplayPage( const char *title, int idx, struct Entry *e, int highlights[MAX_LOCAL_PLAYERS]) { int x = 80; int y = 5 + FontH(); FontStr(title, Vec2iNew(5, 5)); while (idx < MAX_ENTRY && e[idx].score > 0 && x < 300) { bool isHighlighted = false; for (int i = 0; i < MAX_LOCAL_PLAYERS; i++) { if (idx == highlights[i]) { isHighlighted = true; break; } } y += DisplayEntry(x, y, idx, &e[idx], isHighlighted); if (y > 198 - FontH()) { y = 20; x += 100; } idx++; } return idx; }
static void ShowPlayerScore(const Vec2i pos, const int score) { char s[16]; sprintf(s, "Score: %d", score); const Vec2i scorePos = Vec2iNew(pos.x - FontStrW(s) / 2, pos.y + 20); FontStr(s, scorePos); }
static void DrawSpawnerName( const TObject *obj, DrawBuffer *b, const struct vec2i offset) { const char *name = obj->Class->u.PickupClass->Name; const struct vec2i textPos = svec2i( (int)obj->thing.Pos.x - b->xTop + offset.x - FontStrW(name) / 2, (int)obj->thing.Pos.y - b->yTop + offset.y); FontStr(name, textPos); }
static void DrawSpawnerName( const TObject *obj, DrawBuffer *b, const Vec2i offset) { const char *name = obj->Class->u.PickupClass->Name; const Vec2i textPos = Vec2iNew( obj->tileItem.x - b->xTop + offset.x - FontStrW(name) / 2, obj->tileItem.y - b->yTop + offset.y); FontStr(name, textPos); }
// Display a character and the player name above it, with the character // centered around the target position void DisplayCharacterAndName(Vec2i pos, Character *c, char *name) { Vec2i namePos; // Move the point down a bit since the default character draw point is at // its feet pos.y += 8; namePos = Vec2iAdd(pos, Vec2iNew(-FontStrW(name) / 2, -30)); DrawCharacterSimple( c, pos, DIRECTION_DOWN, STATE_IDLE, -1, GUNSTATE_READY, &c->table); FontStr(name, namePos); }
static void DrawObjectiveName( const TTileItem *ti, DrawBuffer *b, const Vec2i offset) { const int objective = ObjectiveFromTileItem(ti->flags); const Objective *o = CArrayGet(&gMission.missionData->Objectives, objective); const char *typeName = ObjectiveTypeStr(o->Type); const Vec2i textPos = Vec2iNew( ti->x - b->xTop + offset.x - FontStrW(typeName) / 2, ti->y - b->yTop + offset.y); FontStr(typeName, textPos); }
static void DrawObjectiveName( const Thing *ti, DrawBuffer *b, const struct vec2i offset) { const int objective = ObjectiveFromThing(ti->flags); const Objective *o = CArrayGet(&gMission.missionData->Objectives, objective); const char *typeName = ObjectiveTypeStr(o->Type); const struct vec2i textPos = svec2i( (int)ti->Pos.x - b->xTop + offset.x - FontStrW(typeName) / 2, (int)ti->Pos.y - b->yTop + offset.y); FontStr(typeName, textPos); }
void DisplayMapItemWithDensity( const struct vec2i pos, const MapObjectDensity *mod, const bool isHighlighted) { DisplayMapItem(pos, mod->M); if (isHighlighted) { FontCh('>', svec2i_add(pos, svec2i(-8, -4))); } char s[10]; sprintf(s, "%d", mod->Density); FontStr(s, svec2i_add(pos, svec2i(-8, 5))); }
void UITooltipDraw(GraphicsDevice *device, Vec2i pos, const char *s) { Vec2i bgSize = FontStrSize(s); pos = Vec2iAdd(pos, Vec2iNew(10, 10)); // add offset DrawRectangle( device, Vec2iAdd(pos, Vec2iScale(Vec2iUnit(), -TOOLTIP_PADDING)), Vec2iAdd(bgSize, Vec2iScale(Vec2iUnit(), 2 * TOOLTIP_PADDING)), bgColor, 0); FontStr(s, pos); }
static void DrawChatter( const TTileItem *ti, DrawBuffer *b, const Vec2i offset) { const TActor *a = CArrayGet(&gActors, ti->id); // Draw character text if (strlen(a->Chatter) > 0) { const Vec2i textPos = Vec2iNew( a->tileItem.x - b->xTop + offset.x - FontStrW(a->Chatter) / 2, a->tileItem.y - b->yTop + offset.y - ACTOR_HEIGHT); FontStr(a->Chatter, textPos); } }
static void SaveTemplateDisplayTitle( menu_t *menu, GraphicsDevice *g, Vec2i pos, Vec2i size, void *data) { UNUSED(g); PlayerSelectMenuData *d = data; char buf[256]; UNUSED(menu); UNUSED(size); // Display "Save <template>..." title sprintf(buf, "Save %s...", d->display.pData->name); FontStr(buf, Vec2iAdd(pos, Vec2iNew(0, 0))); }
static void PlayerSelectionDraw(void *data) { const PlayerSelectionData *pData = data; GraphicsBlitBkg(&gGraphicsDevice); const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; int idx = 0; for (int i = 0; i < (int)gPlayerDatas.size; i++, idx++) { const PlayerData *p = CArrayGet(&gPlayerDatas, i); if (!p->IsLocal) { idx--; continue; } if (p->inputDevice != INPUT_DEVICE_UNSET) { MenuDisplay(&pData->menus[idx].ms); } else { Vec2i center = Vec2iZero(); const char *prompt = "Press Fire to join..."; const Vec2i offset = Vec2iScaleDiv(FontStrSize(prompt), -2); switch (GetNumPlayers(false, false, true)) { case 1: // Center of screen center = Vec2iNew(w / 2, h / 2); break; case 2: // Side by side center = Vec2iNew(idx * w / 2 + w / 4, h / 2); break; case 3: case 4: // Four corners center = Vec2iNew( (idx & 1) * w / 2 + w / 4, (idx / 2) * h / 2 + h / 4); break; default: CASSERT(false, "not implemented"); break; } FontStr(prompt, Vec2iAdd(center, offset)); } } }
void DisplayCharacter( const Vec2i pos, const Character *c, const bool hilite, const bool showGun) { DrawCharacterSimple( c, pos, DIRECTION_DOWN, STATE_IDLE, -1, GUNSTATE_READY, &c->table); if (hilite) { FontCh('>', Vec2iAdd(pos, Vec2iNew(-8, -16))); if (showGun) { FontStr(c->Gun->name, Vec2iAdd(pos, Vec2iNew(-8, 8))); } } }
static void SaveTemplateDisplayTitle( const menu_t *menu, GraphicsDevice *g, const Vec2i pos, const Vec2i size, const void *data) { UNUSED(g); const PlayerSelectMenuData *d = data; char buf[256]; UNUSED(menu); UNUSED(size); // Display "Save <template>..." title const PlayerData *p = PlayerDataGetByUID(d->display.PlayerUID); sprintf(buf, "Save %s...", p->name); FontStr(buf, Vec2iAdd(pos, Vec2iNew(0, 0))); }
void DrawCharacterSimple( Character *c, const Vec2i pos, const direction_e d, const bool hilite, const bool showGun) { ActorPics pics; GetCharacterPics( &pics, c, d, STATE_IDLE, NULL, GUNSTATE_READY, false, NULL, NULL, 0); DrawActorPics(&pics, pos, d); if (hilite) { FontCh('>', Vec2iAdd(pos, Vec2iNew(-8, -16))); if (showGun) { FontStr(c->Gun->name, Vec2iAdd(pos, Vec2iNew(-8, 8))); } } }
static void DrawObjectiveCounts(HUD *hud) { int x = 5 + GAUGE_WIDTH; int y = hud->device->cachedConfig.Res.y - 5 - FontH(); for (int i = 0; i < (int)gMission.missionData->Objectives.size; i++) { MissionObjective *mo = CArrayGet(&gMission.missionData->Objectives, i); const ObjectiveDef *o = CArrayGet(&gMission.Objectives, i); // Don't draw anything for optional objectives if (mo->Required == 0) { continue; } // Objective color dot Draw_Rect(x, y + 3, 2, 2, o->color); x += 5; char s[32]; int itemsLeft = mo->Required - o->done; if (itemsLeft > 0) { if (!(mo->Flags & OBJECTIVE_UNKNOWNCOUNT)) { sprintf(s, "%s: %d", ObjectiveTypeStr(mo->Type), itemsLeft); } else { sprintf(s, "%s: ?", ObjectiveTypeStr(mo->Type)); } } else { strcpy(s, "Done"); } FontStr(s, Vec2iNew(x, y)); DrawNumUpdate( CArrayGet(&hud->objectiveUpdates, i), "%d", o->done, Vec2iNew(x + FontStrW(s) - 8, y), 0); x += 40; } }
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); } }
// 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); } } }
static void MissionSummaryDraw(void *data) { // This will only draw once const struct MissionOptions *m = data; GraphicsBlitBkg(&gGraphicsDevice); const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; // Display password if (strlen(gAutosave.LastMission.Password) > 0) { char s[64]; sprintf(s, "Last password: %s", gAutosave.LastMission.Password); FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_CENTER; opts.VAlign = ALIGN_END; opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad.y = opts.Area.y / 12; FontStrOpt(s, Vec2iZero(), opts); } // Display objectives and bonuses Vec2i pos = Vec2iNew(w / 6, h / 2 + h / 10); int idx = 1; for (int i = 0; i < (int)m->missionData->Objectives.size; i++) { const struct Objective *o = CArrayGet(&m->Objectives, i); const MissionObjective *mo = CArrayGet(&m->missionData->Objectives, i); // Do not mention optional objectives with none completed if (o->done == 0 && mo->Required == 0) { continue; } // Objective icon DrawObjectiveInfo(m, i, Vec2iAdd(pos, Vec2iNew(-26, FontH()))); // Objective completion text char s[100]; sprintf(s, "Objective %d: %d of %d, %d required", idx, o->done, mo->Count, mo->Required); FontOpts opts = FontOptsNew(); opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = pos; if (mo->Required == 0) { // Show optional objectives in purple opts.Mask = colorPurple; } FontStrOpt(s, Vec2iZero(), opts); // Objective status text opts = FontOptsNew(); opts.HAlign = ALIGN_END; opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = pos; if (o->done < mo->Required) { opts.Mask = colorRed; FontStrOpt("Failed", Vec2iZero(), opts); } else if ( o->done == mo->Count && o->done > mo->Required && AreAnySurvived()) { opts.Mask = colorGreen; char buf[16]; sprintf(buf, "Perfect: %d", PERFECT_BONUS); FontStrOpt(buf, Vec2iZero(), opts); } else if (mo->Required > 0) { FontStrOpt("Done", Vec2iZero(), opts); } else { FontStrOpt("Bonus!", Vec2iZero(), opts); } pos.y += 15; idx++; } // Draw other bonuses if (AreAnySurvived()) { char s[64]; sprintf(s, "Access bonus: %d", GetAccessBonus(m)); FontStr(s, pos); pos.y += FontH() + 1; int seconds; const int timeBonus = GetTimeBonus(m, &seconds); sprintf(s, "Time bonus: %d secs x 25 = %d", seconds, timeBonus); FontStr(s, pos); } // Draw per-player summaries Vec2i size; switch (gPlayerDatas.size) { case 1: size = Vec2iNew(w, h / 2); DrawPlayerSummary(Vec2iZero(), size, CArrayGet(&gPlayerDatas, 0)); break; case 2: // side by side size = Vec2iNew(w / 2, h / 2); DrawPlayerSummary(Vec2iZero(), size, CArrayGet(&gPlayerDatas, 0)); DrawPlayerSummary( Vec2iNew(w / 2, 0), size, CArrayGet(&gPlayerDatas, 1)); break; case 3: // fallthrough case 4: // 2x2 size = Vec2iNew(w / 2, h / 4); DrawPlayerSummary(Vec2iZero(), size, CArrayGet(&gPlayerDatas, 0)); DrawPlayerSummary( Vec2iNew(w / 2, 0), size, CArrayGet(&gPlayerDatas, 1)); DrawPlayerSummary( Vec2iNew(0, h / 4), size, CArrayGet(&gPlayerDatas, 2)); if (gPlayerDatas.size == 4) { DrawPlayerSummary( Vec2iNew(w / 2, h / 4), size, CArrayGet(&gPlayerDatas, 3)); } break; default: CASSERT(false, "not implemented"); break; } }
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 MissionSummaryDraw( const menu_t *menu, GraphicsDevice *g, const Vec2i p, const Vec2i size, const void *data) { UNUSED(menu); UNUSED(p); UNUSED(size); const struct MissionOptions *m = data; const int w = gGraphicsDevice.cachedConfig.Res.x; const int h = gGraphicsDevice.cachedConfig.Res.y; // Display password if (strlen(gAutosave.LastMission.Password) > 0) { char s[64]; sprintf(s, "Last password: %s", gAutosave.LastMission.Password); FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_CENTER; opts.VAlign = ALIGN_END; opts.Area = g->cachedConfig.Res; opts.Pad.y = opts.Area.y / 12; FontStrOpt(s, Vec2iZero(), opts); } // Display objectives and bonuses Vec2i pos = Vec2iNew(w / 6, h / 2 + h / 10); int idx = 1; CA_FOREACH(const Objective, o, m->missionData->Objectives) // Do not mention optional objectives with none completed if (o->done == 0 && !ObjectiveIsRequired(o)) { continue; } // Objective icon DrawObjectiveInfo(o, Vec2iAdd(pos, Vec2iNew(-26, FontH()))); // Objective completion text char s[100]; sprintf(s, "Objective %d: %d of %d, %d required", idx, o->done, o->Count, o->Required); FontOpts opts = FontOptsNew(); opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = pos; if (!ObjectiveIsRequired(o)) { // Show optional objectives in purple opts.Mask = colorPurple; } FontStrOpt(s, Vec2iZero(), opts); // Objective status text opts = FontOptsNew(); opts.HAlign = ALIGN_END; opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = pos; if (!ObjectiveIsComplete(o)) { opts.Mask = colorRed; FontStrOpt("Failed", Vec2iZero(), opts); } else if (ObjectiveIsPerfect(o) && AreAnySurvived()) { opts.Mask = colorGreen; char buf[16]; sprintf(buf, "Perfect: %d", PERFECT_BONUS); FontStrOpt(buf, Vec2iZero(), opts); } else if (ObjectiveIsRequired(o)) { FontStrOpt("Done", Vec2iZero(), opts); } else { FontStrOpt("Bonus!", Vec2iZero(), opts); } pos.y += 15; idx++; CA_FOREACH_END() // Draw other bonuses if (AreAnySurvived()) { char s[64]; sprintf(s, "Access bonus: %d", GetAccessBonus(m)); FontStr(s, pos); pos.y += FontH() + 1; int seconds; const int timeBonus = GetTimeBonus(m, &seconds); sprintf(s, "Time bonus: %d secs x 25 = %d", seconds, timeBonus); FontStr(s, pos); } // Draw per-player summaries PlayerData *pds[MAX_LOCAL_PLAYERS]; idx = 0; CA_FOREACH(PlayerData, pd, gPlayerDatas) if (!pd->IsLocal) { continue; } pds[idx] = pd; idx++; CA_FOREACH_END() Vec2i playerSize; switch (idx) { case 1: playerSize = Vec2iNew(w, h / 2); DrawPlayerSummary(Vec2iZero(), playerSize, pds[0]); break; case 2: // side by side playerSize = Vec2iNew(w / 2, h / 2); DrawPlayerSummary(Vec2iZero(), playerSize, pds[0]); DrawPlayerSummary(Vec2iNew(w / 2, 0), playerSize, pds[1]); break; case 3: // fallthrough case 4: // 2x2 playerSize = Vec2iNew(w / 2, h / 4); DrawPlayerSummary(Vec2iZero(), playerSize, pds[0]); DrawPlayerSummary(Vec2iNew(w / 2, 0), playerSize, pds[1]); DrawPlayerSummary(Vec2iNew(0, h / 4), playerSize, pds[2]); if (idx == 4) { DrawPlayerSummary(Vec2iNew(w / 2, h / 4), playerSize, pds[3]); } break; default: CASSERT(false, "not implemented"); break; } }
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; } }
void MenuDisplayPlayerControls( menu_t *menu, GraphicsDevice *g, Vec2i pos, Vec2i size, void *data) { UNUSED(g); char s[256]; MenuDisplayPlayerControlsData *d = data; Vec2i textPos = Vec2iNew(0, pos.y + size.y - FontH()); int textWidth = 0; UNUSED(menu); switch (d->pData->inputDevice) { case INPUT_DEVICE_KEYBOARD: { input_keys_t *keys = &d->inputConfig->PlayerKeys[d->pData->deviceIndex].Keys; sprintf(s, "(%s, %s, %s, %s, %s and %s)", SDL_GetKeyName(keys->left), SDL_GetKeyName(keys->right), SDL_GetKeyName(keys->up), SDL_GetKeyName(keys->down), SDL_GetKeyName(keys->button1), SDL_GetKeyName(keys->button2)); textWidth = FontStrW(s); } break; case INPUT_DEVICE_MOUSE: sprintf(s, "(mouse wheel to scroll, left and right click)"); break; case INPUT_DEVICE_JOYSTICK: sprintf(s, "(%s)", InputDeviceName(d->pData->inputDevice, d->pData->deviceIndex)); break; case INPUT_DEVICE_NET: sprintf(s, "(Network)"); break; case INPUT_DEVICE_AI: sprintf(s, "(Computer)"); break; default: assert(0 && "unknown device"); break; } // If the text is too long, split the text with a newline textWidth = FontStrW(s); if (textWidth < 125) { textPos.x = pos.x - textWidth / 2; FontStr(s, textPos); } else { // find the first whitespace before half of the string, split there, // and print two lines char *secondLine; for (secondLine = &s[strlen(s) / 2]; secondLine > s; secondLine--) { if (isspace(*secondLine)) { *secondLine = '\0'; secondLine++; break; } } textWidth = FontStrW(s); textPos.x = pos.x - textWidth / 2; textPos.y -= FontH(); FontStr(s, textPos); textWidth = FontStrW(secondLine); textPos.x = pos.x - textWidth / 2; textPos.y += FontH(); FontStr(secondLine, textPos); } }