sm-zombiereloaded-3/src/zr/volfeatures/volanticamp.inc

929 lines
26 KiB
SourcePawn

/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: volanticamp.inc
* Type: Module
* Description: Anti-camp handler.
*
* Copyright (C) 2009-2013 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/>.
*
* ============================================================================
*/
/**
* Actions to do with players in anti-camp volumes.
*/
enum VolAnticampAction
{
Anticamp_NoAction, /** Do nothing but give a warning. */
Anticamp_Damage, /** Give damage to player. */
Anticamp_Slay, /** Slay player. */
Anticamp_Drug, /** Drug player. */
Anticamp_Ignite /** Ignite player. */
}
/**
* Warning types.
*/
enum VolAnticampeWarningType
{
Anticamp_NoWarning, /** No warning messages. */
Anticamp_Chat, /** Print warning in chat area. */
Anticamp_Center, /** Print centered warning message. */
Anticamp_Menu /** Print a menu-like warning message with close option. */
}
/**
* Data structure for a anti-camp volume.
*/
enum VolTypeAnticamp
{
bool:Anticamp_InUse, /** Specifies if the data index is used or not. */
Float:Anticamp_Interval, /** How often to trigger an action. */
Handle:Anticamp_Timer, /** Action timer handle. */
VolAnticampAction:Anticamp_Action, /** What to do with players in anti-camp volumes */
Float:Anticamp_Amount, /** Amount depending on action type. Usually time in seconds or damage amount. */
VolAnticampeWarningType:Anticamp_Warning, /** How to warn the player. */
String:Anticamp_Message[256] /** Override warning message. Max 256 characters. */
}
/**
* Anti-camp data.
*/
new AnticampData[ZR_VOLUMES_MAX][VolTypeAnticamp];
/**
* Event callback. Enables a anticamp volume.
*
* @param volumeIndex The volume index.
*/
VolAnticampEnable(volumeIndex)
{
new Float:interval;
new Handle:timer;
// 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])
{
// Kill timer if it exist.
timer = AnticampData[dataindex][Anticamp_Timer];
if (timer != INVALID_HANDLE)
{
KillTimer(timer);
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, volumeIndex, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Vol state", "Enabled anticamp volume %d.", volumeIndex);
}
else
{
LogEvent(_, LogType_Error, LOG_CORE_EVENTS, LogModule_Volfeatures, "Config Validation", "Warning: Invalid interval %.2f in anticamp volume %d.", interval, volumeIndex);
}
}
}
/**
* Starts all existing anticamp timers.
*
* TODO: Reuse code! This is almost duplicate of VolAnticampEnable.
*/
stock VolAnticampEnableAll()
{
new Float:interval;
new dataindex;
// Loop through all volumes.
for (new volumeindex = 0; volumeindex < ZR_VOLUMES_MAX; volumeindex++)
{
// Check if unused.
if (!VolInUse(volumeindex))
{
// Volume not in use, skip it.
continue;
}
// Check if it's a anticamp volume.
if (VolIsType(volumeindex, VolFeature_Anticamp))
{
// Get data index.
dataindex = Volumes[volumeindex][Vol_DataIndex];
// Kill timer if it exist.
timer = AnticampData[dataindex][Anticamp_Timer];
if (timer != INVALID_HANDLE)
{
KillTimer(timer);
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, volumeindex, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Vol state", "Enabled anticamp volume %d.", volumeIndex);
}
else
{
LogEvent(_, LogType_Error, LOG_CORE_EVENTS, LogModule_Volfeatures, "Config Validation", "Warning: Invalid interval %.2f in anticamp volume %d.", interval, volumeIndex);
}
}
}
}
/**
* 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);
}
}
/**
* Called when a player leave a anticamp volume.
*
* @param client The client index.
* @param volumeIndex Index of volume the player left.
*/
VolAnticampOnPlayerLeave(client, volumeIndex)
{
new dataindex = Volumes[volumeIndex][Vol_DataIndex];
switch (AnticampData[dataindex][Anticamp_Action])
{
case Anticamp_Drug:
{
// TODO: Un-drug player.
}
case Anticamp_Ignite:
{
ExtinguishEntity(client);
}
}
}
/**
* Timer callback for anticamp volumes. Applies actions on players in volumes.
*/
public Action:Event_VolAnticampTrigger(Handle:timer, any:volumeIndex)
{
// Loop through all players.
for (new client = 1; client <= MaxClients; client++)
{
// Validate client's connection state.
if (!IsClientConnected(client) || !IsClientInGame(client) || !IsPlayerAlive(client))
{
continue;
}
// Check if the volume is unused.
if (!VolInUse(volumeIndex))
{
continue;
}
// Check if the volume is disabled.
if (!VolIsEnabled(volumeIndex))
{
continue;
}
// Check if it's a anticamp volume.
if (VolIsType(volumeIndex, VolFeature_Anticamp))
{
// Check if player is in the volume.
if (VolPlayerInVolume[client][volumeIndex])
{
// 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];
// Set client language.
SetGlobalTransTarget(client);
// Get player name.
decl String:name[64];
GetClientName(client, name, sizeof(name));
// 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 or below.
new damage = RoundToNearest(amount);
new health = GetClientHealth(client) - damage;
if (health > 0)
{
SetEntityHealth(client, health);
}
else
{
// Health is zero or below. Kill player.
ForcePlayerSuicide(client);
// Log event.
LogEvent(false, LogType_Normal, LOG_GAME_EVENTS, LogModule_Volfeatures, "Anti-camp", "%t", "Vol Slay", name, volumeIndex);
}
}
case Anticamp_Slay:
{
// Instantly kill the player.
ForcePlayerSuicide(client);
// Log event.
LogEvent(false, LogType_Normal, LOG_GAME_EVENTS, LogModule_Volfeatures, "Anti-camp", "%t", "Vol Slay", name, volumeIndex);
}
case Anticamp_Drug:
{
// TODO: Trigger sm_drug on client some how.
}
case Anticamp_Ignite:
{
// Validate amount.
if (amount > 0.0)
{
// Extinguish player first.
ExtinguishEntity(client);
// Ignite player for "amount" seconds.
IgniteEntity(client, amount);
// Log event.
LogEvent(false, LogType_Normal, LOG_GAME_EVENTS, LogModule_Volfeatures, "Anti-camp", "%t", "Vol Ignite", name, volumeIndex);
}
}
}
}
/**
* 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 default anticamp 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])
{
// Mark as in use.
AnticampData[dataindex][Anticamp_InUse] = true;
// Return the new index.
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_Slay:
{
return shortName ? strcopy(buffer, maxlen, "slay") : strcopy(buffer, maxlen, "Kill 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, "slay", false))
{
return Anticamp_Slay;
}
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, "menu") : 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;
}