From c34e32097d1471f595b2531c9e377a8a3fff0493 Mon Sep 17 00:00:00 2001 From: Greyscale Date: Sat, 18 Apr 2009 01:44:41 +0200 Subject: [PATCH] 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. --- src/zombiereloaded.sp | 56 ++-- src/zr/cvars.inc | 24 +- src/zr/event.inc | 66 ++-- src/zr/overlays.inc | 45 --- src/zr/playerclasses/apply.inc | 27 +- src/zr/playerclasses/clientoverlays.inc | 2 +- src/zr/roundend.inc | 405 ++++++++++++++++++++++++ src/zr/zombie.inc | 324 +++++++++---------- src/zr/zombiereloaded.inc | 169 ++++------ 9 files changed, 708 insertions(+), 410 deletions(-) delete mode 100644 src/zr/overlays.inc create mode 100644 src/zr/roundend.inc diff --git a/src/zombiereloaded.sp b/src/zombiereloaded.sp index 189aa1d..8c46ea1 100644 --- a/src/zombiereloaded.sp +++ b/src/zombiereloaded.sp @@ -17,15 +17,28 @@ #define VERSION "3.0-dev" +// Core include. #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/anticamp" @@ -34,31 +47,31 @@ #include "zr/menu" #include "zr/sayhooks" -// Weapons +// Weapons (module) #include "zr/weapons/weapons" -// Sound effects +// Sound effects (module) #include "zr/soundeffects/soundeffects" -// Antistick +// Antistick (module) #include "zr/antistick" -// Hitgroups +// Hitgroups (module) #include "zr/hitgroups" -// Knockback +// Knockback (module) #include "zr/knockback" -// Spawn protect +// Spawn protect (module) #include "zr/spawnprotect" -// Respawn +// Respawn (module) #include "zr/respawn" -// Napalm +// Napalm (module) #include "zr/napalm" -// ZHP +// ZHP (module) #include "zr/zhp" #include "zr/zadmin" @@ -77,7 +90,8 @@ public Plugin:myinfo = public bool:AskPluginLoad(Handle:myself, bool:late, String:error[], err_max) { - CreateGlobals(); + // Todo: External API + //CreateGlobals(); return true; } @@ -146,6 +160,7 @@ public OnMapStart() LoadDownloadData(); // Forward event to modules. + RoundEndOnMapStart(); ClassLoad(); WeaponsLoad(); SEffectsOnMapStart(); @@ -190,6 +205,7 @@ public OnClientPutInServer(client) bMotherInfectImmune[client] = false; // Forward event to modules. + RoundEndGetClientDXLevel(client); ClassClientInit(client); SEffectsClientInit(client); WeaponsClientInit(client); @@ -198,7 +214,6 @@ public OnClientPutInServer(client) ZHPClientInit(client); ClientHookAttack(client); - ZRFindClientDXLevel(client); for (new x = 0; x < MAXTIMERS; x++) { @@ -229,7 +244,6 @@ public OnClientDisconnect(client) MapChangeCleanup() { - tRound = INVALID_HANDLE; tInfect = INVALID_HANDLE; AntiStickReset(); @@ -246,7 +260,7 @@ MapChangeCleanup() } } -ZREnd() +/*ZREnd() { TerminateRound(3.0, Game_Commencing); @@ -272,4 +286,4 @@ ZREnd() } } } -} \ No newline at end of file +}*/ \ No newline at end of file diff --git a/src/zr/cvars.inc b/src/zr/cvars.inc index d84ee42..6cdc1eb 100644 --- a/src/zr/cvars.inc +++ b/src/zr/cvars.inc @@ -64,9 +64,9 @@ enum ZRSettings Handle:CVAR_ANTISTICK_INTERVAL, Handle:CVAR_PROTECT, Handle:CVAR_CONSECUTIVE_INFECT, - Handle:CVAR_OVERLAYS, - Handle:CVAR_OVERLAYS_HUMAN, - Handle:CVAR_OVERLAYS_ZOMBIE, + Handle:CVAR_ROUNDEND_OVERLAY, + Handle:CVAR_ROUNDEND_OVERLAY_ZOMBIE, + Handle:CVAR_ROUNDEND_OVERLAY_HUMAN, Handle:CVAR_ZMARKET_BUYZONE, Handle:CVAR_ZSPAWN, 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_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_OVERLAYS] = CreateConVar("zr_overlays", "1", "Will show overlays that tell who the winner of the round was (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_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] = CreateConVar("zr_roundend_overlay", "1", "Shows an overlay to all clients when a team wins. (0: Disable)"); + gCvars[CVAR_ROUNDEND_OVERLAY_HUMAN] = CreateConVar("zr_roundend_overlays_human", "overlays/zr/humans_win", "Path to \"humans win\" overlay"); + 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_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)"); @@ -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_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_UPDATE_INTERVAL], UpdateIntervalHook); @@ -198,19 +200,15 @@ HookCvars() HookConVarChange(FindConVar("mp_autoteambalance"), AutoTeamBalanceHook); HookConVarChange(FindConVar("mp_limitteams"), LimitTeamsHook); - - HookConVarChange(FindConVar("mp_restartgame"), RestartGameHook); } UnhookCvars() { UnhookConVarChange(FindConVar("mp_autoteambalance"), AutoTeamBalanceHook); 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); @@ -225,7 +223,7 @@ public EnableHook(Handle:convar, const String:oldValue[], const String:newValue[ { ZREnd(); } -} +}*/ public AutoTeamBalanceHook(Handle:convar, const String:oldValue[], const String:newValue[]) { diff --git a/src/zr/event.inc b/src/zr/event.inc index 00fe2c2..1b1c8f9 100644 --- a/src/zr/event.inc +++ b/src/zr/event.inc @@ -36,37 +36,39 @@ public Action:RoundStart(Handle:event, const String:name[], bool:dontBroadcast) { ChangeLightStyle(); - // Forward event to sub-modules. - SEffectsOnRoundStart(); - AntiStickOnRoundStart(); - - if (tRound != INVALID_HANDLE) - { - KillTimer(tRound); - tRound = INVALID_HANDLE; - } - if (tInfect != INVALID_HANDLE) { KillTimer(tInfect); 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"); + + // Forward event to sub-modules. + RoundEndOnRoundStart(); + SEffectsOnRoundStart(); + AntiStickOnRoundStart(); } public Action:RoundFreezeEnd(Handle:event, const String:name[], bool:dontBroadcast) { 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) { KillTimer(tInfect); @@ -78,17 +80,14 @@ public Action:RoundFreezeEnd(Handle:event, const String:name[], bool:dontBroadca tInfect = CreateTimer(randlen, MotherZombie, _, TIMER_FLAG_NO_MAPCHANGE); // Forward events to modules. + RoundEndOnRoundFreezeEnd(); ZTeleEnable(); } public Action:RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) { - if (tRound != INVALID_HANDLE) - { - KillTimer(tRound); - tRound = INVALID_HANDLE; - } + new reason = GetEventInt(event, "reason"); if (tInfect != INVALID_HANDLE) { @@ -108,19 +107,11 @@ public Action:RoundEnd(Handle:event, const String:name[], bool:dontBroadcast) bZombie[x] = false; } + // Balance teams. BalanceTeams(); - new reason = GetEventInt(event, "reason"); - - if (reason == CTs_PreventEscape) - { - ShowOverlays(5.0, Human); - } - else if (reason == Terrorists_Escaped) - { - ShowOverlays(5.0, Zombie); - } - + // Forward event to modules. + RoundEndOnRoundEnd(reason); ZTeleReset(); } @@ -296,8 +287,11 @@ public Action:PlayerDeath(Handle:event, const String:name[], bool:dontBroadcast) RespawnOnClientDeath(index, attacker, weapon); ZHPOnClientDeath(index); - new ZTeam:team = IsRoundOver(); - RoundWin(team); + new RoundEndOutcome:outcome; + if (RoundEndGetRoundStatus(outcome)) + { + RoundEndTerminateRound(outcome); + } } public Action:PlayerJump(Handle:event, const String:name[], bool:dontBroadcast) diff --git a/src/zr/overlays.inc b/src/zr/overlays.inc deleted file mode 100644 index 022d274..0000000 --- a/src/zr/overlays.inc +++ /dev/null @@ -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 \"\""); - } - } -} diff --git a/src/zr/playerclasses/apply.inc b/src/zr/playerclasses/apply.inc index d4f73db..8e91a8d 100644 --- a/src/zr/playerclasses/apply.inc +++ b/src/zr/playerclasses/apply.inc @@ -54,7 +54,7 @@ bool:ClassApplyAttributes(client, bool:improved = false) */ 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. 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) { - decl String:overlay[256]; - - // Validate DirectX requirements. - if (dxLevel[client] < DXLEVEL_MIN) + // If dxLevel is 0, then query on client failed, so try again, then stop. + if (!dxLevel[client]) { - // DirectX version is too old. - // TODO: Log warning? + // Query dxlevel cvar. + RoundEndGetClientDXLevel(client); 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. if (cachetype == ZR_CLASS_CACHE_PLAYER) { - ClassGetOverlayPath(client, overlay, sizeof(overlay), cachetype); + ClassGetOverlayPath(client, overlaypath, sizeof(overlaypath), cachetype); } else { - ClassGetOverlayPath(classindex, overlay, sizeof(overlay), cachetype); + ClassGetOverlayPath(classindex, overlaypath, sizeof(overlaypath), cachetype); } - ClassOverlayInitialize(client, overlay); + ClassOverlayInitialize(client, overlaypath); return true; } diff --git a/src/zr/playerclasses/clientoverlays.inc b/src/zr/playerclasses/clientoverlays.inc index da24f42..65e8166 100644 --- a/src/zr/playerclasses/clientoverlays.inc +++ b/src/zr/playerclasses/clientoverlays.inc @@ -27,7 +27,7 @@ new bClientOverlayOn[MAXPLAYERS + 1]; /** * Path to the currently active overlay. */ -new String:ActiveOverlay[MAXPLAYERS + 1][256]; +new String:ActiveOverlay[MAXPLAYERS + 1][PLATFORM_MAX_PATH]; bool:ClientHasOverlay(client) { diff --git a/src/zr/roundend.inc b/src/zr/roundend.inc new file mode 100644 index 0000000..30f911a --- /dev/null +++ b/src/zr/roundend.inc @@ -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(); +} \ No newline at end of file diff --git a/src/zr/zombie.inc b/src/zr/zombie.inc index ea46094..7e55301 100644 --- a/src/zr/zombie.inc +++ b/src/zr/zombie.inc @@ -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); - TerminateRound(StringToFloat(newValue), Round_Draw); + // Create array. + 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. tInfect = INVALID_HANDLE; - // Create array. - new Handle: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 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); - } + // Create eligible player list. + new Handle:arrayEligibleClients = INVALID_HANDLE; + new eligibleclients = CreateEligibleClientList(arrayEligibleClients, true, true, true, true); // If there are no eligible client's then stop. - new eligibleclients = GetArraySize(arrayEligibleClients); if (!eligibleclients) { return; @@ -217,6 +238,9 @@ public Action:MotherZombie(Handle:timer) // Mother zombies have been infected. g_bZombieSpawned = true; + + // Destroy handle. + CloseHandle(arrayEligibleClients); } /** @@ -230,7 +254,7 @@ public Action:MotherZombie(Handle:timer) InfectPlayer(client, attacker = -1, bool:motherinfect = false) { // Check if the attacker was specified. - if (attacker > 0) + if (ZRIsValidClient(attacker)) { // Fire death event and set weapon info. new Handle:event = CreateEvent("player_death"); @@ -253,14 +277,21 @@ InfectPlayer(client, attacker = -1, bool:motherinfect = false) ztele_count[client] = 0; // In use? // Terminate the round if the last player was infected. - new ZTeam:team = IsRoundOver(); - RoundWin(team); + new RoundEndOutcome:outcome; + if (RoundEndGetRoundStatus(outcome)) + { + RoundEndTerminateRound(outcome); + } // Switch the player to terrorists. 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. ClassOnClientInfected(client, motherinfect); @@ -360,159 +391,131 @@ JumpBoost(client, Float:distance, Float:height) SetPlayerVelocity(client, vel, false); } +/** + * Finds a new zombie if the last one disconnects. + * + * @param client The client index. + */ PlayerLeft(client) { - if (!IsClientConnected(client) || !IsClientInGame(client)) + // If client is dead, then stop. + if (!IsPlayerAlive(client)) { return; } - new ZTeam:team = IsRoundOver(); - if (team == Zombie) - { - RoundWin(team); - return; - } - - if (!IsPlayerAlive(client) || !IsPlayerZombie(client)) + // If client isn't a zombie, then stop. + if (!IsPlayerZombie(client)) { return; } - new zombiecount = GetZTeamCount(Zombie); - if (zombiecount > 1) + // Initialize count variables + 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; } - new count = GetTeamClientCount(CS_TEAM_CT); - if (count <= 1) + // If there is 1 or no humans left, then stop. + if (humancount <= 1) { return; } - new Handle:aClients = CreateArray(); + // Create eligible player list. + new Handle:arrayEligibleClients = INVALID_HANDLE; - for (new x = 1; x <= MaxClients; x++) - { - if (!IsClientInGame(x) || !IsPlayerAlive(x) || client == x || GetClientTeam(x) != CS_TEAM_CT || bMotherInfectImmune[x]) - { - continue; - } - - PushArrayCell(aClients, x); - } + // Create eligible client list, with no mother infect immunities + new eligibleclients = CreateEligibleClientList(arrayEligibleClients, true, true, true); - new size = GetArraySize(aClients); - if (!size) + // If there are no eligible client's then stop. + if (!eligibleclients) { return; } - new randclient = GetArrayCell(aClients, GetRandomInt(0, size-1)); - InfectPlayer(randclient, _, true); - + // Get a random valid array index. + 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"); - - 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; } - new ZTeam:pTeam = GetPlayerZTeam(x); - if (pTeam == team) - { - count++; - } + CS_RespawnPlayer(client); } - return count; -} - -ZTeam:IsRoundOver() -{ - new bool:zombies = false; - new bool:humans = false; + // Move every other client back to CT - for (new x = 1; x <= MaxClients; x++) + // x = array index + // client = client index. + for (new x = 0; x < eligibleclients; x += 2) { - if (!IsClientInGame(x) || !IsPlayerAlive(x)) + // Get client stored in array index. + client = GetArrayCell(arrayEligibleClients, x); + + // Switch client to CT + CS_SwitchTeam(client, CS_TEAM_CT); + + // If spawn is false, then stop. + if (!spawn) { continue; } - if (IsPlayerZombie(x)) - { - 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++; + CS_RespawnPlayer(client); } } @@ -539,13 +542,6 @@ RemoveObjectives() } } -public Action:RoundOver(Handle:timer) -{ - tRound = INVALID_HANDLE; - - RoundWin(Human); -} - bool:IsPlayerZombie(client) { return bZombie[client]; @@ -554,14 +550,4 @@ bool:IsPlayerZombie(client) bool:IsPlayerHuman(client) { return !bZombie[client]; -} - -ZTeam:GetPlayerZTeam(client) -{ - if (IsPlayerZombie(client)) - { - return Zombie; - } - - return Human; -} +} \ No newline at end of file diff --git a/src/zr/zombiereloaded.inc b/src/zr/zombiereloaded.inc index 61286c3..c9a58ae 100644 --- a/src/zr/zombiereloaded.inc +++ b/src/zr/zombiereloaded.inc @@ -5,25 +5,7 @@ * Author: Greyscale * ==================== */ - -#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 /** @@ -56,16 +38,6 @@ * @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 */ @@ -91,11 +63,6 @@ new bool:bZombie[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. */ @@ -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. * @@ -260,6 +147,58 @@ bool:ZRIsValidClient(client, bool:console = false) 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. * @@ -291,7 +230,7 @@ bool:ZRIsClientOnTeam(client, team = -1) * * @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 == -1)