diff --git a/src/zombiereloaded.sp b/src/zombiereloaded.sp index 4bbe46a..6140b4b 100755 --- a/src/zombiereloaded.sp +++ b/src/zombiereloaded.sp @@ -27,6 +27,7 @@ #include "zr/models" #include "zr/overlays" #include "zr/playerclasses" +#include "zr/antistick" #include "zr/anticamp" #include "zr/teleport" #include "zr/zombie" @@ -205,6 +206,7 @@ MapChangeCleanup() tRound = INVALID_HANDLE; tInfect = INVALID_HANDLE; AmbienceStopAll(); + AntiStickReset(); for (new client = 1; client <= maxclients; client++) { diff --git a/src/zr/antistick.inc b/src/zr/antistick.inc new file mode 100644 index 0000000..e6145ff --- /dev/null +++ b/src/zr/antistick.inc @@ -0,0 +1,200 @@ +/** + * ==================== + * Zombie:Reloaded + * File: antistick.inc + * Author: Greyscale + * ==================== + */ + +/** + * @section Offsets relating to player hull dimensions. +*/ +#define PLAYER_HULL_XY_OFFSET 32 +#define PLAYER_HULL_Z_OFFSET 12 +#define PLAYER_HULL_STACK_OFFSET 14 +/** + * @endsection +*/ + +/** + * Handle to keep track of AntiStickTimer +*/ +new Handle:tAntiStick = INVALID_HANDLE; + +/** + * Restarts the AntiStickTimer function. +*/ +AntiStickRestart() +{ + if (tAntiStick != INVALID_HANDLE) + { + KillTimer(tAntiStick); + } + + new bool:antistick = GetConVarBool(gCvars[CVAR_ANTISTICK]); + if (antistick) + { + new Float:interval = GetConVarFloat(gCvars[CVAR_ANTISTICK_INTERVAL]); + tAntiStick = CreateTimer(interval, AntiStickTimer, _, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + } +} + +/** + * Resets the handle that keeps track of the AntiStickTimer function. +*/ +AntiStickReset() +{ + tAntiStick = INVALID_HANDLE; +} + +/** + * Pushes a player away from another to prevent sticking. (Used on infect) + * + * @param attacker The player to push away. + * @param client The client to push player away from. +*/ +AntiStick(attacker, client) +{ + // Validate player is valid client and in-game + if (!attacker || !IsClientInGame(attacker)) + { + return; + } + + // Retrieve cvar value for force of the push, if 0 is returned, stop function + new Float:force = GetConVarFloat(gCvars[CVAR_INFECT_ANTISTICK_FORCE]); + if (force == 0.0) + { + return; + } + + new Float:vector[3]; + + new Float:attackerloc[3]; + new Float:clientloc[3]; + + GetClientAbsOrigin(attacker, attackerloc); + GetClientAbsOrigin(client, clientloc); + + MakeVectorFromPoints(attackerloc, clientloc, vector); + + NormalizeVector(vector, vector); + ScaleVector(vector, force); + + // Push player + TeleportEntity(attacker, NULL_VECTOR, NULL_VECTOR, vector); +} + +/** + * Checks if a player is currently stuck within another player + * + * @param client The client index. + * @return The client index of the other stuck player, -1 when + * player is not stuck. +*/ +AntiStickIsStuck(client) +{ + new Float:clientloc[3]; + new Float:stuckloc[3]; + + GetClientAbsOrigin(client, clientloc); + + // x = client index + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + // Validate player is in-game, alive, and isn't the player being checked ('client') + if (!IsClientInGame(x) || !IsPlayerAlive(x) || x == client) + { + continue; + } + + GetClientAbsOrigin(x, stuckloc); + + // x-y plane distance formula: sqrt((x2-x1)^2 + (y2-y1)^2) + new Float:xydistance = SquareRoot(Pow(stuckloc[0] - clientloc[0], 2.0) + Pow(stuckloc[1] - clientloc[1], 2.0)); + if (xydistance < PLAYER_HULL_XY_OFFSET) + { + if (clientloc[2] <= stuckloc[2]) + { + new Float:eyeloc[3]; + GetPlayerEyePosition(client, eyeloc); + + // Get the distance between the eyes and feet and subtract the stack "view crush" + new Float:eyedistance = FloatAbs(eyeloc[2] - clientloc[2]) - PLAYER_HULL_STACK_OFFSET; + new Float:zdistance = FloatAbs(stuckloc[2] - clientloc[2]); + + if (zdistance <= eyedistance + PLAYER_HULL_Z_OFFSET) + { + return x; + } + } + } + } + + return -1; +} + +/** + * Repeated timer function. + * Automatically unsticks players that are stuck together. +*/ +public Action:AntiStickTimer(Handle:timer) +{ + // x = client index + new maxplayers = GetMaxClients(); + for (new x = 1; x <= maxplayers; x++) + { + // Validate player is in-game and alive + if (!IsClientInGame(x) || !IsPlayerAlive(x)) + { + continue; + } + + // Stop if the player isn't stuck + new stuckindex = AntiStickIsStuck(x); + if (stuckindex == -1) + { + continue; + } + + if (CanCollide(x)) + { + NoCollide(x, true); + CreateTimer(0.5, AntiStickSolidify, x, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + } + + if (CanCollide(stuckindex)) + { + NoCollide(stuckindex, true); + CreateTimer(0.5, AntiStickSolidify, stuckindex, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE); + } + } +} + +/** + * Repeated timer function. + * Re-solidifies a player being unstuck. + * + * @param index The client index. +*/ +public Action:AntiStickSolidify(Handle:timer, any:index) +{ + // Validate player is in-game, alive, and is being unstuck + if (!IsClientInGame(index) || !IsPlayerAlive(index) || CanCollide(index)) + { + return Plugin_Stop; + } + + // Stop if the player is still stuck + if (AntiStickIsStuck(index) > -1) + { + return Plugin_Continue; + } + + NoCollide(index, false); + + return Plugin_Stop; +} + + \ No newline at end of file diff --git a/src/zr/cvars.inc b/src/zr/cvars.inc index 9605b71..da9bf35 100755 --- a/src/zr/cvars.inc +++ b/src/zr/cvars.inc @@ -57,6 +57,8 @@ enum ZRSettings Handle:CVAR_SUICIDE_WORLD_DAMAGE, Handle:CVAR_SPAWN_MIN, Handle:CVAR_SPAWN_MAX, + Handle:CVAR_ANTISTICK, + Handle:CVAR_ANTISTICK_INTERVAL, Handle:CVAR_PROTECT, Handle:CVAR_CONSECUTIVE_INFECT, Handle:CVAR_OVERLAYS, @@ -72,7 +74,6 @@ enum ZRSettings Handle:CVAR_ZTELE_ZOMBIE_DELAY, Handle:CVAR_ZTELE_ZOMBIE_LIMIT, Handle:CVAR_ZTELE_RESET_BUFFERS, - Handle:CVAR_ZSTUCK, Handle:CVAR_ZHP, Handle:CVAR_ZHP_DEFAULT, Handle:CVAR_CASHFILL, @@ -144,6 +145,8 @@ CreateCvars() gCvars[CVAR_SUICIDE_WORLD_DAMAGE] = CreateConVar("zr_suicide_world_damage", "1", "Respawn zombies as zombies if they were killed by the world, like elevators, doors and lasers. (0: Disable)"); gCvars[CVAR_SPAWN_MIN] = CreateConVar("zr_spawn_min", "30", "Minimum time a player is picked to be zombie after the round starts, in seconds"); gCvars[CVAR_SPAWN_MAX] = CreateConVar("zr_spawn_max", "50", "Maximum time a player is picked to be zombie after the round starts, in seconds"); + gCvars[CVAR_ANTISTICK] = CreateConVar("zr_antistick", "1", "Enable the anti-stick module, which will automatically unstick players stuck within each other. (0:Disable)"); + 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)"); @@ -159,7 +162,6 @@ CreateCvars() gCvars[CVAR_ZTELE_ZOMBIE_DELAY] = CreateConVar("zr_tele_zombie_delay", "0", "Teleport delay for zombies. (0: No delay)"); gCvars[CVAR_ZTELE_ZOMBIE_LIMIT] = CreateConVar("zr_tele_zombie_limit", "8", "Maximum number of teleports zombies can do. (0: Zombies can't use the teleporter. -1: Unlimited)"); gCvars[CVAR_ZTELE_RESET_BUFFERS] = CreateConVar("zr_tele_reset_buffers", "1", "Reset custom saved teleport locations on each round start. (0: Disable)"); - gCvars[CVAR_ZSTUCK] = CreateConVar("zr_zstuck", "1", "Allow players that are stuck together to get unstuck (0: Disable)"); gCvars[CVAR_ZHP] = CreateConVar("zr_zhp", "1", "Allows clients to enable/disable zombie health display (1: On, 0: Off)"); gCvars[CVAR_ZHP_DEFAULT] = CreateConVar("zr_zhp_default", "1", "The default value of zombie health display to new clients (1: On, 0: Off)"); gCvars[CVAR_CASHFILL] = CreateConVar("zr_cashfill", "1", "Enable the mod to set the players cash to zr_cashamount (0: Disabled)"); diff --git a/src/zr/event.inc b/src/zr/event.inc index a307a02..52b97fd 100755 --- a/src/zr/event.inc +++ b/src/zr/event.inc @@ -34,6 +34,7 @@ public Action:RoundStart(Handle:event, const String:name[], bool:dontBroadcast) { ChangeLightStyle(); AmbienceRestartAll(); + AntiStickRestart(); RefreshList(); if (tRound != INVALID_HANDLE) diff --git a/src/zr/menu.inc b/src/zr/menu.inc index 7b70689..011e1cb 100755 --- a/src/zr/menu.inc +++ b/src/zr/menu.inc @@ -20,7 +20,6 @@ MainMenu(client) decl String:zmarket[128]; decl String:zspawn[128]; decl String:ztele[128]; - decl String:zstuck[128]; decl String:zhp[128]; Format(zmenu, sizeof(zmenu), "%t", "!zmenu menu"); @@ -29,7 +28,6 @@ MainMenu(client) Format(zmarket, sizeof(zmarket), "%t", "!zmenu market"); Format(zspawn, sizeof(zspawn), "%t", "!zmenu spawn"); Format(ztele, sizeof(ztele), "%t", "!zmenu tele"); - Format(zstuck, sizeof(zstuck), "%t", "!zmenu stuck"); Format(zhp, sizeof(zhp), "%t (%d HP)", "!zmenu hp", GetClientHealth(client)); AddMenuItem(menu_main, "zmenu", zmenu, ITEMDRAW_DISABLED); @@ -56,7 +54,6 @@ MainMenu(client) AddMenuItem(menu_main, "zspawn", zspawn); AddMenuItem(menu_main, "ztele", ztele); - AddMenuItem(menu_main, "zstuck", zstuck); AddMenuItem(menu_main, "zhp", zhp); DisplayMenu(menu_main, client, MENU_TIME_FOREVER); @@ -101,11 +98,6 @@ public MainMenuHandle(Handle:menu_main, MenuAction:action, client, slot) } } case 6: - { - ZStuck(client); - MainMenu(client); - } - case 7: { ZHP(client); MainMenu(client); diff --git a/src/zr/offsets.inc b/src/zr/offsets.inc index 42e7675..7add0bb 100755 --- a/src/zr/offsets.inc +++ b/src/zr/offsets.inc @@ -175,6 +175,18 @@ NoCollide(client, bool:nocollide) } } +bool:CanCollide(client) +{ + new collisionstate = GetEntData(client, offsCollision, 1); + + if (collisionstate == 2) + { + return false; + } + + return true; +} + SetPlayerMoney(client, amount) { SetEntData(client, offsMoney, amount, 4, true); diff --git a/src/zr/sayhooks.inc b/src/zr/sayhooks.inc index 3f867f8..ab6b239 100755 --- a/src/zr/sayhooks.inc +++ b/src/zr/sayhooks.inc @@ -63,11 +63,6 @@ public Action:SayCommand(client, argc) AbortTeleport(client, false); } - else if (StrEqual(args, "!zstuck", false)) - { - ZStuck(client); - } - else if (StrEqual(args, "!zhp", false)) { ZHP(client); @@ -230,52 +225,6 @@ ZSpawn(client) AddPlayerToList(client); } -ZStuck(client) -{ - new bool:stuck = GetConVarBool(gCvars[CVAR_ZSTUCK]); - if (!stuck) - { - ZR_PrintToChat(client, "Feature is disabled"); - - return; - } - - if (!IsPlayerAlive(client)) - { - return; - } - - new Float:clientloc[3]; - new Float:stuckloc[3]; - - GetClientAbsOrigin(client, clientloc); - new maxplayers = GetMaxClients(); - for (new x = 1; x <= maxplayers; x++) - { - if (!IsClientInGame(x) || !IsPlayerAlive(x)) - { - continue; - } - - GetClientAbsOrigin(x, stuckloc); - if (GetVectorDistance(clientloc, stuckloc) <= 60) - { - NoCollide(x, true); - CreateTimer(0.5, CollisionOn, x, TIMER_FLAG_NO_MAPCHANGE); - } - } - - new Float:x = GetRandomFloat(-150.0, 150.0); - new Float:y = GetRandomFloat(-150.0, 150.0); - - new Float:nudge[3]; - - nudge[0] = x; - nudge[1] = y; - - SetPlayerVelocity(client, nudge, true); -} - public Action:CollisionOn(Handle:timer, any:index) { if (!IsClientInGame(index)) diff --git a/src/zr/zombie.inc b/src/zr/zombie.inc index a75b42f..a47a862 100755 --- a/src/zr/zombie.inc +++ b/src/zr/zombie.inc @@ -650,35 +650,6 @@ RemoveObjectives() } } -AntiStick(attacker, client) -{ - if (!attacker || !IsClientInGame(attacker)) - { - return; - } - - new Float:force = GetConVarFloat(gCvars[CVAR_INFECT_ANTISTICK_FORCE]); - if (force == 0.0) - { - return; - } - - new Float:vector[3]; - - new Float:attackerloc[3]; - new Float:clientloc[3]; - - GetClientAbsOrigin(attacker, attackerloc); - GetClientAbsOrigin(client, clientloc); - - MakeVectorFromPoints(attackerloc, clientloc, vector); - - NormalizeVector(vector, vector); - ScaleVector(vector, force); - - TeleportEntity(attacker, NULL_VECTOR, NULL_VECTOR, vector); -} - ZVisionPreCheck(client) { if (IsFakeClient(client)) @@ -901,7 +872,7 @@ RespawnPlayer(client) return; } - if (zombieSpawned && GetConVarBool(gCvars[CVAR_SUICIDE_WORLD_DAMAGE]) && gKilledByWorld[client]) + if (GetConVarBool(gCvars[CVAR_SUICIDE_WORLD_DAMAGE]) && gKilledByWorld[client]) { Zombify(client, 0); gKilledByWorld[client] = false;