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)); } }
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() }
void ShowControls(void) { FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_CENTER; opts.VAlign = ALIGN_END; opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad.y = 10; #ifdef __GCWZERO__ FontStrOpt( "(use joystick or D pad + START + SELECT)", Vec2iZero(), opts); #else FontStrOpt( "(use joystick 1 or arrow keys + Enter/Backspace)", Vec2iZero(), opts); #endif }
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); } }
void MenuDisplay(const MenuSystem *ms) { const menu_t *menu = ms->current; if (menu->type == MENU_TYPE_CUSTOM) { menu->u.customData.displayFunc( menu, ms->graphics, ms->pos, ms->size, menu->u.customData.data); } else { MenuDisplayItems(ms); if (strlen(menu->u.normal.title) != 0) { FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_CENTER; opts.Area = ms->size; opts.Pad = Vec2iNew(20, 20); FontStrOpt(menu->u.normal.title, ms->pos, opts); } MenuDisplaySubmenus(ms); } for (int i = 0; i < (int)ms->customDisplayFuncs.size; i++) { MenuCustomDisplayFunc *cdf = CArrayGet(&ms->customDisplayFuncs, i); cdf->Func(NULL, ms->graphics, ms->pos, ms->size, cdf->Data); } if (menu->customDisplayFunc) { menu->customDisplayFunc( menu, ms->graphics, ms->pos, ms->size, menu->customDisplayData); } }
void WallClockDraw(WallClock *wc) { char s[50]; sprintf(s, "%02d:%02d", wc->hours, wc->minutes); FontOpts opts = FontOptsNew(); opts.VAlign = ALIGN_END; opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = Vec2iNew(10, 5 + FontH()); FontStrOpt(s, Vec2iZero(), opts); }
static void DrawWeaponStatus( HUD *hud, const TActor *actor, Vec2i pos, const FontAlign hAlign, const FontAlign vAlign) { const Weapon *weapon = ActorGetGun(actor); // Draw gun icon, and allocate padding to draw the gun icon const GunDescription *g = ActorGetGun(actor)->Gun; const Vec2i iconPos = Vec2iAligned( Vec2iNew(pos.x - 2, pos.y - 2), g->Icon->size, hAlign, vAlign, gGraphicsDevice.cachedConfig.Res); Blit(&gGraphicsDevice, g->Icon, iconPos); // don't draw gauge if not reloading if (weapon->lock > 0) { const Vec2i gaugePos = Vec2iAdd(pos, Vec2iNew(-1 + GUN_ICON_PAD, -1)); const Vec2i size = Vec2iNew(GAUGE_WIDTH - GUN_ICON_PAD, FontH() + 2); const color_t barColor = { 0, 0, 255, 255 }; const int maxLock = weapon->Gun->Lock; int innerWidth; color_t backColor = { 128, 128, 128, 255 }; if (maxLock == 0) { innerWidth = 0; } else { innerWidth = MAX(1, size.x * (maxLock - weapon->lock) / maxLock); } DrawGauge( hud->device, gaugePos, size, innerWidth, barColor, backColor, hAlign, vAlign); } FontOpts opts = FontOptsNew(); opts.HAlign = hAlign; opts.VAlign = vAlign; opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = Vec2iNew(pos.x + GUN_ICON_PAD, pos.y); char buf[128]; if (ConfigGetBool(&gConfig, "Game.Ammo") && weapon->Gun->AmmoId >= 0) { // Include ammo counter sprintf(buf, "%s %d/%d", weapon->Gun->name, ActorGunGetAmmo(actor, weapon), AmmoGetById(&gAmmo, weapon->Gun->AmmoId)->Max); } else { strcpy(buf, weapon->Gun->name); } FontStrOpt(buf, Vec2iZero(), opts); }
void FPSCounterDraw(FPSCounter *counter) { char s[50]; counter->framesDrawn++; sprintf(s, "FPS: %d", counter->fps); FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_END; opts.VAlign = ALIGN_END; opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = Vec2iNew(10, 5 + FontH()); FontStrOpt(s, Vec2iZero(), opts); }
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 DrawHealth( GraphicsDevice *device, const TActor *actor, const Vec2i pos, const FontAlign hAlign, const FontAlign vAlign) { char s[50]; Vec2i gaugePos = Vec2iAdd(pos, Vec2iNew(-1, -1)); Vec2i size = Vec2iNew(GAUGE_WIDTH, FontH() + 2); HSV hsv = { 0.0, 1.0, 1.0 }; color_t barColor; int health = actor->health; const int maxHealth = ActorGetCharacter(actor)->maxHealth; int innerWidth; color_t backColor = { 50, 0, 0, 255 }; innerWidth = MAX(1, size.x * health / maxHealth); if (actor->poisoned) { hsv.h = 120.0; hsv.v = 0.5; } else { double maxHealthHue = 50.0; double minHealthHue = 0.0; hsv.h = ((maxHealthHue - minHealthHue) * health / maxHealth + minHealthHue); } barColor = ColorTint(colorWhite, hsv); DrawGauge( device, gaugePos, size, innerWidth, barColor, backColor, hAlign, vAlign); sprintf(s, "%d", health); FontOpts opts = FontOptsNew(); opts.HAlign = hAlign; opts.VAlign = vAlign; opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = pos; FontStrOpt(s, Vec2iZero(), opts); }
static void MenuDisplayItems(const MenuSystem *ms) { int d = ms->current->u.normal.displayItems; if ((d & MENU_DISPLAY_ITEMS_CREDITS) && ms->creditsDisplayer != NULL) { ShowCredits(ms->creditsDisplayer); } if (d & MENU_DISPLAY_ITEMS_AUTHORS) { PicPaletted *logo = PicManagerGetOldPic(&gPicManager, PIC_LOGO); DrawTPic( MS_CENTER_X(*ms, logo->w), ms->pos.y + ms->size.y / 12, logo); FontOpts opts = FontOptsNew(); opts.HAlign = ALIGN_END; opts.Area = ms->size; opts.Pad = Vec2iNew(20, 20); FontStrOpt("Version: " CDOGS_SDL_VERSION, ms->pos, opts); } }
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); }
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; } }
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 DrawNumUpdate( const HUDNumUpdate *update, const char *formatText, int currentValue, Vec2i pos, int flags) { if (update->Timer <= 0 || update->Amount == 0) { return; } color_t color = update->Amount > 0 ? colorGreen : colorRed; char s[50]; if (!(flags & HUDFLAGS_PLACE_RIGHT)) { // Find the right position to draw the update // Make sure the update is displayed lined up with the lowest digits // Find the position of where the normal text is displayed, // and move to its right sprintf(s, formatText, currentValue); pos.x += FontStrW(s); // Then find the size of the update, and move left sprintf(s, "%s%d", update->Amount > 0 ? "+" : "", update->Amount); pos.x -= FontStrW(s); // The final position should ensure the score update's lowest digit // lines up with the normal score's lowest digit } else { sprintf(s, "%s%d", update->Amount > 0 ? "+" : "", update->Amount); } // Now animate the score update based on its stage int timer = update->TimerMax - update->Timer; if (timer < NUM_UPDATE_POP_UP_DURATION_MS) { // update is still popping up // calculate height int popupHeight = timer * NUM_UPDATE_POP_UP_HEIGHT / NUM_UPDATE_POP_UP_DURATION_MS; pos.y -= popupHeight; } else if (timer < NUM_UPDATE_POP_UP_DURATION_MS + NUM_UPDATE_FALL_DOWN_DURATION_MS) { // update is falling down // calculate height timer -= NUM_UPDATE_POP_UP_DURATION_MS; timer = NUM_UPDATE_FALL_DOWN_DURATION_MS - timer; int popupHeight = timer * NUM_UPDATE_POP_UP_HEIGHT / NUM_UPDATE_FALL_DOWN_DURATION_MS; pos.y -= popupHeight; } else { // Change alpha so that the update fades away color.a = (Uint8)(update->Timer * 255 / update->TimerMax); } FontOpts opts = FontOptsNew(); if (flags & HUDFLAGS_PLACE_RIGHT) { opts.HAlign = ALIGN_END; } if (flags & HUDFLAGS_PLACE_BOTTOM) { opts.VAlign = ALIGN_END; pos.y += BOTTOM_PADDING; } opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = pos; opts.Mask = color; opts.Blend = true; FontStrOpt(s, Vec2iZero(), opts); }
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); } }
// Draw player's score, health etc. static void DrawPlayerStatus( HUD *hud, const PlayerData *data, const TActor *p, const int flags, const Rect2i r) { if (p != NULL) { DrawObjectiveCompass( hud->device, Vec2iFull2Real(p->Pos), r, hud->showExit); } Vec2i pos = Vec2iNew(5, 5); FontOpts opts = FontOptsNew(); if (flags & HUDFLAGS_PLACE_RIGHT) { opts.HAlign = ALIGN_END; } if (flags & HUDFLAGS_PLACE_BOTTOM) { opts.VAlign = ALIGN_END; pos.y += BOTTOM_PADDING; } opts.Area = gGraphicsDevice.cachedConfig.Res; opts.Pad = pos; FontStrOpt(data->name, Vec2iZero(), opts); const int rowHeight = 1 + FontH(); pos.y += rowHeight; char s[50]; if (IsScoreNeeded(gCampaign.Entry.Mode)) { if (ConfigGetBool(&gConfig, "Game.Ammo")) { // Display money instead of ammo sprintf(s, "Cash: $%d", data->score); } else { sprintf(s, "Score: %d", data->score); } } else { s[0] = 0; } if (p) { // Score/money opts.Pad = pos; FontStrOpt(s, Vec2iZero(), opts); // Health pos.y += rowHeight; DrawHealth(hud->device, p, pos, opts.HAlign, opts.VAlign); // Lives pos.y += rowHeight; DrawLives(hud->device, data, pos, opts.HAlign, opts.VAlign); // Weapon pos.y += rowHeight + LIVES_ROW_EXTRA_Y; DrawWeaponStatus(hud, p, pos, opts.HAlign, opts.VAlign); } else { opts.Pad = pos; FontStrOpt(s, Vec2iZero(), opts); } if (ConfigGetBool(&gConfig, "Interface.ShowHUDMap") && !(flags & HUDFLAGS_SHARE_SCREEN) && IsAutoMapEnabled(gCampaign.Entry.Mode)) { DrawRadar(hud->device, p, RADAR_SCALE, flags, hud->showExit); } }