More work on volfeatures. See details.

Finished base event handler for volfeatures.
Finished zr_voladd, zr_vol_remove, zr_vol_list and zr_vol_dumpstates console commands.
Removed unused function call in class module.
Fixed minior bugs in parameter parser (paramtools.inc).
Made a debug tools module with console commands for directly testing certain functions.
This commit is contained in:
richard 2009-07-16 10:05:40 +02:00
parent 1dff60542e
commit d666fbf7ce
13 changed files with 1861 additions and 176 deletions

View File

@ -932,17 +932,19 @@
"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})."
} }
// =========================== // ===========================

View File

@ -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.
@ -137,6 +138,7 @@ public OnMapStart()
SEffectsOnMapStart(); SEffectsOnMapStart();
AntiStickOnMapStart(); AntiStickOnMapStart();
ZSpawnOnMapStart(); ZSpawnOnMapStart();
} }
/** /**

View File

@ -45,6 +45,8 @@ CommandsInit()
ZHPOnCommandsCreate(); ZHPOnCommandsCreate();
VolOnCommandsCreate(); VolOnCommandsCreate();
DebugOnCommandsCreate();
// Forward event to modules. (hook commands) // Forward event to modules. (hook commands)
ClassOnCommandsHook(); ClassOnCommandsHook();
DamageOnCommandsHook(); DamageOnCommandsHook();

View File

@ -423,8 +423,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
View 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;
}

View File

@ -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
{ {

View File

@ -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))
{ {

View File

@ -61,6 +61,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");
} }

View File

@ -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;
}

View File

@ -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]");
}

View File

@ -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);
}
}
} }

View File

@ -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
{ {

View File

@ -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;
} }