/* * ============================================================================ * * Zombie:Reloaded * * File: volcommands.inc * Type: Module * Description: Command handler for volumetric features. * * 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 . * * ============================================================================ */ /* Add volume ------------ Syntax: zr_vol_add [params] Parameters: zn, yn, zn Max and min location. type Feature type. params A string with optional parameters: team=all|humans|zombies delay=0 effect=none|wireframe|smoke effect_color=0,0,0 enabled=1 Example: zr_vol_add 0 0 0 100 200 300 anticamp team=humans delay=5 amount=100 */ /** * Creates commands for managing volumes. */ VolOnCommandsCreate() { RegConsoleCmd("zr_vol_add", VolAddVolumeCommand, "Creates a rectangular volume in the map. Usage: zr_vol_add [params]"); RegConsoleCmd("zr_vol_remove", VolRemoveVolumeCommand, "Removes an existing volume in the map. Usage: zr_vol_remove "); RegConsoleCmd("zr_vol_list", VolListCommand, "Lists existing volumes in the map, or dumps detail data to the specified volume. Usage: zr_vol_list [volume index]"); RegConsoleCmd("zr_vol_dumpstates", VolDumpStatesCommand, "Dumps volume states for the specified player. Usage: zr_vol_dumpstates "); } /** * Command callback for creating a new volume. */ public Action:VolAddVolumeCommand(client, argc) { decl String:buffer[640]; buffer[0] = 0; // Check if privileged. if (!ZRIsClientPrivileged(client, OperationType_Configuration)) { TranslationReplyToCommand(client, "No access to command"); return Plugin_Handled; } if (argc < 7) { // Write syntax info. StrCat(buffer, sizeof(buffer), "Creates a rectangular volume in the map. Usage: zr_vol_add [params]\n\n"); StrCat(buffer, sizeof(buffer), "Parameters:\n"); StrCat(buffer, sizeof(buffer), "x1, y1, z1 Coordinates to first corner (any corner)\n"); StrCat(buffer, sizeof(buffer), "x2, y2, z2 Coordinates to oposite corner (diagonally to oposite height)\n"); StrCat(buffer, sizeof(buffer), "type Volumetric feature type:\n"); StrCat(buffer, sizeof(buffer), " anticamp\n"); StrCat(buffer, sizeof(buffer), " classedit\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[512]; 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); } 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); } // Send enable event to volume. VolOnEnabled(volindex); 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; // Check if privileged. if (!ZRIsClientPrivileged(client, OperationType_Configuration)) { TranslationReplyToCommand(client, "No access to command"); return Plugin_Handled; } if (argc < 1) { // Write syntax info. ReplyToCommand(client, "Removes an existing volume in the map. Usage: zr_vol_remove "); return Plugin_Handled; } // Get volume index. GetCmdArg(1, arg, sizeof(arg)); volindex = StringToInt(arg); // Validate index. if (!VolIsValidIndex(volindex)) { ReplyToCommand(client, "Invalid volume index."); return Plugin_Handled; } // Check if volume exist. if (!Volumes[volindex][Vol_InUse]) { ReplyToCommand(client, "Volume %d doesn't exist.", volindex); return Plugin_Handled; } // Remove volume. VolRemove(volindex); ReplyToCommand(client, "Successfully disabled and removed volume %d.", volindex); return Plugin_Handled; } /** * Command callback for listing volumes or dumping data. */ public Action:VolListCommand(client, argc) { decl String:buffer[1022]; // Two chars reserved for newline and null terminator. decl String:linebuffer[128]; decl String:valuebuffer[32]; decl String:arg[16]; buffer[0] = 0; linebuffer[0] = 0; new volindex; new volcount; new volcache[VolumeAttributes]; if (argc < 1) { // No volume specified. Display syntax and list volumes. StrCat(buffer, sizeof(buffer), "Lists existing volumes in the map, or dumps detail data to the specified volume. Usage: zr_vol_list [volume index]\n\n"); StrCat(buffer, sizeof(buffer), "ID: Type: Min loc: Max loc:\n"); StrCat(buffer, sizeof(buffer), "--------------------------------------------------------------------------------"); ReplyToCommand(client, buffer); // Loop through all indexes. for (volindex = 0; volindex < ZR_VOLUMES_MAX; volindex++) { // Check if in use. if (Volumes[volindex][Vol_InUse]) { // Cache volume data. volcache = Volumes[volindex]; // Add to list. VolTypeToString(volcache[Vol_Type], valuebuffer, sizeof(valuebuffer), true); Format(linebuffer, sizeof(linebuffer), "%-4d %-15s %-8.2f %-8.2f %-8.2f %-8.2f %-8.2f %-8.2f", volindex, valuebuffer, volcache[Vol_xMin], volcache[Vol_yMin], volcache[Vol_zMin], volcache[Vol_xMax], volcache[Vol_yMax], volcache[Vol_zMax]); ReplyToCommand(client, linebuffer); volcount++; } } Format(linebuffer, sizeof(linebuffer), "\nTotal volumes: %d", volcount); ReplyToCommand(client, linebuffer); return Plugin_Handled; } else { // Dump data for the specified volume. // Get volume index. GetCmdArg(1, arg, sizeof(arg)); volindex = StringToInt(arg); // Validate index. if (!VolIsValidIndex(volindex)) { ReplyToCommand(client, "The specified volume index is invalid: %d", volindex); return Plugin_Handled; } // Check if unused. if (!VolInUse(volindex)) { ReplyToCommand(client, "The specified volume doesn't exist: %d.", volindex); return Plugin_Handled; } // Cache volume data. volcache = Volumes[volindex]; // Dump generic volume data. Format(linebuffer, sizeof(linebuffer), "Volume data at index %d:\n", volindex); StrCat(buffer, sizeof(buffer), linebuffer); StrCat(buffer, sizeof(buffer), "--------------------------------------------------------------------------------"); ReplyToCommand(client, buffer); // Clear buffer. buffer[0] = 0; Format(linebuffer, sizeof(linebuffer), "ID: %d\n", volindex); StrCat(buffer, sizeof(buffer), linebuffer); Format(linebuffer, sizeof(linebuffer), "Enabled: %d\n", volcache[Vol_Enabled]); StrCat(buffer, sizeof(buffer), linebuffer); VolTypeToString(volcache[Vol_Type], valuebuffer, sizeof(valuebuffer)); Format(linebuffer, sizeof(linebuffer), "Type: %s\n", valuebuffer); StrCat(buffer, sizeof(buffer), linebuffer); Format(linebuffer, sizeof(linebuffer), "Min loc: %-8.2f %-8.2f %-8.2f\n", volcache[Vol_xMin], volcache[Vol_yMin], volcache[Vol_zMin]); StrCat(buffer, sizeof(buffer), linebuffer); Format(linebuffer, sizeof(linebuffer), "Max loc: %-8.2f %-8.2f %-8.2f\n", volcache[Vol_xMax], volcache[Vol_yMax], volcache[Vol_zMax]); StrCat(buffer, sizeof(buffer), linebuffer); VolEffectToString(volcache[Vol_Effect], valuebuffer, sizeof(valuebuffer)); Format(linebuffer, sizeof(linebuffer), "Effect: %s\n", valuebuffer); StrCat(buffer, sizeof(buffer), linebuffer); Format(linebuffer, sizeof(linebuffer), "Effect color: %d, %d, %d\n", volcache[Vol_EffectColor][0], volcache[Vol_EffectColor][1], volcache[Vol_EffectColor][2]); StrCat(buffer, sizeof(buffer), linebuffer); VolTeamToString(volcache[Vol_TeamFilter], valuebuffer, sizeof(valuebuffer)); Format(linebuffer, sizeof(linebuffer), "Team filter: %s\n", valuebuffer); StrCat(buffer, sizeof(buffer), linebuffer); Format(linebuffer, sizeof(linebuffer), "Trigger delay: %.2f", volcache[Vol_TriggerDelay]); StrCat(buffer, sizeof(buffer), linebuffer); // Print generic attributes. ReplyToCommand(client, buffer); // Clear buffer. buffer[0] = 0; // Get type spesific attributes. switch (volcache[Vol_Type]) { case VolFeature_Anticamp: { VolAnticampDumpData(volcache[Vol_DataIndex], buffer, sizeof(buffer)); } case VolFeature_ClassEdit: { VolClassEditDumpData(volcache[Vol_DataIndex], buffer, sizeof(buffer)); } } // 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; // Check if privileged. if (!ZRIsClientPrivileged(client, OperationType_Generic)) { TranslationReplyToCommand(client, "No access to command"); return Plugin_Handled; } if (argc < 1) { ReplyToCommand(client, "Dumps volume states for the specified player. Usage: zr_vol_dumpstates "); return Plugin_Handled; } // Get target. GetCmdArg(1, target, sizeof(target)); targetclient = FindTarget(client, target); // Validate target. if (targetclient <= 0) { // Note: FindTarget automatically print error messages. return Plugin_Handled; } // Print header. ReplyToCommand(client, "Volume ID: Player in volume:\n----------------------------------------"); // Get player states. new bool:statebuffer[ZR_VOLUMES_MAX]; VolGetPlayerStates(targetclient, statebuffer, sizeof(statebuffer)); // Set language. SetGlobalTransTarget(client); // Loop through each volume. for (new volumeindex = 0; volumeindex < ZR_VOLUMES_MAX; volumeindex++) { // Check if volume is in use. if (VolInUse(volumeindex)) { // Dump state. ReplyToCommand(client, "%-11d %t", volumeindex, statebuffer[volumeindex] ? "Yes" : "No"); } } return Plugin_Handled; } /** * Creates a new volume with minimum parameters. * * Note: Extra volume attributes must be set using VolSetAttributes. * * @param index Optional. Add volume at the specified index. * @param locMin Minimum x, y and z values. * @param locMax Maximum x, y and z values. * @param volumeType Specifies the volumetric feature type. * @param dataIndex Data index in remote array for feature data. * * @return The new volume index, or -1 if failed. */ VolAdd(volumeIndex = -1, Float:locMin[3], Float:locMax[3], VolumeFeatureTypes:volumeType, dataIndex) { if (volumeIndex < 0) { // Get a free volume index. volumeIndex = VolGetFreeVolume(); } // Validate index. if (VolIsValidIndex(volumeIndex)) { // Mark volume as enabled and in use. Volumes[volumeIndex][Vol_Enabled] = true; Volumes[volumeIndex][Vol_InUse] = true; // Set location data. Volumes[volumeIndex][Vol_xMin] = locMin[0]; Volumes[volumeIndex][Vol_yMin] = locMin[1]; Volumes[volumeIndex][Vol_zMin] = locMin[2]; Volumes[volumeIndex][Vol_xMax] = locMax[0]; Volumes[volumeIndex][Vol_yMax] = locMax[1]; Volumes[volumeIndex][Vol_zMax] = locMax[2]; // Set type. Volumes[volumeIndex][Vol_Type] = volumeType; Volumes[volumeIndex][Vol_DataIndex] = dataIndex; // Update number of volumes. VolumeCount++; // Return the new index. return volumeIndex; } else { // No free volumes or invalid index. return -1; } } /** * Removes the specified volume. * * @param volumeIndex The volume index. * @return True if successful, false otherwise. */ bool:VolRemove(volumeIndex) { // Validate index. if (VolIsValidIndex(volumeIndex)) { // Trigger event to clean up data and stop timers. VolOnDisabled(volumeIndex); // Clear feature data. switch (Volumes[volumeIndex][Vol_Type]) { case VolFeature_Anticamp: { VolAnticampReset(Volumes[volumeIndex][Vol_DataIndex]); } } // Clear volume data. VolClear(volumeIndex); return true; } else { // Invalid index. return false; } } /** * 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_Effect] = VolEffect_None; Volumes[volumeIndex][Vol_EffectColor][0] = 0; Volumes[volumeIndex][Vol_EffectColor][1] = 0; Volumes[volumeIndex][Vol_EffectColor][2] = 0; new dataindex = Volumes[volumeIndex][Vol_DataIndex]; if (dataindex >= 0) { switch (Volumes[volumeIndex][Vol_Type]) { case VolFeature_Anticamp: { VolAnticampReset(dataindex); } case VolFeature_ClassEdit: { VolClassEditReset(dataindex); } } } Volumes[volumeIndex][Vol_Type] = VolFeature_Invalid; Volumes[volumeIndex][Vol_DataIndex] = -1; Volumes[volumeIndex][Vol_TeamFilter] = VolTeam_All; Volumes[volumeIndex][Vol_TriggerDelay] = 0.0; } /** * Clears all volumes. */ VolClearAll() { for (new volindex = 0; volindex < ZR_VOLUMES_MAX; volindex++) { VolClear(volindex); } } /** * Sets extra attributes on a volume. * * @param volumeIndex The volume index. * @param attributes A string with one or more attributes in key=value * format. * @return Number of successful attributes set, -1 on error. */ VolSetAttributes(volumeIndex, const String:attributes[]) { new attribCount; new successfulCount; new VolumeFeatureTypes:voltype; new dataindex; decl String:attribName[64]; decl String:attribValue[256]; // Validate volume index. if (!VolIsValidIndex(volumeIndex)) { return -1; } // Count attributes. attribCount = GetParameterCount(attributes); // Check if empty. if (!attribCount) { return -1; } // Get volumetric feature type. voltype = Volumes[volumeIndex][Vol_Type]; // Get feature data index. dataindex = Volumes[volumeIndex][Vol_DataIndex]; // Loop through all attributes. for (new attrib = 0; attrib < attribCount; attrib++) { // Get attribute name. GetParameterName(attribName, sizeof(attribName), attributes, attrib); // Get attribute value. GetParameterValue(attribValue, sizeof(attribValue), attributes, attribName); LogEvent(_, LogType_Normal, LOG_DEBUG, LogModule_Volfeatures, "Set attribute", "Got parameter: \"%s\" = \"%s\"", attribName, attribValue); // Check generic attributes. if (StrEqual(attribName, "teamfilter", false)) { // Parse team string value. if (VolSetTeamString(volumeIndex, attribValue)) { successfulCount++; } } else if (StrEqual(attribName, "delay", false)) { // Parse delay string value. if (VolSetDelayString(volumeIndex, attribValue)) { successfulCount++; } } else if (StrEqual(attribName, "effect", false)) { // Parse effect string value. if (VolSetEffectString(volumeIndex, attribValue)) { successfulCount++; } } else if (StrEqual(attribName, "effect_color", false)) { // Parse effect color string value. if (VolSetEffectColorString(volumeIndex, attribValue)) { successfulCount++; } } else if (StrEqual(attribName, "enabled", false)) { // Parse enabled string value. if (VolSetEnabledString(volumeIndex, attribValue)) { successfulCount++; } } // Pass attribute onto the volumetric feature attribute handler. else { switch (voltype) { case VolFeature_Anticamp: { if (VolAnticampSetAttribute(dataindex, attribName, attribValue)) { successfulCount++; } } case VolFeature_ClassEdit: { if (VolClassEditSetAttribute(dataindex, attribName, attribValue)) { successfulCount++; } } } } } // Return number of successfully attributes set. return successfulCount; }