From 666ac57836a6acbcbe2ac36cff02008f92d6d995 Mon Sep 17 00:00:00 2001 From: Richard Helgeby Date: Sat, 5 Jan 2013 22:58:43 +0100 Subject: [PATCH] Implemented delayed infection mode. Fixed immunity_cooldown attribute not loaded properly. --- .../sourcemod/configs/zr/playerclasses.txt | 12 +- src/zr/immunityhandler.inc | 187 ++++++++++++------ src/zr/infect.inc | 1 + src/zr/playerclasses/playerclasses.inc | 18 +- 4 files changed, 146 insertions(+), 72 deletions(-) diff --git a/cstrike/addons/sourcemod/configs/zr/playerclasses.txt b/cstrike/addons/sourcemod/configs/zr/playerclasses.txt index 5e1ca28..b8a1339 100644 --- a/cstrike/addons/sourcemod/configs/zr/playerclasses.txt +++ b/cstrike/addons/sourcemod/configs/zr/playerclasses.txt @@ -37,13 +37,15 @@ // (Not implemented) "full" - Completely immune. Humans can't be infected, zombies don't receive damage or knock back. Careful with this, it might not be that fun. // "infect" - Humans are immune to infections until HP go below a threshold. Threshold at zero enable stabbing to death. // (Not implemented) "damage" - Zombies are immune to damage from humans/grenades, but still vulnerable to knock back. -// (Not implemented) "delay" - Delay infection for a certain number of seconds. +// "delay" - Delay infection for a certain number of seconds. // (Not implemented) "shield" - Shield against infections (humans) or knock back (zombies) for a certain amount of seconds (similar to TF2's übercharge). // immunity_amount number Immunity data value (humans only). Depends on the immunity mode above: // "infect" - HP threshold. Infection will be allowed when HP go below this value. Zero will enable stabbing to death. // (Not implemented) "delay" - Number of seconds the infection is delayed since first hit by a zombie. // (Not implemented) "shield" - Number of seconds the shield is active. -// immunity_cooldown number (Not implemented) Number of seconds of cooldown for temporary immunity effects (currently just shield mode). +// immunity_cooldown number Number of seconds of cooldown for temporary immunity effects, depending on mode. +// "shield" - Number of seconds the player has to wait before the shield can be used again. +// "delay" - Number of seconds the delay is reduced every time a zombie attack, while a delayed infection is in progress. // no_fall_damage on/off Disables fall damage. // health number How many health points to give. // health_regen_interval decimal Sets the regeneration interval. 0 to disable. @@ -501,9 +503,9 @@ "napalm_time" "0.0" // Player behavior - "immunity_mode" "none" - "immunity_amount" "1" - "immunity_cooldown" "60" + "immunity_mode" "delay" // Delay infection + "immunity_amount" "10" // 10 seconds. + "immunity_cooldown" "2" // Reduce delay 2 seconds every subsequent attack (infect faster). "no_fall_damage" "0" "health" "100" diff --git a/src/zr/immunityhandler.inc b/src/zr/immunityhandler.inc index ef8ed07..8c94b18 100644 --- a/src/zr/immunityhandler.inc +++ b/src/zr/immunityhandler.inc @@ -55,6 +55,8 @@ new PlayerImmunityLastUse[MAXPLAYERS + 1] = {0, ...}; */ new bool:PlayerImmunityThresholdPassed[MAXPLAYERS + 1] = {false, ...}; +/*____________________________________________________________________________*/ + /** * Handles immunity when a client is about to be infected. This function may * delay or block infection according to the immunity mode class settings. @@ -103,6 +105,8 @@ bool:ImmunityOnClientInfect(client, attacker) return false; } +/*____________________________________________________________________________*/ + /** * TraceAttack hook. * @@ -150,35 +154,12 @@ bool:ImmunityOnClientTraceAttack(client, attacker, Float:damage, hitgroup, damag // Check if damage give HP below the infection threshold. if (ImmunityBelowInfectThreshold(client, damage)) { - //PrintToChatAll("ImmunityOnClientTraceAttack - Threshold passed"); PlayerImmunityThresholdPassed[client] = true; } else { PlayerImmunityThresholdPassed[client] = false; } - - /*new threshold = ClassGetImmunityAmount(client); - new clientHP = GetClientHealth(client); - new dmg = RoundToNearest(damage); - PrintToChatAll("threshold=%d, clientHp=%d", threshold, clientHP); - - // Prevent humans with low HP from dying when a zombie is - // attacking, and stab to death is disabled (threshold above zero). - if (clientHP - dmg <= 0.0 && threshold > 0) - { - // Client is about to be infected. Remove damage, but don't - // block the damage event. The infect module need it to detect - // zombie attack. - damage = 0.0; - - // Client is about to be infected, re-add HP so they aren't - // killed by knife. Don't block the damage, the infect - // module need the damage event. - //PrintToChatAll("Re-add HP."); - //SetEntityHealth(client, clientHP + dmg); - return false; - }*/ } case Immunity_Damage: { @@ -209,23 +190,13 @@ bool:ImmunityOnClientTraceAttack(client, attacker, Float:damage, hitgroup, damag // Block damage from attacker. return true; } - case Immunity_Delay: - { - // Client must be human. - if (InfectIsClientInfected(client)) - { - return false; - } - - // Block damage if there's an infection in progress. - - } } - //PrintToChatAll("Allow damage."); return false; } +/*____________________________________________________________________________*/ + /** * TakeDamage hook. * @@ -239,8 +210,6 @@ bool:ImmunityOnClientTraceAttack(client, attacker, Float:damage, hitgroup, damag */ bool:ImmunityOnClientDamage(client, attacker, &Float:damage) { - //PrintToChatAll("ImmunityOnClientDamage(client=%d, attacker=%d, damage=%f)", client, attacker, damage); - // Check if there is no attacker (world damage). if (!ZRIsClientValid(attacker)) { @@ -259,11 +228,6 @@ bool:ImmunityOnClientDamage(client, attacker, &Float:damage) // attacking, and stab to death is disabled (threshold above zero). if (ImmunityBelowInfectThreshold(client, damage)) { - //PrintToChatAll("ImmunityOnClientDamage - Below threshold, removing damage."); - // Client is about to be infected. Remove damage so the client - // won't die. - //damage = 0.0; - // Fake hurt event because it's not triggered when the damage // was removed (because no one is actually hurt). InfectOnClientHurt(client, attacker, "knife"); @@ -272,12 +236,25 @@ bool:ImmunityOnClientDamage(client, attacker, &Float:damage) return true; } } + case Immunity_Delay: + { + // Fake hurt event because it's not triggered when the damage + // was removed (because no one is actually hurt). This event must + // still be triggered so that subsequent attacks are registered, + // without dealing any damage. + InfectOnClientHurt(client, attacker, "knife"); + + // Block damage to prevent player from dying. + return true; + } } // Allow damage. return false; } +/*____________________________________________________________________________*/ + /** * Client is about to receive knock back (zombie). * @@ -289,6 +266,8 @@ bool:ImmunityOnClientDamage(client, attacker, &Float:damage) { }*/ +/*____________________________________________________________________________*/ + /** * Handles infect mode immunity. * @@ -303,20 +282,15 @@ bool:ImmunityOnClientDamage(client, attacker, &Float:damage) */ bool:ImmunityInfectModeHandler(client) { - //PrintToChatAll("ImmunityInfectModeHandler(client=%d, attacker=%d)", client, attacker); - // Note: ImmunityOnClientDamage and ImmunityOnClientTraceAttack hook into // the damage module to prevent humans with low HP from dying when // they're not supposed to. new threshold = ClassGetImmunityAmount(client); - //PrintToChatAll("threshold=%d", threshold); - // Check if infection is disabled. if (threshold == 0) { // Infection is handled here: blocked. - //PrintToChatAll("Infection disabled, block infection."); return true; } @@ -326,10 +300,11 @@ bool:ImmunityInfectModeHandler(client) return false; } - //PrintToChatAll("Above threshold, block infection."); return true; } +/*____________________________________________________________________________*/ + /** * Handles delayed infections. * @@ -346,27 +321,46 @@ bool:ImmunityDelayModeHandler(client, attacker) // Additional attacks while a delayed infection is in progress will // speedup the infection. - // Trigger timer event to reduce duration. - ImmunityDelayTimerHandler(PlayerImmunityTimer[client], client); + // Get reduction amount for subsequent zombie attack. + new reduction = ClassGetImmunityCooldown(client); + if (reduction > 0) + { + // Reduce duration. Add one because the timer handler itself reduce + // duration by one. + PlayerImmunityDuration[client] -= reduction + 1; + + // Note: This feature can be used to trigger an instant infection + // when a human receive a second attack, by setting the + // reduction value high enough. + + // Trigger timer event to reduce delay and infect faster. + ImmunityDelayTimerHandler(PlayerImmunityTimer[client], client); + } // Block infection. - return false; + return true; } // Start a delayed infection. Initialize duration and attacker. PlayerImmunityDuration[client] = ClassGetImmunityAmount(client); PlayerImmunityAttacker[client] = attacker; - // Create repated 1-second timer. + // Create repated 1-second timer for handling the countdown. PlayerImmunityTimer[client] = CreateTimer(1.0, ImmunityDelayTimerHandler, client, TIMER_FLAG_NO_MAPCHANGE | TIMER_REPEAT); // Block infection. - return false; + return true; } +/*____________________________________________________________________________*/ + +/** + * Delayed infection timer handler. Handles countdown and infection when time + * is up. + */ public Action:ImmunityDelayTimerHandler(Handle:timer, any:client) { - // Verify that client is connected and alive. + // Verify that client is still connected and alive. if (!IsClientInGame(client) || !IsPlayerAlive(client)) { // Client disconnected or died. Abort immunity action. @@ -375,19 +369,16 @@ public Action:ImmunityDelayTimerHandler(Handle:timer, any:client) return Plugin_Stop; } - // TODO: Read cvar for reduction amount. - new reduction = 2; - // Reduce duration. - PlayerImmunityDuration[client] -= reduction; + PlayerImmunityDuration[client] -= 1; // Check if time is up. if (PlayerImmunityDuration[client] <= 0) { // Time is up. Reset data. PlayerImmunityDuration[client] = 0; - PlayerImmunityTimer[client] = INVALID_HANDLE; - ZREndTimer(PlayerImmunityTimer[client]); + //PlayerImmunityTimer[client] = INVALID_HANDLE; + ImmunityAbortHandler(client); // Infect client. InfectHumanToZombie(client, PlayerImmunityAttacker[client]); @@ -398,6 +389,8 @@ public Action:ImmunityDelayTimerHandler(Handle:timer, any:client) return Plugin_Continue; } +/*____________________________________________________________________________*/ + /** * Handles shield mode immunity. * @@ -427,6 +420,8 @@ bool:ImmunityShieldModeHandler(client, bool:asZombie = true) return false; } +/*____________________________________________________________________________*/ + /** * Aborts any immunity mode in action (shields, delays, etc.). Resets values. * @@ -449,6 +444,8 @@ ImmunityAbortHandler(client, bool:resetLastUse = true) } } +/*____________________________________________________________________________*/ + /** * Aborts all immunity modes in action. * @@ -462,26 +459,63 @@ ImmunityAbortAll(bool:resetLastUse = true) } } +/*____________________________________________________________________________*/ + +/** + * Client was infected. + */ +ImmunityOnClientInfected(client) +{ + // In case client was infected through an admin command or mother zombie + // selection, abort other actions in progress. + ImmunityAbortHandler(client); +} + +/*____________________________________________________________________________*/ + +/** + * Client was turned back into a human. + */ ImmunityOnClientHuman(client) { ImmunityAbortHandler(client); } +/*____________________________________________________________________________*/ + +/** + * Client died. + */ ImmunityOnClientDeath(client) { ImmunityAbortHandler(client, false); } +/*____________________________________________________________________________*/ + +/** + * Client connected to the server. + */ ImmunityClientInit(client) { ImmunityAbortHandler(client); } +/*____________________________________________________________________________*/ + +/** + * Client spawned. + */ ImmunityClientSpawn(client) { ImmunityAbortHandler(client, false); } +/*____________________________________________________________________________*/ + +/** + * Client disconnected. + */ ImmunityOnClientDisconnect(client) { ImmunityAbortHandler(client); @@ -498,29 +532,54 @@ ImmunityOnClientDisconnect(client) } } +/*____________________________________________________________________________*/ + +/** + * Client changed team. + */ ImmunityOnClientTeam(client) { ImmunityAbortHandler(client); } +/*____________________________________________________________________________*/ + +/** + * Round ended. + */ ImmunityOnRoundEnd() { ImmunityAbortAll(); } +/*____________________________________________________________________________*/ + +/** + * Map ended. + */ ImmunityOnMapEnd() { ImmunityAbortAll(); } +/*____________________________________________________________________________*/ + +/** + * Returns whether the specified damage will take a client's HP below the + * infection threshold. Only used by "infect" immunity mode. + * + * @param client Client index. + * @param damage Damage applied to client. + * + * @return True if client HP go below threshold (immunity_amount) when + * applying damage, false otherwise. + */ bool:ImmunityBelowInfectThreshold(client, Float:damage) { new threshold = ClassGetImmunityAmount(client); new clientHP = GetClientHealth(client); new dmg = RoundToNearest(damage); - //PrintToChatAll("threshold=%d, clientHp=%d", threshold, clientHP); - // Check if the damage go below the HP threshold. if (clientHP - dmg <= 0.0 && threshold > 0) { @@ -530,6 +589,8 @@ bool:ImmunityBelowInfectThreshold(client, Float:damage) return false; } +/*____________________________________________________________________________*/ + /** * Converts a string to an immunity mode. * @@ -572,6 +633,8 @@ ImmunityMode:ImmunityStringToMode(const String:mode[]) return Immunity_Invalid; } +/*____________________________________________________________________________*/ + /** * Converts an immunity mode to a string. * diff --git a/src/zr/infect.inc b/src/zr/infect.inc index d61f6de..64b2b20 100644 --- a/src/zr/infect.inc +++ b/src/zr/infect.inc @@ -751,6 +751,7 @@ InfectHumanToZombie(client, attacker = -1, bool:motherinfect = false, bool:respa ZTeleOnClientInfected(client); ZHPOnClientInfected(client); APIOnClientInfected(client, attacker, motherinfect, respawnoverride, respawn); + ImmunityOnClientInfected(client); } /** diff --git a/src/zr/playerclasses/playerclasses.inc b/src/zr/playerclasses/playerclasses.inc index 02d9af0..4bf885c 100644 --- a/src/zr/playerclasses/playerclasses.inc +++ b/src/zr/playerclasses/playerclasses.inc @@ -615,6 +615,7 @@ ClassLoad() KvGetString(kvClassData, "immunity_mode", immunity_mode, sizeof(immunity_mode), ZR_CLASS_DEFAULT_IMMUNITY_MODE); ClassData[ClassCount][Class_ImmunityMode] = ImmunityStringToMode(immunity_mode); ClassData[ClassCount][Class_ImmunityAmount] = KvGetNum(kvClassData, "immunity_amount", ZR_CLASS_DEFAULT_IMMUNITY_AMOUNT); + ClassData[ClassCount][Class_ImmunityCooldown] = KvGetNum(kvClassData, "immunity_cooldown", ZR_CLASS_DEFAULT_IMMUNITY_COOLDOWN); ClassData[ClassCount][Class_NoFallDamage] = ConfigKvGetStringBool(kvClassData, "no_fall_damage", ZR_CLASS_DEFAULT_NO_FALL_DAMAGE); ClassData[ClassCount][Class_Health] = KvGetNum(kvClassData, "health", ZR_CLASS_DEFAULT_HEALTH); @@ -756,9 +757,10 @@ bool:ClassReloadDataCache() ClassDataCache[classindex][Class_HasNapalm] = ClassData[classindex][Class_HasNapalm]; ClassDataCache[classindex][Class_NapalmTime] = ClassData[classindex][Class_NapalmTime]; - /* Player behaviour */ + /* Player behavior */ ClassDataCache[classindex][Class_ImmunityMode] = ClassData[classindex][Class_ImmunityMode]; ClassDataCache[classindex][Class_ImmunityAmount] = ClassData[classindex][Class_ImmunityAmount]; + ClassDataCache[classindex][Class_ImmunityCooldown] = ClassData[classindex][Class_ImmunityCooldown]; ClassDataCache[classindex][Class_NoFallDamage] = ClassData[classindex][Class_NoFallDamage]; ClassDataCache[classindex][Class_Health] = ClassData[classindex][Class_Health]; ClassDataCache[classindex][Class_HealthRegenInterval] = ClassData[classindex][Class_HealthRegenInterval]; @@ -821,9 +823,10 @@ bool:ClassReloadPlayerCache(client, classindex, cachetype = ZR_CLASS_CACHE_MODIF ClassPlayerCache[client][Class_HasNapalm] = ClassData[classindex][Class_HasNapalm]; ClassPlayerCache[client][Class_NapalmTime] = ClassData[classindex][Class_NapalmTime]; - /* Player behaviour */ + /* Player behavior */ ClassPlayerCache[client][Class_ImmunityMode] = ClassData[classindex][Class_ImmunityMode]; ClassPlayerCache[client][Class_ImmunityAmount] = ClassData[classindex][Class_ImmunityAmount]; + ClassPlayerCache[client][Class_ImmunityCooldown] = ClassData[classindex][Class_ImmunityCooldown]; ClassPlayerCache[client][Class_NoFallDamage] = ClassData[classindex][Class_NoFallDamage]; ClassPlayerCache[client][Class_Health] = ClassData[classindex][Class_Health]; ClassPlayerCache[client][Class_HealthRegenInterval] = ClassData[classindex][Class_HealthRegenInterval]; @@ -861,9 +864,10 @@ bool:ClassReloadPlayerCache(client, classindex, cachetype = ZR_CLASS_CACHE_MODIF ClassPlayerCache[client][Class_HasNapalm] = ClassDataCache[classindex][Class_HasNapalm]; ClassPlayerCache[client][Class_NapalmTime] = ClassDataCache[classindex][Class_NapalmTime]; - /* Player behaviour */ + /* Player behavior */ ClassPlayerCache[client][Class_ImmunityMode] = ClassDataCache[classindex][Class_ImmunityMode]; ClassPlayerCache[client][Class_ImmunityAmount] = ClassDataCache[classindex][Class_ImmunityAmount]; + ClassPlayerCache[client][Class_ImmunityCooldown] = ClassDataCache[classindex][Class_ImmunityCooldown]; ClassPlayerCache[client][Class_NoFallDamage] = ClassDataCache[classindex][Class_NoFallDamage]; ClassPlayerCache[client][Class_Health] = ClassDataCache[classindex][Class_Health]; ClassPlayerCache[client][Class_HealthRegenInterval] = ClassDataCache[classindex][Class_HealthRegenInterval]; @@ -1376,10 +1380,14 @@ ClassDumpData(index, cachetype, String:buffer[], maxlen) Format(attribute, sizeof(attribute), "napalm_time: \"%f\"\n", ClassGetNapalmTime(index, cachetype)); cellcount += StrCat(buffer, maxlen, attribute); - Format(attribute, sizeof(attribute), "immunity_mode: \"%d\"\n", ClassGetImmunityMode(index, cachetype)); + ImmunityModeToString(ClassGetImmunityMode(index, cachetype), format_buffer, sizeof(format_buffer)); + Format(attribute, sizeof(attribute), "immunity_mode: \"%s\"\n", format_buffer); cellcount += StrCat(buffer, maxlen, attribute); - Format(attribute, sizeof(attribute), "immunity_amount: \"%f\"\n", ClassGetImmunityAmount(index, cachetype)); + Format(attribute, sizeof(attribute), "immunity_amount: \"%d\"\n", ClassGetImmunityAmount(index, cachetype)); + cellcount += StrCat(buffer, maxlen, attribute); + + Format(attribute, sizeof(attribute), "immunity_cooldown: \"%d\"\n", ClassGetImmunityCooldown(index, cachetype)); cellcount += StrCat(buffer, maxlen, attribute); Format(attribute, sizeof(attribute), "no_fall_damage: \"%d\"\n", ClassGetNoFallDamage(index, cachetype));