Пример #1
0
static GameLoopResult DogfightScoresUpdate(GameLoopData *data, LoopRunner *l)
{
	PlayerList *pl = data->Data;

	const GameLoopResult result = MenuUpdate(&pl->ms);
	if (result == UPDATE_RESULT_OK)
	{
		// Calculate PVP rounds won
		int maxScore = 0;
		CA_FOREACH(PlayerData, p, gPlayerDatas)
			if (IsPlayerAlive(p))
			{
				p->Totals.Score++;
				maxScore = MAX(maxScore, p->Totals.Score);
			}
		CA_FOREACH_END()
		gCampaign.IsComplete =
			maxScore == ModeMaxRoundsWon(gCampaign.Entry.Mode);
		CASSERT(maxScore <= ModeMaxRoundsWon(gCampaign.Entry.Mode),
			"score exceeds max rounds won");
		if (gCampaign.IsComplete)
		{
			LoopRunnerChange(l, ScreenDogfightFinalScores());
		}
		else
		{
			LoopRunnerChange(
				l, HighScoresScreen(&gCampaign, &gGraphicsDevice));
		}
	}
Пример #2
0
static bool CanSeeAPlayer(const TActor *a)
{
    const Vec2i realPos = Vec2iFull2Real(a->Pos);
    CA_FOREACH(const PlayerData, p, gPlayerDatas)
    if (!IsPlayerAlive(p))
    {
        continue;
    }
    const TActor *player = ActorGetByUID(p->ActorUID);
    const Vec2i playerRealPos = Vec2iFull2Real(player->Pos);
    // Can see player if:
    // - Clear line of sight, and
    // - If they are close, or if facing and they are not too far
    if (!AIHasClearShot(realPos, playerRealPos))
    {
        continue;
    }
    const int distance = CHEBYSHEV_DISTANCE(
                             realPos.x, realPos.y, playerRealPos.x, playerRealPos.y);
    const bool isClose = distance < 16 * 4;
    const bool isNotTooFar = distance < 16 * 30;
    if (isClose ||
            (isNotTooFar && AIIsFacing(a, player->Pos, a->direction)))
    {
        return true;
    }
    CA_FOREACH_END()
    return false;
}
Пример #3
0
static bool CanSeeAPlayer(const TActor *a)
{
	const Vec2i realPos = Vec2iFull2Real(a->Pos);
	for (int i = 0; i < MAX_PLAYERS; i++)
	{
		if (!IsPlayerAlive(i))
		{
			continue;
		}
		const TActor *player = CArrayGet(&gActors, gPlayerIds[i]);
		const Vec2i playerRealPos = Vec2iFull2Real(player->Pos);
		// Can see player if:
		// - Clear line of sight, and
		// - If they are close, or if facing and they are not too far
		if (!AIHasClearShot(realPos, playerRealPos))
		{
			continue;
		}
		const int distance = CHEBYSHEV_DISTANCE(
			realPos.x, realPos.y, playerRealPos.x, playerRealPos.y);
		const bool isClose = distance < 16 * 4;
		const bool isNotTooFar = distance < 16 * 30;
		if (isClose ||
			(isNotTooFar && IsFacing(realPos, playerRealPos, a->direction)))
		{
			return true;
		}
	}
	return false;
}
Пример #4
0
static void DrawHealthUpdate(const HUDNumUpdate *u, const int flags)
{
	const PlayerData *p = PlayerDataGetByUID(u->u.PlayerUID);
	if (!IsPlayerAlive(p)) return;
	const int rowHeight = 1 + FontH();
	const int y = 5 + rowHeight * 2;
	const TActor *a = ActorGetByUID(p->ActorUID);
	DrawNumUpdate(u, "%d", a->health, Vec2iNew(5, y), flags);
}
Пример #5
0
static void DrawScoreUpdate(const HUDNumUpdate *u, const int flags)
{
	if (!IsScoreNeeded(gCampaign.Entry.Mode))
	{
		return;
	}
	const PlayerData *p = PlayerDataGetByUID(u->u.PlayerUID);
	if (!IsPlayerAlive(p)) return;
	const int rowHeight = 1 + FontH();
	const int y = 5 + rowHeight;
	DrawNumUpdate(u, "Score: %d", p->score, Vec2iNew(5, y), flags);
}
Пример #6
0
static bool DidPlayerShoot(void)
{
	for (int i = 0; i < MAX_PLAYERS; i++)
	{
		if (IsPlayerAlive(i))
		{
			TActor *player = CArrayGet(&gActors, gPlayerIds[i]);
			return player->lastCmd & CMD_BUTTON1;
		}
	}
	return 0;
}
Пример #7
0
static void DrawAmmoUpdate(const HUDNumUpdate *u, const int flags)
{
	const PlayerData *p = PlayerDataGetByUID(u->u.PlayerUID);
	if (!IsPlayerAlive(p)) return;
	const int rowHeight = 1 + FontH();
	const int y = 5 + rowHeight * 4 + LIVES_ROW_EXTRA_Y;
	const TActor *a = ActorGetByUID(p->ActorUID);
	const Weapon *w = ActorGetGun(a);
	char gunNameBuf[256];
	sprintf(gunNameBuf, "%s %%d", w->Gun->name);
	const int ammo = ActorGunGetAmmo(a, w);
	DrawNumUpdate(u, gunNameBuf, ammo, Vec2iNew(5 + GUN_ICON_PAD, y), flags);
}
Пример #8
0
static bool IsFacingPlayer(TActor *actor, direction_e d)
{
    CA_FOREACH(const PlayerData, p, gPlayerDatas)
    if (!IsPlayerAlive(p))
    {
        continue;
    }
    const TActor *player = ActorGetByUID(p->ActorUID);
    if (AIIsFacing(actor, player->Pos, d))
    {
        return true;
    }
    CA_FOREACH_END()
    return false;
}
Пример #9
0
static bool DidPlayerShoot(void)
{
    CA_FOREACH(const PlayerData, p, gPlayerDatas)
    if (!IsPlayerAlive(p))
    {
        continue;
    }
    const TActor *player = ActorGetByUID(p->ActorUID);
    if (player->lastCmd & CMD_BUTTON1)
    {
        return true;
    }
    CA_FOREACH_END()
    return false;
}
Пример #10
0
void HealthPickupsUpdate(HealthPickups *h, int ticks)
{
	// Don't spawn pickups if not allowed
	if (!AreHealthPickupsAllowed(gCampaign.Entry.Mode) ||
		!gConfig.Game.HealthPickups)
	{
		return;
	}

	double scalar = 1.0;
	// Update time until next spawn based on:
	// Damage taken (find player with lowest health)
	int minHealth = ModeMaxHealth(gCampaign.Entry.Mode);
	int maxHealth = minHealth;
	for (int i = 0; i < (int)gPlayerDatas.size; i++)
	{
		const PlayerData *p = CArrayGet(&gPlayerDatas, i);
		if (!IsPlayerAlive(p))
		{
			continue;
		}
		const TActor *player = CArrayGet(&gActors, p->Id);
		minHealth = MIN(minHealth, player->health);
	}
	// Double spawn rate if near 0 health
	scalar *= (minHealth + maxHealth) / (maxHealth * 2.0);

	// Scale down over time
	scalar *= pow(TIME_DECAY_EXPONENT, h->pickupsSpawned);

	h->timeUntilNextSpawn = (int)floor(scalar * SPAWN_TIME);

	// Update time
	h->timer += ticks;

	// Attempt to add health if time reached, and we haven't placed too many
	if (h->timer >= h->timeUntilNextSpawn &&
		h->map->NumExplorableTiles / MAX_TILES_PER_PICKUP + 1 > h->numPickups)
	{
		h->timer = 0;

		if (TryPlacePickup(h))
		{
			h->pickupsSpawned++;
			h->numPickups++;
		}
	}
}
Пример #11
0
static bool IsFacingPlayer(TActor *actor, direction_e d)
{
	for (int i = 0; i < MAX_PLAYERS; i++)
	{
		if (gPlayerIds[i] < 0)
		{
			continue;
		}
		const TActor *player = CArrayGet(&gActors, gPlayerIds[i]);
		if (IsPlayerAlive(i) && IsFacing(actor->Pos, player->Pos, d))
		{
			return true;
		}
	}
	return false;
}
Пример #12
0
TActor *AIGetClosestPlayer(Vec2i fullpos)
{
    int i;
    int minDistance = -1;
    TActor *closestPlayer = NULL;
    for (i = 0; i < gOptions.numPlayers; i++)
    {
        if (IsPlayerAlive(i))
        {
            TActor *p = CArrayGet(&gActors, gPlayerIds[i]);
            Vec2i pPos = Vec2iFull2Real(p->Pos);
            int distance = CHEBYSHEV_DISTANCE(
                               fullpos.x, fullpos.y, pPos.x, pPos.y);
            if (!closestPlayer || distance < minDistance)
            {
                closestPlayer = p;
                minDistance = distance;
            }
        }
    }
    return closestPlayer;
}
Пример #13
0
static void HandleGameEvent(
	GameEvent *e,
	HUD *hud,
	ScreenShake *shake,
	HealthPickups *hp,
	EventHandlers *eventHandlers)
{
	switch (e->Type)
	{
		case GAME_EVENT_SCORE:
			Score(&gPlayerDatas[e->u.Score.PlayerIndex], e->u.Score.Score);
			HUDAddScoreUpdate(hud, e->u.Score.PlayerIndex, e->u.Score.Score);
			break;
		case GAME_EVENT_SOUND_AT:
			if (e->u.SoundAt.Sound)
			{
				SoundPlayAt(
					&gSoundDevice, e->u.SoundAt.Sound, e->u.SoundAt.Pos);
			}
			break;
		case GAME_EVENT_SCREEN_SHAKE:
			*shake = ScreenShakeAdd(
				*shake, e->u.ShakeAmount, gConfig.Graphics.ShakeMultiplier);
			break;
		case GAME_EVENT_SET_MESSAGE:
			HUDDisplayMessage(
				hud, e->u.SetMessage.Message, e->u.SetMessage.Ticks);
			break;
		case GAME_EVENT_GAME_START:
			if (eventHandlers->netInput.channel.state ==
				CHANNEL_STATE_CONNECTED)
			{
				NetInputSendMsg(
					&eventHandlers->netInput, SERVER_MSG_GAME_START);
			}
			break;
		case GAME_EVENT_ADD_HEALTH_PICKUP:
			MapPlaceHealth(e->u.AddPos);
			break;
		case GAME_EVENT_TAKE_HEALTH_PICKUP:
			if (IsPlayerAlive(e->u.PickupPlayer))
			{
				TActor *player = CArrayGet(
					&gActors, gPlayerIds[e->u.PickupPlayer]);
				ActorHeal(player, HEALTH_PICKUP_HEAL_AMOUNT);
				HealthPickupsRemoveOne(hp);
				HUDAddHealthUpdate(
					hud, e->u.PickupPlayer, HEALTH_PICKUP_HEAL_AMOUNT);
			}
			break;
		case GAME_EVENT_MOBILE_OBJECT_REMOVE:
			MobObjDestroy(e->u.MobileObjectRemoveId);
			break;
		case GAME_EVENT_ADD_BULLET:
			BulletAdd(
				e->u.AddBullet.Bullet,
				e->u.AddBullet.MuzzlePos, e->u.AddBullet.MuzzleHeight,
				e->u.AddBullet.Angle, e->u.AddBullet.Direction,
				e->u.AddBullet.Flags, e->u.AddBullet.PlayerIndex);
			break;
		case GAME_EVENT_ADD_MUZZLE_FLASH:
			AddMuzzleFlash(
				e->u.AddMuzzleFlash.FullPos,
				e->u.AddMuzzleFlash.MuzzleHeight,
				e->u.AddMuzzleFlash.Sprites,
				e->u.AddMuzzleFlash.Direction,
				e->u.AddMuzzleFlash.Color,
				e->u.AddMuzzleFlash.Duration);
			break;
		case GAME_EVENT_ADD_FIREBALL:
			AddFireball(e->u.AddFireball);
			break;
		case GAME_EVENT_HIT_CHARACTER:
			HitCharacter(
				e->u.HitCharacter.Flags,
				e->u.HitCharacter.PlayerIndex,
				CArrayGet(&gActors, e->u.HitCharacter.TargetId),
				e->u.HitCharacter.Special,
				e->u.HitCharacter.HasHitSound);
			break;
		case GAME_EVENT_ACTOR_IMPULSE:
			{
				TActor *a = CArrayGet(&gActors, e->u.ActorImpulse.Id);
				a->Vel = Vec2iAdd(a->Vel, e->u.ActorImpulse.Vel);
			}
			break;
		case GAME_EVENT_DAMAGE_CHARACTER:
			DamageCharacter(
				e->u.DamageCharacter.Power,
				e->u.DamageCharacter.PlayerIndex,
				CArrayGet(&gActors, e->u.DamageCharacter.TargetId));
			if (e->u.DamageCharacter.Power != 0)
			{
				HUDAddHealthUpdate(
					hud,
					e->u.DamageCharacter.TargetPlayerIndex,
					-e->u.DamageCharacter.Power);
			}
			break;
		case GAME_EVENT_TRIGGER:
			{
				const Tile *t = MapGetTile(&gMap, e->u.Trigger.TilePos);
				for (int i = 0; i < (int)t->triggers.size; i++)
				{
					Trigger **tp = CArrayGet(&t->triggers, i);
					if ((*tp)->id == e->u.Trigger.Id)
					{
						TriggerActivate(*tp, &gMap.triggers);
						break;
					}
				}
			}
			break;
		case GAME_EVENT_UPDATE_OBJECTIVE:
			{
				struct Objective *o = CArrayGet(
					&gMission.Objectives, e->u.UpdateObjective.ObjectiveIndex);
				o->done += e->u.UpdateObjective.Update;
				MissionObjective *mo = CArrayGet(
					&gMission.missionData->Objectives,
					e->u.UpdateObjective.ObjectiveIndex);
				switch (mo->Type)
				{
				case OBJECTIVE_COLLECT:
					{
						GameEvent e1;
						e1.Type = GAME_EVENT_SCORE;
						e1.u.Score.PlayerIndex =
							e->u.UpdateObjective.PlayerIndex;
						e1.u.Score.Score = PICKUP_SCORE;
						HandleGameEvent(&e1, hud, shake, hp, eventHandlers);
					}
					break;
				case OBJECTIVE_DESTROY:
					{
						GameEvent e1;
						e1.Type = GAME_EVENT_SCORE;
						e1.u.Score.PlayerIndex =
							e->u.UpdateObjective.PlayerIndex;
						e1.u.Score.Score = OBJECT_SCORE;
						HandleGameEvent(&e1, hud, shake, hp, eventHandlers);
					}
					break;
				default:
					// No other special objective handling
					break;
				}
				// Display a text update effect for the objective
				HUDAddObjectiveUpdate(
					hud,
					e->u.UpdateObjective.ObjectiveIndex,
					e->u.UpdateObjective.Update);
				MissionSetMessageIfComplete(&gMission);
			}
			break;
		case GAME_EVENT_MISSION_COMPLETE:
			HUDDisplayMessage(hud, "Mission complete", -1);
			hud->showExit = true;
			MapShowExitArea(&gMap);
			break;
		case GAME_EVENT_MISSION_INCOMPLETE:
			gMission.state = MISSION_STATE_PLAY;
			break;
		case GAME_EVENT_MISSION_PICKUP:
			gMission.state = MISSION_STATE_PICKUP;
			gMission.pickupTime = gMission.time;
			SoundPlay(&gSoundDevice, StrSound("whistle"));
			break;
		case GAME_EVENT_MISSION_END:
			gMission.isDone = true;
			break;
		default:
			assert(0 && "unknown game event");
			break;
	}
}
Пример #14
0
static void Campaign(GraphicsDevice *graphics, CampaignOptions *co)
{
	if (co->IsClient)
	{
		// If connecting to a server, we've already received the mission index
		// Do nothing
	}
	else if (IsPasswordAllowed(co->Entry.Mode))
	{
		MissionSave m;
		AutosaveLoadMission(&gAutosave, &m, co->Entry.Path);
		co->MissionIndex = EnterPassword(graphics, &m);
	}
	else
	{
		co->MissionIndex = 0;
	}

	bool run = false;
	bool gameOver = true;
	do
	{
		// Unready all the players
		for (int i = 0; i < (int)gPlayerDatas.size; i++)
		{
			PlayerData *p = CArrayGet(&gPlayerDatas, i);
			p->Ready = false;
		}

		CampaignAndMissionSetup(1, co, &gMission);

		if (IsGameOptionsNeeded(co->Entry.Mode))
		{
			debug(D_NORMAL, ">> Game options\n");
			if (!GameOptions(co->Entry.Mode))
			{
				run = false;
				goto bail;
			}
			co->OptionsSet = true;

			// If enabled, start net server
			if (!co->IsClient && ConfigGetBool(&gConfig, "StartServer"))
			{
				NetServerOpen(&gNetServer);
			}
		}

		// Mission briefing
		if (IsMissionBriefingNeeded(co->Entry.Mode))
		{
			if (!ScreenMissionBriefing(&gMission))
			{
				run = false;
				goto bail;
			}
		}

		// Equip guns
		if (!PlayerEquip())
		{
			run = false;
			goto bail;
		}

		if (co->IsClient)
		{
			if (!ScreenWaitForGameStart())
			{
				run = false;
				goto bail;
			}
		}

		run = RunGame(co, &gMission, &gMap);
		// Don't quit if all players died, that's normal for PVP modes
		if (IsPVP(co->Entry.Mode) &&
			GetNumPlayers(PLAYER_ALIVE_OR_DYING, false, false) == 0)
		{
			run = true;
		}

		const int survivingPlayers =
			GetNumPlayers(PLAYER_ALIVE, false, false);
		// In co-op (non-PVP) modes, at least one player must survive
		if (!IsPVP(co->Entry.Mode))
		{
			gameOver = survivingPlayers == 0 ||
				co->MissionIndex == (int)gCampaign.Setting.Missions.size - 1;
		}

		int maxScore = 0;
		for (int i = 0; i < (int)gPlayerDatas.size; i++)
		{
			PlayerData *p = CArrayGet(&gPlayerDatas, i);
			p->survived = IsPlayerAlive(p);
			if (IsPlayerAlive(p))
			{
				TActor *player = ActorGetByUID(p->ActorUID);
				p->hp = player->health;
				p->RoundsWon++;
				maxScore = MAX(maxScore, p->RoundsWon);
			}
		}
		if (IsPVP(co->Entry.Mode))
		{
			gameOver = maxScore == ModeMaxRoundsWon(co->Entry.Mode);
			CASSERT(maxScore <= ModeMaxRoundsWon(co->Entry.Mode),
				"score exceeds max rounds won");
		}

		MissionEnd();
		MusicPlayMenu(&gSoundDevice);

		if (run)
		{
			switch (co->Entry.Mode)
			{
			case GAME_MODE_DOGFIGHT:
				ScreenDogfightScores();
				break;
			case GAME_MODE_DEATHMATCH:
				ScreenDeathmatchFinalScores();
				break;
			default:
				ScreenMissionSummary(&gCampaign, &gMission);
				// Note: must use cached value because players get cleaned up
				// in CleanupMission()
				if (gameOver && survivingPlayers > 0)
				{
					ScreenVictory(&gCampaign);
				}
				break;
			}
		}

		// Check if any scores exceeded high scores, if we're not a PVP mode
		if (!IsPVP(co->Entry.Mode))
		{
			bool allTime = false;
			bool todays = false;
			for (int i = 0; i < (int)gPlayerDatas.size; i++)
			{
				PlayerData *p = CArrayGet(&gPlayerDatas, i);
				if (((run && !p->survived) || gameOver) && p->IsLocal)
				{
					EnterHighScore(p);
					allTime |= p->allTime >= 0;
					todays |= p->today >= 0;
				}

				if (!p->survived)
				{
					p->totalScore = 0;
					p->missions = 0;
				}
				else
				{
					p->missions++;
				}
				p->lastMission = co->MissionIndex;
			}
			if (allTime)
			{
				DisplayAllTimeHighScores(graphics);
			}
			if (todays)
			{
				DisplayTodaysHighScores(graphics);
			}
		}
		if (!HasRounds(co->Entry.Mode))
		{
			co->MissionIndex++;
		}

	bail:
		// Need to terminate the mission later as it is used in calculating scores
		MissionOptionsTerminate(&gMission);
	} while (run && !gameOver);

	// Final screen
	if (run)
	{
		switch (co->Entry.Mode)
		{
		case GAME_MODE_DOGFIGHT:
			ScreenDogfightFinalScores();
			break;
		default:
			// no end screen
			break;
		}
	}

	NetServerClose(&gNetServer);
}
Пример #15
0
static void Campaign(GraphicsDevice *graphics, CampaignOptions *co)
{
	if (IsPasswordAllowed(co->Entry.Mode))
	{
		MissionSave m;
		AutosaveLoadMission(
			&gAutosave, &m, co->Entry.Path, co->Entry.BuiltinIndex);
		co->MissionIndex = EnterPassword(graphics, &m);
	}
	else
	{
		co->MissionIndex = 0;
	}

	bool run = false;
	bool gameOver = true;
	do
	{
		CampaignAndMissionSetup(1, co, &gMission);

		if (IsGameOptionsNeeded(gCampaign.Entry.Mode))
		{
			debug(D_NORMAL, ">> Game options\n");
			if (!GameOptions(gCampaign.Entry.Mode))
			{
				run = false;
				goto bail;
			}
			gCampaign.OptionsSet = true;
		}

		// Mission briefing
		if (IsMissionBriefingNeeded(co->Entry.Mode))
		{
			if (!ScreenMissionBriefing(&gMission))
			{
				run = false;
				goto bail;
			}
		}

		// Equip guns
		if (!PlayerEquip())
		{
			run = false;
			goto bail;
		}

		// Initialise before waiting for game start;
		// server will send us messages
		GameEventsInit(&gGameEvents);

		if (gCampaign.IsClient)
		{
			if (!ScreenWaitForGameStart())
			{
				run = false;
				goto bail;
			}
		}

		MapLoad(&gMap, &gMission, co);

		// Seed random if PVP mode (otherwise players will always spawn in same
		// position)
		if (IsPVP(co->Entry.Mode))
		{
			srand((unsigned int)time(NULL));
		}

		if (!gCampaign.IsClient)
		{
			MapLoadDynamic(&gMap, &gMission, &co->Setting.characters);
			// Note: place players first,
			// as bad guys are placed away from players
			StartPlayers(ModeMaxHealth(co->Entry.Mode), co->MissionIndex);
			AddAndPlacePlayers();
			if (!IsPVP(co->Entry.Mode))
			{
				InitializeBadGuys();
				CreateEnemies();
			}
		}
		MusicPlayGame(
			&gSoundDevice, gCampaign.Entry.Path, gMission.missionData->Song);
		run = RunGame(&gMission, &gMap);
		// Don't quit if all players died, that's normal for PVP modes
		if (IsPVP(co->Entry.Mode) && GetNumPlayers(true, false, false) == 0)
		{
			run = true;
		}
		GameEventsTerminate(&gGameEvents);

		const int survivingPlayers = GetNumPlayers(true, false, false);
		// In co-op (non-PVP) modes, at least one player must survive
		if (!IsPVP(co->Entry.Mode))
		{
			gameOver = survivingPlayers == 0 ||
				co->MissionIndex == (int)gCampaign.Setting.Missions.size - 1;
		}

		int maxScore = 0;
		for (int i = 0; i < (int)gPlayerDatas.size; i++)
		{
			PlayerData *p = CArrayGet(&gPlayerDatas, i);
			p->survived = IsPlayerAlive(p);
			if (IsPlayerAlive(p))
			{
				TActor *player = CArrayGet(&gActors, p->Id);
				p->hp = player->health;
				p->RoundsWon++;
				maxScore = MAX(maxScore, p->RoundsWon);
			}
		}
		if (IsPVP(co->Entry.Mode))
		{
			gameOver = maxScore == ModeMaxRoundsWon(co->Entry.Mode);
			CASSERT(maxScore <= ModeMaxRoundsWon(co->Entry.Mode),
				"score exceeds max rounds won");
		}

		MissionEnd();
		MusicPlayMenu(&gSoundDevice);

		if (run)
		{
			switch (co->Entry.Mode)
			{
			case GAME_MODE_DOGFIGHT:
				ScreenDogfightScores();
				break;
			case GAME_MODE_DEATHMATCH:
				ScreenDeathmatchFinalScores();
				break;
			default:
				ScreenMissionSummary(&gCampaign, &gMission);
				// Note: must use cached value because players get cleaned up
				// in CleanupMission()
				if (gameOver && survivingPlayers > 0)
				{
					ScreenVictory(&gCampaign);
				}
				break;
			}
		}

		// Check if any scores exceeded high scores, if we're not a PVP mode
		if (!IsPVP(co->Entry.Mode))
		{
			bool allTime = false;
			bool todays = false;
			for (int i = 0; i < (int)gPlayerDatas.size; i++)
			{
				PlayerData *p = CArrayGet(&gPlayerDatas, i);
				if (((run && !p->survived) || gameOver) && p->IsLocal)
				{
					EnterHighScore(p);
					allTime |= p->allTime >= 0;
					todays |= p->today >= 0;
				}

				if (!p->survived)
				{
					p->totalScore = 0;
					p->missions = 0;
				}
				else
				{
					p->missions++;
				}
				p->lastMission = co->MissionIndex;
			}
			if (allTime)
			{
				DisplayAllTimeHighScores(graphics);
			}
			if (todays)
			{
				DisplayTodaysHighScores(graphics);
			}
		}
		if (!HasRounds(co->Entry.Mode))
		{
			co->MissionIndex++;
		}

	bail:
		// Need to terminate the mission later as it is used in calculating scores
		MissionOptionsTerminate(&gMission);
	} while (run && !gameOver);

	// Final screen
	if (run)
	{
		switch (co->Entry.Mode)
		{
		case GAME_MODE_DOGFIGHT:
			ScreenDogfightFinalScores();
			break;
		default:
			// no end screen
			break;
		}
	}
}
Пример #16
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);
	}
}
Пример #17
0
int IsMissionComplete(struct MissionOptions *options)
{
	int rescuesRequired = 0;
	int i;

	if (!CanCompleteMission(options))
	{
		return 0;
	}

	// Check if dogfight is complete
	if (gCampaign.Entry.Mode == CAMPAIGN_MODE_DOGFIGHT &&
		GetNumPlayersAlive() <= 1)
	{
		return 1;
	}

	// Check that all surviving players are in exit zone
	for (i = 0; i < MAX_PLAYERS; i++)
	{
		if (!IsPlayerAlive(i))
		{
			continue;
		}
		TActor *player = CArrayGet(&gActors, gPlayerIds[i]);
		if (!MapIsTileInExit(&gMap, &player->tileItem))
		{
			return 0;
		}
	}

	// Find number of rescues required
	// TODO: support multiple rescue objectives
	for (i = 0; i < (int)options->missionData->Objectives.size; i++)
	{
		MissionObjective *mobj =
			CArrayGet(&options->missionData->Objectives, i);
		if (mobj->Type == OBJECTIVE_RESCUE)
		{
			rescuesRequired = mobj->Required;
			break;
		}
	}
	// Check that enough prisoners are in exit zone
	if (rescuesRequired > 0)
	{
		int prisonersRescued = 0;
		for (i = 0; i < (int)gActors.size; i++)
		{
			TActor *a = CArrayGet(&gActors, i);
			if (!a->isInUse)
			{
				continue;
			}
			if (a->character == CharacterStoreGetPrisoner(
				&gCampaign.Setting.characters, 0) &&
				MapIsTileInExit(&gMap, &a->tileItem))
			{
				prisonersRescued++;
			}
		}
		if (prisonersRescued < rescuesRequired)
		{
			return 0;
		}
	}

	return 1;
}