Merged heads.
This commit is contained in:
commit
162bd1e107
BIN
bin/spcomp
BIN
bin/spcomp
Binary file not shown.
@ -1092,16 +1092,18 @@
|
|||||||
"ru" "Здоровье: {1}"
|
"ru" "Здоровье: {1}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===========================
|
||||||
|
// Volumetric features (module)
|
||||||
|
// ===========================
|
||||||
|
|
||||||
|
"Vol Anticamp Message"
|
||||||
"Unfair camping"
|
|
||||||
{
|
{
|
||||||
"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}"
|
"#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})."
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -80,7 +80,7 @@ warning is logged in the SourceMod error logs.
|
|||||||
|
|
||||||
The validation prevents unexpected or invalid behaviour in the plugin. Dealing
|
The validation prevents unexpected or invalid behaviour in the plugin. Dealing
|
||||||
with errors or warnings in error logs helps a lot troubleshooting eventual
|
with errors or warnings in error logs helps a lot troubleshooting eventual
|
||||||
eventual issues in the plugin caused by incorrect configurations.
|
issues in the plugin caused by incorrect configurations.
|
||||||
|
|
||||||
It's also possible to specify the path of configuration files. This can be used
|
It's also possible to specify the path of configuration files. This can be used
|
||||||
in combination with map configs, so it's possible to have different
|
in combination with map configs, so it's possible to have different
|
||||||
|
@ -63,6 +63,10 @@ public const MaxClients; /**< Maximum number of players the server supports (dyn
|
|||||||
* If you return false (or return nothing), the client will be rejected. If the client is
|
* If you return false (or return nothing), the client will be rejected. If the client is
|
||||||
* rejected by this forward or any other, OnClientDisconnect will not be called.
|
* rejected by this forward or any other, OnClientDisconnect will not be called.
|
||||||
*
|
*
|
||||||
|
* Note: Do not write to rejectmsg if you plan on returning true. If multiple plugins write
|
||||||
|
* to the string buffer, it is not defined which plugin's string will be shown to the client,
|
||||||
|
* but it is guaranteed one of them will.
|
||||||
|
*
|
||||||
* @param client Client index.
|
* @param client Client index.
|
||||||
* @param rejectmsg Buffer to store the rejection message when the connection is refused.
|
* @param rejectmsg Buffer to store the rejection message when the connection is refused.
|
||||||
* @param maxlen Maximum number of characters for rejection buffer.
|
* @param maxlen Maximum number of characters for rejection buffer.
|
||||||
|
@ -817,9 +817,11 @@ native bool:FindNextConCommand(Handle:search, String:buffer[], max_size, &bool:i
|
|||||||
native bool:SendConVarValue(client, Handle:convar, const String:value[]);
|
native bool:SendConVarValue(client, Handle:convar, const String:value[]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Appends a string to Valve's sv_tags convar and makes sure it remains after mapchanges.
|
* Adds an informational string to the server's public "tags".
|
||||||
|
* This string should be a short, unique identifier.
|
||||||
*
|
*
|
||||||
* Note: Tags are automatically removed on plugin unload
|
* Note: Tags are automatically removed when a plugin unloads.
|
||||||
|
* Note: Currently, this function does nothing because of bugs in the Valve master.
|
||||||
*
|
*
|
||||||
* @param tag Tag string to append.
|
* @param tag Tag string to append.
|
||||||
* @noreturn
|
* @noreturn
|
||||||
@ -827,9 +829,7 @@ native bool:SendConVarValue(client, Handle:convar, const String:value[]);
|
|||||||
native AddServerTag(const String:tag[]);
|
native AddServerTag(const String:tag[]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a string from valve's sv_tags convar.
|
* Removes a tag previously added by the calling plugin.
|
||||||
*
|
|
||||||
* Note: You can only remove tags created by you.
|
|
||||||
*
|
*
|
||||||
* @param tag Tag string to remove.
|
* @param tag Tag string to remove.
|
||||||
* @noreturn
|
* @noreturn
|
||||||
|
@ -94,6 +94,8 @@ enum MenuAction
|
|||||||
#define VOTEINFO_ITEM_INDEX 0 /**< Item index */
|
#define VOTEINFO_ITEM_INDEX 0 /**< Item index */
|
||||||
#define VOTEINFO_ITEM_VOTES 1 /**< Number of votes for the item */
|
#define VOTEINFO_ITEM_VOTES 1 /**< Number of votes for the item */
|
||||||
|
|
||||||
|
#define VOTEFLAG_NO_REVOTES (1<<0) /**< Players cannot change their votes */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reasons a menu can be cancelled (MenuAction_Cancel).
|
* Reasons a menu can be cancelled (MenuAction_Cancel).
|
||||||
*/
|
*/
|
||||||
@ -469,22 +471,24 @@ native CancelVote();
|
|||||||
* @param clients Array of clients to broadcast to.
|
* @param clients Array of clients to broadcast to.
|
||||||
* @param numClients Number of clients in the array.
|
* @param numClients Number of clients in the array.
|
||||||
* @param time Maximum time to leave menu on the screen.
|
* @param time Maximum time to leave menu on the screen.
|
||||||
|
* @param flags Optional voting flags.
|
||||||
* @return True on success, false if this menu already has a vote session
|
* @return True on success, false if this menu already has a vote session
|
||||||
* in progress.
|
* in progress.
|
||||||
* @error Invalid Handle, or a vote is already in progress.
|
* @error Invalid Handle, or a vote is already in progress.
|
||||||
*/
|
*/
|
||||||
native bool:VoteMenu(Handle:menu, clients[], numClients, time);
|
native bool:VoteMenu(Handle:menu, clients[], numClients, time, flags=0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a vote menu to all clients. See VoteMenu() for more information.
|
* Sends a vote menu to all clients. See VoteMenu() for more information.
|
||||||
*
|
*
|
||||||
* @param menu Menu Handle.
|
* @param menu Menu Handle.
|
||||||
* @param time Maximum time to leave menu on the screen.
|
* @param time Maximum time to leave menu on the screen.
|
||||||
|
* @param flags Optional voting flags.
|
||||||
* @return True on success, false if this menu already has a vote session
|
* @return True on success, false if this menu already has a vote session
|
||||||
* in progress.
|
* in progress.
|
||||||
* @error Invalid Handle.
|
* @error Invalid Handle.
|
||||||
*/
|
*/
|
||||||
stock VoteMenuToAll(Handle:menu, time)
|
stock VoteMenuToAll(Handle:menu, time, flags=0)
|
||||||
{
|
{
|
||||||
new num = GetMaxClients();
|
new num = GetMaxClients();
|
||||||
new total;
|
new total;
|
||||||
@ -499,7 +503,7 @@ stock VoteMenuToAll(Handle:menu, time)
|
|||||||
players[total++] = i;
|
players[total++] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
return VoteMenu(menu, players, total, time);
|
return VoteMenu(menu, players, total, time, flags);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Callback for when a vote has ended and results are available.
|
* Callback for when a vote has ended and results are available.
|
||||||
@ -543,7 +547,7 @@ native CheckVoteDelay();
|
|||||||
/**
|
/**
|
||||||
* Returns whether a client is in the pool of clients allowed
|
* Returns whether a client is in the pool of clients allowed
|
||||||
* to participate in the current vote. This is determined by
|
* to participate in the current vote. This is determined by
|
||||||
* the client list passed to StartVote().
|
* the client list passed to VoteMenu().
|
||||||
*
|
*
|
||||||
* @param client Client index.
|
* @param client Client index.
|
||||||
* @return True if client is allowed to vote, false otherwise.
|
* @return True if client is allowed to vote, false otherwise.
|
||||||
@ -555,12 +559,13 @@ native bool:IsClientInVotePool(client);
|
|||||||
* Redraws the current vote menu to a client in the voting pool.
|
* Redraws the current vote menu to a client in the voting pool.
|
||||||
*
|
*
|
||||||
* @param client Client index.
|
* @param client Client index.
|
||||||
|
* @param revotes True to allow revotes, false otherwise.
|
||||||
* @return True on success, false if the client is in the vote pool
|
* @return True on success, false if the client is in the vote pool
|
||||||
* but cannot vote again.
|
* but cannot vote again.
|
||||||
* @error No vote in progress, client is not in the voting pool,
|
* @error No vote in progress, client is not in the voting pool,
|
||||||
* or client index is invalid.
|
* or client index is invalid.
|
||||||
*/
|
*/
|
||||||
native bool:RedrawClientVoteMenu(client);
|
native bool:RedrawClientVoteMenu(client, bool:revotes=true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a style's global Handle.
|
* Returns a style's global Handle.
|
||||||
@ -807,3 +812,4 @@ stock bool:IsNewVoteAllowed()
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +57,14 @@ enum TFTeam
|
|||||||
TFTeam_Blue = 3
|
TFTeam_Blue = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a client on fire for 10 seconds.
|
||||||
|
*
|
||||||
|
* @param client Player's index.
|
||||||
|
* @noreturn
|
||||||
|
* @error Invalid client index, client not in game, or no mod support.
|
||||||
|
*/
|
||||||
|
native TF2_IgnitePlayer(client, target);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Respawns a client
|
* Respawns a client
|
||||||
|
@ -37,6 +37,6 @@
|
|||||||
|
|
||||||
#define SOURCEMOD_V_MAJOR 1 /**< SourceMod Major version */
|
#define SOURCEMOD_V_MAJOR 1 /**< SourceMod Major version */
|
||||||
#define SOURCEMOD_V_MINOR 2 /**< SourceMod Minor version */
|
#define SOURCEMOD_V_MINOR 2 /**< SourceMod Minor version */
|
||||||
#define SOURCEMOD_V_RELEASE 0 /**< SourceMod Release version */
|
#define SOURCEMOD_V_RELEASE 1 /**< SourceMod Release version */
|
||||||
|
|
||||||
#define SOURCEMOD_VERSION "1.2.0" /**< SourceMod version string (major.minor.release.build) */
|
#define SOURCEMOD_VERSION "1.2.1" /**< SourceMod version string (major.minor.release.build) */
|
||||||
|
@ -80,6 +80,7 @@
|
|||||||
#include "zr/zcookies"
|
#include "zr/zcookies"
|
||||||
#include "zr/jumpboost"
|
#include "zr/jumpboost"
|
||||||
#include "zr/volfeatures/volfeatures"
|
#include "zr/volfeatures/volfeatures"
|
||||||
|
#include "zr/debugtools"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Record plugin info.
|
* Record plugin info.
|
||||||
@ -136,6 +137,7 @@ public OnMapStart()
|
|||||||
InfectOnMapStart();
|
InfectOnMapStart();
|
||||||
SEffectsOnMapStart();
|
SEffectsOnMapStart();
|
||||||
ZSpawnOnMapStart();
|
ZSpawnOnMapStart();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -46,6 +46,8 @@ CommandsInit()
|
|||||||
ZHPOnCommandsCreate();
|
ZHPOnCommandsCreate();
|
||||||
VolOnCommandsCreate();
|
VolOnCommandsCreate();
|
||||||
|
|
||||||
|
DebugOnCommandsCreate();
|
||||||
|
|
||||||
// Forward event to modules. (hook commands)
|
// Forward event to modules. (hook commands)
|
||||||
ClassOnCommandsHook();
|
ClassOnCommandsHook();
|
||||||
DamageOnCommandsHook();
|
DamageOnCommandsHook();
|
||||||
|
@ -429,8 +429,8 @@ CvarsCreate()
|
|||||||
// Volumetric Features (module)
|
// Volumetric Features (module)
|
||||||
// ===========================
|
// ===========================
|
||||||
g_hCvarsList[CVAR_VOL] = CreateConVar("zr_vol", "1", "Enables volumetric features.");
|
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_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", "1.0", "How often to check for delayed events, in seconds. Use lower values for more precise delays.");
|
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.");
|
||||||
|
|
||||||
|
|
||||||
// ===========================
|
// ===========================
|
||||||
|
105
src/zr/debugtools.inc
Normal file
105
src/zr/debugtools.inc
Normal file
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* ============================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
DebugOnCommandsCreate()
|
||||||
|
{
|
||||||
|
// Custom test commands:
|
||||||
|
RegConsoleCmd("zr_getparametercount", Command_GetParameterCount, "Test GetParameterCount function in paramtools.inc. Usage: zr_getparametercount <paramstring>");
|
||||||
|
RegConsoleCmd("zr_getparametervalue", Command_GetParameterValue, "Test GetParameterValue function in paramtools.inc. Usage: zr_getparametervalue <paramname> <paramstring>");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Action:Command_GetParameterCount(client, argc)
|
||||||
|
{
|
||||||
|
if (argc < 1)
|
||||||
|
{
|
||||||
|
ReplyToCommand(client, "No parameter string passed. Usage: zr_getparametercount <paramstring>");
|
||||||
|
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 <paramname> <paramstring>");
|
||||||
|
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;
|
||||||
|
}
|
@ -8,6 +8,8 @@
|
|||||||
* Description: Provides functions for parsing strings with parameters in
|
* Description: Provides functions for parsing strings with parameters in
|
||||||
* key=value format.
|
* key=value format.
|
||||||
*
|
*
|
||||||
|
* Note: Does not support spaces or quoted strings.
|
||||||
|
*
|
||||||
* Copyright (C) 2009 Greyscale, Richard Helgeby
|
* Copyright (C) 2009 Greyscale, Richard Helgeby
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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 lenRawString = strlen(rawString);
|
||||||
new paramCount;
|
new paramCount;
|
||||||
|
new searchPos;
|
||||||
|
new splitPos;
|
||||||
|
|
||||||
// Check if the raw string is empty.
|
// Check if the raw string is empty.
|
||||||
if (lenRawString == 0)
|
if (lenRawString == 0)
|
||||||
@ -42,20 +46,26 @@ stock GetParameterCount(const String:rawString[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Count number of "=".
|
// Count number of "=".
|
||||||
for (new searchPos = 0; searchPos < lenRawString; searchPos++)
|
for (splitPos = 0; splitPos < lenRawString; splitPos++)
|
||||||
{
|
{
|
||||||
// Set searchPos and check if "=" was found.
|
// Find next "=".
|
||||||
searchPos = StrContains(rawString[searchPos], "=");
|
searchPos = StrContains(rawString[splitPos], "=", false);
|
||||||
if (searchPos)
|
|
||||||
|
// 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++;
|
paramCount++;
|
||||||
|
|
||||||
// Increment one position so we dont find the same "=" again.
|
// Update split position.
|
||||||
searchPos++;
|
splitPos += searchPos;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Exit loop.
|
// No need to continue. "=" not found.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,9 +96,11 @@ stock GetParameterValue(String:buffer[], maxlen, const String:rawString[], const
|
|||||||
new valuePos;
|
new valuePos;
|
||||||
new valueLen;
|
new valueLen;
|
||||||
new nextPos;
|
new nextPos;
|
||||||
|
new splitPos;
|
||||||
|
|
||||||
// Get the position of parameter.
|
// Get the position of parameter.
|
||||||
paramPos = StrContains(rawString, parameter, false);
|
paramPos = StrContains(rawString, parameter, false);
|
||||||
|
splitPos = paramPos;
|
||||||
|
|
||||||
// Check if found.
|
// Check if found.
|
||||||
if (paramPos >= 0)
|
if (paramPos >= 0)
|
||||||
@ -96,8 +108,8 @@ stock GetParameterValue(String:buffer[], maxlen, const String:rawString[], const
|
|||||||
// Get parameter length.
|
// Get parameter length.
|
||||||
paramLen = strlen(parameter);
|
paramLen = strlen(parameter);
|
||||||
|
|
||||||
// Get the position of the next parameter.
|
// Get the position of the next parameter by finding the next space.
|
||||||
nextPos = StrContains(rawString[paramPos + 1], " ");
|
nextPos = StrContains(rawString[splitPos], " ");
|
||||||
|
|
||||||
// Check if the next parameter was found.
|
// Check if the next parameter was found.
|
||||||
if (nextPos >= 0)
|
if (nextPos >= 0)
|
||||||
@ -105,8 +117,9 @@ stock GetParameterValue(String:buffer[], maxlen, const String:rawString[], const
|
|||||||
// Calculate value starting position.
|
// Calculate value starting position.
|
||||||
valuePos = paramPos + paramLen + 1;
|
valuePos = paramPos + paramLen + 1;
|
||||||
|
|
||||||
// Calculate value length.
|
// Calculate value length. Note: Adding 1 for space to the null
|
||||||
valueLen = nextPos - valuePos;
|
// terminator.
|
||||||
|
valueLen = nextPos + splitPos - valuePos + 1;
|
||||||
|
|
||||||
// Check if value length is longer than buffer size.
|
// Check if value length is longer than buffer size.
|
||||||
if (valueLen > maxlen)
|
if (valueLen > maxlen)
|
||||||
@ -145,6 +158,7 @@ stock GetParameterName(String:buffer[], maxlen, const String:rawString[], parame
|
|||||||
new paramPos;
|
new paramPos;
|
||||||
new valuePos;
|
new valuePos;
|
||||||
new nextValuePos;
|
new nextValuePos;
|
||||||
|
new splitPos;
|
||||||
|
|
||||||
// Check if the raw string is empty.
|
// Check if the raw string is empty.
|
||||||
if (strlen(rawString) == 0)
|
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.
|
// Get the value starting position for the previous index.
|
||||||
for (new index = 0; index < parameterIndex - 1; 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.
|
// 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.
|
// of the parameter name.
|
||||||
nextValuePos = StrContains(rawString[paramPos], "=");
|
nextValuePos = StrContains(rawString[splitPos], "=") + splitPos;
|
||||||
|
|
||||||
// Check if a value is specified.
|
// Check if a value is specified.
|
||||||
if (nextValuePos > 0)
|
if (nextValuePos > 0)
|
||||||
{
|
{
|
||||||
// Return the parameter name between paramPos and nextValuePos.
|
// 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
|
else
|
||||||
{
|
{
|
||||||
@ -189,7 +207,7 @@ stock GetParameterName(String:buffer[], maxlen, const String:rawString[], parame
|
|||||||
if (valuePos > 0)
|
if (valuePos > 0)
|
||||||
{
|
{
|
||||||
// Return the parameter name.
|
// Return the parameter name.
|
||||||
return strcopy(buffer, valuePos, rawString);
|
return strcopy(buffer, valuePos + 1, rawString); // + 1 to include null terminator.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -384,7 +384,7 @@ stock ClassGetIndex(const String:name[], cachetype = ZR_CLASS_CACHE_MODIFIED)
|
|||||||
*/
|
*/
|
||||||
stock ClassGetActiveIndex(client)
|
stock ClassGetActiveIndex(client)
|
||||||
{
|
{
|
||||||
new teamid = GetClientTeam(client);
|
new teamid;
|
||||||
|
|
||||||
if (!ZRIsClientOnTeam(client))
|
if (!ZRIsClientOnTeam(client))
|
||||||
{
|
{
|
||||||
|
@ -56,6 +56,7 @@ TranslationInit()
|
|||||||
{
|
{
|
||||||
// Load translations phrases used by plugin.
|
// Load translations phrases used by plugin.
|
||||||
LoadTranslations("common.phrases.txt");
|
LoadTranslations("common.phrases.txt");
|
||||||
|
LoadTranslations("core.phrases.txt");
|
||||||
LoadTranslations("zombiereloaded.phrases.txt");
|
LoadTranslations("zombiereloaded.phrases.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
* Actions to do with players in anti-camp volumes.
|
||||||
*/
|
*/
|
||||||
enum VolAnticampAction
|
enum VolAnticampAction
|
||||||
{
|
{
|
||||||
anticamp_no_action, /** Do nothing but give a warning. */
|
Anticamp_NoAction, /** Do nothing but give a warning. */
|
||||||
anticamp_damage, /** Give damage to player. */
|
Anticamp_Damage, /** Give damage to player. */
|
||||||
anticamp_slay, /** Slay player. */
|
Anticamp_Slay, /** Slay player. */
|
||||||
anticamp_drug, /** Drug player. */
|
Anticamp_Drug, /** Drug player. */
|
||||||
anticamp_fire /** Ignite player. */
|
Anticamp_Ignite /** Ignite player. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -57,13 +42,797 @@ enum VolAnticampAction
|
|||||||
*/
|
*/
|
||||||
enum VolAnticampeWarningType
|
enum VolAnticampeWarningType
|
||||||
{
|
{
|
||||||
anticamp_no_warning, /** No warning messages. */
|
Anticamp_NoWarning, /** No warning messages. */
|
||||||
anticamp_chat, /** Print warning in chat area. */
|
Anticamp_Chat, /** Print warning in chat area. */
|
||||||
anticamp_center, /** Print centered warning message. */
|
Anticamp_Center, /** Print centered warning message. */
|
||||||
anticamp_menu /** Print a menu-like warning message with close option. */
|
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.
|
* Anti-camp data.
|
||||||
*/
|
*/
|
||||||
new AnticampData[ZR_VOLUMES_MAX][VolTypeAnticamp];
|
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;
|
||||||
|
}
|
||||||
|
@ -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
|
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 <x1> <y1> <z1> <x2> <y2> <z2> <type> [params]");
|
||||||
|
RegAdminCmd("zr_vol_remove", VolRemoveVolumeCommand, ADMFLAG_CONFIG, "Removes an existing volume in the map. Usage: zr_vol_remove <volume index>");
|
||||||
|
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 <index|targetname>");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command callback for creating a new volume.
|
||||||
|
*/
|
||||||
public Action:VolAddVolumeCommand(client, argc)
|
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 <x1> <y1> <z1> <x2> <y2> <z2> <type> [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 <volume index>");
|
||||||
|
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 <index|targetname>");
|
||||||
|
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.
|
* 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 locMin Minimum x, y and z values.
|
||||||
* @param locMax Maximum x, y and z values.
|
* @param locMax Maximum x, y and z values.
|
||||||
* @param volumeType Specifies the volumetric feature type.
|
* @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.
|
* @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)
|
||||||
|
{
|
||||||
|
if (volumeIndex < 0)
|
||||||
{
|
{
|
||||||
new volumeIndex;
|
|
||||||
|
|
||||||
// Get a free volume index.
|
// Get a free volume index.
|
||||||
volumeIndex = VolGetFreeVolume();
|
volumeIndex = VolGetFreeVolume();
|
||||||
|
}
|
||||||
|
|
||||||
// Validate index.
|
// Validate index.
|
||||||
if (volumeIndex >= 0)
|
if (VolIsValidIndex(volumeIndex))
|
||||||
{
|
{
|
||||||
// Mark volume as enabled and in use.
|
// Mark volume as enabled and in use.
|
||||||
Volumes[volumeIndex][vol_enabled] = true;
|
Volumes[volumeIndex][Vol_Enabled] = true;
|
||||||
Volumes[volumeIndex][vol_in_use] = true;
|
Volumes[volumeIndex][Vol_InUse] = true;
|
||||||
|
|
||||||
// Set location data.
|
// Set location data.
|
||||||
Volumes[volumeIndex][vol_x_min] = locMin[0];
|
Volumes[volumeIndex][Vol_xMin] = locMin[0];
|
||||||
Volumes[volumeIndex][vol_y_min] = locMin[1];
|
Volumes[volumeIndex][Vol_yMin] = locMin[1];
|
||||||
Volumes[volumeIndex][vol_z_min] = locMin[2];
|
Volumes[volumeIndex][Vol_zMin] = locMin[2];
|
||||||
|
|
||||||
Volumes[volumeIndex][vol_x_max] = locMax[0];
|
Volumes[volumeIndex][Vol_xMax] = locMax[0];
|
||||||
Volumes[volumeIndex][vol_y_max] = locMax[1];
|
Volumes[volumeIndex][Vol_yMax] = locMax[1];
|
||||||
Volumes[volumeIndex][vol_z_max] = locMax[2];
|
Volumes[volumeIndex][Vol_zMax] = locMax[2];
|
||||||
|
|
||||||
// Set type.
|
// Set type.
|
||||||
Volumes[volumeIndex][vol_type] = volumeType;
|
Volumes[volumeIndex][Vol_Type] = volumeType;
|
||||||
Volumes[volumeIndex][vol_data_index] = dataIndex;
|
Volumes[volumeIndex][Vol_DataIndex] = dataIndex;
|
||||||
|
|
||||||
|
// Update number of volumes.
|
||||||
|
VolumeCount++;
|
||||||
|
|
||||||
// Return the new index.
|
// Return the new index.
|
||||||
return volumeIndex;
|
return volumeIndex;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No free volumes.
|
// No free volumes or invalid index.
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,14 +541,22 @@ VolAdd(Float:locMin[3], Float:locMax[3], VolumeFeatureTypes:volumeType, dataInde
|
|||||||
bool:VolRemove(volumeIndex)
|
bool:VolRemove(volumeIndex)
|
||||||
{
|
{
|
||||||
// Validate index.
|
// Validate index.
|
||||||
if (volumeIndex >= 0)
|
if (VolIsValidIndex(volumeIndex))
|
||||||
{
|
{
|
||||||
// Trigger event.
|
// Trigger event to clean up data and stop timers.
|
||||||
VolOnVolumeDisabled(volumeIndex);
|
VolOnDisabled(volumeIndex);
|
||||||
|
|
||||||
// Mark volume as disabled and unused.
|
// Clear feature data.
|
||||||
Volumes[volumeIndex][vol_enabled] = false;
|
switch (Volumes[volumeIndex][Vol_Type])
|
||||||
Volumes[volumeIndex][vol_in_use] = false;
|
{
|
||||||
|
case VolFeature_Anticamp:
|
||||||
|
{
|
||||||
|
VolAnticampReset(Volumes[volumeIndex][Vol_DataIndex]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear volume data.
|
||||||
|
VolClear(volumeIndex);
|
||||||
|
|
||||||
return true;
|
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.
|
* Sets extra attributes on a volume.
|
||||||
*
|
*
|
||||||
@ -142,6 +604,8 @@ VolSetAttributes(volumeIndex, const String:attributes[])
|
|||||||
{
|
{
|
||||||
new attribCount;
|
new attribCount;
|
||||||
new successfulCount;
|
new successfulCount;
|
||||||
|
new VolumeFeatureTypes:voltype;
|
||||||
|
new dataindex;
|
||||||
decl String:attribName[64];
|
decl String:attribName[64];
|
||||||
decl String:attribValue[256];
|
decl String:attribValue[256];
|
||||||
|
|
||||||
@ -160,8 +624,14 @@ VolSetAttributes(volumeIndex, const String:attributes[])
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get volumetric feature type.
|
||||||
|
voltype = Volumes[volumeIndex][Vol_Type];
|
||||||
|
|
||||||
|
// Get feature data index.
|
||||||
|
dataindex = Volumes[volumeIndex][Vol_DataIndex];
|
||||||
|
|
||||||
// Loop through all attributes.
|
// Loop through all attributes.
|
||||||
for (new attrib = 0; attrib > attribCount; attrib++)
|
for (new attrib = 0; attrib < attribCount; attrib++)
|
||||||
{
|
{
|
||||||
// Get attribute name.
|
// Get attribute name.
|
||||||
GetParameterName(attribName, sizeof(attribName), attributes, attrib);
|
GetParameterName(attribName, sizeof(attribName), attributes, attrib);
|
||||||
@ -169,8 +639,10 @@ VolSetAttributes(volumeIndex, const String:attributes[])
|
|||||||
// Get attribute value.
|
// Get attribute value.
|
||||||
GetParameterValue(attribValue, sizeof(attribValue), attributes, attribName);
|
GetParameterValue(attribValue, sizeof(attribValue), attributes, attribName);
|
||||||
|
|
||||||
// Check names and set volume attributes.
|
LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Set attribute", "Got parameter: \"%s\" = \"%s\"", attribName, attribValue);
|
||||||
if (strcmp(attribName, "team", false))
|
|
||||||
|
// Check generic attributes.
|
||||||
|
if (StrEqual(attribName, "teamfilter", false))
|
||||||
{
|
{
|
||||||
// Parse team string value.
|
// Parse team string value.
|
||||||
if (VolSetTeamString(volumeIndex, attribValue))
|
if (VolSetTeamString(volumeIndex, attribValue))
|
||||||
@ -178,7 +650,7 @@ VolSetAttributes(volumeIndex, const String:attributes[])
|
|||||||
successfulCount++;
|
successfulCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcmp(attribName, "delay", false))
|
else if (StrEqual(attribName, "delay", false))
|
||||||
{
|
{
|
||||||
// Parse delay string value.
|
// Parse delay string value.
|
||||||
if (VolSetDelayString(volumeIndex, attribValue))
|
if (VolSetDelayString(volumeIndex, attribValue))
|
||||||
@ -186,7 +658,7 @@ VolSetAttributes(volumeIndex, const String:attributes[])
|
|||||||
successfulCount++;
|
successfulCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcmp(attribName, "effect", false))
|
else if (StrEqual(attribName, "effect", false))
|
||||||
{
|
{
|
||||||
// Parse effect string value.
|
// Parse effect string value.
|
||||||
if (VolSetEffectString(volumeIndex, attribValue))
|
if (VolSetEffectString(volumeIndex, attribValue))
|
||||||
@ -194,7 +666,7 @@ VolSetAttributes(volumeIndex, const String:attributes[])
|
|||||||
successfulCount++;
|
successfulCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcmp(attribName, "effect_color", false))
|
else if (StrEqual(attribName, "effect_color", false))
|
||||||
{
|
{
|
||||||
// Parse effect color string value.
|
// Parse effect color string value.
|
||||||
if (VolSetEffectColorString(volumeIndex, attribValue))
|
if (VolSetEffectColorString(volumeIndex, attribValue))
|
||||||
@ -202,7 +674,7 @@ VolSetAttributes(volumeIndex, const String:attributes[])
|
|||||||
successfulCount++;
|
successfulCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcmp(attribName, "enabled", false))
|
else if (StrEqual(attribName, "enabled", false))
|
||||||
{
|
{
|
||||||
// Parse enabled string value.
|
// Parse enabled string value.
|
||||||
if (VolSetEnabledString(volumeIndex, attribValue))
|
if (VolSetEnabledString(volumeIndex, attribValue))
|
||||||
@ -210,16 +682,23 @@ VolSetAttributes(volumeIndex, const String:attributes[])
|
|||||||
successfulCount++;
|
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 number of successfully attributes set.
|
||||||
return successfulCount;
|
return successfulCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates commands for managing volumes.
|
|
||||||
*/
|
|
||||||
VolOnCommandsCreate()
|
|
||||||
{
|
|
||||||
RegAdminCmd("zr_vol_add", VolAddVolumeCommand, ADMFLAG_GENERIC, "Adds a new volume. Usage: zr_vol_add <x1> <y1> <z1> <x2> <y2> <z2> <type> [params]");
|
|
||||||
}
|
|
||||||
|
@ -40,8 +40,10 @@ VolOnPlayerEnter(client, volumeIndex)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Event", "Player %N entered volume %d.", client, volumeIndex);
|
||||||
|
|
||||||
// Forward event to features.
|
// Forward event to features.
|
||||||
// VolAnticampStart(client, volume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,8 +61,10 @@ VolOnPlayerLeave(client, volumeIndex)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Event", "Player %N left volume %d.", client, volumeIndex);
|
||||||
|
|
||||||
// Forward event to features.
|
// Forward event to features.
|
||||||
// VolAnticampStop(client, volume);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,6 +110,9 @@ VolOnRoundStart()
|
|||||||
|
|
||||||
// Start main timer.
|
// Start main timer.
|
||||||
VolStartUpdateTimer();
|
VolStartUpdateTimer();
|
||||||
|
|
||||||
|
// Start volumes.
|
||||||
|
VolEnableVolumes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,26 +123,42 @@ VolOnRoundEnd()
|
|||||||
// Stop main timer.
|
// Stop main timer.
|
||||||
VolStopUpdateTimer();
|
VolStopUpdateTimer();
|
||||||
|
|
||||||
// Forward stop event to features.
|
// Stop volumes.
|
||||||
// VolAnticampStop();
|
VolDisableVolumes();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a volume is disabled.
|
* Called when a volume is disabled.
|
||||||
* @param volumeIndex The volume index.
|
* @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.
|
* Called when a volume is enabled.
|
||||||
* @param volumeIndex The volume index.
|
* @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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,30 +36,30 @@
|
|||||||
enum VolumeAttributes
|
enum VolumeAttributes
|
||||||
{
|
{
|
||||||
/* General */
|
/* General */
|
||||||
bool:vol_enabled, /** Volume state. */
|
bool:Vol_Enabled, /** Volume state. */
|
||||||
bool:vol_in_use, /** Marks if the volume is used. */
|
bool:Vol_InUse, /** Marks if the volume is used. */
|
||||||
|
|
||||||
/* Location */
|
/* Location */
|
||||||
Float:vol_x_min, /** Minimum x position. */
|
Float:Vol_xMin, /** Minimum x position. */
|
||||||
Float:vol_x_max, /** Maximum x position. */
|
Float:Vol_xMax, /** Maximum x position. */
|
||||||
|
|
||||||
Float:vol_y_min, /** Minimum y position. */
|
Float:Vol_yMin, /** Minimum y position. */
|
||||||
Float:vol_y_max, /** Maximum y position. */
|
Float:Vol_yMax, /** Maximum y position. */
|
||||||
|
|
||||||
Float:vol_z_min, /** Minimum z position. */
|
Float:Vol_zMin, /** Minimum z position. */
|
||||||
Float:vol_z_max, /** Maximum z position. */
|
Float:Vol_zMax, /** Maximum z position. */
|
||||||
|
|
||||||
/* Style */
|
/* Style */
|
||||||
VolumeEffects:vol_effect, /** Visual effect to apply on the volume. */
|
VolumeEffects:Vol_Effect, /** Visual effect to apply on the volume. */
|
||||||
vol_effect_color[3], /** Render color of the effect. RGB colors. */
|
Vol_EffectColor[3], /** Render color of the effect. RGB colors. */
|
||||||
|
|
||||||
/* Data */
|
/* Data */
|
||||||
VolumeFeatureTypes:vol_type, /** The volumetric feature type. */
|
VolumeFeatureTypes:Vol_Type, /** The volumetric feature type. */
|
||||||
vol_data_index, /** Index in remote feature array. */
|
Vol_DataIndex, /** Index in remote feature array. */
|
||||||
|
|
||||||
/* Behaviour */
|
/* Behaviour */
|
||||||
VolumeTeamFilters:vol_team_filter, /** Team filtering. Trigger by certain teams, or all. */
|
VolumeTeamFilters:Vol_TeamFilter, /** 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. */
|
Float:Vol_TriggerDelay /** Trigger delay. How many seconds players have to stay to trigger volume events. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -67,7 +67,8 @@ enum VolumeAttributes
|
|||||||
*/
|
*/
|
||||||
enum VolumeFeatureTypes
|
enum VolumeFeatureTypes
|
||||||
{
|
{
|
||||||
VolFeature_Anticamp = 0,
|
VolFeature_Invalid = 0,
|
||||||
|
VolFeature_Anticamp,
|
||||||
VolFeature_Knockback
|
VolFeature_Knockback
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,6 +107,11 @@ new VolumeCount;
|
|||||||
*/
|
*/
|
||||||
new Float:VolPlayerLoc[MAXPLAYERS + 1][3];
|
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
|
* Specifies whether the volumetric features module is enabled or not. Synced
|
||||||
* with zr_vol CVAR.
|
* 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
|
* 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.
|
* 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;
|
new Handle:hVolUpdateTimer;
|
||||||
|
|
||||||
@ -138,6 +144,8 @@ new Float:VolTriggerInterval;
|
|||||||
#include "zr/volfeatures/volevents"
|
#include "zr/volfeatures/volevents"
|
||||||
#include "zr/volfeatures/volgenericattributes"
|
#include "zr/volfeatures/volgenericattributes"
|
||||||
#include "zr/volfeatures/volcommands"
|
#include "zr/volfeatures/volcommands"
|
||||||
|
|
||||||
|
// Sub features.
|
||||||
#include "zr/volfeatures/volanticamp"
|
#include "zr/volfeatures/volanticamp"
|
||||||
|
|
||||||
|
|
||||||
@ -148,6 +156,9 @@ VolLoad()
|
|||||||
{
|
{
|
||||||
// Cache CVAR value.
|
// Cache CVAR value.
|
||||||
VolEnabled = GetConVarBool(g_hCvarsList[CVAR_VOL]);
|
VolEnabled = GetConVarBool(g_hCvarsList[CVAR_VOL]);
|
||||||
|
|
||||||
|
// Initialize sub features.
|
||||||
|
VolAnticampInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,8 +168,51 @@ VolDisable()
|
|||||||
{
|
{
|
||||||
VolEnabled = false;
|
VolEnabled = false;
|
||||||
VolStopUpdateTimer();
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -196,6 +250,7 @@ bool:VolStartUpdateTimer()
|
|||||||
else
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -236,7 +291,7 @@ bool:VolStartTriggerTimer()
|
|||||||
if (VolTriggerInterval > 0.0)
|
if (VolTriggerInterval > 0.0)
|
||||||
{
|
{
|
||||||
// Start the timer.
|
// Start the timer.
|
||||||
hVolTriggerTimer = CreateTimer(VolTriggerInterval, Event_VolUpdateTimer, _, TIMER_REPEAT);
|
hVolTriggerTimer = CreateTimer(VolTriggerInterval, Event_VolTriggerTimer, _, TIMER_REPEAT);
|
||||||
|
|
||||||
// Trigger timer started.
|
// Trigger timer started.
|
||||||
return true;
|
return true;
|
||||||
@ -244,6 +299,7 @@ bool:VolStartTriggerTimer()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Trigger timer not running. Either disabled or invalid interval.
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -302,7 +358,7 @@ VolResetCountDown(client = -1)
|
|||||||
*/
|
*/
|
||||||
VolUpdatePlayerLocation(client = -1)
|
VolUpdatePlayerLocation(client = -1)
|
||||||
{
|
{
|
||||||
if (client <= 0)
|
if (client > 0)
|
||||||
{
|
{
|
||||||
// Assume the client is valid and save location in array.
|
// Assume the client is valid and save location in array.
|
||||||
GetClientAbsOrigin(client, VolPlayerLoc[client]);
|
GetClientAbsOrigin(client, VolPlayerLoc[client]);
|
||||||
@ -343,10 +399,11 @@ VolUpdatePlayerChanges()
|
|||||||
// Check if client is in game and alive.
|
// Check if client is in game and alive.
|
||||||
if (!IsClientConnected(client) || !IsClientInGame(client) || !IsPlayerAlive(client))
|
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));
|
VolGetPlayerStates(client, volumeStates, sizeof(volumeStates));
|
||||||
|
|
||||||
// Update player location cache.
|
// Update player location cache.
|
||||||
@ -356,23 +413,37 @@ VolUpdatePlayerChanges()
|
|||||||
VolGetPlayerStates(client, volumeNewStates, sizeof(volumeNewStates));
|
VolGetPlayerStates(client, volumeNewStates, sizeof(volumeNewStates));
|
||||||
|
|
||||||
// Loop through each volume and compare states.
|
// 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];
|
newState = volumeNewStates[volumeIndex];
|
||||||
oldState = volumeStates[volumeIndex];
|
oldState = volumeStates[volumeIndex];
|
||||||
|
|
||||||
// Compare new states with old states.
|
// Check for no change.
|
||||||
if (newState == oldState)
|
if (newState == oldState)
|
||||||
{
|
{
|
||||||
// No change. Skip to next volume.
|
// No change. Skip to next volume.
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if client entered the volume.
|
// Check if client entered the volume.
|
||||||
else if (!newState && oldState)
|
if (newState && !oldState)
|
||||||
{
|
{
|
||||||
// Get trigger delay value.
|
// 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.
|
// Check if the volume has a trigger delay.
|
||||||
if (trigger_delay > 0.0)
|
if (trigger_delay > 0.0)
|
||||||
@ -382,17 +453,23 @@ VolUpdatePlayerChanges()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Update cache.
|
||||||
|
VolPlayerInVolume[client][volumeIndex] = true;
|
||||||
|
|
||||||
// No trigger delay, trigger event instantly.
|
// No trigger delay, trigger event instantly.
|
||||||
VolOnPlayerEnter(client, volumeIndex);
|
VolOnPlayerEnter(client, volumeIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if client left the volume.
|
// Check if client left the volume.
|
||||||
else if (newState && !oldState)
|
else if (!newState && oldState)
|
||||||
{
|
{
|
||||||
// Make sure count down value is reset.
|
// Make sure count down value is reset.
|
||||||
VolPlayerCountDown[client][volumeIndex] = -1.0;
|
VolPlayerCountDown[client][volumeIndex] = -1.0;
|
||||||
|
|
||||||
|
// Update cache.
|
||||||
|
VolPlayerInVolume[client][volumeIndex] = false;
|
||||||
|
|
||||||
// Trigger event.
|
// Trigger event.
|
||||||
VolOnPlayerLeave(client, volumeIndex);
|
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 min Minimum x, y and z values of the location.
|
||||||
* @param max Maximum 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
|
* @return True if the position is within min and max values. False
|
||||||
* otherwise.
|
* 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.
|
// Cache to avoid re-indexing arrays.
|
||||||
new Float:posX = pos[0];
|
new Float:posX = point[0];
|
||||||
new Float:posY = pos[1];
|
new Float:posY = point[1];
|
||||||
new Float:posZ = pos[2];
|
new Float:posZ = point[2];
|
||||||
|
|
||||||
// Check if within x boundaries.
|
// Check if within x boundaries.
|
||||||
if ((posX >= min[0]) && (posX <= max[0]))
|
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.
|
// Check if within x boundaries.
|
||||||
if ((posZ >= min[2]) && (posZ <= max[2]))
|
if ((posZ >= min[2]) && (posZ <= max[2]))
|
||||||
{
|
{
|
||||||
// The player is within the location boundaries.
|
// The point is within the location boundaries.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The player is outside the location boundaries.
|
// The point is outside the location boundaries.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -443,9 +520,22 @@ bool:IsPositionInLocation(Float:pos[3], Float:min[3], Float:max[3])
|
|||||||
* @param volumeIndex The volume index.
|
* @param volumeIndex The volume index.
|
||||||
* @return True if in use, false otherwise.
|
* @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++)
|
for (new volumeIndex = 0; volumeIndex < ZR_VOLUMES_MAX; volumeIndex++)
|
||||||
{
|
{
|
||||||
// Check if it's free.
|
// Check if it's free.
|
||||||
if (!VolIsInUse(volumeIndex))
|
if (!VolInUse(volumeIndex))
|
||||||
{
|
{
|
||||||
return volumeIndex;
|
return volumeIndex;
|
||||||
}
|
}
|
||||||
@ -488,6 +578,69 @@ VolGetFreeVolume()
|
|||||||
return -1;
|
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
|
* Gets wether a client is within volumes or not. Result is stored in a boolean
|
||||||
* array.
|
* array.
|
||||||
@ -506,25 +659,25 @@ VolGetPlayerStates(client, bool:buffer[], maxlen)
|
|||||||
new Float:volMaxBuffer[3];
|
new Float:volMaxBuffer[3];
|
||||||
|
|
||||||
// Loop through all available volumes.
|
// 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.
|
// Chache volume to avoid re-indexing.
|
||||||
volumeBuffer = Volumes[volumeIndex];
|
volumeBuffer = Volumes[volumeIndex];
|
||||||
|
|
||||||
// Get min positions.
|
// Get min positions.
|
||||||
volMinBuffer[0] = volumeBuffer[vol_x_min];
|
volMinBuffer[0] = volumeBuffer[Vol_xMin];
|
||||||
volMinBuffer[1] = volumeBuffer[vol_y_min];
|
volMinBuffer[1] = volumeBuffer[Vol_yMin];
|
||||||
volMinBuffer[2] = volumeBuffer[vol_z_min];
|
volMinBuffer[2] = volumeBuffer[Vol_zMin];
|
||||||
|
|
||||||
// Get max positions.
|
// Get max positions.
|
||||||
volMaxBuffer[0] = volumeBuffer[vol_x_min];
|
volMaxBuffer[0] = volumeBuffer[Vol_xMax];
|
||||||
volMaxBuffer[1] = volumeBuffer[vol_y_min];
|
volMaxBuffer[1] = volumeBuffer[Vol_yMax];
|
||||||
volMaxBuffer[2] = volumeBuffer[vol_z_min];
|
volMaxBuffer[2] = volumeBuffer[Vol_zMax];
|
||||||
|
|
||||||
// Check the cached player location.
|
// Check the cached player location.
|
||||||
if (IsPositionInLocation(VolPlayerLoc[client], volMinBuffer, volMaxBuffer))
|
if (IsPointInLocation(VolPlayerLoc[client], volMinBuffer, volMaxBuffer))
|
||||||
{
|
{
|
||||||
// Mark player as in volume.
|
// Mark player as in volume.
|
||||||
buffer[volumeIndex] = true;
|
buffer[volumeIndex] = true;
|
||||||
@ -541,6 +694,65 @@ VolGetPlayerStates(client, bool:buffer[], maxlen)
|
|||||||
return volCount;
|
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.
|
* 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.
|
// Loop through all volumes.
|
||||||
for (new volumeIndex = 0; volumeIndex < ZR_VOLUMES_MAX; volumeIndex++)
|
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.
|
// Get count down value.
|
||||||
countDown = VolPlayerCountDown[client][volumeIndex];
|
countDown = VolPlayerCountDown[client][volumeIndex];
|
||||||
|
|
||||||
@ -571,15 +790,21 @@ public Action:Event_VolTriggerTimer(Handle:timer)
|
|||||||
// Substract by trigger interval.
|
// Substract by trigger interval.
|
||||||
countDown -= VolTriggerInterval;
|
countDown -= VolTriggerInterval;
|
||||||
|
|
||||||
// Check if zero or below.
|
// Check if time is up.
|
||||||
if (countDown <= 0.0)
|
if (countDown <= 0.0)
|
||||||
{
|
{
|
||||||
|
// Update cache.
|
||||||
|
VolPlayerInVolume[client][volumeIndex] = true;
|
||||||
|
|
||||||
// Trigger volume enter event.
|
// Trigger volume enter event.
|
||||||
VolOnPlayerEnter(client, volumeIndex);
|
VolOnPlayerEnter(client, volumeIndex);
|
||||||
|
|
||||||
// Reset count down value.
|
// Reset count down value.
|
||||||
VolPlayerCountDown[client][volumeIndex] = -1.0;
|
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)
|
if (isEnabled)
|
||||||
{
|
{
|
||||||
// Volumetric features is enabled.
|
// Volumetric features is enabled.
|
||||||
VolEnabled = true;
|
VolEnable();
|
||||||
|
|
||||||
// Start timers.
|
|
||||||
VolStartUpdateTimer();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -44,21 +44,21 @@ stock bool:VolSetTeamString(volumeIndex, const String:team[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert value.
|
// Convert value.
|
||||||
if (strcmp(team, "all", false))
|
if (StrEqual(team, "all", false))
|
||||||
{
|
{
|
||||||
teamfilter = VolTeam_All;
|
teamfilter = VolTeam_All;
|
||||||
}
|
}
|
||||||
else if (strcmp(team, "humans", false))
|
else if (StrEqual(team, "humans", false))
|
||||||
{
|
{
|
||||||
teamfilter = VolTeam_Humans;
|
teamfilter = VolTeam_Humans;
|
||||||
}
|
}
|
||||||
else if (strcmp(team, "zombies", false))
|
else if (StrEqual(team, "zombies", false))
|
||||||
{
|
{
|
||||||
teamfilter = VolTeam_Zombies;
|
teamfilter = VolTeam_Zombies;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply value.
|
// Apply value.
|
||||||
Volumes[volumeIndex][vol_team_filter] = teamfilter;
|
Volumes[volumeIndex][Vol_TeamFilter] = teamfilter;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -71,7 +71,38 @@ stock bool:VolSetTeamString(volumeIndex, const String:team[])
|
|||||||
*/
|
*/
|
||||||
stock VolSetTeam(volumeIndex, VolumeTeamFilters: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);
|
triggerdelay = StringToFloat(delay);
|
||||||
|
|
||||||
// Apply value.
|
// Apply value.
|
||||||
Volumes[volumeIndex][vol_trigger_delay] = triggerdelay;
|
Volumes[volumeIndex][Vol_TriggerDelay] = triggerdelay;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,7 +139,7 @@ stock bool:VolSetDelayString(volumeIndex, const String:delay[])
|
|||||||
*/
|
*/
|
||||||
stock VolSetDelay(volumeIndex, Float: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.
|
// 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;
|
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;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -156,7 +187,38 @@ stock bool:VolSetEffectString(volumeIndex, const String:effect[])
|
|||||||
*/
|
*/
|
||||||
stock VolSetEffect(volumeIndex, VolumeEffects: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]);
|
blue = StringToInt(colors[2]);
|
||||||
|
|
||||||
// Apply values.
|
// Apply values.
|
||||||
Volumes[volumeIndex][vol_effect_color][0] = red;
|
Volumes[volumeIndex][Vol_EffectColor][0] = red;
|
||||||
Volumes[volumeIndex][vol_effect_color][1] = green;
|
Volumes[volumeIndex][Vol_EffectColor][1] = green;
|
||||||
Volumes[volumeIndex][vol_effect_color][2] = blue;
|
Volumes[volumeIndex][Vol_EffectColor][2] = blue;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,31 +289,31 @@ stock bool:VolSetEnabledString(volumeIndex, const String:enabled[])
|
|||||||
new bool:val = bool:StringToInt(enabled);
|
new bool:val = bool:StringToInt(enabled);
|
||||||
|
|
||||||
// Check yes or no values first.
|
// Check yes or no values first.
|
||||||
if (strcmp(enabled, "yes", false) == 0)
|
if (StrEqual(enabled, "yes", false))
|
||||||
{
|
{
|
||||||
val = true;
|
val = true;
|
||||||
}
|
}
|
||||||
else if (strcmp(enabled, "no", false) == 0)
|
else if (StrEqual(enabled, "no", false))
|
||||||
{
|
{
|
||||||
val = false;
|
val = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the new value is different from the current.
|
// Check if the new value is different from the current.
|
||||||
if (Volumes[volumeIndex][vol_enabled] != val)
|
if (Volumes[volumeIndex][Vol_Enabled] != val)
|
||||||
{
|
{
|
||||||
// Forward event.
|
// Forward event.
|
||||||
if (val)
|
if (val)
|
||||||
{
|
{
|
||||||
VolOnVolumeEnabled(volumeIndex);
|
VolOnEnabled(volumeIndex);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
VolOnVolumeDisabled(volumeIndex);
|
VolOnDisabled(volumeIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply converted value.
|
// Apply converted value.
|
||||||
Volumes[volumeIndex][vol_enabled] = val;
|
Volumes[volumeIndex][Vol_Enabled] = val;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +326,7 @@ stock bool:VolSetEnabledString(volumeIndex, const String:enabled[])
|
|||||||
stock VolSetEnabled(volumeIndex, bool:enabled)
|
stock VolSetEnabled(volumeIndex, bool:enabled)
|
||||||
{
|
{
|
||||||
// Check if the new value is different from the current.
|
// Check if the new value is different from the current.
|
||||||
if (Volumes[volumeIndex][vol_enabled] != enabled)
|
if (Volumes[volumeIndex][Vol_Enabled] != enabled)
|
||||||
{
|
{
|
||||||
// Forward event.
|
// Forward event.
|
||||||
if (enabled)
|
if (enabled)
|
||||||
@ -277,5 +339,5 @@ stock VolSetEnabled(volumeIndex, bool:enabled)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Volumes[volumeIndex][vol_enabled] = enabled;
|
Volumes[volumeIndex][Vol_Enabled] = enabled;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user