Fixed admin infect bug (I think), made roundend core module, handles all round end events, modified ClassApplyOverlay, removed mp_restartgame hook, recoded PlayerLeft and BalanceTeam functions. And added comments.

This commit is contained in:
Greyscale 2009-04-18 01:44:41 +02:00
parent 879446ac7c
commit c34e32097d
9 changed files with 708 additions and 410 deletions

View File

@ -17,15 +17,28 @@
#define VERSION "3.0-dev" #define VERSION "3.0-dev"
// Core include.
#include "zr/zombiereloaded" #include "zr/zombiereloaded"
#include "zr/global"
#include "zr/cvars"
#include "zr/translation"
#include "zr/offsets"
#include "zr/models"
#include "zr/overlays"
// Class system // External api (not done)
//#include "zr/global"
// Cvars (core)
#include "zr/cvars"
// Translations (core)
#include "zr/translation"
// Offsets (core)
#include "zr/offsets"
// Models (core)
#include "zr/models"
// Round end (core)
#include "zr/roundend"
// Class system (module)
#include "zr/playerclasses/playerclasses" #include "zr/playerclasses/playerclasses"
#include "zr/anticamp" #include "zr/anticamp"
@ -34,31 +47,31 @@
#include "zr/menu" #include "zr/menu"
#include "zr/sayhooks" #include "zr/sayhooks"
// Weapons // Weapons (module)
#include "zr/weapons/weapons" #include "zr/weapons/weapons"
// Sound effects // Sound effects (module)
#include "zr/soundeffects/soundeffects" #include "zr/soundeffects/soundeffects"
// Antistick // Antistick (module)
#include "zr/antistick" #include "zr/antistick"
// Hitgroups // Hitgroups (module)
#include "zr/hitgroups" #include "zr/hitgroups"
// Knockback // Knockback (module)
#include "zr/knockback" #include "zr/knockback"
// Spawn protect // Spawn protect (module)
#include "zr/spawnprotect" #include "zr/spawnprotect"
// Respawn // Respawn (module)
#include "zr/respawn" #include "zr/respawn"
// Napalm // Napalm (module)
#include "zr/napalm" #include "zr/napalm"
// ZHP // ZHP (module)
#include "zr/zhp" #include "zr/zhp"
#include "zr/zadmin" #include "zr/zadmin"
@ -77,7 +90,8 @@ public Plugin:myinfo =
public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max)
{ {
CreateGlobals(); // Todo: External API
//CreateGlobals();
return true; return true;
} }
@ -146,6 +160,7 @@ public OnMapStart()
LoadDownloadData(); LoadDownloadData();
// Forward event to modules. // Forward event to modules.
RoundEndOnMapStart();
ClassLoad(); ClassLoad();
WeaponsLoad(); WeaponsLoad();
SEffectsOnMapStart(); SEffectsOnMapStart();
@ -190,6 +205,7 @@ public OnClientPutInServer(client)
bMotherInfectImmune[client] = false; bMotherInfectImmune[client] = false;
// Forward event to modules. // Forward event to modules.
RoundEndGetClientDXLevel(client);
ClassClientInit(client); ClassClientInit(client);
SEffectsClientInit(client); SEffectsClientInit(client);
WeaponsClientInit(client); WeaponsClientInit(client);
@ -198,7 +214,6 @@ public OnClientPutInServer(client)
ZHPClientInit(client); ZHPClientInit(client);
ClientHookAttack(client); ClientHookAttack(client);
ZRFindClientDXLevel(client);
for (new x = 0; x < MAXTIMERS; x++) for (new x = 0; x < MAXTIMERS; x++)
{ {
@ -229,7 +244,6 @@ public OnClientDisconnect(client)
MapChangeCleanup() MapChangeCleanup()
{ {
tRound = INVALID_HANDLE;
tInfect = INVALID_HANDLE; tInfect = INVALID_HANDLE;
AntiStickReset(); AntiStickReset();
@ -246,7 +260,7 @@ MapChangeCleanup()
} }
} }
ZREnd() /*ZREnd()
{ {
TerminateRound(3.0, Game_Commencing); TerminateRound(3.0, Game_Commencing);
@ -272,4 +286,4 @@ ZREnd()
} }
} }
} }
} }*/

View File

@ -64,9 +64,9 @@ enum ZRSettings
Handle:CVAR_ANTISTICK_INTERVAL, Handle:CVAR_ANTISTICK_INTERVAL,
Handle:CVAR_PROTECT, Handle:CVAR_PROTECT,
Handle:CVAR_CONSECUTIVE_INFECT, Handle:CVAR_CONSECUTIVE_INFECT,
Handle:CVAR_OVERLAYS, Handle:CVAR_ROUNDEND_OVERLAY,
Handle:CVAR_OVERLAYS_HUMAN, Handle:CVAR_ROUNDEND_OVERLAY_ZOMBIE,
Handle:CVAR_OVERLAYS_ZOMBIE, Handle:CVAR_ROUNDEND_OVERLAY_HUMAN,
Handle:CVAR_ZMARKET_BUYZONE, Handle:CVAR_ZMARKET_BUYZONE,
Handle:CVAR_ZSPAWN, Handle:CVAR_ZSPAWN,
Handle:CVAR_ZTELE, Handle:CVAR_ZTELE,
@ -154,9 +154,9 @@ CreateCvars()
gCvars[CVAR_ANTISTICK_INTERVAL] = CreateConVar("zr_antistick_interval", "1.0", "How often, in seconds, the anti-stick module checks each player for being stuck. (1.0: Default)"); gCvars[CVAR_ANTISTICK_INTERVAL] = CreateConVar("zr_antistick_interval", "1.0", "How often, in seconds, the anti-stick module checks each player for being stuck. (1.0: Default)");
gCvars[CVAR_PROTECT] = CreateConVar("zr_protect", "10", "Players that join late will be protected for this long, in seconds (0: Disable)"); gCvars[CVAR_PROTECT] = CreateConVar("zr_protect", "10", "Players that join late will be protected for this long, in seconds (0: Disable)");
gCvars[CVAR_CONSECUTIVE_INFECT] = CreateConVar("zr_consecutive_infect", "0", "Allow player to be randomly chosen twice in a row to be a mother zombie (0: Disable)"); gCvars[CVAR_CONSECUTIVE_INFECT] = CreateConVar("zr_consecutive_infect", "0", "Allow player to be randomly chosen twice in a row to be a mother zombie (0: Disable)");
gCvars[CVAR_OVERLAYS] = CreateConVar("zr_overlays", "1", "Will show overlays that tell who the winner of the round was (0: Disable)"); gCvars[CVAR_ROUNDEND_OVERLAY] = CreateConVar("zr_roundend_overlay", "1", "Shows an overlay to all clients when a team wins. (0: Disable)");
gCvars[CVAR_OVERLAYS_HUMAN] = CreateConVar("zr_overlays_human", "overlays/zr/humans_win", "The overlay shown to tell everyone that humans won when zr_overlays is 1"); gCvars[CVAR_ROUNDEND_OVERLAY_HUMAN] = CreateConVar("zr_roundend_overlays_human", "overlays/zr/humans_win", "Path to \"humans win\" overlay");
gCvars[CVAR_OVERLAYS_ZOMBIE] = CreateConVar("zr_overlays_zombie", "overlays/zr/zombies_win", "The overlay shown to tell everyone that zombies won when zr_overlays is 1"); gCvars[CVAR_ROUNDEND_OVERLAY_ZOMBIE] = CreateConVar("zr_roundend_overlays_zombie", "overlays/zr/zombies_win", "Path to \"zombies win\" overlay");
gCvars[CVAR_ZMARKET_BUYZONE] = CreateConVar("zr_zmarket_buyzone", "1", "Must be in buyzone to access !zmarket, if Market is installed (0: Can be used anywhere)"); gCvars[CVAR_ZMARKET_BUYZONE] = CreateConVar("zr_zmarket_buyzone", "1", "Must be in buyzone to access !zmarket, if Market is installed (0: Can be used anywhere)");
gCvars[CVAR_ZSPAWN] = CreateConVar("zr_zspawn", "1", "Allow players to spawn if they just joined the game (0: Disable)"); gCvars[CVAR_ZSPAWN] = CreateConVar("zr_zspawn", "1", "Allow players to spawn if they just joined the game (0: Disable)");
gCvars[CVAR_ZTELE] = CreateConVar("zr_tele", "1", "Allow players to use the teleporter to get to spawn. (0: Disable)"); gCvars[CVAR_ZTELE] = CreateConVar("zr_tele", "1", "Allow players to use the teleporter to get to spawn. (0: Disable)");
@ -184,7 +184,9 @@ CreateCvars()
gCvars[CVAR_ANTICAMP_UPDATE_INTERVAL] = CreateConVar("zr_anticamp_update_interval", "1", "How often to update player locations (in seconds)."); gCvars[CVAR_ANTICAMP_UPDATE_INTERVAL] = CreateConVar("zr_anticamp_update_interval", "1", "How often to update player locations (in seconds).");
gCvars[CVAR_ANTICAMP_ECHO] = CreateConVar("zr_anticamp_echo", "1", "Log kills done by anticamp to admin chat."); gCvars[CVAR_ANTICAMP_ECHO] = CreateConVar("zr_anticamp_echo", "1", "Log kills done by anticamp to admin chat.");
HookConVarChange(gCvars[CVAR_ENABLE], EnableHook); // TODO: Recode.
//HookConVarChange(gCvars[CVAR_ENABLE], EnableHook);
HookConVarChange(gCvars[CVAR_ANTICAMP], AnticampHook); HookConVarChange(gCvars[CVAR_ANTICAMP], AnticampHook);
HookConVarChange(gCvars[CVAR_ANTICAMP_UPDATE_INTERVAL], UpdateIntervalHook); HookConVarChange(gCvars[CVAR_ANTICAMP_UPDATE_INTERVAL], UpdateIntervalHook);
@ -198,19 +200,15 @@ HookCvars()
HookConVarChange(FindConVar("mp_autoteambalance"), AutoTeamBalanceHook); HookConVarChange(FindConVar("mp_autoteambalance"), AutoTeamBalanceHook);
HookConVarChange(FindConVar("mp_limitteams"), LimitTeamsHook); HookConVarChange(FindConVar("mp_limitteams"), LimitTeamsHook);
HookConVarChange(FindConVar("mp_restartgame"), RestartGameHook);
} }
UnhookCvars() UnhookCvars()
{ {
UnhookConVarChange(FindConVar("mp_autoteambalance"), AutoTeamBalanceHook); UnhookConVarChange(FindConVar("mp_autoteambalance"), AutoTeamBalanceHook);
UnhookConVarChange(FindConVar("mp_limitteams"), LimitTeamsHook); UnhookConVarChange(FindConVar("mp_limitteams"), LimitTeamsHook);
UnhookConVarChange(FindConVar("mp_restartgame"), RestartGameHook);
} }
public EnableHook(Handle:convar, const String:oldValue[], const String:newValue[]) /*public EnableHook(Handle:convar, const String:oldValue[], const String:newValue[])
{ {
new bool:enable = bool:StringToInt(newValue); new bool:enable = bool:StringToInt(newValue);
@ -225,7 +223,7 @@ public EnableHook(Handle:convar, const String:oldValue[], const String:newValue[
{ {
ZREnd(); ZREnd();
} }
} }*/
public AutoTeamBalanceHook(Handle:convar, const String:oldValue[], const String:newValue[]) public AutoTeamBalanceHook(Handle:convar, const String:oldValue[], const String:newValue[])
{ {

View File

@ -36,37 +36,39 @@ public Action:RoundStart(Handle:event, const String:name[], bool:dontBroadcast)
{ {
ChangeLightStyle(); ChangeLightStyle();
// Forward event to sub-modules.
SEffectsOnRoundStart();
AntiStickOnRoundStart();
if (tRound != INVALID_HANDLE)
{
KillTimer(tRound);
tRound = INVALID_HANDLE;
}
if (tInfect != INVALID_HANDLE) if (tInfect != INVALID_HANDLE)
{ {
KillTimer(tInfect); KillTimer(tInfect);
tInfect = INVALID_HANDLE; tInfect = INVALID_HANDLE;
} }
g_bZombieSpawned = false;
for (new x = 1; x<= MaxClients; x++)
{
if (!IsClientInGame(x))
{
continue;
}
bZombie[x] = false;
}
// Balance teams, and respawn all players.
BalanceTeams(true);
ZR_PrintToChat(0, "Round objective"); ZR_PrintToChat(0, "Round objective");
// Forward event to sub-modules.
RoundEndOnRoundStart();
SEffectsOnRoundStart();
AntiStickOnRoundStart();
} }
public Action:RoundFreezeEnd(Handle:event, const String:name[], bool:dontBroadcast) public Action:RoundFreezeEnd(Handle:event, const String:name[], bool:dontBroadcast)
{ {
RemoveObjectives(); RemoveObjectives();
if (tRound != INVALID_HANDLE)
{
KillTimer(tRound);
}
new Float:roundlen = GetConVarFloat(FindConVar("mp_roundtime")) * 60.0;
tRound = CreateTimer(roundlen, RoundOver, _, TIMER_FLAG_NO_MAPCHANGE);
if (tInfect != INVALID_HANDLE) if (tInfect != INVALID_HANDLE)
{ {
KillTimer(tInfect); KillTimer(tInfect);
@ -78,17 +80,14 @@ public Action:RoundFreezeEnd(Handle:event, const String:name[], bool:dontBroadca
tInfect = CreateTimer(randlen, MotherZombie, _, TIMER_FLAG_NO_MAPCHANGE); tInfect = CreateTimer(randlen, MotherZombie, _, TIMER_FLAG_NO_MAPCHANGE);
// Forward events to modules. // Forward events to modules.
RoundEndOnRoundFreezeEnd();
ZTeleEnable(); ZTeleEnable();
} }
public Action:RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) public Action:RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
{ {
if (tRound != INVALID_HANDLE) new reason = GetEventInt(event, "reason");
{
KillTimer(tRound);
tRound = INVALID_HANDLE;
}
if (tInfect != INVALID_HANDLE) if (tInfect != INVALID_HANDLE)
{ {
@ -108,19 +107,11 @@ public Action:RoundEnd(Handle:event, const String:name[], bool:dontBroadcast)
bZombie[x] = false; bZombie[x] = false;
} }
// Balance teams.
BalanceTeams(); BalanceTeams();
new reason = GetEventInt(event, "reason"); // Forward event to modules.
RoundEndOnRoundEnd(reason);
if (reason == CTs_PreventEscape)
{
ShowOverlays(5.0, Human);
}
else if (reason == Terrorists_Escaped)
{
ShowOverlays(5.0, Zombie);
}
ZTeleReset(); ZTeleReset();
} }
@ -296,8 +287,11 @@ public Action:PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
RespawnOnClientDeath(index, attacker, weapon); RespawnOnClientDeath(index, attacker, weapon);
ZHPOnClientDeath(index); ZHPOnClientDeath(index);
new ZTeam:team = IsRoundOver(); new RoundEndOutcome:outcome;
RoundWin(team); if (RoundEndGetRoundStatus(outcome))
{
RoundEndTerminateRound(outcome);
}
} }
public Action:PlayerJump(Handle:event, const String:name[], bool:dontBroadcast) public Action:PlayerJump(Handle:event, const String:name[], bool:dontBroadcast)

View File

@ -1,45 +0,0 @@
/**
* ====================
* Zombie:Reloaded
* File: overlays.inc
* Author: Greyscale
* ====================
*/
ShowOverlays(Float:time, ZTeam:winner)
{
new bool:overlays = GetConVarBool(gCvars[CVAR_OVERLAYS]);
if (overlays)
{
decl String:overlay[64];
if (winner == Human)
{
GetConVarString(gCvars[CVAR_OVERLAYS_HUMAN], overlay, sizeof(overlay));
}
else if (winner == Zombie)
{
GetConVarString(gCvars[CVAR_OVERLAYS_ZOMBIE], overlay, sizeof(overlay));
}
for (new x = 1; x <= MaxClients; x++)
{
if (IsClientInGame(x))
{
ZRDisplayClientOverlay(x, overlay);
}
}
CreateTimer(time, KillOverlays);
}
}
public Action:KillOverlays(Handle:timer)
{
for (new x = 1; x <= MaxClients; x++)
{
if (IsClientInGame(x))
{
ClientCommand(x, "r_screenoverlay \"\"");
}
}
}

View File

@ -54,7 +54,7 @@ bool:ClassApplyAttributes(client, bool:improved = false)
*/ */
bool:ClassApplyModel(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) bool:ClassApplyModel(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER)
{ {
decl String:modelpath[256]; decl String:modelpath[PLATFORM_MAX_PATH];
// Get the model path from the specified cache. // Get the model path from the specified cache.
if (cachetype == ZR_CLASS_CACHE_PLAYER) if (cachetype == ZR_CLASS_CACHE_PLAYER)
@ -130,27 +130,34 @@ bool:ClassApplyAlpha(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER)
*/ */
bool:ClassApplyOverlay(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER) bool:ClassApplyOverlay(client, classindex, cachetype = ZR_CLASS_CACHE_PLAYER)
{ {
decl String:overlay[256]; // If dxLevel is 0, then query on client failed, so try again, then stop.
if (!dxLevel[client])
// Validate DirectX requirements.
if (dxLevel[client] < DXLEVEL_MIN)
{ {
// DirectX version is too old. // Query dxlevel cvar.
// TODO: Log warning? RoundEndGetClientDXLevel(client);
return false; return false;
} }
// If client doesn't meet minimum requirement, then print unsupported text.
if (dxLevel[client] < ROUNDEND_MIN_DXLEVEL)
{
ZR_PrintCenterText(client, "DX90 not supported", dxLevel[client], ROUNDEND_MIN_DXLEVEL);
return false;
}
decl String:overlaypath[PLATFORM_MAX_PATH];
// Get the overlay path from the specified cache. // Get the overlay path from the specified cache.
if (cachetype == ZR_CLASS_CACHE_PLAYER) if (cachetype == ZR_CLASS_CACHE_PLAYER)
{ {
ClassGetOverlayPath(client, overlay, sizeof(overlay), cachetype); ClassGetOverlayPath(client, overlaypath, sizeof(overlaypath), cachetype);
} }
else else
{ {
ClassGetOverlayPath(classindex, overlay, sizeof(overlay), cachetype); ClassGetOverlayPath(classindex, overlaypath, sizeof(overlaypath), cachetype);
} }
ClassOverlayInitialize(client, overlay); ClassOverlayInitialize(client, overlaypath);
return true; return true;
} }

View File

@ -27,7 +27,7 @@ new bClientOverlayOn[MAXPLAYERS + 1];
/** /**
* Path to the currently active overlay. * Path to the currently active overlay.
*/ */
new String:ActiveOverlay[MAXPLAYERS + 1][256]; new String:ActiveOverlay[MAXPLAYERS + 1][PLATFORM_MAX_PATH];
bool:ClientHasOverlay(client) bool:ClientHasOverlay(client)
{ {

405
src/zr/roundend.inc Normal file
View File

@ -0,0 +1,405 @@
/*
* ============================================================================
*
* 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
/**
* Minimum dx level required to see overlays.
*/
#define ROUNDEND_MIN_DXLEVEL 90
/**
* 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;
}
/**
* 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;
}
// Get outcome of the round.
new RoundEndOutcome:outcome = RoundEndReasonToOutcome(reason);
// Display the overlay to all clients.
RoundEndOverlayStart(ROUNDEND_DELAY_NORMAL, outcome);
}
/**
* 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);
}
}
}
/**
* 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] >= ROUNDEND_MIN_DXLEVEL)
{
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], ROUNDEND_MIN_DXLEVEL);
}
}
/**
* 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(gCvars[CVAR_ROUNDEND_OVERLAY]);
if (!overlay)
{
return;
}
decl String:overlaypath[PLATFORM_MAX_PATH];
switch(outcome)
{
// Show "zombies win" overlay.
case ZombiesWin:
{
GetConVarString(gCvars[CVAR_ROUNDEND_OVERLAY_ZOMBIE], overlaypath, sizeof(overlaypath));
}
// Show "humans win" overlay.
case HumansWin:
{
GetConVarString(gCvars[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();
}

View File

@ -90,10 +90,60 @@ ChangeLightStyle()
} }
} }
public RestartGameHook(Handle:convar, const String:oldValue[], const String:newValue[]) /**
* Create an array populated with eligible clients to be zombie.
*
* @param arrayEligibleClients The handle of the array, don't forget to call CloseHandle
* on it when finished!
* @param immunity True to ignore clients immune from mother infect, false to count them.
*/
CreateEligibleClientList(&Handle:arrayEligibleClients, bool:team = false, bool:alive = false, bool:human = false, bool:immunity = false)
{ {
SetConVarInt(FindConVar("mp_restartgame"), 0); // Create array.
TerminateRound(StringToFloat(newValue), Round_Draw); arrayEligibleClients = CreateArray();
// Populate list with eligible clients.
// x = client index.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
// If client isn't on a team, then stop.
if (team && !ZRIsClientOnTeam(x))
{
continue;
}
// If client is dead, then stop.
if (alive && !IsPlayerAlive(x))
{
continue;
}
// If client is already zombie (via admin), then stop.
if (human && !IsPlayerHuman(x))
{
continue;
}
// If client is immune from being a mother zombie, then stop.
if (immunity && bMotherInfectImmune[x])
{
// Take away immunity.
bMotherInfectImmune[x] = false;
continue;
}
// Add eligible client to array.
PushArrayCell(arrayEligibleClients, x);
}
return GetArraySize(arrayEligibleClients);
} }
/** /**
@ -106,40 +156,11 @@ public Action:MotherZombie(Handle:timer)
// Reset timer handle. // Reset timer handle.
tInfect = INVALID_HANDLE; tInfect = INVALID_HANDLE;
// Create array. // Create eligible player list.
new Handle:arrayEligibleClients = CreateArray(); new Handle:arrayEligibleClients = INVALID_HANDLE;
new eligibleclients = CreateEligibleClientList(arrayEligibleClients, true, true, true, true);
// Populate list with eligible clients.
// x = client index.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
// If client is dead, then stop.
if (!IsPlayerAlive(x))
{
continue;
}
// If client is immune from being a mother zombie, then stop.
if (bMotherInfectImmune[x])
{
// Take away immunity.
bMotherInfectImmune[x] = false;
continue;
}
// Add eligible client to array.
PushArrayCell(arrayEligibleClients, x);
}
// If there are no eligible client's then stop. // If there are no eligible client's then stop.
new eligibleclients = GetArraySize(arrayEligibleClients);
if (!eligibleclients) if (!eligibleclients)
{ {
return; return;
@ -217,6 +238,9 @@ public Action:MotherZombie(Handle:timer)
// Mother zombies have been infected. // Mother zombies have been infected.
g_bZombieSpawned = true; g_bZombieSpawned = true;
// Destroy handle.
CloseHandle(arrayEligibleClients);
} }
/** /**
@ -230,7 +254,7 @@ public Action:MotherZombie(Handle:timer)
InfectPlayer(client, attacker = -1, bool:motherinfect = false) InfectPlayer(client, attacker = -1, bool:motherinfect = false)
{ {
// Check if the attacker was specified. // Check if the attacker was specified.
if (attacker > 0) if (ZRIsValidClient(attacker))
{ {
// Fire death event and set weapon info. // Fire death event and set weapon info.
new Handle:event = CreateEvent("player_death"); new Handle:event = CreateEvent("player_death");
@ -253,14 +277,21 @@ InfectPlayer(client, attacker = -1, bool:motherinfect = false)
ztele_count[client] = 0; // In use? ztele_count[client] = 0; // In use?
// Terminate the round if the last player was infected. // Terminate the round if the last player was infected.
new ZTeam:team = IsRoundOver(); new RoundEndOutcome:outcome;
RoundWin(team); if (RoundEndGetRoundStatus(outcome))
{
RoundEndTerminateRound(outcome);
}
// Switch the player to terrorists. // Switch the player to terrorists.
CS_SwitchTeam(client, CS_TEAM_T); CS_SwitchTeam(client, CS_TEAM_T);
// Flag player to be immune from being mother zombie twice.
bMotherInfectImmune[client] = motherinfect; // Check if consecutive infection protection is enabled.
new bool:consecutive_infect = GetConVarBool(gCvars[CVAR_CONSECUTIVE_INFECT]);
// Flag player to be immune from being mother zombie twice, if consecutive infect protection is enabled.
bMotherInfectImmune[client] = consecutive_infect ? motherinfect : false;
// Forward event to modules. // Forward event to modules.
ClassOnClientInfected(client, motherinfect); ClassOnClientInfected(client, motherinfect);
@ -360,159 +391,131 @@ JumpBoost(client, Float:distance, Float:height)
SetPlayerVelocity(client, vel, false); SetPlayerVelocity(client, vel, false);
} }
/**
* Finds a new zombie if the last one disconnects.
*
* @param client The client index.
*/
PlayerLeft(client) PlayerLeft(client)
{ {
if (!IsClientConnected(client) || !IsClientInGame(client)) // If client is dead, then stop.
if (!IsPlayerAlive(client))
{ {
return; return;
} }
new ZTeam:team = IsRoundOver(); // If client isn't a zombie, then stop.
if (team == Zombie) if (!IsPlayerZombie(client))
{
RoundWin(team);
return;
}
if (!IsPlayerAlive(client) || !IsPlayerZombie(client))
{ {
return; return;
} }
new zombiecount = GetZTeamCount(Zombie); // Initialize count variables
if (zombiecount > 1) new zombiecount;
new humancount;
// Count valid clients. (true to only allow living clients)
ZRCountValidClients(zombiecount, humancount);
// If there are other zombies besides the disconnecting player, then stop.
if (zombiecount - 1)
{ {
return; return;
} }
new count = GetTeamClientCount(CS_TEAM_CT); // If there is 1 or no humans left, then stop.
if (count <= 1) if (humancount <= 1)
{ {
return; return;
} }
new Handle:aClients = CreateArray(); // Create eligible player list.
new Handle:arrayEligibleClients = INVALID_HANDLE;
for (new x = 1; x <= MaxClients; x++) // Create eligible client list, with no mother infect immunities
{ new eligibleclients = CreateEligibleClientList(arrayEligibleClients, true, true, true);
if (!IsClientInGame(x) || !IsPlayerAlive(x) || client == x || GetClientTeam(x) != CS_TEAM_CT || bMotherInfectImmune[x])
{
continue;
}
PushArrayCell(aClients, x); // If there are no eligible client's then stop.
} if (!eligibleclients)
new size = GetArraySize(aClients);
if (!size)
{ {
return; return;
} }
new randclient = GetArrayCell(aClients, GetRandomInt(0, size-1)); // Get a random valid array index.
InfectPlayer(randclient, _, true); new randindex = GetRandomInt(0, eligibleclients - 1);
// Get the client stored in the random array index.
new randclient = GetArrayCell(arrayEligibleClients, randindex);
// Infect player.
InfectPlayer(randclient);
// Tell client they have been randomly been chosen to replace disconnecting zombie.
ZR_PrintToChat(randclient, "Zombie replacement"); ZR_PrintToChat(randclient, "Zombie replacement");
CloseHandle(aClients); // Destroy handle.
CloseHandle(arrayEligibleClients);
} }
GetZTeamCount(ZTeam:team) /**
* Balances teams
*
* @param spawn If true, it will respawn player after switching their team.
*/
BalanceTeams(bool:spawn = false)
{ {
new count = 0; // Create eligible player list.
new Handle:arrayEligibleClients = INVALID_HANDLE;
new eligibleclients = CreateEligibleClientList(arrayEligibleClients, true);
for (new x = 1; x <= MaxClients; x++) // If there are no eligible client's then stop.
if (!eligibleclients)
{ {
if (!IsClientInGame(x) || !IsPlayerAlive(x)) 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);
// If spawn is false, then stop.
if (!spawn)
{ {
continue; continue;
} }
new ZTeam:pTeam = GetPlayerZTeam(x); CS_RespawnPlayer(client);
if (pTeam == team)
{
count++;
}
} }
return count; // Move every other client back to CT
}
ZTeam:IsRoundOver() // x = array index
// client = client index.
for (new x = 0; x < eligibleclients; x += 2)
{ {
new bool:zombies = false; // Get client stored in array index.
new bool:humans = false; client = GetArrayCell(arrayEligibleClients, x);
for (new x = 1; x <= MaxClients; x++) // Switch client to CT
{ CS_SwitchTeam(client, CS_TEAM_CT);
if (!IsClientInGame(x) || !IsPlayerAlive(x))
// If spawn is false, then stop.
if (!spawn)
{ {
continue; continue;
} }
if (IsPlayerZombie(x)) CS_RespawnPlayer(client);
{
zombies = true;
}
else
{
humans = true;
}
}
if (zombies && !humans)
{
return Zombie;
}
if (humans && !zombies)
{
if (g_bZombieSpawned)
{
return Human;
}
}
return Neither;
}
RoundWin(ZTeam:team)
{
if (team == Human)
{
TerminateRound(5.0, CTs_PreventEscape);
}
else if (team == Zombie)
{
TerminateRound(5.0, Terrorists_Escaped);
}
}
BalanceTeams()
{
new count = 0;
new cPlayers[MAXPLAYERS];
for (new x = 1; x <= MaxClients; x++)
{
if (!IsClientInGame(x) || GetClientTeam(x) <= 1)
{
continue;
}
CS_SwitchTeam(x, CS_TEAM_T);
cPlayers[count++] = x;
}
for (new x = 0; x < count; x++)
{
if (!IsClientInGame(cPlayers[x]) || GetClientTeam(cPlayers[x]) <= 1)
{
continue;
}
CS_SwitchTeam(cPlayers[x], CS_TEAM_CT);
x++;
} }
} }
@ -539,13 +542,6 @@ RemoveObjectives()
} }
} }
public Action:RoundOver(Handle:timer)
{
tRound = INVALID_HANDLE;
RoundWin(Human);
}
bool:IsPlayerZombie(client) bool:IsPlayerZombie(client)
{ {
return bZombie[client]; return bZombie[client];
@ -555,13 +551,3 @@ bool:IsPlayerHuman(client)
{ {
return !bZombie[client]; return !bZombie[client];
} }
ZTeam:GetPlayerZTeam(client)
{
if (IsPlayerZombie(client))
{
return Zombie;
}
return Human;
}

View File

@ -6,24 +6,6 @@
* ==================== * ====================
*/ */
#define Target_Bombed 1 // Target Successfully Bombed!
#define VIP_Escaped 2 // The VIP has escaped!
#define VIP_Assassinated 3 // VIP has been assassinated!
#define Terrorists_Escaped 4 // The terrorists have escaped!
#define CTs_PreventEscape 5 // The CT's have prevented most of the terrorists from escaping!
#define Escaping_Terrorists_Neutralized 6 // Escaping terrorists have all been neutralized!
#define Bomb_Defused 7 // The bomb has been defused!
#define CTs_Win 8 // Counter-Terrorists Win!
#define Terrorists_Win 9 // Terrorists Win!
#define Round_Draw 10 // Round Draw!
#define All_Hostages_Rescued 11 // All Hostages have been rescued!
#define Target_Saved 12 // Target has been saved!
#define Hostages_Not_Rescued 13 // Hostages have not been rescued!
#define Terrorists_Not_Escaped 14 // Terrorists have not escaped!
#define VIP_Not_Escaped 15 // VIP has not escaped!
#define Game_Commencing 16 // Game Commencing!
#define DXLEVEL_MIN 90
#define DEFAULT_FOV 90 #define DEFAULT_FOV 90
/** /**
@ -56,16 +38,6 @@
* @endsection * @endsection
*/ */
/**
* Lists possible returns of the game at any time.
*/
enum ZTeam
{
Neither, /** Round is not over */
Zombie, /** Round is over because zombies win */
Human, /** Round is over because humans wins */
}
/** /**
* Global variable set to true if market plugin is installed * Global variable set to true if market plugin is installed
*/ */
@ -91,11 +63,6 @@ new bool:bZombie[MAXPLAYERS + 1];
*/ */
new bool:bMotherInfectImmune[MAXPLAYERS + 1]; new bool:bMotherInfectImmune[MAXPLAYERS + 1];
/**
* Global variable to store round win timer handle.
*/
new Handle:tRound = INVALID_HANDLE;
/** /**
* Global variable to store the infect timer handle. * Global variable to store the infect timer handle.
*/ */
@ -161,86 +128,6 @@ ZRBoolToConfigSetting(bool:bOption, String:option[], maxlen)
} }
} }
/**
* Global variable to store a convar query cookie
*/
new QueryCookie:mat_dxlevel;
/**
* Finds DX level of a client.
*
* @param client The client index.
*/
ZRFindClientDXLevel(client)
{
// If client is fake (or bot), then stop.
if (IsFakeClient(client))
{
return;
}
// Query mat_dxlevel on client.
mat_dxlevel = QueryClientConVar(client, "mat_dxlevel", ZRDXLevelClientQuery);
}
/**
* 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 ZRDXLevelClientQuery(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);
}
/**
* Displays overlay to client, or prints unsupported message on client's screen.
*
* @param client The client index.
* @param overlay The overlay path.
*/
ZRDisplayClientOverlay(client, const String:overlay[])
{
// If dxLevel is 0, then query on client failed, so try again, then stop.
if (!dxLevel[client])
{
// Query dxlevel cvar.
ZRFindClientDXLevel(client);
return;
}
// If dxLevel is above or equal to minimum requirement, then display overlay.
if (dxLevel[client] >= 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], DXLEVEL_MIN);
}
}
/** /**
* Check if a client index is a valid player. * Check if a client index is a valid player.
* *
@ -260,6 +147,58 @@ bool:ZRIsValidClient(client, bool:console = false)
return console ? (client >= 0) : (client > 0); return console ? (client >= 0) : (client > 0);
} }
/**
* Count clients on each team.
*
* @param zombies This is set to the number of clients that are zombies.
* @param humans This is set to the number of clients that are humans.
* @param alive If true it will only count live players, false will count alive and dead.
* @return True if successful (zombie has spawned), false otherwise.
*/
bool:ZRCountValidClients(&zombiecount = 0, &humancount = 0, bool:alive = true)
{
// If zombie hasn't spawned, then stop.
if (!g_bZombieSpawned)
{
return false;
}
// x = client index.
for (new x = 1; x <= MaxClients; x++)
{
// If client isn't in-game, then stop.
if (!IsClientInGame(x))
{
continue;
}
// If client isn't on a team, then stop.
if (!ZRIsClientOnTeam(x))
{
continue;
}
// If player must be alive, and player is dead, then stop.
if (alive && !IsPlayerAlive(x))
{
continue;
}
// If player is a zombie, then increment zombie variable.
if (IsPlayerZombie(x))
{
zombiecount++;
}
// If player is a human, then increment human variable.
else if (IsPlayerHuman(x))
{
humancount++;
}
}
return true;
}
/** /**
* Check if a client index is on a team. * Check if a client index is on a team.
* *
@ -291,7 +230,7 @@ bool:ZRIsClientOnTeam(client, team = -1)
* *
* @param team (Optional) Team to check if there are clients on. * @param team (Optional) Team to check if there are clients on.
*/ */
ZRTeamHasClients(team = -1) bool:ZRTeamHasClients(team = -1, bool:alive = false)
{ {
// If team is // If team is
if (team == -1) if (team == -1)