Esempio n. 1
0
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);
}
Esempio n. 2
0
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);
		}
	}
}
Esempio n. 3
0
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);
	}
}
Esempio n. 4
0
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);
	}
}
Esempio n. 5
0
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);
}
Esempio n. 6
0
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);
}
Esempio n. 7
0
// 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);
		}
	}
}
Esempio n. 8
0
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);
	}
}
Esempio n. 9
0
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);
	}
}
Esempio n. 10
0
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;
	}
}
Esempio n. 11
0
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);
	}
}
Esempio n. 12
0
static void DisplayAt(int x, int y, const char *s, int hilite)
{
	color_t mask = hilite ? colorRed : colorWhite;
	FontStrMask(s, Vec2iNew(x, y), mask);
}