diff --git a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt
index 79871d0..a94bc35 100644
--- a/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt
+++ b/cstrike/addons/sourcemod/translations/zombiereloaded.phrases.txt
@@ -932,17 +932,19 @@
"ru" "Здоровье: {1}"
}
+ // ===========================
+ // Volumetric features (module)
+ // ===========================
-
- "Unfair camping"
+ "Vol Anticamp Message"
{
- "en" "An admin has marked this area as unfair, please move along, or die."
+ "en" "This area is restricted, please move along."
}
- "Unfair camper slayed"
+ "Vol Slay"
{
"#format" "{1:s},{2:d}"
- "en" "Player {1} has been slayed for camping in a restricted area. (ID: {2})"
+ "en" "Player \"{1}\" has been slayed for camping in a restricted area (ID: {2})."
}
// ===========================
diff --git a/src/zombiereloaded.sp b/src/zombiereloaded.sp
index 5e923d9..aa8f4f0 100644
--- a/src/zombiereloaded.sp
+++ b/src/zombiereloaded.sp
@@ -80,6 +80,7 @@
#include "zr/zcookies"
#include "zr/jumpboost"
#include "zr/volfeatures/volfeatures"
+#include "zr/debugtools"
/**
* Record plugin info.
@@ -137,6 +138,7 @@ public OnMapStart()
SEffectsOnMapStart();
AntiStickOnMapStart();
ZSpawnOnMapStart();
+
}
/**
diff --git a/src/zr/commands.inc b/src/zr/commands.inc
index 6f343e8..4dcb552 100644
--- a/src/zr/commands.inc
+++ b/src/zr/commands.inc
@@ -45,6 +45,8 @@ CommandsInit()
ZHPOnCommandsCreate();
VolOnCommandsCreate();
+ DebugOnCommandsCreate();
+
// Forward event to modules. (hook commands)
ClassOnCommandsHook();
DamageOnCommandsHook();
diff --git a/src/zr/cvars.inc b/src/zr/cvars.inc
index 99b14de..4efcc38 100644
--- a/src/zr/cvars.inc
+++ b/src/zr/cvars.inc
@@ -423,8 +423,8 @@ CvarsCreate()
// Volumetric Features (module)
// ===========================
g_hCvarsList[CVAR_VOL] = CreateConVar("zr_vol", "1", "Enables volumetric features.");
- g_hCvarsList[CVAR_VOL_UPDATE_INTERVAL] = CreateConVar("zr_vol_update_interval", "1.0", "How often to update player positions and trigger events, in seconds.");
- g_hCvarsList[CVAR_VOL_TRIGGER_INTERVAL] = CreateConVar("zr_vol_trigger_interval", "1.0", "How often to check for delayed events, in seconds. Use lower values for more precise delays.");
+ g_hCvarsList[CVAR_VOL_UPDATE_INTERVAL] = CreateConVar("zr_vol_update_interval", "0.5", "How often to update player positions and trigger events, in seconds.");
+ g_hCvarsList[CVAR_VOL_TRIGGER_INTERVAL] = CreateConVar("zr_vol_trigger_interval", "0.5", "How often to check for delayed events, in seconds. Use lower values for more precise delays.");
// ===========================
diff --git a/src/zr/debugtools.inc b/src/zr/debugtools.inc
new file mode 100644
index 0000000..e8082ae
--- /dev/null
+++ b/src/zr/debugtools.inc
@@ -0,0 +1,105 @@
+/*
+ * ============================================================================
+ *
+ * Zombie:Reloaded
+ *
+ * File: debugtools.inc
+ * Type: Core
+ * Description: Place to put custom functions and test stuff.
+ *
+ * Copyright (C) 2009 Greyscale, Richard Helgeby
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ * ============================================================================
+ */
+
+DebugOnCommandsCreate()
+{
+ // Custom test commands:
+ RegConsoleCmd("zr_getparametercount", Command_GetParameterCount, "Test GetParameterCount function in paramtools.inc. Usage: zr_getparametercount ");
+ RegConsoleCmd("zr_getparametervalue", Command_GetParameterValue, "Test GetParameterValue function in paramtools.inc. Usage: zr_getparametervalue ");
+}
+
+public Action:Command_GetParameterCount(client, argc)
+{
+ if (argc < 1)
+ {
+ ReplyToCommand(client, "No parameter string passed. Usage: zr_getparametercount ");
+ return Plugin_Handled;
+ }
+
+ decl String:argbuffer[256];
+ decl String:paramstring[256];
+
+ paramstring[0] = 0;
+
+ // Join the last parameters in a string.
+ for (new arg = 1; arg <= argc; arg++)
+ {
+ GetCmdArg(arg, argbuffer, sizeof(argbuffer));
+ StrCat(paramstring, sizeof(paramstring), argbuffer);
+
+ // Add space, except on the last parameter.
+ if (arg < argc)
+ {
+ StrCat(paramstring, sizeof(paramstring), " ");
+ }
+ }
+
+ ReplyToCommand(client, "Parameter string: \"%s\"", paramstring);
+ new paramcount = GetParameterCount(paramstring);
+ ReplyToCommand(client, "Parameter count: %d", paramcount);
+
+ return Plugin_Handled;
+}
+
+public Action:Command_GetParameterValue(client, argc)
+{
+ if (argc < 2)
+ {
+ ReplyToCommand(client, "Missing parameters. Usage: zr_getparametervalue ");
+ return Plugin_Handled;
+ }
+
+ decl String:paramname[256];
+ decl String:paramstring[256];
+
+ decl String:valuebuffer[64];
+ decl String:argbuffer[256];
+
+ paramstring[0] = 0;
+ valuebuffer[0] = 0;
+
+ GetCmdArg(1, paramname, sizeof(paramname));
+
+ // Join the last parameters in a string.
+ for (new arg = 2; arg <= argc; arg++)
+ {
+ GetCmdArg(arg, argbuffer, sizeof(argbuffer));
+ StrCat(paramstring, sizeof(paramstring), argbuffer);
+
+ // Add space, except on the last parameter.
+ if (arg < argc)
+ {
+ StrCat(paramstring, sizeof(paramstring), " ");
+ }
+ }
+
+ new retval = GetParameterValue(valuebuffer, sizeof(valuebuffer), paramstring, paramname);
+
+ ReplyToCommand(client, "Return value: %d\nParameter string: \"%s\"\nParameter value: \"%s\"", retval, paramstring, valuebuffer);
+
+ return Plugin_Handled;
+}
\ No newline at end of file
diff --git a/src/zr/paramtools.inc b/src/zr/paramtools.inc
index cb61b20..701ab7a 100644
--- a/src/zr/paramtools.inc
+++ b/src/zr/paramtools.inc
@@ -8,6 +8,8 @@
* Description: Provides functions for parsing strings with parameters in
* key=value format.
*
+ * Note: Does not support spaces or quoted strings.
+ *
* Copyright (C) 2009 Greyscale, Richard Helgeby
*
* This program is free software: you can redistribute it and/or modify
@@ -34,6 +36,8 @@ stock GetParameterCount(const String:rawString[])
{
new lenRawString = strlen(rawString);
new paramCount;
+ new searchPos;
+ new splitPos;
// Check if the raw string is empty.
if (lenRawString == 0)
@@ -42,20 +46,26 @@ stock GetParameterCount(const String:rawString[])
}
// Count number of "=".
- for (new searchPos = 0; searchPos < lenRawString; searchPos++)
+ for (splitPos = 0; splitPos < lenRawString; splitPos++)
{
- // Set searchPos and check if "=" was found.
- searchPos = StrContains(rawString[searchPos], "=");
- if (searchPos)
+ // Find next "=".
+ searchPos = StrContains(rawString[splitPos], "=", false);
+
+ // Note: searchPos is an offset from rawString[splitPos]. If searchPos
+ // is 0, then the real position is splitPos.
+
+ // Check if "=" was found.
+ if (searchPos >= 0)
{
+ // Parameter found.
paramCount++;
- // Increment one position so we dont find the same "=" again.
- searchPos++;
+ // Update split position.
+ splitPos += searchPos;
}
else
{
- // Exit loop.
+ // No need to continue. "=" not found.
break;
}
}
@@ -86,9 +96,11 @@ stock GetParameterValue(String:buffer[], maxlen, const String:rawString[], const
new valuePos;
new valueLen;
new nextPos;
+ new splitPos;
// Get the position of parameter.
paramPos = StrContains(rawString, parameter, false);
+ splitPos = paramPos;
// Check if found.
if (paramPos >= 0)
@@ -96,8 +108,8 @@ stock GetParameterValue(String:buffer[], maxlen, const String:rawString[], const
// Get parameter length.
paramLen = strlen(parameter);
- // Get the position of the next parameter.
- nextPos = StrContains(rawString[paramPos + 1], " ");
+ // Get the position of the next parameter by finding the next space.
+ nextPos = StrContains(rawString[splitPos], " ");
// Check if the next parameter was found.
if (nextPos >= 0)
@@ -105,8 +117,9 @@ stock GetParameterValue(String:buffer[], maxlen, const String:rawString[], const
// Calculate value starting position.
valuePos = paramPos + paramLen + 1;
- // Calculate value length.
- valueLen = nextPos - valuePos;
+ // Calculate value length. Note: Adding 1 for space to the null
+ // terminator.
+ valueLen = nextPos + splitPos - valuePos + 1;
// Check if value length is longer than buffer size.
if (valueLen > maxlen)
@@ -145,6 +158,7 @@ stock GetParameterName(String:buffer[], maxlen, const String:rawString[], parame
new paramPos;
new valuePos;
new nextValuePos;
+ new splitPos;
// Check if the raw string is empty.
if (strlen(rawString) == 0)
@@ -157,22 +171,26 @@ stock GetParameterName(String:buffer[], maxlen, const String:rawString[], parame
// Get the value starting position for the previous index.
for (new index = 0; index < parameterIndex - 1; index++)
{
- valuePos = StrContains(rawString[valuePos], "=");
+ valuePos = StrContains(rawString[splitPos], "=");
+ splitPos += valuePos;
}
- // Find the next space from valuePos where the specified parameter
+ // Find the next space from splitPos where the specified parameter
// starts.
- paramPos = StrContains(rawString[valuePos], " ") + 1;
+ paramPos = StrContains(rawString[splitPos], " ") + splitPos + 1;
- // Find the next value position from paramPos to get the end position
+ // Update split position.
+ splitPos += paramPos;
+
+ // Find the next value position from splitPos to get the end position
// of the parameter name.
- nextValuePos = StrContains(rawString[paramPos], "=");
+ nextValuePos = StrContains(rawString[splitPos], "=") + splitPos;
// Check if a value is specified.
if (nextValuePos > 0)
{
// Return the parameter name between paramPos and nextValuePos.
- return strcopy(buffer, nextValuePos - paramPos, rawString[paramPos]);
+ return strcopy(buffer, nextValuePos - paramPos + 1, rawString[paramPos]); // + 1 to include null terminator.
}
else
{
@@ -189,7 +207,7 @@ stock GetParameterName(String:buffer[], maxlen, const String:rawString[], parame
if (valuePos > 0)
{
// Return the parameter name.
- return strcopy(buffer, valuePos, rawString);
+ return strcopy(buffer, valuePos + 1, rawString); // + 1 to include null terminator.
}
else
{
diff --git a/src/zr/playerclasses/filtertools.inc b/src/zr/playerclasses/filtertools.inc
index aa86c85..d63ca73 100644
--- a/src/zr/playerclasses/filtertools.inc
+++ b/src/zr/playerclasses/filtertools.inc
@@ -384,7 +384,7 @@ stock ClassGetIndex(const String:name[], cachetype = ZR_CLASS_CACHE_MODIFIED)
*/
stock ClassGetActiveIndex(client)
{
- new teamid = GetClientTeam(client);
+ new teamid;
if (!ZRIsClientOnTeam(client))
{
diff --git a/src/zr/translation.inc b/src/zr/translation.inc
index f2ae06e..1b8ccfa 100644
--- a/src/zr/translation.inc
+++ b/src/zr/translation.inc
@@ -61,6 +61,7 @@ TranslationInit()
{
// Load translations phrases used by plugin.
LoadTranslations("common.phrases.txt");
+ LoadTranslations("core.phrases.txt");
LoadTranslations("zombiereloaded.phrases.txt");
}
diff --git a/src/zr/volfeatures/volanticamp.inc b/src/zr/volfeatures/volanticamp.inc
index bc314c1..d99fe98 100644
--- a/src/zr/volfeatures/volanticamp.inc
+++ b/src/zr/volfeatures/volanticamp.inc
@@ -25,31 +25,16 @@
* ============================================================================
*/
-/**
- * Data structure for a anti-camp volume.
- */
-enum VolTypeAnticamp
-{
- Float:anticamp_interval, /** How often to trigger an action. */
- Handle:anticamp_timer, /** Action timer handle. */
-
- VolAnticampAction:anticamp_action, /** What to do with players in anti-camp volumes */
- Float:anticamp_amount, /** Amount depending on action type. Usually time in seconds or damage amount. */
-
- VolAnticampeWarningType:anticamp_warning, /** How to warn the player. */
- String:anticamp_message[256] /** Override warning message. Max 256 characters. */
-}
-
/**
* Actions to do with players in anti-camp volumes.
*/
enum VolAnticampAction
{
- anticamp_no_action, /** Do nothing but give a warning. */
- anticamp_damage, /** Give damage to player. */
- anticamp_slay, /** Slay player. */
- anticamp_drug, /** Drug player. */
- anticamp_fire /** Ignite player. */
+ Anticamp_NoAction, /** Do nothing but give a warning. */
+ Anticamp_Damage, /** Give damage to player. */
+ Anticamp_Slay, /** Slay player. */
+ Anticamp_Drug, /** Drug player. */
+ Anticamp_Ignite /** Ignite player. */
}
/**
@@ -57,13 +42,797 @@ enum VolAnticampAction
*/
enum VolAnticampeWarningType
{
- anticamp_no_warning, /** No warning messages. */
- anticamp_chat, /** Print warning in chat area. */
- anticamp_center, /** Print centered warning message. */
- anticamp_menu /** Print a menu-like warning message with close option. */
+ Anticamp_NoWarning, /** No warning messages. */
+ Anticamp_Chat, /** Print warning in chat area. */
+ Anticamp_Center, /** Print centered warning message. */
+ Anticamp_Menu /** Print a menu-like warning message with close option. */
+}
+
+/**
+ * Data structure for a anti-camp volume.
+ */
+enum VolTypeAnticamp
+{
+ bool:Anticamp_InUse, /** Specifies if the data index is used or not. */
+ Float:Anticamp_Interval, /** How often to trigger an action. */
+ Handle:Anticamp_Timer, /** Action timer handle. */
+
+ VolAnticampAction:Anticamp_Action, /** What to do with players in anti-camp volumes */
+ Float:Anticamp_Amount, /** Amount depending on action type. Usually time in seconds or damage amount. */
+
+ VolAnticampeWarningType:Anticamp_Warning, /** How to warn the player. */
+ String:Anticamp_Message[256] /** Override warning message. Max 256 characters. */
}
/**
* Anti-camp data.
*/
new AnticampData[ZR_VOLUMES_MAX][VolTypeAnticamp];
+
+/**
+ * Event callback. Enables a anticamp volume.
+ *
+ * @param volumeIndex The volume index.
+ */
+VolAnticampEnable(volumeIndex)
+{
+ new Float:interval;
+
+ // Validate index.
+ if (!VolIsValidIndex(volumeIndex))
+ {
+ return;
+ }
+
+ // Get data index.
+ new dataindex = Volumes[volumeIndex][Vol_DataIndex];
+
+ // Validate data index.
+ if (!VolAnticampValidateIndex(dataindex))
+ {
+ return;
+ }
+
+ // Check if in use.
+ if (AnticampData[dataindex][Anticamp_InUse])
+ {
+ // Start timer if not running.
+ if (AnticampData[dataindex][Anticamp_Timer] == INVALID_HANDLE)
+ {
+ // Get interval.
+ interval = AnticampData[dataindex][Anticamp_Interval];
+
+ // Validate interval.
+ if (interval > 0.0)
+ {
+ AnticampData[dataindex][Anticamp_Timer] = CreateTimer(interval, Event_VolAnticampTrigger, _, TIMER_REPEAT);
+ }
+ }
+
+ LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Vol state", "Enabled anticamp volume %d.", volumeIndex);
+ }
+}
+
+/**
+ * Starts all existing anticamp timers.
+ */
+stock VolAnticampEnableAll()
+{
+ new Float:interval;
+
+ // Loop through all volumes.
+ for (new dataindex = 0; dataindex < ZR_VOLUMES_MAX; dataindex++)
+ {
+ // Check if in use.
+ if (AnticampData[dataindex][Anticamp_InUse])
+ {
+ // Start timer if not running.
+ if (AnticampData[dataindex][Anticamp_Timer] == INVALID_HANDLE)
+ {
+ // Get interval.
+ interval = AnticampData[dataindex][Anticamp_Interval];
+
+ // Validate interval.
+ if (interval > 0.0)
+ {
+ AnticampData[dataindex][Anticamp_Timer] = CreateTimer(interval, Event_VolAnticampTrigger, _, TIMER_REPEAT);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Event callback. Stops existing anticamp timer on a volume.
+ */
+VolAnticampDisable(volumeIndex)
+{
+ new Handle:timerbuffer;
+
+ // Validate index.
+ if (!VolIsValidIndex(volumeIndex))
+ {
+ return;
+ }
+
+ // Get data index.
+ new dataindex = Volumes[volumeIndex][Vol_DataIndex];
+
+ // Validate data index.
+ if (!VolAnticampValidateIndex(dataindex))
+ {
+ return;
+ }
+
+ // Check if in use.
+ if (AnticampData[dataindex][Anticamp_InUse])
+ {
+ // Stop timer.
+ timerbuffer = AnticampData[dataindex][Anticamp_Timer];
+ if (timerbuffer != INVALID_HANDLE)
+ {
+ KillTimer(timerbuffer);
+ AnticampData[dataindex][Anticamp_Timer] = INVALID_HANDLE;
+ }
+
+ LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Vol state", "Disabled anticamp volume %d.", volumeIndex);
+ }
+
+}
+
+/**
+ * Stops all existing anticamp timers.
+ */
+stock VolAnticampDisableAll()
+{
+ new Handle:timerbuffer;
+
+ // Loop through all volumes.
+ for (new dataindex = 0; dataindex < ZR_VOLUMES_MAX; dataindex++)
+ {
+ // Check if in use.
+ if (AnticampData[dataindex][Anticamp_InUse])
+ {
+ // Stop timer.
+ timerbuffer = AnticampData[dataindex][Anticamp_Timer];
+ if (timerbuffer != INVALID_HANDLE)
+ {
+ KillTimer(timerbuffer);
+ AnticampData[dataindex][Anticamp_Timer] = INVALID_HANDLE;
+ }
+ }
+ }
+}
+
+/**
+ * Disables feature and resets data to defaults at the specified index.
+ *
+ * @param dataIndex Local data index.
+ */
+VolAnticampReset(dataIndex)
+{
+ AnticampData[dataIndex][Anticamp_InUse] = false;
+
+ AnticampData[dataIndex][Anticamp_Interval] = 1.0;
+ if (AnticampData[dataIndex][Anticamp_Timer] != INVALID_HANDLE)
+ {
+ KillTimer(AnticampData[dataIndex][Anticamp_Timer]);
+ AnticampData[dataIndex][Anticamp_Timer] = INVALID_HANDLE;
+ }
+
+ AnticampData[dataIndex][Anticamp_Action] = Anticamp_Damage;
+ AnticampData[dataIndex][Anticamp_Amount] = 5.0;
+
+ AnticampData[dataIndex][Anticamp_Warning] = Anticamp_Chat;
+ Format(String:AnticampData[dataIndex][Anticamp_Message], 256, "");
+}
+
+/**
+ * Initialization event for anticamp feature.
+ */
+VolAnticampInit()
+{
+ // Set default attributes.
+ for (new dataindex = 0; dataindex < ZR_VOLUMES_MAX; dataindex++)
+ {
+ VolAnticampReset(dataindex);
+ }
+}
+
+/**
+ * Timer callback for anticamp volumes. Applies actions on players in volumes.
+ */
+public Action:Event_VolAnticampTrigger(Handle:timer)
+{
+ // Loop through all players.
+ for (new client = 1; client <= MaxClients; client++)
+ {
+ // Loop through all volumes.
+ for (new volumeindex = 0; volumeindex < ZR_VOLUMES_MAX; volumeindex++)
+ {
+ // Check if the volume is enabled and in use.
+ if (VolIsEnabled(volumeindex) && VolInUse(volumeindex))
+ {
+ // Check if player is in a anticamp volume.
+ if (VolPlayerInVolume[client][volumeindex] && Volumes[volumeindex][Vol_Type] == VolFeature_Anticamp)
+ {
+ // Apply action.
+ VolAnticampApplyAction(client, Volumes[volumeindex][Vol_DataIndex], volumeindex);
+ }
+ }
+ }
+ }
+}
+
+/**
+ * Applies action on a client for the specified volume.
+ *
+ * @param client The client index.
+ * @param dataIndex Local data index.
+ * @param volumeIndex The volume index.
+ */
+VolAnticampApplyAction(client, dataIndex, volumeIndex)
+{
+ new Float:amount = AnticampData[dataIndex][Anticamp_Amount];
+
+ // Send warning message.
+ VolAnticampWarnPlayer(client, dataIndex);
+
+ switch (AnticampData[dataIndex][Anticamp_Action])
+ {
+ case Anticamp_NoAction:
+ {
+ // Do nothing.
+ }
+ case Anticamp_Damage:
+ {
+ // Give damage to player. Kill if zero HP.
+ new damage = RoundToNearest(amount);
+ new health = GetClientHealth(client) - damage;
+ decl String:name[64];
+ decl String:buffer[256];
+
+ if (health > 0)
+ {
+ SetEntityHealth(client, health);
+ }
+ else
+ {
+ // Health is zero or below. Kill player.
+ ForcePlayerSuicide(client);
+
+ // Log event.
+ GetClientName(client, name, sizeof(name));
+ SetGlobalTransTarget(client);
+ Format(buffer, sizeof(buffer), "%t", "Vol Slay", name, volumeIndex);
+
+ /*if (LogFlagCheck(LOG_GAME_EVENTS, LOG_MODULE_ANTICAMP)) ZR_LogMessageFormatted(client, "anticamp", "kill", "%s", true, buffer);
+ if (anticamp_echo)
+ {
+ FormatTextString(buffer, sizeof(buffer));
+ ZR_PrintToAdminChat(buffer);
+ }*/
+ }
+ }
+ case Anticamp_Slay:
+ {
+
+ }
+ case Anticamp_Drug:
+ {
+
+ }
+ case Anticamp_Ignite:
+ {
+
+ }
+ }
+}
+
+/**
+ * Gives a warning to the specified player for the specified volume.
+ *
+ * @param client The client index.
+ * @param dataIndex Local data index.
+ */
+VolAnticampWarnPlayer(client, dataIndex)
+{
+ decl String:buffer[256];
+ new bool:custommessage = (strlen(AnticampData[dataIndex][Anticamp_Message]) > 0) ? true : false;
+
+ // Set language.
+ SetGlobalTransTarget(client);
+
+ // Format message.
+ if (custommessage)
+ {
+ // Use custom message.
+ strcopy(buffer, sizeof(buffer), AnticampData[dataIndex][Anticamp_Message]);
+ }
+ else
+ {
+ // Use message in translations file.
+ Format(buffer, sizeof(buffer), "%t", "Vol Anticamp Message");
+ }
+
+ switch (AnticampData[dataIndex][Anticamp_Warning])
+ {
+ case Anticamp_NoWarning:
+ {
+ // Do nothing.
+ }
+ case Anticamp_Chat:
+ {
+ // Apply ZR formatting and print chat message.
+ TranslationPluginFormatString(buffer, sizeof(buffer));
+ PrintToChat(client, buffer);
+ }
+ case Anticamp_Center:
+ {
+ // Print centered message.
+ PrintCenterText(client, buffer);
+ }
+ case Anticamp_Menu:
+ {
+ // Display the message in a menu panel.
+ new Handle:panel = CreatePanel();
+
+ SetPanelTitle(panel, "Zombie:Reloaded");
+ DrawPanelItem(panel, "", ITEMDRAW_SPACER);
+ DrawPanelItem(panel, buffer);
+ DrawPanelItem(panel, "", ITEMDRAW_SPACER);
+
+ SetPanelCurrentKey(panel, 10);
+
+ Format(buffer, sizeof(buffer), "%t", "Exit");
+ DrawPanelItem(panel, buffer, ITEMDRAW_CONTROL);
+
+ SendPanelToClient(panel, client, Handler_AnitcampDummy, 10);
+ CloseHandle(panel);
+ }
+ }
+}
+
+/**
+ * Dummy handler for panel messages.
+ */
+public Handler_AnitcampDummy(Handle:menu, MenuAction:action, param1, param2)
+{
+ // Do nothing.
+}
+
+/**
+ * Gets the first free anticamp data index.
+ *
+ * @return The first free anticamp data index if successful, or -1 if
+ * there are no free volumes.
+ */
+VolAnticampGetFreeIndex()
+{
+ // Loop through all indexes.
+ for (new dataindex = 0; dataindex < ZR_VOLUMES_MAX; dataindex++)
+ {
+ // Check if it's free.
+ if (!AnticampData[dataindex][Anticamp_InUse])
+ {
+ return dataindex;
+ }
+ }
+
+ // No free index found.
+ return -1;
+}
+
+/**
+ * Validates local data index.
+ *
+ * @param dataIndex Index to validate.
+ * @return True if valid, false otherwise.
+ */
+bool:VolAnticampValidateIndex(dataIndex)
+{
+ if (dataIndex >= 0 && dataIndex < ZR_VOLUMES_MAX)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+/**
+ * Dumps data to be used by zr_vol_list command.
+ *
+ * @param dataIndex Index in anticamp data array.
+ * @param buffer Destination string buffer.
+ * @param maxlen Size of destination buffer.
+ * @return Number of cells written.
+ */
+VolAnticampDumpData(dataIndex, String:buffer[], maxlen)
+{
+ decl String:linebuffer[128];
+ decl String:valuebuffer[256];
+ new anticampcache[VolTypeAnticamp];
+ new cellswritten;
+
+ // Validate index.
+ if (!VolAnticampValidateIndex(dataIndex))
+ {
+ return 0;
+ }
+
+ // Initialize and clear buffer.
+ buffer[0] = 0;
+
+ // Cache data.
+ anticampcache = AnticampData[dataIndex];
+
+ Format(linebuffer, sizeof(linebuffer), "Interval: %.2f\n", anticampcache[Anticamp_Interval]);
+ cellswritten += StrCat(buffer, maxlen, linebuffer);
+
+ VolAnticampActionToString(anticampcache[Anticamp_Action], valuebuffer, sizeof(valuebuffer));
+ Format(linebuffer, sizeof(linebuffer), "Action: %s\n", valuebuffer);
+ cellswritten += StrCat(buffer, maxlen, linebuffer);
+
+ Format(linebuffer, sizeof(linebuffer), "Action amount: %.2f\n", anticampcache[Anticamp_Amount]);
+ cellswritten += StrCat(buffer, maxlen, linebuffer);
+
+ VolAnticampWarningToString(anticampcache[Anticamp_Warning], valuebuffer, sizeof(valuebuffer));
+ Format(linebuffer, sizeof(linebuffer), "Warning type: %s\n", valuebuffer);
+ cellswritten += StrCat(buffer, maxlen, linebuffer);
+
+ strcopy(valuebuffer, sizeof(valuebuffer), anticampcache[Anticamp_Message]);
+ Format(linebuffer, sizeof(linebuffer), "Warning message: \"%s\"\n", valuebuffer);
+ cellswritten += StrCat(buffer, maxlen, linebuffer);
+
+ return cellswritten;
+}
+
+
+/**************************************
+ * *
+ * CONVERTING FUNCTIONS *
+ * *
+ **************************************/
+
+/**
+ * Converts a action type to a string.
+ *
+ * @param actionType Action type to convert.
+ * @param buffer Destination string buffer.
+ * @param maxlen Size of destination buffer.
+ * @param shortName Optional. Write short name or human readable name.
+ * Default is human readable (false).
+ * @return Number of cells written.
+ */
+VolAnticampActionToString(VolAnticampAction:actionType, String:buffer[], maxlen, bool:shortName = false)
+{
+ switch (actionType)
+ {
+ case Anticamp_NoAction:
+ {
+ return shortName ? strcopy(buffer, maxlen, "none") : strcopy(buffer, maxlen, "No action");
+ }
+ case Anticamp_Damage:
+ {
+ return shortName ? strcopy(buffer, maxlen, "damage") : strcopy(buffer, maxlen, "Damage player");
+ }
+ case Anticamp_Drug:
+ {
+ return shortName ? strcopy(buffer, maxlen, "drug") : strcopy(buffer, maxlen, "Drug player ");
+ }
+ case Anticamp_Ignite:
+ {
+ return shortName ? strcopy(buffer, maxlen, "ignite") : strcopy(buffer, maxlen, "Ignite player");
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Converts a action string type to a action type.
+ *
+ * @param action Action string type to convert.
+ * @return Action type or Anticamp_NoAction if failed.
+ */
+stock VolAnticampAction:VolAnticampStringToAction(const String:action[])
+{
+ // Check if empty.
+ if (strlen(action) == 0)
+ {
+ return Anticamp_NoAction;
+ }
+
+ if (StrEqual(action, "none", false))
+ {
+ return Anticamp_NoWarning;
+ }
+ else if (StrEqual(action, "damage", false))
+ {
+ return Anticamp_Damage;
+ }
+ else if (StrEqual(action, "drug", false))
+ {
+ return Anticamp_Drug;
+ }
+ else if (StrEqual(action, "ignite", false))
+ {
+ return Anticamp_Ignite;
+ }
+
+ // No match.
+ return Anticamp_NoAction;
+}
+
+/**
+ * Converts a warning type to a string.
+ *
+ * @param warningType Warning type to convert.
+ * @param buffer Destination string buffer.
+ * @param maxlen Size of destination buffer.
+ * @param shortName Optional. Write short name or human readable name.
+ * Default is human readable (false).
+ * @return Number of cells written.
+ */
+VolAnticampWarningToString(VolAnticampeWarningType:warningType, String:buffer[], maxlen, bool:shortName = false)
+{
+ switch (warningType)
+ {
+ case Anticamp_NoWarning:
+ {
+ return shortName ? strcopy(buffer, maxlen, "none") : strcopy(buffer, maxlen, "No warning");
+ }
+ case Anticamp_Chat:
+ {
+ return shortName ? strcopy(buffer, maxlen, "chat") : strcopy(buffer, maxlen, "Chat area");
+ }
+ case Anticamp_Center:
+ {
+ return shortName ? strcopy(buffer, maxlen, "center") : strcopy(buffer, maxlen, "Centered message");
+ }
+ case Anticamp_Menu:
+ {
+ return shortName ? strcopy(buffer, maxlen, "meny") : strcopy(buffer, maxlen, "Message in menu panel");
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * Converts a warning string type to a warning type.
+ *
+ * @param warning Warning string type to convert.
+ * @return Warning type, or Anticamp_NoWarning if failed.
+ */
+stock VolAnticampeWarningType:VolAnticampStringToWarning(const String:warning[])
+{
+ // Check if empty.
+ if (strlen(warning) == 0)
+ {
+ return Anticamp_NoWarning;
+ }
+
+ if (StrEqual(warning, "none", false))
+ {
+ return Anticamp_NoWarning;
+ }
+ else if (StrEqual(warning, "chat", false))
+ {
+ return Anticamp_Chat;
+ }
+ else if (StrEqual(warning, "center", false))
+ {
+ return Anticamp_Center;
+ }
+ else if (StrEqual(warning, "menu", false))
+ {
+ return Anticamp_Menu;
+ }
+
+ // No match.
+ return Anticamp_NoWarning;
+}
+
+
+/**************************************
+ * *
+ * ATTRIBUTE FUNCTIONS *
+ * *
+ **************************************/
+
+/**
+ * Sets anticamp spesific attributes on a anticamp volume.
+ *
+ * @param dataIndex Local data index.
+ * @param attribName Attribute to modify.
+ * @param attribVlue Attribute value to set.
+ * @return True if successfully set, false otherwise.
+ */
+bool:VolAnticampSetAttribute(dataIndex, const String:attribName[], const String:attribValue[])
+{
+ // Validate data index.
+ if (!VolAnticampValidateIndex(dataIndex))
+ {
+ return false;
+ }
+
+ // Check attribute names.
+ if (StrEqual(attribName, "interval", false))
+ {
+ if (VolAnticampSetIntervalString(dataIndex, attribValue))
+ {
+ return true;
+ }
+ }
+ else if (StrEqual(attribName, "action", false))
+ {
+ if (VolAnticampSetActionString(dataIndex, attribValue))
+ {
+ return true;
+ }
+ }
+ else if (StrEqual(attribName, "amount", false))
+ {
+ if (VolAnticampSetAmountString(dataIndex, attribValue))
+ {
+ return true;
+ }
+ }
+ else if (StrEqual(attribName, "warning", false))
+ {
+ if (VolAnticampSetWarningString(dataIndex, attribValue))
+ {
+ return true;
+ }
+ }
+ else if (StrEqual(attribName, "message", false))
+ {
+ // Unsupported because of technical limits in command parser. Spaces
+ // and quoted strings aren't supported yet.
+ }
+
+ return false;
+}
+
+/**
+ * Parses a interval string value and applies it to the specified volume.
+ *
+ * @param dataIndex Local data index.
+ * @param interval Interval to set. A floating point number formatted as a
+ * string.
+ * @return True if successfully set, false otherwise.
+ */
+bool:VolAnticampSetIntervalString(dataIndex, const String:interval[])
+{
+ new Float:anticampinterval;
+
+ // Check if string value is empty.
+ if (strlen(interval) == 0)
+ {
+ return false;
+ }
+
+ // Convert value.
+ anticampinterval = StringToFloat(interval);
+
+ // Apply value.
+ AnticampData[dataIndex][Anticamp_Interval] = anticampinterval;
+ return true;
+}
+
+/**
+ * Parses a action type string value and applies it to the specified volume.
+ *
+ * @param dataIndex Local data index.
+ * @param action Action type to set.
+ * @return True if successfully set, false otherwise.
+ */
+bool:VolAnticampSetActionString(dataIndex, const String:action[])
+{
+ // Check if string value is empty.
+ if (strlen(action) == 0)
+ {
+ return false;
+ }
+
+ // Check effect string values and apply them to the volume.
+ if (StrEqual(action, "none", false))
+ {
+ AnticampData[dataIndex][Anticamp_Action] = Anticamp_NoAction;
+ return true;
+ }
+ else if (StrEqual(action, "damage", false))
+ {
+ AnticampData[dataIndex][Anticamp_Action] = Anticamp_Damage;
+ return true;
+ }
+ else if (StrEqual(action, "slay", false))
+ {
+ AnticampData[dataIndex][Anticamp_Action] = Anticamp_Slay;
+ return true;
+ }
+ else if (StrEqual(action, "drug", false))
+ {
+ AnticampData[dataIndex][Anticamp_Action] = Anticamp_Drug;
+ return true;
+ }
+ else if (StrEqual(action, "ignite", false))
+ {
+ AnticampData[dataIndex][Anticamp_Action] = Anticamp_Ignite;
+ return true;
+ }
+
+ // The string value didn't match any valid action types.
+ return false;
+}
+
+/**
+ * Parses a action amount string value and applies it to the specified volume.
+ *
+ * @param dataIndex Local data index.
+ * @param amount Amount to set. A floating point number formatted as a
+ * string.
+ * @return True if successfully set, false otherwise.
+ */
+bool:VolAnticampSetAmountString(dataIndex, const String:amount[])
+{
+ new Float:actionamount;
+
+ // Check if string value is empty.
+ if (strlen(amount) == 0)
+ {
+ return false;
+ }
+
+ // Convert value.
+ actionamount = StringToFloat(amount);
+
+ // Apply value.
+ AnticampData[dataIndex][Anticamp_Amount] = actionamount;
+ return true;
+}
+
+/**
+ * Parses a warning type string value and applies it to the specified volume.
+ *
+ * @param dataIndex Local data index.
+ * @param warning warning type to set.
+ * @return True if successfully set, false otherwise.
+ */
+bool:VolAnticampSetWarningString(dataIndex, const String:warning[])
+{
+
+ // Check if string value is empty.
+ if (strlen(warning) == 0)
+ {
+ return false;
+ }
+
+ // Check effect string values and apply them to the volume.
+ if (StrEqual(warning, "none", false))
+ {
+ AnticampData[dataIndex][Anticamp_Warning] = Anticamp_NoWarning;
+ return true;
+ }
+ else if (StrEqual(warning, "chat", false))
+ {
+ AnticampData[dataIndex][Anticamp_Warning] = Anticamp_Chat;
+ return true;
+ }
+ else if (StrEqual(warning, "center", false))
+ {
+ AnticampData[dataIndex][Anticamp_Warning] = Anticamp_Center;
+ return true;
+ }
+ else if (StrEqual(warning, "menu", false))
+ {
+ AnticampData[dataIndex][Anticamp_Warning] = Anticamp_Menu;
+ return true;
+ }
+
+ // The string value didn't match any valid action types.
+ return false;
+}
diff --git a/src/zr/volfeatures/volcommands.inc b/src/zr/volfeatures/volcommands.inc
index de40f3d..4488ba8 100644
--- a/src/zr/volfeatures/volcommands.inc
+++ b/src/zr/volfeatures/volcommands.inc
@@ -49,9 +49,433 @@ Example:
zr_vol_add 0 0 0 100 200 300 anticamp team=humans delay=5 effect=wireframe effect_color=255,0,0
*/
+
+/**
+ * Creates commands for managing volumes.
+ */
+VolOnCommandsCreate()
+{
+ RegAdminCmd("zr_vol_add", VolAddVolumeCommand, ADMFLAG_CONFIG, "Creates a rectangular volume in the map. Usage: zr_vol_add [params]");
+ RegAdminCmd("zr_vol_remove", VolRemoveVolumeCommand, ADMFLAG_CONFIG, "Removes an existing volume in the map. Usage: zr_vol_remove ");
+ RegConsoleCmd("zr_vol_list", VolListCommand, "Lists existing volumes in the map, or dumps detail data to the specified volume. Usage: zr_vol_list [volume index]");
+ RegConsoleCmd("zr_vol_dumpstates", VolDumpStatesCommand, "Dumps volume states for the specified player. Usage: zr_vol_dumpstates ");
+}
+
+/**
+ * Command callback for creating a new volume.
+ */
public Action:VolAddVolumeCommand(client, argc)
{
+ decl String:buffer[640];
+ buffer[0] = 0;
+ if (argc < 7)
+ {
+ // Write syntax info.
+ StrCat(buffer, sizeof(buffer), "Creates a rectangular volume in the map. Usage: zr_vol_add [params]\n\n");
+ StrCat(buffer, sizeof(buffer), "Parameters:\n");
+ StrCat(buffer, sizeof(buffer), "x1, y1, z1 Coordinates to first corner (any corner)\n");
+ StrCat(buffer, sizeof(buffer), "x2, y2, z2 Coordinates to oposite corner (diagonally to oposite height)\n");
+ StrCat(buffer, sizeof(buffer), "type Volumetric feature type:\n");
+ StrCat(buffer, sizeof(buffer), " anticamp\n");
+ StrCat(buffer, sizeof(buffer), "params Parameter string with additional volume data. Generic parameters:\n");
+ StrCat(buffer, sizeof(buffer), " teamfilter=all|humans|zombies\n");
+ StrCat(buffer, sizeof(buffer), " delay=0\n");
+ StrCat(buffer, sizeof(buffer), " effect=none|wireframe|smoke\n");
+ StrCat(buffer, sizeof(buffer), " effect_color=0,0,0\n");
+ StrCat(buffer, sizeof(buffer), " enabled=1");
+
+ ReplyToCommand(client, buffer);
+ return Plugin_Handled;
+ }
+
+ new Float:x1;
+ new Float:y1;
+ new Float:z1;
+ new Float:x2;
+ new Float:y2;
+ new Float:z2;
+ new Float:min[3];
+ new Float:max[3];
+ new VolumeFeatureTypes:voltype;
+ new Float:floatbuffer;
+ new volindex;
+ new dataindex;
+ new paramcount;
+
+ decl String:params[256];
+ decl String:argbuffer[256];
+
+ params[0] = 0;
+
+ // Get a free volume index.
+ volindex = VolGetFreeVolume();
+
+ // Validate index.
+ if (!VolIsValidIndex(volindex))
+ {
+ ReplyToCommand(client, "Cannot add volume. Maximum number of volumes reached.");
+ return Plugin_Handled;
+ }
+
+ // Get positions.
+ GetCmdArg(1, argbuffer, sizeof(argbuffer));
+ x1 = StringToFloat(argbuffer);
+
+ GetCmdArg(2, argbuffer, sizeof(argbuffer));
+ y1 = StringToFloat(argbuffer);
+
+ GetCmdArg(3, argbuffer, sizeof(argbuffer));
+ z1 = StringToFloat(argbuffer);
+
+ GetCmdArg(4, argbuffer, sizeof(argbuffer));
+ x2 = StringToFloat(argbuffer);
+
+ GetCmdArg(5, argbuffer, sizeof(argbuffer));
+ y2 = StringToFloat(argbuffer);
+
+ GetCmdArg(6, argbuffer, sizeof(argbuffer));
+ z2 = StringToFloat(argbuffer);
+
+ // 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 volume. Both locations are equal.");
+ return Plugin_Handled;
+ }
+ }
+ }
+
+ // Sort out max and min values so 1-values are smaller.
+ if (FloatCompare(x1, x2) == 1)
+ {
+ // x1 is bigger than x2. Swap values.
+ floatbuffer = x1;
+ x1 = x2;
+ x2 = floatbuffer;
+ }
+ if (FloatCompare(y1, y2) == 1)
+ {
+ // y1 is bigger than y2. Swap values.
+ floatbuffer = y1;
+ y1 = y2;
+ y2 = floatbuffer;
+ }
+ if (FloatCompare(z1, z2) == 1)
+ {
+ // z1 is bigger than z2. Swap values.
+ floatbuffer = z1;
+ z1 = z2;
+ z2 = floatbuffer;
+ }
+
+ // Copy coordinates to location vectors.
+ min[0] = x1;
+ min[1] = y1;
+ min[2] = z1;
+ max[0] = x2;
+ max[1] = y2;
+ max[2] = z2;
+
+ // Get volume type.
+ GetCmdArg(7, argbuffer, sizeof(argbuffer));
+ voltype = VolGetTypeFromString(argbuffer);
+
+ // Validate volume type.
+ if (voltype == VolFeature_Invalid)
+ {
+ ReplyToCommand(client, "Cannot add volume. Invalid volume type: %s", argbuffer);
+ return Plugin_Handled;
+ }
+
+ // Get free data index for the specified type.
+ dataindex = VolGetFreeDataIndex(voltype);
+
+ // Validate data index.
+ if (dataindex < 0)
+ {
+ ReplyToCommand(client, "Cannot add volume. Out of free data indexes for type \"%s\"", argbuffer);
+ return Plugin_Handled;
+ }
+
+ // Add volume.
+ volindex = VolAdd(volindex, min, max, voltype, dataindex);
+
+ // Get additional parameters if they exist.
+ if (argc >= 8)
+ {
+ // Join the last parameters in a string.
+ for (new arg = 8; arg <= argc; arg++)
+ {
+ GetCmdArg(arg, argbuffer, sizeof(argbuffer));
+ StrCat(params, sizeof(params), argbuffer);
+
+ // Add space, except on the last parameter.
+ if (arg < argc)
+ {
+ StrCat(params, sizeof(params), " ");
+ }
+ }
+
+ // Set attributes.
+ paramcount = VolSetAttributes(volindex, params);
+ LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Add volume", "Parsing parameter string. Param count = \"%d\". String = \"%s\"", paramcount, params);
+ }
+ else
+ {
+ // No attributes set.
+ paramcount = 0;
+ }
+
+ if (paramcount < 1)
+ {
+ Format(buffer, sizeof(buffer), "No additional attributes set.");
+ }
+ else
+ {
+ Format(buffer, sizeof(buffer), "Additional attributes set: %d", paramcount);
+ }
+
+ ReplyToCommand(client, "Added volume at index %d. %s", volindex, buffer);
+ return Plugin_Handled;
+}
+
+/**
+ * Command callback for removing a volume.
+ */
+public Action:VolRemoveVolumeCommand(client, argc)
+{
+ decl String:arg[16];
+ new volindex;
+
+ if (argc < 1)
+ {
+ // Write syntax info.
+ ReplyToCommand(client, "Removes an existing volume in the map. Usage: zr_vol_remove ");
+ return Plugin_Handled;
+ }
+
+ // Get volume index.
+ GetCmdArg(1, arg, sizeof(arg));
+ volindex = StringToInt(arg);
+
+ // Validate index.
+ if (!VolIsValidIndex(volindex))
+ {
+ ReplyToCommand(client, "Invalid volume index.");
+ return Plugin_Handled;
+ }
+
+ // Check if volume exist.
+ if (!Volumes[volindex][Vol_InUse])
+ {
+ ReplyToCommand(client, "Volume %d doesn't exist.", volindex);
+ return Plugin_Handled;
+ }
+
+ // Remove volume.
+ VolRemove(volindex);
+
+ ReplyToCommand(client, "Successfully disabled and removed volume %d.", volindex);
+ return Plugin_Handled;
+}
+
+/**
+ * Command callback for listing volumes or dumping data.
+ */
+public Action:VolListCommand(client, argc)
+{
+ decl String:buffer[1022]; // Two chars reserved for newline and null terminator.
+ decl String:linebuffer[128];
+ decl String:valuebuffer[32];
+ decl String:arg[16];
+
+ buffer[0] = 0;
+ linebuffer[0] = 0;
+
+ new volindex;
+ new volcount;
+ new volcache[VolumeAttributes];
+
+ if (argc < 1)
+ {
+ // No volume specified. Display syntax and list volumes.
+ StrCat(buffer, sizeof(buffer), "Lists existing volumes in the map, or dumps detail data to the specified volume. Usage: zr_vol_list [volume index]\n\n");
+ StrCat(buffer, sizeof(buffer), "ID: Type: Min loc: Max loc:\n");
+ StrCat(buffer, sizeof(buffer), "--------------------------------------------------------------------------------");
+ ReplyToCommand(client, buffer);
+
+ // Loop through all indexes.
+ for (volindex = 0; volindex < ZR_VOLUMES_MAX; volindex++)
+ {
+ // Check if in use.
+ if (Volumes[volindex][Vol_InUse])
+ {
+ // Cache volume data.
+ volcache = Volumes[volindex];
+
+ // Add to list.
+ VolTypeToString(volcache[Vol_Type], valuebuffer, sizeof(valuebuffer), true);
+ Format(linebuffer, sizeof(linebuffer), "%-4d %-15s %-8.2f %-8.2f %-8.2f %-8.2f %-8.2f %-8.2f",
+ volindex,
+ valuebuffer,
+ volcache[Vol_xMin],
+ volcache[Vol_yMin],
+ volcache[Vol_zMin],
+ volcache[Vol_xMax],
+ volcache[Vol_yMax],
+ volcache[Vol_zMax]);
+
+ ReplyToCommand(client, linebuffer);
+ volcount++;
+ }
+ }
+
+ Format(linebuffer, sizeof(linebuffer), "\nTotal volumes: %d", volcount);
+ ReplyToCommand(client, linebuffer);
+ return Plugin_Handled;
+ }
+ else
+ {
+ // Dump data for the specified volume.
+
+ // Get volume index.
+ GetCmdArg(1, arg, sizeof(arg));
+ volindex = StringToInt(arg);
+
+ // Validate index.
+ if (!VolIsValidIndex(volindex))
+ {
+ ReplyToCommand(client, "The specified volume index is invalid: %d", volindex);
+ return Plugin_Handled;
+ }
+
+ // Check if unused.
+ if (!VolInUse(volindex))
+ {
+ ReplyToCommand(client, "The specified volume doesn't exist: %d.", volindex);
+ return Plugin_Handled;
+ }
+
+ // Cache volume data.
+ volcache = Volumes[volindex];
+
+ // Dump generic volume data.
+ Format(linebuffer, sizeof(linebuffer), "Volume data at index %d:\n", volindex);
+ StrCat(buffer, sizeof(buffer), linebuffer);
+ StrCat(buffer, sizeof(buffer), "--------------------------------------------------------------------------------");
+ ReplyToCommand(client, buffer);
+
+ // Clear buffer.
+ buffer[0] = 0;
+
+ Format(linebuffer, sizeof(linebuffer), "ID: %d\n", volindex);
+ StrCat(buffer, sizeof(buffer), linebuffer);
+
+ Format(linebuffer, sizeof(linebuffer), "Enabled: %d\n", volcache[Vol_Enabled]);
+ StrCat(buffer, sizeof(buffer), linebuffer);
+
+ VolTypeToString(volcache[Vol_Type], valuebuffer, sizeof(valuebuffer));
+ Format(linebuffer, sizeof(linebuffer), "Type: %s\n", valuebuffer);
+ StrCat(buffer, sizeof(buffer), linebuffer);
+
+ Format(linebuffer, sizeof(linebuffer), "Min loc: %-8.2f %-8.2f %-8.2f\n", volcache[Vol_xMin], volcache[Vol_yMin], volcache[Vol_zMin]);
+ StrCat(buffer, sizeof(buffer), linebuffer);
+
+ Format(linebuffer, sizeof(linebuffer), "Max loc: %-8.2f %-8.2f %-8.2f\n", volcache[Vol_xMax], volcache[Vol_yMax], volcache[Vol_zMax]);
+ StrCat(buffer, sizeof(buffer), linebuffer);
+
+ VolEffectToString(volcache[Vol_Effect], valuebuffer, sizeof(valuebuffer));
+ Format(linebuffer, sizeof(linebuffer), "Effect: %s\n", valuebuffer);
+ StrCat(buffer, sizeof(buffer), linebuffer);
+
+ Format(linebuffer, sizeof(linebuffer), "Effect color: %d, %d, %d\n", volcache[Vol_EffectColor][0], volcache[Vol_EffectColor][1], volcache[Vol_EffectColor][2]);
+ StrCat(buffer, sizeof(buffer), linebuffer);
+
+ VolTeamToString(volcache[Vol_TeamFilter], valuebuffer, sizeof(valuebuffer));
+ Format(linebuffer, sizeof(linebuffer), "Team filter: %s\n", valuebuffer);
+ StrCat(buffer, sizeof(buffer), linebuffer);
+
+ Format(linebuffer, sizeof(linebuffer), "Trigger delay: %.2f", volcache[Vol_TriggerDelay]);
+ StrCat(buffer, sizeof(buffer), linebuffer);
+
+ // Print generic attributes.
+ ReplyToCommand(client, buffer);
+
+ // Clear buffer.
+ buffer[0] = 0;
+
+ // Get type spesific attributes.
+ switch (volcache[Vol_Type])
+ {
+ case VolFeature_Anticamp:
+ {
+ VolAnticampDumpData(volcache[Vol_DataIndex], buffer, sizeof(buffer));
+ }
+ case VolFeature_Knockback:
+ {
+
+ }
+ }
+
+ // Print type spesific attributes if any.
+ if (strlen(buffer) > 0)
+ {
+ ReplyToCommand(client, buffer);
+ }
+
+ return Plugin_Handled;
+ }
+}
+
+public Action:VolDumpStatesCommand(client, argc)
+{
+ decl String:target[64];
+ new targetclient;
+
+ if (argc < 1)
+ {
+ ReplyToCommand(client, "Dumps volume states for the specified player. Usage: zr_vol_dumpstates ");
+ return Plugin_Handled;
+ }
+
+ // Get target.
+ GetCmdArg(1, target, sizeof(target));
+ targetclient = FindTarget(client, target);
+
+ // Validate target.
+ if (targetclient <= 0)
+ {
+ // Note: FindTarget automatically print error messages.
+ return Plugin_Handled;
+ }
+
+ // Print header.
+ ReplyToCommand(client, "Volume ID: Player in volume:\n----------------------------------------");
+
+ // Get player states.
+ new bool:statebuffer[ZR_VOLUMES_MAX];
+ VolGetPlayerStates(targetclient, statebuffer, sizeof(statebuffer));
+
+ // Set language.
+ SetGlobalTransTarget(client);
+
+ // Loop through each volume.
+ for (new volumeindex = 0; volumeindex < ZR_VOLUMES_MAX; volumeindex++)
+ {
+ // Check if volume is in use.
+ if (VolInUse(volumeindex))
+ {
+ // Dump state.
+ ReplyToCommand(client, "%-11d %t", volumeindex, statebuffer[volumeindex] ? "Yes" : "No");
+ }
+ }
+
+ return Plugin_Handled;
}
/**
@@ -59,6 +483,7 @@ public Action:VolAddVolumeCommand(client, argc)
*
* Note: Extra volume attributes must be set using VolSetAttributes.
*
+ * @param index Optional. Add volume at the specified index.
* @param locMin Minimum x, y and z values.
* @param locMax Maximum x, y and z values.
* @param volumeType Specifies the volumetric feature type.
@@ -66,39 +491,43 @@ public Action:VolAddVolumeCommand(client, argc)
*
* @return The new volume index, or -1 if failed.
*/
-VolAdd(Float:locMin[3], Float:locMax[3], VolumeFeatureTypes:volumeType, dataIndex)
+VolAdd(volumeIndex = -1, Float:locMin[3], Float:locMax[3], VolumeFeatureTypes:volumeType, dataIndex)
{
- new volumeIndex;
-
- // Get a free volume index.
- volumeIndex = VolGetFreeVolume();
+ if (volumeIndex < 0)
+ {
+ // Get a free volume index.
+ volumeIndex = VolGetFreeVolume();
+ }
// Validate index.
- if (volumeIndex >= 0)
+ if (VolIsValidIndex(volumeIndex))
{
// Mark volume as enabled and in use.
- Volumes[volumeIndex][vol_enabled] = true;
- Volumes[volumeIndex][vol_in_use] = true;
+ Volumes[volumeIndex][Vol_Enabled] = true;
+ Volumes[volumeIndex][Vol_InUse] = true;
// Set location data.
- Volumes[volumeIndex][vol_x_min] = locMin[0];
- Volumes[volumeIndex][vol_y_min] = locMin[1];
- Volumes[volumeIndex][vol_z_min] = locMin[2];
+ Volumes[volumeIndex][Vol_xMin] = locMin[0];
+ Volumes[volumeIndex][Vol_yMin] = locMin[1];
+ Volumes[volumeIndex][Vol_zMin] = locMin[2];
- Volumes[volumeIndex][vol_x_max] = locMax[0];
- Volumes[volumeIndex][vol_y_max] = locMax[1];
- Volumes[volumeIndex][vol_z_max] = locMax[2];
+ Volumes[volumeIndex][Vol_xMax] = locMax[0];
+ Volumes[volumeIndex][Vol_yMax] = locMax[1];
+ Volumes[volumeIndex][Vol_zMax] = locMax[2];
// Set type.
- Volumes[volumeIndex][vol_type] = volumeType;
- Volumes[volumeIndex][vol_data_index] = dataIndex;
+ Volumes[volumeIndex][Vol_Type] = volumeType;
+ Volumes[volumeIndex][Vol_DataIndex] = dataIndex;
+
+ // Update number of volumes.
+ VolumeCount++;
// Return the new index.
return volumeIndex;
}
else
{
- // No free volumes.
+ // No free volumes or invalid index.
return -1;
}
}
@@ -112,14 +541,22 @@ VolAdd(Float:locMin[3], Float:locMax[3], VolumeFeatureTypes:volumeType, dataInde
bool:VolRemove(volumeIndex)
{
// Validate index.
- if (volumeIndex >= 0)
+ if (VolIsValidIndex(volumeIndex))
{
- // Trigger event.
- VolOnVolumeDisabled(volumeIndex);
+ // Trigger event to clean up data and stop timers.
+ VolOnDisabled(volumeIndex);
- // Mark volume as disabled and unused.
- Volumes[volumeIndex][vol_enabled] = false;
- Volumes[volumeIndex][vol_in_use] = false;
+ // Clear feature data.
+ switch (Volumes[volumeIndex][Vol_Type])
+ {
+ case VolFeature_Anticamp:
+ {
+ VolAnticampReset(Volumes[volumeIndex][Vol_DataIndex]);
+ }
+ }
+
+ // Clear volume data.
+ VolClear(volumeIndex);
return true;
}
@@ -130,6 +567,31 @@ bool:VolRemove(volumeIndex)
}
}
+/**
+ * Clears volume data at the specified index.
+ *
+ * @param volumeIndex The volume index.
+ */
+VolClear(volumeIndex)
+{
+ Volumes[volumeIndex][Vol_Enabled] = false;
+ Volumes[volumeIndex][Vol_InUse] = false;
+
+ Volumes[volumeIndex][Vol_xMin] = 0.0;
+ Volumes[volumeIndex][Vol_yMin] = 0.0;
+ Volumes[volumeIndex][Vol_zMin] = 0.0;
+
+ Volumes[volumeIndex][Vol_xMax] = 0.0;
+ Volumes[volumeIndex][Vol_yMax] = 0.0;
+ Volumes[volumeIndex][Vol_zMax] = 0.0;
+
+ Volumes[volumeIndex][Vol_Type] = VolFeature_Invalid;
+ Volumes[volumeIndex][Vol_DataIndex] = -1;
+
+ Volumes[volumeIndex][Vol_TeamFilter] = VolTeam_All;
+ Volumes[volumeIndex][Vol_TriggerDelay] = 0.0;
+}
+
/**
* Sets extra attributes on a volume.
*
@@ -142,6 +604,8 @@ VolSetAttributes(volumeIndex, const String:attributes[])
{
new attribCount;
new successfulCount;
+ new VolumeFeatureTypes:voltype;
+ new dataindex;
decl String:attribName[64];
decl String:attribValue[256];
@@ -160,8 +624,14 @@ VolSetAttributes(volumeIndex, const String:attributes[])
return -1;
}
+ // Get volumetric feature type.
+ voltype = Volumes[volumeIndex][Vol_Type];
+
+ // Get feature data index.
+ dataindex = Volumes[volumeIndex][Vol_DataIndex];
+
// Loop through all attributes.
- for (new attrib = 0; attrib > attribCount; attrib++)
+ for (new attrib = 0; attrib < attribCount; attrib++)
{
// Get attribute name.
GetParameterName(attribName, sizeof(attribName), attributes, attrib);
@@ -169,8 +639,10 @@ VolSetAttributes(volumeIndex, const String:attributes[])
// Get attribute value.
GetParameterValue(attribValue, sizeof(attribValue), attributes, attribName);
- // Check names and set volume attributes.
- if (strcmp(attribName, "team", false))
+ LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Set attribute", "Got parameter: \"%s\" = \"%s\"", attribName, attribValue);
+
+ // Check generic attributes.
+ if (StrEqual(attribName, "teamfilter", false))
{
// Parse team string value.
if (VolSetTeamString(volumeIndex, attribValue))
@@ -178,7 +650,7 @@ VolSetAttributes(volumeIndex, const String:attributes[])
successfulCount++;
}
}
- else if (strcmp(attribName, "delay", false))
+ else if (StrEqual(attribName, "delay", false))
{
// Parse delay string value.
if (VolSetDelayString(volumeIndex, attribValue))
@@ -186,7 +658,7 @@ VolSetAttributes(volumeIndex, const String:attributes[])
successfulCount++;
}
}
- else if (strcmp(attribName, "effect", false))
+ else if (StrEqual(attribName, "effect", false))
{
// Parse effect string value.
if (VolSetEffectString(volumeIndex, attribValue))
@@ -194,7 +666,7 @@ VolSetAttributes(volumeIndex, const String:attributes[])
successfulCount++;
}
}
- else if (strcmp(attribName, "effect_color", false))
+ else if (StrEqual(attribName, "effect_color", false))
{
// Parse effect color string value.
if (VolSetEffectColorString(volumeIndex, attribValue))
@@ -202,7 +674,7 @@ VolSetAttributes(volumeIndex, const String:attributes[])
successfulCount++;
}
}
- else if (strcmp(attribName, "enabled", false))
+ else if (StrEqual(attribName, "enabled", false))
{
// Parse enabled string value.
if (VolSetEnabledString(volumeIndex, attribValue))
@@ -210,16 +682,23 @@ VolSetAttributes(volumeIndex, const String:attributes[])
successfulCount++;
}
}
+
+ // Pass attribute onto the volumetric feature attribute handler.
+ else
+ {
+ switch (voltype)
+ {
+ case VolFeature_Anticamp:
+ {
+ if (VolAnticampSetAttribute(dataindex, attribName, attribValue))
+ {
+ successfulCount++;
+ }
+ }
+ }
+ }
}
// Return number of successfully attributes set.
return successfulCount;
}
-
-/**
- * Creates commands for managing volumes.
- */
-VolOnCommandsCreate()
-{
- RegAdminCmd("zr_vol_add", VolAddVolumeCommand, ADMFLAG_GENERIC, "Adds a new volume. Usage: zr_vol_add [params]");
-}
diff --git a/src/zr/volfeatures/volevents.inc b/src/zr/volfeatures/volevents.inc
index 3531c3d..33f1398 100644
--- a/src/zr/volfeatures/volevents.inc
+++ b/src/zr/volfeatures/volevents.inc
@@ -39,9 +39,11 @@ VolOnPlayerEnter(client, volumeIndex)
// Volumetric features disabled.
return;
}
-
+
+ LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Event", "Player %N entered volume %d.", client, volumeIndex);
+
// Forward event to features.
- // VolAnticampStart(client, volume);
+
}
/**
@@ -59,8 +61,10 @@ VolOnPlayerLeave(client, volumeIndex)
return;
}
+ LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Event", "Player %N left volume %d.", client, volumeIndex);
+
// Forward event to features.
- // VolAnticampStop(client, volume);
+
}
/**
@@ -106,6 +110,9 @@ VolOnRoundStart()
// Start main timer.
VolStartUpdateTimer();
+
+ // Start volumes.
+ VolEnableVolumes();
}
/**
@@ -116,26 +123,42 @@ VolOnRoundEnd()
// Stop main timer.
VolStopUpdateTimer();
- // Forward stop event to features.
- // VolAnticampStop();
+ // Stop volumes.
+ VolDisableVolumes();
}
/**
* Called when a volume is disabled.
* @param volumeIndex The volume index.
*/
-VolOnVolumeDisabled(volumeIndex)
+VolOnDisabled(volumeIndex)
{
- // Forward stop event to features.
+ new VolumeFeatureTypes:voltype = Volumes[volumeIndex][Vol_Type];
+ // Forward stop event to features.
+ switch (voltype)
+ {
+ case VolFeature_Anticamp:
+ {
+ VolAnticampDisable(volumeIndex);
+ }
+ }
}
/**
* Called when a volume is enabled.
* @param volumeIndex The volume index.
*/
-VolOnVolumeEnabled(volumeIndex)
+VolOnEnabled(volumeIndex)
{
- // Forward start event to features.
+ new VolumeFeatureTypes:voltype = Volumes[volumeIndex][Vol_Type];
+ // Forward stop event to features.
+ switch (voltype)
+ {
+ case VolFeature_Anticamp:
+ {
+ VolAnticampEnable(volumeIndex);
+ }
+ }
}
diff --git a/src/zr/volfeatures/volfeatures.inc b/src/zr/volfeatures/volfeatures.inc
index f0192d7..1f67dfd 100644
--- a/src/zr/volfeatures/volfeatures.inc
+++ b/src/zr/volfeatures/volfeatures.inc
@@ -36,30 +36,30 @@
enum VolumeAttributes
{
/* General */
- bool:vol_enabled, /** Volume state. */
- bool:vol_in_use, /** Marks if the volume is used. */
+ bool:Vol_Enabled, /** Volume state. */
+ bool:Vol_InUse, /** Marks if the volume is used. */
/* Location */
- Float:vol_x_min, /** Minimum x position. */
- Float:vol_x_max, /** Maximum x position. */
+ Float:Vol_xMin, /** Minimum x position. */
+ Float:Vol_xMax, /** Maximum x position. */
- Float:vol_y_min, /** Minimum y position. */
- Float:vol_y_max, /** Maximum y position. */
+ Float:Vol_yMin, /** Minimum y position. */
+ Float:Vol_yMax, /** Maximum y position. */
- Float:vol_z_min, /** Minimum z position. */
- Float:vol_z_max, /** Maximum z position. */
+ Float:Vol_zMin, /** Minimum z position. */
+ Float:Vol_zMax, /** Maximum z position. */
/* Style */
- VolumeEffects:vol_effect, /** Visual effect to apply on the volume. */
- vol_effect_color[3], /** Render color of the effect. RGB colors. */
+ VolumeEffects:Vol_Effect, /** Visual effect to apply on the volume. */
+ Vol_EffectColor[3], /** Render color of the effect. RGB colors. */
/* Data */
- VolumeFeatureTypes:vol_type, /** The volumetric feature type. */
- vol_data_index, /** Index in remote feature array. */
+ VolumeFeatureTypes:Vol_Type, /** The volumetric feature type. */
+ Vol_DataIndex, /** Index in remote feature array. */
/* Behaviour */
- VolumeTeamFilters:vol_team_filter, /** Team filtering. Trigger by certain teams, or all. */
- Float:vol_trigger_delay /** Trigger delay. How many seconds players have to stay to trigger volume events. */
+ VolumeTeamFilters:Vol_TeamFilter, /** Team filtering. Trigger by certain teams, or all. */
+ Float:Vol_TriggerDelay /** Trigger delay. How many seconds players have to stay to trigger volume events. */
}
/**
@@ -67,7 +67,8 @@ enum VolumeAttributes
*/
enum VolumeFeatureTypes
{
- VolFeature_Anticamp = 0,
+ VolFeature_Invalid = 0,
+ VolFeature_Anticamp,
VolFeature_Knockback
}
@@ -106,6 +107,11 @@ new VolumeCount;
*/
new Float:VolPlayerLoc[MAXPLAYERS + 1][3];
+/**
+ * Cache that specifies if a player is in a volume or not.
+ */
+new bool:VolPlayerInVolume[MAXPLAYERS + 1][ZR_VOLUMES_MAX];
+
/**
* Specifies whether the volumetric features module is enabled or not. Synced
* with zr_vol CVAR.
@@ -121,7 +127,7 @@ new Float:VolPlayerCountDown[MAXPLAYERS + 1][ZR_VOLUMES_MAX];
* The handle for a timer that updates player locations. This is the main timer
* and any feature events can't be updated faster than this interval.
*
- * Note: Some features may have its own timer.
+ * Note: Some features may have its own timer for actions on players.
*/
new Handle:hVolUpdateTimer;
@@ -138,6 +144,8 @@ new Float:VolTriggerInterval;
#include "zr/volfeatures/volevents"
#include "zr/volfeatures/volgenericattributes"
#include "zr/volfeatures/volcommands"
+
+// Sub features.
#include "zr/volfeatures/volanticamp"
@@ -148,6 +156,9 @@ VolLoad()
{
// Cache CVAR value.
VolEnabled = GetConVarBool(g_hCvarsList[CVAR_VOL]);
+
+ // Initialize sub features.
+ VolAnticampInit();
}
/**
@@ -157,8 +168,51 @@ VolDisable()
{
VolEnabled = false;
VolStopUpdateTimer();
+ VolDisableVolumes();
- // TODO: Send disable/stop event to volumes with their own timers.
+ LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Disabled", "Volfeatures disabled.");
+}
+
+/**
+ * Function alias for starting volumetric features.
+ */
+VolEnable()
+{
+ VolEnabled = true;
+ VolStartUpdateTimer();
+ VolEnableVolumes();
+
+ LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Enabled", "Volfeatures enabled.");
+}
+
+/**
+ * Disables all enabled volumes.
+ */
+VolDisableVolumes()
+{
+ // Trigger disable event on all enabled volumes in use.
+ for (new volindex = 0; volindex < ZR_VOLUMES_MAX; volindex++)
+ {
+ if (Volumes[volindex][Vol_InUse] && Volumes[volindex][Vol_Enabled])
+ {
+ VolOnDisabled(volindex);
+ }
+ }
+}
+
+/**
+ * Enables all disabled volumes.
+ */
+VolEnableVolumes()
+{
+ // Trigger enable event on all volumes in use.
+ for (new volindex = 0; volindex < ZR_VOLUMES_MAX; volindex++)
+ {
+ if (Volumes[volindex][Vol_InUse] && !Volumes[volindex][Vol_Enabled])
+ {
+ VolOnEnabled(volindex);
+ }
+ }
}
/**
@@ -195,7 +249,8 @@ bool:VolStartUpdateTimer()
}
else
{
- // Volumetric features disabled.
+ // Volumetric features disabled.
+ LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Volfeatures, "Config Validation", "Warning: Console variable \"zr_vol_update_interval\" is set to zero or negative. Must be positive.");
return false;
}
}
@@ -236,7 +291,7 @@ bool:VolStartTriggerTimer()
if (VolTriggerInterval > 0.0)
{
// Start the timer.
- hVolTriggerTimer = CreateTimer(VolTriggerInterval, Event_VolUpdateTimer, _, TIMER_REPEAT);
+ hVolTriggerTimer = CreateTimer(VolTriggerInterval, Event_VolTriggerTimer, _, TIMER_REPEAT);
// Trigger timer started.
return true;
@@ -244,6 +299,7 @@ bool:VolStartTriggerTimer()
else
{
// Trigger timer not running. Either disabled or invalid interval.
+ LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Volfeatures, "Config Validation", "Warning: Console variable \"zr_vol_trigger_interval\" is set to zero or negative. Must be positive.");
return false;
}
}
@@ -302,7 +358,7 @@ VolResetCountDown(client = -1)
*/
VolUpdatePlayerLocation(client = -1)
{
- if (client <= 0)
+ if (client > 0)
{
// Assume the client is valid and save location in array.
GetClientAbsOrigin(client, VolPlayerLoc[client]);
@@ -343,10 +399,11 @@ VolUpdatePlayerChanges()
// Check if client is in game and alive.
if (!IsClientConnected(client) || !IsClientInGame(client) || !IsPlayerAlive(client))
{
- return;
+ // Skip client.
+ continue;
}
- // Get the current volume states.
+ // Get the current volume states based on player location cache.
VolGetPlayerStates(client, volumeStates, sizeof(volumeStates));
// Update player location cache.
@@ -356,23 +413,37 @@ VolUpdatePlayerChanges()
VolGetPlayerStates(client, volumeNewStates, sizeof(volumeNewStates));
// Loop through each volume and compare states.
- for (new volumeIndex = 0; volumeIndex < VolumeCount; volumeIndex++)
+ for (new volumeIndex = 0; volumeIndex < ZR_VOLUMES_MAX; volumeIndex++)
{
+ // Check if the volume is disabled and unused.
+ if (!VolInUse(volumeIndex) || !VolIsEnabled(volumeIndex))
+ {
+ // Skip volume.
+ continue;
+ }
+
+ // Check team filtering on the volume.
+ if (!VolTeamFilterMatch(client, volumeIndex))
+ {
+ // Team filter mismatch.
+ continue;
+ }
+
newState = volumeNewStates[volumeIndex];
oldState = volumeStates[volumeIndex];
- // Compare new states with old states.
+ // Check for no change.
if (newState == oldState)
{
// No change. Skip to next volume.
- break;
+ continue;
}
// Check if client entered the volume.
- else if (!newState && oldState)
+ if (newState && !oldState)
{
// Get trigger delay value.
- trigger_delay = Volumes[volumeIndex][vol_trigger_delay];
+ trigger_delay = Volumes[volumeIndex][Vol_TriggerDelay];
// Check if the volume has a trigger delay.
if (trigger_delay > 0.0)
@@ -382,17 +453,23 @@ VolUpdatePlayerChanges()
}
else
{
+ // Update cache.
+ VolPlayerInVolume[client][volumeIndex] = true;
+
// No trigger delay, trigger event instantly.
VolOnPlayerEnter(client, volumeIndex);
}
}
// Check if client left the volume.
- else if (newState && !oldState)
+ else if (!newState && oldState)
{
// Make sure count down value is reset.
VolPlayerCountDown[client][volumeIndex] = -1.0;
+ // Update cache.
+ VolPlayerInVolume[client][volumeIndex] = false;
+
// Trigger event.
VolOnPlayerLeave(client, volumeIndex);
}
@@ -401,20 +478,20 @@ VolUpdatePlayerChanges()
}
/**
- * Returns wether a position is within a certain location.
+ * Returns wether a point is within a certain location.
*
- * @param loc The position to check.
+ * @param point The point to check.
* @param min Minimum x, y and z values of the location.
* @param max Maximum x, y and z values of the location.
* @return True if the position is within min and max values. False
* otherwise.
*/
-bool:IsPositionInLocation(Float:pos[3], Float:min[3], Float:max[3])
+bool:IsPointInLocation(Float:point[3], Float:min[3], Float:max[3])
{
- // Cache location to avoid re-indexing arrays.
- new Float:posX = pos[0];
- new Float:posY = pos[1];
- new Float:posZ = pos[2];
+ // Cache to avoid re-indexing arrays.
+ new Float:posX = point[0];
+ new Float:posY = point[1];
+ new Float:posZ = point[2];
// Check if within x boundaries.
if ((posX >= min[0]) && (posX <= max[0]))
@@ -425,13 +502,13 @@ bool:IsPositionInLocation(Float:pos[3], Float:min[3], Float:max[3])
// Check if within x boundaries.
if ((posZ >= min[2]) && (posZ <= max[2]))
{
- // The player is within the location boundaries.
+ // The point is within the location boundaries.
return true;
}
}
}
- // The player is outside the location boundaries.
+ // The point is outside the location boundaries.
return false;
}
@@ -443,9 +520,22 @@ bool:IsPositionInLocation(Float:pos[3], Float:min[3], Float:max[3])
* @param volumeIndex The volume index.
* @return True if in use, false otherwise.
*/
-bool:VolIsInUse(volumeIndex)
+bool:VolInUse(volumeIndex)
{
- return Volumes[volumeIndex][vol_in_use];
+ return Volumes[volumeIndex][Vol_InUse];
+}
+
+/**
+ * Returns wether a volume is enabled or not.
+ *
+ * Note: Does not validate index.
+ *
+ * @param volumeIndex The volume index.
+ * @return True if enabled, false otherwise.
+ */
+bool:VolIsEnabled(volumeIndex)
+{
+ return Volumes[volumeIndex][Vol_Enabled];
}
/**
@@ -478,7 +568,7 @@ VolGetFreeVolume()
for (new volumeIndex = 0; volumeIndex < ZR_VOLUMES_MAX; volumeIndex++)
{
// Check if it's free.
- if (!VolIsInUse(volumeIndex))
+ if (!VolInUse(volumeIndex))
{
return volumeIndex;
}
@@ -488,6 +578,69 @@ VolGetFreeVolume()
return -1;
}
+/**
+ * Gets a free index in the data array for the specified volume type.
+ *
+ * @param volumeType Volumetric feature type.
+ * @return Data index, or -1 on error.
+ */
+VolGetFreeDataIndex(VolumeFeatureTypes:volumeType)
+{
+ switch (volumeType)
+ {
+ case VolFeature_Anticamp:
+ {
+ return VolAnticampGetFreeIndex();
+ }
+ case VolFeature_Knockback:
+ {
+ // TOTO: Finish incomplete feature.
+ return -1;
+ }
+ }
+
+ // No match.
+ return -1;
+}
+
+/**
+ * Checks if the specified client match the team filtering for the specified
+ * volume.
+ *
+ * @param client The client index.
+ * @param volumeIndex The volume to check team filtering on.
+ * @return True if client pass the team filtering, false otherwise.
+ */
+bool:VolTeamFilterMatch(client, volumeIndex)
+{
+ new VolumeTeamFilters:filter;
+
+ // Chache filter value.
+ filter = Volumes[volumeIndex][Vol_TeamFilter];
+
+ switch (filter)
+ {
+ case VolTeam_All:
+ {
+ // All maches everyone.
+ return true;
+ }
+ case VolTeam_Humans:
+ {
+ // Check if client is a human.
+ return InfectIsClientHuman(client);
+ }
+ case VolTeam_Zombies:
+ {
+ // Check if client is a zombie.
+ return InfectIsClientInfected(client);
+ }
+ }
+
+ // Invalid filter value.
+ return false;
+}
+
/**
* Gets wether a client is within volumes or not. Result is stored in a boolean
* array.
@@ -506,25 +659,25 @@ VolGetPlayerStates(client, bool:buffer[], maxlen)
new Float:volMaxBuffer[3];
// Loop through all available volumes.
- for (new volumeIndex = 0; volumeIndex < VolumeCount && volumeIndex < maxlen; volumeIndex++)
+ for (new volumeIndex = 0; volumeIndex < ZR_VOLUMES_MAX && volumeIndex < maxlen; volumeIndex++)
{
- if (VolIsInUse(volumeIndex))
+ if (VolInUse(volumeIndex))
{
// Chache volume to avoid re-indexing.
volumeBuffer = Volumes[volumeIndex];
// Get min positions.
- volMinBuffer[0] = volumeBuffer[vol_x_min];
- volMinBuffer[1] = volumeBuffer[vol_y_min];
- volMinBuffer[2] = volumeBuffer[vol_z_min];
+ volMinBuffer[0] = volumeBuffer[Vol_xMin];
+ volMinBuffer[1] = volumeBuffer[Vol_yMin];
+ volMinBuffer[2] = volumeBuffer[Vol_zMin];
// Get max positions.
- volMaxBuffer[0] = volumeBuffer[vol_x_min];
- volMaxBuffer[1] = volumeBuffer[vol_y_min];
- volMaxBuffer[2] = volumeBuffer[vol_z_min];
+ volMaxBuffer[0] = volumeBuffer[Vol_xMax];
+ volMaxBuffer[1] = volumeBuffer[Vol_yMax];
+ volMaxBuffer[2] = volumeBuffer[Vol_zMax];
// Check the cached player location.
- if (IsPositionInLocation(VolPlayerLoc[client], volMinBuffer, volMaxBuffer))
+ if (IsPointInLocation(VolPlayerLoc[client], volMinBuffer, volMaxBuffer))
{
// Mark player as in volume.
buffer[volumeIndex] = true;
@@ -541,6 +694,65 @@ VolGetPlayerStates(client, bool:buffer[], maxlen)
return volCount;
}
+/**
+ * Converts a string into a volumetric feature type.
+ *
+ * @param volType String to convert. Name of type.
+ * @return Volumetric feature type or VolFeature_Invalid on error.
+ */
+VolumeFeatureTypes:VolGetTypeFromString(const String:volType[])
+{
+ // Check if empty.
+ if (strlen(volType) == 0)
+ {
+ return VolFeature_Invalid;
+ }
+
+ // Match types.
+ if (StrEqual(volType, "anticamp", false))
+ {
+ return VolFeature_Anticamp;
+ }
+ else if (StrEqual(volType, "knockback", false))
+ {
+ return VolFeature_Knockback;
+ }
+
+ // No match.
+ return VolFeature_Invalid;
+}
+
+/**
+ * Converts a volume type to a string.
+ *
+ * @param volType Volume type to convert.
+ * @param buffer Destination string buffer.
+ * @param maxlen Size of destination buffer.
+ * @param shortName Optional. Write short name or human readable name.
+ * Default is human readable (false).
+ * @return Number of cells written.
+ */
+VolTypeToString(VolumeFeatureTypes:volType, String:buffer[], maxlen, bool:shortName = false)
+{
+ switch (volType)
+ {
+ case VolFeature_Invalid:
+ {
+ return shortName ? strcopy(buffer, maxlen, "") : strcopy(buffer, maxlen, "(none)");
+ }
+ case VolFeature_Anticamp:
+ {
+ return shortName ? strcopy(buffer, maxlen, "anticamp") : strcopy(buffer, maxlen, "Anti camp");
+ }
+ case VolFeature_Knockback:
+ {
+ return shortName ? strcopy(buffer, maxlen, "knockback") : strcopy(buffer, maxlen, "Knock back modifier");
+ }
+ }
+
+ return 0;
+}
+
/**
* Callback for update timer. This is the main timer in volumetric features.
*/
@@ -562,6 +774,13 @@ public Action:Event_VolTriggerTimer(Handle:timer)
// Loop through all volumes.
for (new volumeIndex = 0; volumeIndex < ZR_VOLUMES_MAX; volumeIndex++)
{
+ // Check if volume is in use and enabled.
+ if (!VolInUse(volumeIndex) || !VolIsEnabled(volumeIndex))
+ {
+ // Not in use or enabled, skip volume.
+ continue;
+ }
+
// Get count down value.
countDown = VolPlayerCountDown[client][volumeIndex];
@@ -571,15 +790,21 @@ public Action:Event_VolTriggerTimer(Handle:timer)
// Substract by trigger interval.
countDown -= VolTriggerInterval;
- // Check if zero or below.
+ // Check if time is up.
if (countDown <= 0.0)
{
+ // Update cache.
+ VolPlayerInVolume[client][volumeIndex] = true;
+
// Trigger volume enter event.
VolOnPlayerEnter(client, volumeIndex);
// Reset count down value.
VolPlayerCountDown[client][volumeIndex] = -1.0;
}
+
+ // Update count down value and continue.
+ VolPlayerCountDown[client][volumeIndex] = countDown;
}
}
}
@@ -595,10 +820,7 @@ public VolEnabledChanged(Handle:cvar, const String:oldvalue[], const String:newv
if (isEnabled)
{
// Volumetric features is enabled.
- VolEnabled = true;
-
- // Start timers.
- VolStartUpdateTimer();
+ VolEnable();
}
else
{
diff --git a/src/zr/volfeatures/volgenericattributes.inc b/src/zr/volfeatures/volgenericattributes.inc
index 461665a..3632b1d 100644
--- a/src/zr/volfeatures/volgenericattributes.inc
+++ b/src/zr/volfeatures/volgenericattributes.inc
@@ -44,21 +44,21 @@ stock bool:VolSetTeamString(volumeIndex, const String:team[])
}
// Convert value.
- if (strcmp(team, "all", false))
+ if (StrEqual(team, "all", false))
{
teamfilter = VolTeam_All;
}
- else if (strcmp(team, "humans", false))
+ else if (StrEqual(team, "humans", false))
{
teamfilter = VolTeam_Humans;
}
- else if (strcmp(team, "zombies", false))
+ else if (StrEqual(team, "zombies", false))
{
teamfilter = VolTeam_Zombies;
}
// Apply value.
- Volumes[volumeIndex][vol_team_filter] = teamfilter;
+ Volumes[volumeIndex][Vol_TeamFilter] = teamfilter;
return true;
}
@@ -71,7 +71,38 @@ stock bool:VolSetTeamString(volumeIndex, const String:team[])
*/
stock VolSetTeam(volumeIndex, VolumeTeamFilters:team[])
{
- Volumes[volumeIndex][vol_team_filter] = team;
+ Volumes[volumeIndex][Vol_TeamFilter] = team;
+}
+
+/**
+ * Converts a team name type to a string.
+ *
+ * @param team Team type to convert.
+ * @param buffer Destination string buffer.
+ * @param maxlen Size of destination buffer.
+ * @param shortName Optional. Write short name or human readable name.
+ * Default is human readable (false).
+ * @return Number of cells written.
+ */
+stock VolTeamToString(VolumeTeamFilters:team, String:buffer[], maxlen, bool:shortName = false)
+{
+ switch (team)
+ {
+ case VolTeam_All:
+ {
+ return shortName ? strcopy(buffer, maxlen, "all") : strcopy(buffer, maxlen, "All");
+ }
+ case VolTeam_Humans:
+ {
+ return shortName ? strcopy(buffer, maxlen, "humans") : strcopy(buffer, maxlen, "Humans");
+ }
+ case VolTeam_Zombies:
+ {
+ return shortName ? strcopy(buffer, maxlen, "zombies") : strcopy(buffer, maxlen, "Zombies");
+ }
+ }
+
+ return 0;
}
/**
@@ -96,7 +127,7 @@ stock bool:VolSetDelayString(volumeIndex, const String:delay[])
triggerdelay = StringToFloat(delay);
// Apply value.
- Volumes[volumeIndex][vol_trigger_delay] = triggerdelay;
+ Volumes[volumeIndex][Vol_TriggerDelay] = triggerdelay;
return true;
}
@@ -108,7 +139,7 @@ stock bool:VolSetDelayString(volumeIndex, const String:delay[])
*/
stock VolSetDelay(volumeIndex, Float:delay)
{
- Volumes[volumeIndex][vol_trigger_delay] = delay;
+ Volumes[volumeIndex][Vol_TriggerDelay] = delay;
}
/**
@@ -128,19 +159,19 @@ stock bool:VolSetEffectString(volumeIndex, const String:effect[])
}
// Check effect string values and apply them to the volume.
- if (strcmp(effect, "none", false))
+ if (StrEqual(effect, "none", false))
{
- Volumes[volumeIndex][vol_effect] = VolEffect_None;
+ Volumes[volumeIndex][Vol_Effect] = VolEffect_None;
return true;
}
- else if (strcmp(effect, "wireframe", false))
+ else if (StrEqual(effect, "wireframe", false))
{
- Volumes[volumeIndex][vol_effect] = VolEffect_Wireframe;
+ Volumes[volumeIndex][Vol_Effect] = VolEffect_Wireframe;
return true;
}
- else if (strcmp(effect, "smoke", false))
+ else if (StrEqual(effect, "smoke", false))
{
- Volumes[volumeIndex][vol_effect] = VolEffect_Smoke;
+ Volumes[volumeIndex][Vol_Effect] = VolEffect_Smoke;
return true;
}
@@ -156,7 +187,38 @@ stock bool:VolSetEffectString(volumeIndex, const String:effect[])
*/
stock VolSetEffect(volumeIndex, VolumeEffects:effect)
{
- Volumes[volumeIndex][vol_effect] = effect;
+ Volumes[volumeIndex][Vol_Effect] = effect;
+}
+
+/**
+ * Converts a volume effect type to a string.
+ *
+ * @param effect Effect type to convert.
+ * @param buffer Destination string buffer.
+ * @param maxlen Size of destination buffer.
+ * @param shortName Optional. Write short name or human readable name.
+ * Default is human readable (false).
+ * @return Number of cells written.
+ */
+stock VolEffectToString(VolumeEffects:effect, String:buffer[], maxlen, bool:shortName = false)
+{
+ switch (effect)
+ {
+ case VolEffect_None:
+ {
+ return shortName ? strcopy(buffer, maxlen, "none") : strcopy(buffer, maxlen, "No effect");
+ }
+ case VolEffect_Wireframe:
+ {
+ return shortName ? strcopy(buffer, maxlen, "wireframe") : strcopy(buffer, maxlen, "Wire frame");
+ }
+ case VolEffect_Smoke:
+ {
+ return shortName ? strcopy(buffer, maxlen, "smoke") : strcopy(buffer, maxlen, "Smoke");
+ }
+ }
+
+ return 0;
}
/**
@@ -187,9 +249,9 @@ stock bool:VolSetEffectColorString(volumeIndex, const String:effect_color[])
blue = StringToInt(colors[2]);
// Apply values.
- Volumes[volumeIndex][vol_effect_color][0] = red;
- Volumes[volumeIndex][vol_effect_color][1] = green;
- Volumes[volumeIndex][vol_effect_color][2] = blue;
+ Volumes[volumeIndex][Vol_EffectColor][0] = red;
+ Volumes[volumeIndex][Vol_EffectColor][1] = green;
+ Volumes[volumeIndex][Vol_EffectColor][2] = blue;
return true;
}
@@ -227,31 +289,31 @@ stock bool:VolSetEnabledString(volumeIndex, const String:enabled[])
new bool:val = bool:StringToInt(enabled);
// Check yes or no values first.
- if (strcmp(enabled, "yes", false) == 0)
+ if (StrEqual(enabled, "yes", false))
{
val = true;
}
- else if (strcmp(enabled, "no", false) == 0)
+ else if (StrEqual(enabled, "no", false))
{
val = false;
}
// Check if the new value is different from the current.
- if (Volumes[volumeIndex][vol_enabled] != val)
+ if (Volumes[volumeIndex][Vol_Enabled] != val)
{
// Forward event.
if (val)
{
- VolOnVolumeEnabled(volumeIndex);
+ VolOnEnabled(volumeIndex);
}
else
{
- VolOnVolumeDisabled(volumeIndex);
+ VolOnDisabled(volumeIndex);
}
}
// Apply converted value.
- Volumes[volumeIndex][vol_enabled] = val;
+ Volumes[volumeIndex][Vol_Enabled] = val;
return true;
}
@@ -264,7 +326,7 @@ stock bool:VolSetEnabledString(volumeIndex, const String:enabled[])
stock VolSetEnabled(volumeIndex, bool:enabled)
{
// Check if the new value is different from the current.
- if (Volumes[volumeIndex][vol_enabled] != enabled)
+ if (Volumes[volumeIndex][Vol_Enabled] != enabled)
{
// Forward event.
if (enabled)
@@ -277,5 +339,5 @@ stock VolSetEnabled(volumeIndex, bool:enabled)
}
}
- Volumes[volumeIndex][vol_enabled] = enabled;
+ Volumes[volumeIndex][Vol_Enabled] = enabled;
}