diff --git a/cstrike/addons/sourcemod/translations/es/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/es/zombiereloaded.phrases.txt index 89621a7..40e5990 100644 --- a/cstrike/addons/sourcemod/translations/es/zombiereloaded.phrases.txt +++ b/cstrike/addons/sourcemod/translations/es/zombiereloaded.phrases.txt @@ -328,6 +328,25 @@ "es" "No esta permitido el cambio de clases." } + // =========================== + // Immunity + // =========================== + + "Immunity Shield Not Available" + { + "en" "You don't have a shield." + } + + "Immunity Shield Cooldown" + { + "en" "{1} seconds until shield is available." + } + + "Immunity Shield Time Left" + { + "en" "Shield time left: {1}" + } + // =========================== // Overlays (core) // =========================== diff --git a/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt index 4da8683..dabd670 100644 --- a/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt +++ b/cstrike/addons/sourcemod/translations/no/zombiereloaded.phrases.txt @@ -327,6 +327,25 @@ "no" "Endring av klasse er ikke tillatt." } + // =========================== + // Immunity + // =========================== + + "Immunity Shield Not Available" + { + "en" "Du har ikke skjold." + } + + "Immunity Shield Cooldown" + { + "en" "{1} sekunder igjen til skjoldet er tilgjengelig." + } + + "Immunity Shield Time Left" + { + "en" "Tid igjen med skjold: {1}" + } + // =========================== // Overlays (core) // =========================== diff --git a/cstrike/addons/sourcemod/translations/ru/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/ru/zombiereloaded.phrases.txt index fff0c2a..6b96744 100644 --- a/cstrike/addons/sourcemod/translations/ru/zombiereloaded.phrases.txt +++ b/cstrike/addons/sourcemod/translations/ru/zombiereloaded.phrases.txt @@ -328,6 +328,25 @@ "ru" "Изменение класса запрещено." } + // =========================== + // Immunity + // =========================== + + "Immunity Shield Not Available" + { + "en" "You don't have a shield." + } + + "Immunity Shield Cooldown" + { + "en" "{1} seconds until shield is available." + } + + "Immunity Shield Time Left" + { + "en" "Shield time left: {1}" + } + // =========================== // Overlays (core) // =========================== diff --git a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt index a88e08e..7241612 100644 --- a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt +++ b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt @@ -341,6 +341,27 @@ "en" "Changing classes is not allowed." } + // =========================== + // Immunity + // =========================== + + "Immunity Shield Not Available" + { + "en" "You don't have a shield." + } + + "Immunity Shield Cooldown" + { + "#format" "{1:d}" + "en" "{1} seconds until shield is available." + } + + "Immunity Shield Time Left" + { + "#format" "{1:d}" + "en" "Shield time left: {1}" + } + // =========================== // Overlays (core) // =========================== diff --git a/src/zr/commands.inc b/src/zr/commands.inc index 9a04e00..e4862ff 100644 --- a/src/zr/commands.inc +++ b/src/zr/commands.inc @@ -46,6 +46,7 @@ CommandsInit() ZHPOnCommandsCreate(); VolOnCommandsCreate(); ZombieSoundsOnCommandsCreate(); + ImmunityOnCommandsCreate(); #if defined ADD_VERSION_INFO VersionOnCommandsCreate(); diff --git a/src/zr/immunityhandler.inc b/src/zr/immunityhandler.inc index 2dca868..7f88406 100644 --- a/src/zr/immunityhandler.inc +++ b/src/zr/immunityhandler.inc @@ -62,6 +62,39 @@ new bool:PlayerImmunityThresholdPassed[MAXPLAYERS + 1] = {false, ...}; /*____________________________________________________________________________*/ +/** + * Console commands are being created. + */ +ImmunityOnCommandsCreate() +{ + RegConsoleCmd("zr_shield", Command_DeployShield, "Deploy the shield, if available."); +} + +/*____________________________________________________________________________*/ + +/** + * Client executed the deploy shield command. + * + * @param client Client index. + * @param argc Number of arguments. + */ +public Action:Command_DeployShield(client, argc) +{ + // Block console. + if (ZRIsConsole(client)) + { + TranslationPrintToServer("Must be player"); + return Plugin_Handled; + } + + // Attempt to deploy shield. + ImmunityDeployShield(client); + + return Plugin_Handled; +} + +/*____________________________________________________________________________*/ + /** * Handles immunity when a client is about to be infected. This function may * delay or block infection according to the immunity mode class settings. @@ -196,6 +229,23 @@ bool:ImmunityOnClientTraceAttack(client, attacker, Float:damage, hitgroup, damag // Block damage from attacker. return true; } + case Immunity_Shield: + { + // Client must be human. + if (InfectIsClientInfected(client)) + { + // Allow damage. + return false; + } + + // Check if shield is active. + if (PlayerImmunityTimer[client] != INVALID_HANDLE) + { + // Block damage for humans with shield enabled (preventing + // zombies from stabbing them to death). + return true; + } + } } // Allow damage. @@ -262,19 +312,6 @@ bool:ImmunityOnClientDamage(client, attacker, &Float:damage) /*____________________________________________________________________________*/ -/** - * Client is about to receive knock back (zombie). - * - * @param Client that's receiving knock back. - * - * @return True if knock back should be blocked, false otherwise. - */ -/*bool:ImmunityOnClientKnockBack(client) -{ -}*/ - -/*____________________________________________________________________________*/ - /** * Handles infect mode immunity. * @@ -371,7 +408,7 @@ public Action:ImmunityDelayTimerHandler(Handle:timer, any:client) if (!IsClientInGame(client) || !IsPlayerAlive(client)) { // Client disconnected or died. Abort immunity action. - ImmunityAbortHandler(client, false); + ImmunityAbortHandler(client); return Plugin_Stop; } @@ -397,36 +434,88 @@ public Action:ImmunityDelayTimerHandler(Handle:timer, any:client) /*____________________________________________________________________________*/ /** - * Handles shield mode immunity. + * Handles shield mode immunity when client is about to become infected. * * Zombies will get a shield against knock back, while humans become immune of * infections. * * @param client Client deploying shield. - * @param asZombie Client is a zombie. * * @return True if infection will be handled by this module, false if * infection can be applied instantly. */ -bool:ImmunityShieldModeHandler(client, bool:asZombie = true) +bool:ImmunityShieldModeHandler(client) { - // Stop if cooldown is still in progress. - - // Deploy shield. - if (asZombie) + // Check if shield is active. + if (PlayerImmunityTimer[client] != INVALID_HANDLE) { - // Enable knock back shield. - } - else - { - // Enable infection shield. + // Block infection. + return true; } + // Shield is not active, allow infection. return false; } /*____________________________________________________________________________*/ +/** + * Attempts to deploy the shield. + * + * @param client Client index. + */ +ImmunityDeployShield(client) +{ + // Check if shield is available. + if (!ImmunityCanDeployShield(client)) + { + // Not available. + return; + } + + // Deploy the shield. + PlayerImmunityDuration[client] = ClassGetImmunityAmount(client); + PlayerImmunityLastUse[client] = GetTime(); + PlayerImmunityTimer[client] = CreateTimer(1.0, ImmunityShieldTimerHandler, client, TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT); + + // Trigger initial countdown. + ImmunityShieldTimerHandler(PlayerImmunityTimer[client], client); +} + +/*____________________________________________________________________________*/ + +/** + * Shield timer handler. Handles countdown and shield removal when time is up. + */ +public Action:ImmunityShieldTimerHandler(Handle:timer, any:client) +{ + // Verify that client is still connected and alive. + if (!IsClientInGame(client) || !IsPlayerAlive(client)) + { + // Client disconnected or died. Abort immunity action. + ImmunityAbortHandler(client); + return Plugin_Stop; + } + + // Reduce duration. + PlayerImmunityDuration[client] -= 1; + + // Print remaining shield time. + TranslationPrintCenterText(client, "Immunity Shield Time Left", PlayerImmunityDuration[client]); + + // Check if time is up. + if (PlayerImmunityDuration[client] <= 0) + { + // Time is up. Reset data, but not last use timestamp. + ImmunityAbortHandler(client, false); + return Plugin_Stop; + } + + return Plugin_Continue; +} + +/*____________________________________________________________________________*/ + /** * Aborts any immunity mode in action (shields, delays, etc.). Resets values. * @@ -466,6 +555,40 @@ ImmunityAbortAll(bool:resetLastUse = true) /*____________________________________________________________________________*/ +/** + * Client is about to receive knock back. + * + * @param Client that's receiving knock back. + * + * @return True if knock back should be blocked, false otherwise. + */ +bool:ImmunityOnClientKnockBack(client) +{ + // Knock back filter is currently only used in shield mode. + if (ClassGetImmunityMode(client) == Immunity_Shield) + { + // Client must be zombie. (In case a future change allow knock back + // on humans.) + if (!InfectIsClientInfected(client)) + { + // Client is human, allow knock back. + return false; + } + + // Block knock back if shield is deployed. + if (PlayerImmunityTimer[client] != INVALID_HANDLE) + { + // Block knock back. + return true; + } + } + + // Allow knock back. + return false; +} + +/*____________________________________________________________________________*/ + /** * Client was infected. */ @@ -687,6 +810,8 @@ ImmunityModeToString(ImmunityMode:mode, String:buffer[], maxlen) return 0; } +/*____________________________________________________________________________*/ + /** * Returns whether the amount value is valid for the specified mode. * @@ -754,6 +879,8 @@ bool:ImmunityIsValidAmount(ImmunityMode:mode, amount) return true; } +/*____________________________________________________________________________*/ + /** * Returns whether the cooldown value is valid for the specified mode. * @@ -820,3 +947,55 @@ bool:ImmunityIsValidCooldown(ImmunityMode:mode, cooldown) // Passed. return true; } + +/*____________________________________________________________________________*/ + +/** + * Returns whether the client is allowed to deploy a shield now. Tests whether + * the client has shield immunity mode and whether cooldown is done, or a shield + * is already active. + * + * @param client Client index. + * @param printResponse Whether a response message is printed on the + * client's screen. + * + * @return True if shield can be deployed, false otherwise. + */ +bool:ImmunityCanDeployShield(client, bool:printResponse = true) +{ + // Check immunity mode. + if (ClassGetImmunityMode(client) != Immunity_Shield) + { + if (printResponse) + { + TranslationPrintToChat(client, "Immunity Shield Not Available"); + } + return false; + } + + // Check if cooldown is still in progress. + new cooldown = ClassGetImmunityCooldown(client); + new timeLeft = PlayerImmunityLastUse[client] + cooldown - GetTime(); + if (timeLeft > 0) + { + if (printResponse) + { + TranslationPrintToChat(client, "Immunity Shield Cooldown", timeLeft); + } + return false; + } + + // Check if a shield is already deployed. + if (PlayerImmunityTimer[client] != INVALID_HANDLE) + { + return false; + } + + // Humans cannot deploy shield before first zombie. + if (!g_bZombieSpawned) + { + return false; + } + + return true; +} diff --git a/src/zr/knockback.inc b/src/zr/knockback.inc index 94de67d..071ad24 100644 --- a/src/zr/knockback.inc +++ b/src/zr/knockback.inc @@ -59,6 +59,12 @@ KnockbackOnClientHurt(client, attacker, const String:weapon[], hitgroup, dmg_hea return; } + // Block knock back if an immunity mode is handling this. + if (ImmunityOnClientKnockBack(client)) + { + return; + } + // Get zombie knockback value. new Float:knockback = ClassGetKnockback(client);