diff --git a/changelog.txt b/changelog.txt index 66340bf..b020e30 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +2009.01.18 - 2.5.1.24 + * Created a anticamp feature that give players damage at a specified interval in custom defined volumes. Only affects humans. + * Re-formatted changes.txt. + 2009.01.17 - 2.5.1.23 * Fixed teleport location text to display as float values instead of decimal. diff --git a/changes.txt b/changes.txt index 1a8a822..6242a70 100644 --- a/changes.txt +++ b/changes.txt @@ -1,60 +1,108 @@ -Behaviour changes from version 2.5.1 -===================================== +Changes from Zombie: Reloaded 2.5.1 +==================================== -* Support for alpha values in classes. New values in classes.txt (per class): +Alpha values (transparency) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Support for alpha values in classes. New values in classes.txt (per class): + + Key: Value: Default: Description: + alpha_spawn <0-255> 255 initial alpha value (0 = transparent) + alpha_damaged <0-255> 255 alpha value when damaged + alpha_damage 0 how much damage to do before + alpha_damaged take effect + +Knockback multiplier +~~~~~~~~~~~~~~~~~~~~~ + +If classes are enabled the zr_zombie_knockback cvar is used as a multiplier. +Makes it possible to set custom knockback on maps using per-map configs. + +Set zr_zombie_knockback to 1 if you use classes and want it the way it was +before. + + knockback = zr_zombie_knockback * class knockback + +Full spawn protection +~~~~~~~~~~~~~~~~~~~~~~ + +Spawn protection makes the players invisible and move faster, and cannot be +infected during protection time. + +A centered counter shows the time remaining. + +Overriding class night vision +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Support for overrriding class nvgs with the zr_zombie_nvgs cvar. Generally, +any non-zero value is considered as on. + + Value: Description: + -1 no override (use value in classes.txt) / nvgs on + 0 never give nvgs + 1 always give nvgs + +Anticamp feature +~~~~~~~~~~~~~~~~~ + +Define hurt volumes in maps that gives x damage at a custom interval. + +When creating volumes they should start a little bit _below_ the floor, so it +covers the players feet. + +The volume is rectangular and you only need two locations in the map to create +it; a top corner, and the bottom corner at the oposite side. + + zr_anticamp_create_volume + - Create a rectangular volume between the specified locations. - Key: Value: Default: Description: - alpha_spawn <0-255> 255 initial alpha value (0 = transparent) - alpha_damaged <0-255> 255 alpha value when damaged - alpha_damage 0 how much damage to do before - alpha_damaged take effect - -* If classes are enabled the zr_zombie_knockback cvar is used as a multiplier. - Makes it possible to set custom knockback on maps using per-map configs. Set - zr_zombie_knockback to 1 if you use classes and want it the way it was before. + zr_anticamp_remove_volume + - Remove a volume. - knockback = zr_zombie_knockback * class knockback + zr_anticamp_list + - List existing volumes. -* Spawn protection makes the players invisible and move faster, and cannot be - infected during protecting time. A centered counter shows the time remaining. +Set default classes +~~~~~~~~~~~~~~~~~~~~ -* Support for overrriding class nvgs with the zr_zombie_nvgs cvar: - Generally, any non-zero value is considered as on. +Setting the default class with a cvar. Works with per-map configs. - Value: Description: - -1 no override (use value in classes.txt) / nvgs on - 0 never give nvgs - 1 always give nvgs + zr_classes_default -* Setting default class with a cvar. Works with per-map configs. +Zombie admin menu +~~~~~~~~~~~~~~~~~~ + +Basic zombie admin commands like adjusting knockback, nvgs, infecting players +and spawn everyone. + +Knockback changes are applied instantly, useful for tuning knockback in-game. + +Note that the changes are not saved. They will reset on map change. In a +future version this menu might save the changes. + + Knockback multiplier + - Fine tune the knockback multiplier. - zr_classes_default - -* Zombie admin menu. Basic zombie admin commands like adjusting knockback, nvgs, - infecting players and spawn everyone. Knockback changes are applied instantly, - useful for fine tuning live. Note that the changes are not saved. They will be - reset on map change. In the future this menu might save the changes. + Class knockback + - Fine tune a class knockback value. - Knockback multiplier - - Fine tune the knockback multiplier. - - Class knockback - - Fine tune a class knockback value. - - Night vision settings - - Override night vision settings (explained earlier). - - Infect - - Infect a player. Lists all players, but could be changed to only list - humans. - - Spawn players - - Spawns all dead players (except spectactors). - - (more zombie admin stuff might come...) + Night vision settings + - Override night vision settings (explained earlier). + + Infect + - Infect a player. Lists all players, but could be changed to only list + humans. + + Spawn players + - Spawns all dead players (except spectactors). + + (more zombie admin stuff might come...) -* Console commands zr_set_class_knockback and zr_get_class_knockback. Changes - are not saved. They will be reset on map change. +Set (and get) class knockback +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Set or get knockback from a class. Changes are not saved. They will be reset on +map change. zr_set_class_knockback - Sets knockback to a specified class. Useful when using per-map configs to @@ -64,4 +112,7 @@ Behaviour changes from version 2.5.1 - Prints the current knockback of a class to the client/server console. Using the zombie admin menu to read a knockback value is easier sometimes. -* Other minior and major bug fixes. See changelog.txt. +More stuff +~~~~~~~~~~~ + +Other minior and major bug fixes. See changelog.txt. diff --git a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt index 14ca9b5..4a27ea9 100644 --- a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt +++ b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt @@ -115,31 +115,31 @@ { "en" "Once a player has been infected, only zombies can use !ztele." "ru" "После инфицирования кого-либо, только зомби могут использовать !ztele" - } + } "!ztele zombies restricted" { "en" "The teleporter is disabled for zombies." } - - "!ztele stuck" - { - "en" "Tip: Use !tp to teleport away if you are stuck." - } - - "!ztele not spawned" - { - "en" "You cannot use the teleporter before you've spawned." - } - - "!ztele offline" - { - "en" "The teleporter is not online yet." - } - - "!ztele cooldown" - { - "en" "Your teleporter is still on a cooldown." + + "!ztele stuck" + { + "en" "Tip: Use !tp to teleport away if you are stuck." + } + + "!ztele not spawned" + { + "en" "You cannot use the teleporter before you've spawned." + } + + "!ztele offline" + { + "en" "The teleporter is not online yet." + } + + "!ztele cooldown" + { + "en" "Your teleporter is still on a cooldown." } "!ztele in progress" @@ -224,6 +224,17 @@ "en" "Nice try n00b!" "ru" "Неплохая попытка n00b!" } + + "Unfair camping" + { + "en" "This area is marked as unfair. Move, go through or die..." + } + + "Unfair camper slayed" + { + "#format" "{1:s},{2:d}" + "en" "Killed %s due to camping in a unfair area (id: %d)." + } "DX90 not supported" { diff --git a/src/zombiereloaded.sp b/src/zombiereloaded.sp index a115045..6a2d0e0 100644 --- a/src/zombiereloaded.sp +++ b/src/zombiereloaded.sp @@ -15,7 +15,7 @@ #undef REQUIRE_PLUGIN #include -#define VERSION "2.5.1.23" +#define VERSION "2.5.1.24" #include "zr/zombiereloaded" #include "zr/global" @@ -26,6 +26,7 @@ #include "zr/classes" #include "zr/models" #include "zr/overlays" +#include "zr/anticamp" #include "zr/teleport" #include "zr/zombie" #include "zr/menu" @@ -123,6 +124,13 @@ public OnMapStart() pClass[i] = classindex; } } + + Anticamp_Startup(); +} + +public OnMapEnd() +{ + Anticamp_Disable(); } public OnConfigsExecuted() diff --git a/src/zr/anticamp.inc b/src/zr/anticamp.inc new file mode 100644 index 0000000..d0f074b --- /dev/null +++ b/src/zr/anticamp.inc @@ -0,0 +1,437 @@ +/** + * ==================== + * Zombie:Reloaded + * File: anticamp.inc + * Authors: Richard Helgeby + * ==================== + */ + +#define MAX_VOLUMES 32 + +enum volume +{ + Float:x_min, + Float:x_max, + Float:y_min, + Float:y_max, + Float:z_min, + Float:z_max, + volume_damage, + Float:volume_interval, + Handle:volume_timer, + bool:volume_in_use +} + +new volumes[MAX_VOLUMES][volume]; +new volume_count; + +new Float:player_loc[MAXPLAYERS][3]; + +new Handle:hUpdateTimer; + +Anticamp_Startup(bool:no_reset = false) +{ + new Float:interval = GetConVarFloat(gCvars[CVAR_ANTICAMP_UPDATE_INTERVAL]); + + // Create timer for updating player locations. + if (hUpdateTimer != INVALID_HANDLE) + { + KillTimer(hUpdateTimer); + hUpdateTimer = INVALID_HANDLE; + } + + if (interval > 0.0) + { + hUpdateTimer = CreateTimer(interval, Event_UpdatePlayerLocations, 0, TIMER_REPEAT); + } + + if (no_reset) + { + // Start existing hurt timers. + for (new vol_index = 0; vol_index < MAX_VOLUMES; vol_index++) + { + if (volumes[vol_index][volume_in_use]) + { + if (volumes[vol_index][volume_timer] != INVALID_HANDLE) + { + KillTimer(volumes[vol_index][volume_timer]); + } + volumes[vol_index][volume_timer] = CreateTimer(volumes[vol_index][volume_interval], Event_HurtPlayers, vol_index, TIMER_REPEAT); + } + } + } + else + { + ResetVolumes(); + } +} + +Anticamp_Disable() +{ + // Kill update timer. + if (hUpdateTimer != INVALID_HANDLE) + { + KillTimer(hUpdateTimer); + hUpdateTimer = INVALID_HANDLE; + } + + // Kill hurt timers. + for (new vol = 0; vol < MAX_VOLUMES; vol++) + { + if (volumes[vol][volume_timer] != INVALID_HANDLE) + { + KillTimer(volumes[vol][volume_timer]); + volumes[vol][volume_timer] = INVALID_HANDLE; + } + } +} + +public UpdateIntervalHook(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + new Float:interval = StringToFloat(newValue); + if (hUpdateTimer != INVALID_HANDLE) + { + KillTimer(hUpdateTimer); + hUpdateTimer = INVALID_HANDLE; + } + + if (interval > 0.0) + { + hUpdateTimer = CreateTimer(interval, Event_UpdatePlayerLocations, 0, TIMER_REPEAT); + } +} + +public AnticampHook(Handle:convar, const String:oldValue[], const String:newValue[]) +{ + new anticamp_enabled = StringToInt(newValue); + if (anticamp_enabled) + { + Anticamp_Startup(true); + } + else + { + Anticamp_Disable(); + } +} + +public Action:Command_AnticampCreateVolume(client, argc) +{ + // Get a unused volume index. + new vol_index = GetFreeVolumeIndex(); + if (vol_index == -1) + { + ReplyToCommand(client, "Maximum volumes reached. Max: %d", MAX_VOLUMES); + return Plugin_Handled; + } + + // Check arguments. + if (argc < 8) + { + ReplyToCommand(client, "Too few parameters specified. Usage: zr_anticamp_create_volume "); + return Plugin_Handled; + } + + decl String:arg_buffer[32]; + + new vol_damage; + new Float:vol_interval; + new Float:x1; + new Float:y1; + new Float:z1; + new Float:x2; + new Float:y2; + new Float:z2; + + // Get arguments. + GetCmdArg(1, arg_buffer, sizeof(arg_buffer)); + vol_damage = StringToInt(arg_buffer); + + GetCmdArg(2, arg_buffer, sizeof(arg_buffer)); + vol_interval = StringToFloat(arg_buffer); + + GetCmdArg(3, arg_buffer, sizeof(arg_buffer)); + x1 = StringToFloat(arg_buffer); + + GetCmdArg(4, arg_buffer, sizeof(arg_buffer)); + y1 = StringToFloat(arg_buffer); + + GetCmdArg(5, arg_buffer, sizeof(arg_buffer)); + z1 = StringToFloat(arg_buffer); + + GetCmdArg(6, arg_buffer, sizeof(arg_buffer)); + x2 = StringToFloat(arg_buffer); + + GetCmdArg(7, arg_buffer, sizeof(arg_buffer)); + y2 = StringToFloat(arg_buffer); + + GetCmdArg(8, arg_buffer, sizeof(arg_buffer)); + z2 = StringToFloat(arg_buffer); + + // Check damage. + if (vol_damage == 0) + { + ReplyToCommand(client, "The damage specified is zero or invalid."); + return Plugin_Handled; + } + + // Check interval. + if (vol_interval <= 0.0) + { + ReplyToCommand(client, "The interval specified is zero or invalid."); + return Plugin_Handled; + } + + // Check if both locations are equal. + if (FloatCompare(x1, x2) == 0) + { + if (FloatCompare(y1, y2) == 0) + { + if (FloatCompare(z1, z2) == 0) + { + ReplyToCommand(client, "Cannot add hurt volume. Both locations are equal."); + return Plugin_Handled; + } + } + } + + // Sort out max and min values. + if (FloatCompare(x1, x2) == 1) + { + volumes[vol_index][x_max] = x1; + volumes[vol_index][x_min] = x2; + } + else + { + volumes[vol_index][x_max] = x2; + volumes[vol_index][x_min] = x1; + } + + if (FloatCompare(y1, y2) == 1) + { + volumes[vol_index][y_max] = y1; + volumes[vol_index][y_min] = y2; + } + else + { + volumes[vol_index][y_max] = y2; + volumes[vol_index][y_min] = y1; + } + + if (FloatCompare(z1, z2) == 1) + { + volumes[vol_index][z_max] = z1; + volumes[vol_index][z_min] = z2; + } + else + { + volumes[vol_index][z_max] = z2; + volumes[vol_index][z_min] = z1; + } + + volumes[vol_index][volume_damage] = vol_damage; + volumes[vol_index][volume_interval] = vol_interval; + volumes[vol_index][volume_timer] = CreateTimer(vol_interval, Event_HurtPlayers, vol_index, TIMER_REPEAT); + volumes[vol_index][volume_in_use] = true; + + ReplyToCommand(client, "Volume created:\nid: %d\nx min:%f\ny min:%f\nz min:%f\nx max:%f\ny max:%f\nz max:%f", + vol_index, + volumes[vol_index][x_min], + volumes[vol_index][y_min], + volumes[vol_index][z_min], + volumes[vol_index][x_max], + volumes[vol_index][y_max], + volumes[vol_index][z_max]); + + volume_count++; + + return Plugin_Handled; +} + +public Action:Command_AnticampRemoveVolume(client, argc) +{ + new vol_index; + decl String:arg_buffer[32]; + new Handle:vol_timer; + + if (argc < 1) + { + ReplyToCommand(client, "Volume ID not specified. Usage: zr_anticamp_remove_volume "); + return Plugin_Handled; + } + + GetCmdArg(1, arg_buffer, sizeof(arg_buffer)); + vol_index = StringToInt(arg_buffer); + + if (IsValidVolume(vol_index)) + { + vol_timer = volumes[vol_index][volume_timer]; + if (vol_timer != INVALID_HANDLE) + { + KillTimer(vol_timer); + volumes[vol_index][volume_timer] = INVALID_HANDLE; + } + volumes[vol_index][volume_in_use] = false; + ReplyToCommand(client, "Removed volume %d.", vol_index); + } + else + { + ReplyToCommand(client, "Invalid volume ID."); + return Plugin_Handled; + } + + return Plugin_Handled; +} + +public Action:Command_AnticampList(client, argc) +{ + decl String:buffer[2048]; + decl String:line[192]; + buffer[0] = 0; + + StrCat(buffer, sizeof(buffer), "id damage interval x_min y_min z_min x_max y_max z_max\n"); + + for (new vol_index = 0; vol_index < MAX_VOLUMES; vol_index++) + { + if (volumes[vol_index][volume_in_use]) + { + Format(line, sizeof(line), "%d %d %f %f %f %f %f %f %f\n", + vol_index, + volumes[vol_index][volume_damage], + volumes[vol_index][volume_interval], + volumes[vol_index][x_min], + volumes[vol_index][y_min], + volumes[vol_index][z_min], + volumes[vol_index][x_max], + volumes[vol_index][y_max], + volumes[vol_index][y_max]); + StrCat(buffer, sizeof(buffer), line); + } + } + + ReplyToCommand(client, buffer); +} + +UpdatePlayerLocations() +{ + new client; + for (client = 1; client <= maxclients; client++) + { + if (IsClientConnected(client) && IsClientInGame(client)) + { + GetClientAbsOrigin(client, player_loc[client]); + } + } +} + +HurtPlayersInVolume(volume_index) +{ + new client; + new client_health; + decl String:client_name[64]; + decl String:buffer[192]; + new anticamp_echo = GetConVarBool(gCvars[CVAR_ANTICAMP_ECHO]); + + for (client = 1; client <= maxclients; client++) + { + if (IsClientConnected(client) && + IsClientInGame(client) && + IsPlayerAlive(client) && + IsPlayerHuman(client)) + { + if (IsPlayerInVolume(client, volume_index)) + { + ZR_PrintToChat(client, "Unfair camping"); + client_health = GetClientHealth(client) - volumes[volume_index][volume_damage]; + if (client_health > 0) + { + SetEntityHealth(client, client_health); + } + else + { + ForcePlayerSuicide(client); + + GetClientName(client, client_name, sizeof(client_name)); + SetGlobalTransTarget(client); + Format(buffer, sizeof(buffer), "%T", "Unfair camper slayed", LANG_SERVER, client_name, volume_index); + + LogAction(client, client, buffer); + if (anticamp_echo) + { + FormatTextString(buffer, sizeof(buffer)); + ZR_PrintToAdminChat(buffer); + } + } + } + } + } +} + +bool:IsPlayerInVolume(client, volume_index) +{ + new Float:player_loc_x = player_loc[client][0]; + new Float:player_loc_y = player_loc[client][1]; + new Float:player_loc_z = player_loc[client][2]; + new debug_flags = GetConVarInt(gCvars[CVAR_DEBUG]); + + if ((player_loc_x >= volumes[volume_index][x_min]) && (player_loc_x <= volumes[volume_index][x_max])) + { + if (debug_flags & 4) PrintToChatAll("[ZR] Debug, Anticamp -- IsPlayerInVolume: Client %d matches X values.", client); + if ((player_loc_y >= volumes[volume_index][y_min]) && (player_loc_y <= volumes[volume_index][y_max])) + { + if (debug_flags & 4) PrintToChatAll("[ZR] Debug, Anticamp -- IsPlayerInVolume: Client %d matches Y values.", client); + if ((player_loc_z >= volumes[volume_index][z_min]) && (player_loc_z <= volumes[volume_index][z_max])) + { + if (debug_flags & 4) PrintToChatAll("[ZR] Debug, Anticamp -- IsPlayerInVolume: Client %d matches Z values.", client); + return true; + } + } + } + return false; +} + +GetFreeVolumeIndex() +{ + for(new vol_index = 0; vol_index < MAX_VOLUMES; vol_index++) + { + if (!volumes[vol_index][volume_in_use]) + { + return vol_index; + } + } + return -1; +} + +bool:IsValidVolume(vol_index) +{ + if (vol_index >= 0 && vol_index < MAX_VOLUMES) + { + return true; + } + else + { + return false; + } +} + +ResetVolumes() +{ + for (new vol_index = 0; vol_index < MAX_VOLUMES; vol_index++) + { + volumes[vol_index][volume_in_use] = false; + if (volumes[vol_index][volume_timer] != INVALID_HANDLE) + { + KillTimer(volumes[vol_index][volume_timer]); + volumes[vol_index][volume_timer] = INVALID_HANDLE; + } + } +} + +public Action:Event_UpdatePlayerLocations(Handle:Timer) +{ + UpdatePlayerLocations(); +} + +public Action:Event_HurtPlayers(Handle:Timer, any:volume_index) +{ + HurtPlayersInVolume(volume_index); +} diff --git a/src/zr/commands.inc b/src/zr/commands.inc index ab05f27..fed8a56 100644 --- a/src/zr/commands.inc +++ b/src/zr/commands.inc @@ -25,6 +25,10 @@ CreateCommands() RegAdminCmd("zr_admin", Command_AdminMenu, ADMFLAG_GENERIC, "Displays the admin menu for Zombie: Reloaded."); RegAdminCmd("zr_knockback_m", Command_KnockbackMMenu, ADMFLAG_GENERIC, "Displays the knockback multiplier menu."); RegAdminCmd("zr_teleadmin", Command_TeleMenu, ADMFLAG_GENERIC, "Displays the teleport admin menu for Zombie: Reloaded."); + + RegAdminCmd("zr_anticamp_create_volume", Command_AnticampCreateVolume, ADMFLAG_GENERIC, "Creates a rectangular hurt volume between two points. Usage: ht_create_volume "); + RegAdminCmd("zr_anticamp_remove_volume", Command_AnticampRemoveVolume, ADMFLAG_GENERIC, "Removes a volume. Use zr_anticamp_list to list volumes. Usage: zr_anticamp_remove_volume "); + RegAdminCmd("zr_anticamp_list", Command_AnticampList, ADMFLAG_GENERIC, "List current volumes."); } public Action:Command_Infect(client, argc) diff --git a/src/zr/cvars.inc b/src/zr/cvars.inc index 7c5ccd8..d273574 100644 --- a/src/zr/cvars.inc +++ b/src/zr/cvars.inc @@ -81,7 +81,10 @@ enum ZRSettings Handle:CVAR_INFECT_SHAKE_AMP, Handle:CVAR_INFECT_SHAKE_FREQUENCY, Handle:CVAR_INFECT_SHAKE_DURATION, - Handle:CVAR_INFECT_ANTISTICK_FORCE + Handle:CVAR_INFECT_ANTISTICK_FORCE, + Handle:CVAR_ANTICAMP, + Handle:CVAR_ANTICAMP_UPDATE_INTERVAL, + Handle:CVAR_ANTICAMP_ECHO } new gCvars[ZRSettings]; @@ -162,8 +165,13 @@ CreateCvars() gCvars[CVAR_INFECT_SHAKE_FREQUENCY] = CreateConVar("zr_infect_shake_frequency", "1.0", "Frequency of the shake, when zr_infect_shake is 1"); gCvars[CVAR_INFECT_SHAKE_DURATION] = CreateConVar("zr_infect_shake_duration", "5.0", "Duration of the shake, when zr_infect_shake is 1"); gCvars[CVAR_INFECT_ANTISTICK_FORCE] = CreateConVar("zr_infect_antistick_force", "-160.0", "Force to push away players from eachother on infection. Negative values push away, positive values pull. (0: Disable)"); + gCvars[CVAR_ANTICAMP] = CreateConVar("zr_anticamp", "1", "Enables or disables hurt volumes for preventing unfair camping. (0: Disable)"); + 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); + HookConVarChange(gCvars[CVAR_ANTICAMP], AnticampHook); + HookConVarChange(gCvars[CVAR_ANTICAMP_UPDATE_INTERVAL], UpdateIntervalHook); AutoExecConfig(true, "zombiereloaded", "sourcemod/zombiereloaded"); }