sm-zombiereloaded-3/src/zr/roundend.inc

503 lines
12 KiB
SourcePawn

/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: roundend.inc
* Description: (Core) Handles round end actions.
*
* ============================================================================
*/
/**
* @section All round end reasons.
*/
#define ROUNDEND_TARGET_BOMBED 1 // Target Successfully Bombed!
#define ROUNDEND_VIP_ESCAPED 2 // The VIP has escaped!
#define ROUNDEND_VIP_ASSASSINATED 3 // VIP has been assassinated!
#define ROUNDEND_TERRORISTS_ESCAPED 4 // The terrorists have escaped!
#define ROUNDEND_CTS_PREVENTESCAPE 5 // The CT's have prevented most of the terrorists from escaping!
#define ROUNDEND_ESCAPING_TERRORISTS_NEUTRALIZED 6 // Escaping terrorists have all been neutralized!
#define ROUNDEND_BOMB_DEFUSED 7 // The bomb has been defused!
#define ROUNDEND_CTS_WIN 8 // Counter-Terrorists Win!
#define ROUNDEND_TERRORISTS_WIN 9 // Terrorists Win!
#define ROUNDEND_ROUND_DRAW 10 // Round Draw!
#define ROUNDEND_ALL_HOSTAGES_RESCUED 11 // All Hostages have been rescued!
#define ROUNDEND_TARGET_SAVED 12 // Target has been saved!
#define ROUNDEND_HOSTAGES_NOT_RESCUED 13 // Hostages have not been rescued!
#define ROUNDEND_TERRORISTS_NOT_ESCAPED 14 // Terrorists have not escaped!
#define ROUNDEND_VIP_NOT_ESCAPED 15 // VIP has not escaped!
#define ROUNDEND_GAME_COMMENCING 16 // Game Commencing!
/**
* @endsection
*/
/**
* Delay between round ending and new round starting. (Normal)
*/
#define ROUNDEND_DELAY_NORMAL 5.0
/**
* Delay between round ending and new round starting. (Short, for ROUNDEND_GAME_COMMENCING)
*/
#define ROUNDEND_DELAY_SHORT 3.0
/**
* Possible round end outcomes.
*/
enum RoundEndOutcome
{
HumansWin, /** Humans have killed all zombies. */
ZombiesWin, /** Zombies have infected all humans. */
Draw, /** Round has ended in unexpected way. */
}
/**
* Global variable to store round win timer handle.
*/
new Handle:tRoundEnd = INVALID_HANDLE;
/**
* Global variable to store a convar query cookie
*/
new QueryCookie:mat_dxlevel;
/**
* Map is starting.
*/
RoundEndOnMapStart()
{
// Reset timer handle.
tRoundEnd = INVALID_HANDLE;
}
/**
* Client is joining the server.
*
* @param client The client index.
*/
RoundEndClientInit(client)
{
// Get client's DX level.
RoundEndGetClientDXLevel(client);
}
/**
* Client has been killed.
*/
RoundEndOnClientDeath()
{
// Terminate the round if the last player was killed.
new RoundEndOutcome:outcome;
if (RoundEndGetRoundStatus(outcome))
{
RoundEndTerminateRound(outcome);
}
}
/**
* Client has been infected.
*/
RoundEndOnClientInfected()
{
// Terminate the round if the last player was infected.
new RoundEndOutcome:outcome;
if (RoundEndGetRoundStatus(outcome))
{
RoundEndTerminateRound(outcome);
}
}
/**
* The round is starting.
*/
RoundEndOnRoundStart()
{
// If round end timer is running, then kill it.
if (tRoundEnd != INVALID_HANDLE)
{
// Kill timer.
KillTimer(tRoundEnd);
// Reset timer handle.
tRoundEnd = INVALID_HANDLE;
}
}
/**
* The freeze time is ending.
*/
RoundEndOnRoundFreezeEnd()
{
// Calculate round length, in seconds.
// Get mp_roundtime. (in minutes)
new Float:roundtime = GetConVarFloat(FindConVar("mp_roundtime"));
// Convert to seconds.
roundtime *= 60.0;
// Start timer.
tRoundEnd = CreateTimer(roundtime, RoundEndTimer, _, TIMER_FLAG_NO_MAPCHANGE);
}
/**
* The round is ending.
*
* @param reason Reason the round has ended.
*/
RoundEndOnRoundEnd(reason)
{
// If round end timer is running, then kill it.
if (tRoundEnd != INVALID_HANDLE)
{
// Kill timer.
KillTimer(tRoundEnd);
// Reset timer handle.
tRoundEnd = INVALID_HANDLE;
}
// Tell plugin no zombies have been spawned.
g_bZombieSpawned = false;
// Get outcome of the round.
new RoundEndOutcome:outcome = RoundEndReasonToOutcome(reason);
// Display the overlay to all clients.
RoundEndOverlayStart(ROUNDEND_DELAY_NORMAL, outcome);
// Balance teams.
RoundEndBalanceTeams();
}
/**
* Finds DX level of a client.
*
* @param client The client index.
*/
RoundEndGetClientDXLevel(client)
{
// If client is fake (or bot), then stop.
if (IsFakeClient(client))
{
return;
}
// Query mat_dxlevel on client.
mat_dxlevel = QueryClientConVar(client, "mat_dxlevel", RoundEndQueryClientDXLevel);
}
/**
* Query callback function.
*
* @param cookie Unique cookie of the query.
* @param client The client index.
* @param result The result of the query (see console.inc enum ConVarQueryResult)
* @param cvarName Name of the cvar.
* @param cvarValue Value of the cvar.
*/
public RoundEndQueryClientDXLevel(QueryCookie:cookie, client, ConVarQueryResult:result, const String:cvarName[], const String:cvarValue[])
{
// If query cookie does not match cookie given by mat_dxlevel query, then stop, this isn't our query.
if (cookie != mat_dxlevel)
{
return;
}
// Reset dxLevel.
dxLevel[client] = 0;
// If result is any other than ConVarQuery_Okay, then stop.
if (result != ConVarQuery_Okay)
{
return;
}
// Copy cvar value to dxLevel array.
dxLevel[client] = StringToInt(cvarValue);
}
/**
* Convert a round_end reason, to a round winner, or draw.
*
* @param reason The round_end reason.
* @return The winner of the round. (see enum RoundEndOutcome)
*/
RoundEndOutcome:RoundEndReasonToOutcome(reason)
{
switch(reason)
{
// CTs won the round.
case ROUNDEND_CTS_WIN:
{
return HumansWin;
}
// Ts won the round.
case ROUNDEND_TERRORISTS_WIN:
{
return ZombiesWin;
}
// Unexpected case.
default:
{
return Draw;
}
}
// Return draw to satisfy compiler. (code will never reach this point.)
return Draw;
}
/**
* Timer callback, called when round time reaches 0.
*
* @param timer The timer handle.
*/
public Action:RoundEndTimer(Handle:timer)
{
// Terminate the round with a normal delay, and CTs as the winner.
TerminateRound(ROUNDEND_DELAY_NORMAL, ROUNDEND_CTS_WIN);
}
/**
* Checks if the round is over.
*
* @param outcome Set to the outcome of the round, if round is over.
* @return True if the round is over, false otherwise.
*/
bool:RoundEndGetRoundStatus(&RoundEndOutcome:outcome)
{
// If zombie hasn't spawned, then stop.
if (!g_bZombieSpawned)
{
// Round isn't over.
return false;
}
// Initialize count variables
new zombiecount;
new humancount;
// Count valid clients. (true to only allow living clients)
ZRCountValidClients(zombiecount, humancount);
// If there are no clients on either teams, then stop.
if (!zombiecount && !humancount)
{
// Round isn't active.
return false;
}
// If there are clients on both teams, then stop.
if (zombiecount && humancount)
{
// Round isn't over.
return false;
}
// We know here, that either zombiecount or humancount is 0. (not both)
// If there are zombies, then zombies won the round.
if (zombiecount)
{
outcome = ZombiesWin;
}
// If there are no zombies, that means there must be humans, they win the round.
else
{
outcome = HumansWin;
}
// Round is over.
return true;
}
/**
* Ends the round with the given outcome.
*
* @param outcome The outcome of the round.
*/
RoundEndTerminateRound(RoundEndOutcome:outcome)
{
switch(outcome)
{
// Zombies won.
case ZombiesWin:
{
// Terminate the round with a normal delay, and Ts as the winner.
TerminateRound(ROUNDEND_DELAY_NORMAL, ROUNDEND_TERRORISTS_WIN);
}
// Humans won.
case HumansWin:
{
// Terminate the round with a normal delay, and CTs as the winner.
TerminateRound(ROUNDEND_DELAY_NORMAL, ROUNDEND_CTS_WIN);
}
}
}
/**
* Restarts the game.
*
* @param delay How long to wait between round end and new round.
*/
RoundEndRestart(Float:delay)
{
// Terminate the round.
TerminateRound(delay, ROUNDEND_GAME_COMMENCING);
}
/**
* Balances teams
*
* @param spawn If true, it will respawn player after switching their team.
*/
RoundEndBalanceTeams()
{
// Create eligible player list.
new Handle:arrayEligibleClients = INVALID_HANDLE;
new eligibleclients = ZRCreateEligibleClientList(arrayEligibleClients, true);
// If there are no eligible client's then stop.
if (!eligibleclients)
{
return;
}
new client;
// Move all clients to T
// x = array index.
// client = client index.
for (new x = 0; x < eligibleclients; x++)
{
// Get client stored in array index.
client = GetArrayCell(arrayEligibleClients, x);
// Switch client to T
CS_SwitchTeam(client, CS_TEAM_T);
}
// Move every other client back to CT
// x = array index
// client = client index.
for (new x = 0; x < eligibleclients; x += 2)
{
// Get client stored in array index.
client = GetArrayCell(arrayEligibleClients, x);
// Switch client to CT
CS_SwitchTeam(client, CS_TEAM_CT);
}
// Destroy handle.
CloseHandle(arrayEligibleClients);
}
/**
* Displays overlay to client, or prints unsupported message on client's screen.
*
* @param client The client index.
* @param overlay The overlay path.
*/
RoundEndDisplayClientOverlay(client, const String:overlay[])
{
// If dxLevel is 0, then query on client failed, so try again, then stop.
if (!dxLevel[client])
{
// Query dxlevel cvar.
RoundEndGetClientDXLevel(client);
return;
}
// If dxLevel is above or equal to minimum requirement, then display overlay.
if (dxLevel[client] >= GENERAL_DXLEVEL_MIN)
{
ClientCommand(client, "r_screenoverlay \"%s\"", overlay);
}
// If client doesn't meet minimum requirement, then print unsupported text.
else
{
ZR_PrintCenterText(client, "DX90 not supported", dxLevel[client], GENERAL_DXLEVEL_MIN);
}
}
/**
* Displays overlays to clients, depending on the outcome.
*
* @param time Time to display overlays.
* @param outcome The outcome of the round.
*/
RoundEndOverlayStart(Float:time, RoundEndOutcome:outcome)
{
// If round end overlays are disabled, then stop.
new bool:overlay = GetConVarBool(g_hCvarsList[CVAR_ROUNDEND_OVERLAY]);
if (!overlay)
{
return;
}
decl String:overlaypath[PLATFORM_MAX_PATH];
switch(outcome)
{
// Show "zombies win" overlay.
case ZombiesWin:
{
GetConVarString(g_hCvarsList[CVAR_ROUNDEND_OVERLAY_ZOMBIE], overlaypath, sizeof(overlaypath));
}
// Show "humans win" overlay.
case HumansWin:
{
GetConVarString(g_hCvarsList[CVAR_ROUNDEND_OVERLAY_HUMAN], overlaypath, sizeof(overlaypath));
}
// Show no overlay.
default:
{
strcopy(overlaypath, sizeof(overlaypath), "");
}
}
// x = client index.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
RoundEndDisplayClientOverlay(x, overlaypath);
}
CreateTimer(time, RoundEndOverlayTimer, _, TIMER_FLAG_NO_MAPCHANGE);
}
RoundEndOverlayStop()
{
// x = client index.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
// Removes overlay from client's screen.
ClientCommand(x, "r_screenoverlay \"\"");
}
}
/**
* Timer callback, stops overlays on all clients.
*
* @param timer The timer handle.
*/
public Action:RoundEndOverlayTimer(Handle:timer)
{
// Stop all overlays.
RoundEndOverlayStop();
}