844e23b293
Added flag on some timers so we are sure they are killed on map change. Added stop event on health regeneration timer when applying classes, if the class has no regeneration. Cvar zr_classes_default_admin is now replaced by admin-only classes. Old cvar is zr_classes_default_admin_mode.
607 lines
17 KiB
SourcePawn
607 lines
17 KiB
SourcePawn
/*
|
|
* ============================================================================
|
|
*
|
|
* Zombie:Reloaded
|
|
*
|
|
* File: volfeatures.inc
|
|
* Type: Module
|
|
* Description: Volumetric feature manager.
|
|
*
|
|
* 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/>.
|
|
*
|
|
* ============================================================================
|
|
*/
|
|
|
|
/**
|
|
* Total volumes that can be created in a map.
|
|
*/
|
|
#define ZR_VOLUMES_MAX 32
|
|
|
|
/**
|
|
* Represent a rectangular volume.
|
|
*/
|
|
enum VolumeAttributes
|
|
{
|
|
/* General */
|
|
bool:Vol_Enabled, /** Volume state. */
|
|
bool:Vol_InUse, /** Marks if the volume is used. */
|
|
|
|
/* Location */
|
|
Float:Vol_xMin, /** Minimum x position. */
|
|
Float:Vol_xMax, /** Maximum x position. */
|
|
|
|
Float:Vol_yMin, /** Minimum y position. */
|
|
Float:Vol_yMax, /** Maximum y position. */
|
|
|
|
Float:Vol_zMin, /** Minimum z position. */
|
|
Float:Vol_zMax, /** Maximum z position. */
|
|
|
|
/* Style */
|
|
VolumeEffects:Vol_Effect, /** Visual effect to apply on the volume. */
|
|
Vol_EffectColor[3], /** Render color of the effect. RGB colors. */
|
|
|
|
/* Data */
|
|
VolumeFeatureTypes:Vol_Type, /** The volumetric feature type. */
|
|
Vol_DataIndex, /** Index in remote feature array. */
|
|
|
|
/* Behaviour */
|
|
VolumeTeamFilters:Vol_TeamFilter, /** Team filtering. Trigger by certain teams, or all. */
|
|
Float:Vol_TriggerDelay, /** Trigger delay. How many seconds players have to stay to trigger volume events. */
|
|
VolumeConflictActions:Vol_ConflictAction, /** What to do if volumes of same type overlap. */
|
|
Vol_Priority, /** Volume priority. */
|
|
}
|
|
|
|
/**
|
|
* Available volumetric feature types.
|
|
*/
|
|
enum VolumeFeatureTypes
|
|
{
|
|
VolFeature_Invalid = 0,
|
|
VolFeature_Anticamp,
|
|
VolFeature_ClassEdit
|
|
}
|
|
|
|
/**
|
|
* Effects that can be applied on a volume. (Currently no effects.)
|
|
*/
|
|
enum VolumeEffects
|
|
{
|
|
VolEffect_None = 0,
|
|
VolEffect_Wireframe,
|
|
VolEffect_Smoke
|
|
}
|
|
|
|
/**
|
|
* Available team filter settings.
|
|
*/
|
|
enum VolumeTeamFilters
|
|
{
|
|
VolTeam_All = 0,
|
|
VolTeam_Humans,
|
|
VolTeam_Zombies
|
|
}
|
|
|
|
/**
|
|
* Conflict actions. What to do with overlapping volumes of same type.
|
|
*/
|
|
enum VolumeConflictActions
|
|
{
|
|
VolPriority = 0, /** Use the volume with highest priority, ignore others. */
|
|
VolMerge /** Try to merge volume settings/attributes, or use priority if there's a merge conflict. */
|
|
}
|
|
|
|
/**
|
|
* Volumes.
|
|
*/
|
|
new Volumes[ZR_VOLUMES_MAX][VolumeAttributes];
|
|
|
|
/**
|
|
* Total number of volumes.
|
|
*/
|
|
new VolumeCount;
|
|
|
|
/**
|
|
* List of player locations. Updated by a timer.
|
|
*/
|
|
new Float:VolPlayerLoc[MAXPLAYERS + 1][3];
|
|
|
|
/**
|
|
* Cache that specifies if a player is in a volume or not.
|
|
*/
|
|
new bool:VolPlayerInVolume[MAXPLAYERS + 1][ZR_VOLUMES_MAX];
|
|
|
|
/**
|
|
* Specifies whether the volumetric features module is enabled or not. Synced
|
|
* with zr_vol CVAR.
|
|
*/
|
|
new bool:VolEnabled;
|
|
|
|
/**
|
|
* Counter for trigger delay.
|
|
*/
|
|
new Float:VolPlayerCountDown[MAXPLAYERS + 1][ZR_VOLUMES_MAX];
|
|
|
|
/**
|
|
* The handle for a timer that updates player locations. This is the main timer
|
|
* and any feature events can't be updated faster than this interval.
|
|
*
|
|
* Note: Some features may have its own timer for actions on players.
|
|
*/
|
|
new Handle:hVolUpdateTimer;
|
|
|
|
/**
|
|
* The handle for a timer that do count down on trigger delays.
|
|
*/
|
|
new Handle:hVolTriggerTimer;
|
|
|
|
/**
|
|
* Cached interval value for trigger timer.
|
|
*/
|
|
new Float:VolTriggerInterval;
|
|
|
|
#include "zr/volfeatures/voltools"
|
|
#include "zr/volfeatures/volevents"
|
|
#include "zr/volfeatures/volgenericattributes"
|
|
#include "zr/volfeatures/volcommands"
|
|
|
|
// Sub features.
|
|
#include "zr/volfeatures/volanticamp"
|
|
#include "zr/volfeatures/volclassedit"
|
|
|
|
|
|
/**
|
|
* Initialize volumetric features.
|
|
*/
|
|
VolInit()
|
|
{
|
|
// Clear all volumes.
|
|
VolClearAll();
|
|
|
|
// Initialize sub features.
|
|
VolAnticampInit();
|
|
VolClassEditInit();
|
|
}
|
|
|
|
/**
|
|
* Initialize volumetric feature settings.
|
|
*/
|
|
VolLoad()
|
|
{
|
|
// Cache CVAR value.
|
|
VolEnabled = GetConVarBool(g_hCvarsList[CVAR_VOL]);
|
|
}
|
|
|
|
/**
|
|
* Function alias for fully stopping volumetric features.
|
|
*/
|
|
VolDisable()
|
|
{
|
|
VolEnabled = false;
|
|
VolStopUpdateTimer();
|
|
VolDisableVolumes();
|
|
|
|
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])
|
|
{
|
|
// Mark as disabled.
|
|
Volumes[volindex][Vol_Enabled] = false;
|
|
|
|
// Trigger player left volume event if inside a volume.
|
|
for (new client = 1; client <= MaxClients; client++)
|
|
{
|
|
// Validate client's connection state.
|
|
if (!IsClientConnected(client) || !IsClientInGame(client))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Check if player is inside the volume.
|
|
if (VolPlayerInVolume[client][volindex])
|
|
{
|
|
// Mark as not in the volume and trigger event.
|
|
VolPlayerInVolume[client][volindex] = false;
|
|
VolOnPlayerLeave(client, volindex);
|
|
}
|
|
}
|
|
|
|
// Trigger disabled event.
|
|
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])
|
|
{
|
|
Volumes[volindex][Vol_Enabled] = true;
|
|
VolOnEnabled(volindex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts the update timer.
|
|
*
|
|
* @return True if timer is started, false otherwise.
|
|
*/
|
|
bool:VolStartUpdateTimer()
|
|
{
|
|
// Check if volumetric features is enabled.
|
|
if (!VolEnabled)
|
|
{
|
|
// Volumetric features disabled.
|
|
return false;
|
|
}
|
|
|
|
// Stop timer if it exist.
|
|
VolStopUpdateTimer();
|
|
|
|
// Get update interval.
|
|
new Float:interval = GetConVarFloat(g_hCvarsList[CVAR_VOL_UPDATE_INTERVAL]);
|
|
|
|
// Validate interval.
|
|
if (interval > 0.0)
|
|
{
|
|
// Create a new timer.
|
|
hVolUpdateTimer = CreateTimer(interval, Event_VolUpdateTimer, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
|
|
|
|
// Also start the trigger delay timer.
|
|
VolStartTriggerTimer();
|
|
|
|
// Volumetric features started.
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Volumetric features disabled.
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Volfeatures, "Config Validation", "Warning: Console variable \"zr_vol_update_interval\" is zero or negative. Must be positive.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Kills the update timer if it exists.
|
|
*/
|
|
VolStopUpdateTimer()
|
|
{
|
|
// Kill the timer if it's running.
|
|
if (hVolUpdateTimer != INVALID_HANDLE)
|
|
{
|
|
KillTimer(hVolUpdateTimer);
|
|
hVolUpdateTimer = INVALID_HANDLE;
|
|
}
|
|
|
|
// Also stop trigger delay timer.
|
|
VolStopTriggerTimer();
|
|
|
|
// Reset all trigger delay counters.
|
|
VolResetCountDown();
|
|
}
|
|
|
|
/**
|
|
* Starts the update timer if it exists.
|
|
*
|
|
* @return True if timer is started, false otherwise.
|
|
*/
|
|
bool:VolStartTriggerTimer()
|
|
{
|
|
// Make sure existing timer is killed.
|
|
VolStopTriggerTimer();
|
|
|
|
// Get trigger interval and cache it.
|
|
VolTriggerInterval = GetConVarFloat(g_hCvarsList[CVAR_VOL_TRIGGER_INTERVAL]);
|
|
|
|
// Validate interval.
|
|
if (VolTriggerInterval > 0.0)
|
|
{
|
|
// Start the timer.
|
|
hVolTriggerTimer = CreateTimer(VolTriggerInterval, Event_VolTriggerTimer, _, TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE);
|
|
|
|
// Trigger timer started.
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// Trigger timer not running. Either disabled or invalid interval.
|
|
LogEvent(false, LogType_Error, LOG_CORE_EVENTS, LogModule_Volfeatures, "Config Validation", "Warning: Console variable \"zr_vol_trigger_interval\" is zero or negative. Must be positive.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Kills the trigger delay timer if it exists.
|
|
*/
|
|
VolStopTriggerTimer()
|
|
{
|
|
// Kill the timer if it's running.
|
|
if (hVolTriggerTimer != INVALID_HANDLE)
|
|
{
|
|
KillTimer(hVolTriggerTimer);
|
|
hVolTriggerTimer = INVALID_HANDLE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets volume trigger delay counters on one or more players.
|
|
*
|
|
* @param client Optional. Specifies a single player to reset. Default is
|
|
* -1, all players.
|
|
*/
|
|
VolResetCountDown(client = -1)
|
|
{
|
|
// Check if a client is specified.
|
|
if (client > -1)
|
|
{
|
|
// Reset volume counters.
|
|
for (new volumeIndex = 0; volumeIndex < ZR_VOLUMES_MAX; volumeIndex++)
|
|
{
|
|
VolPlayerCountDown[client][volumeIndex] = -1.0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Reset all volume counters.
|
|
for (new clientIndex = 0; clientIndex < MAXPLAYERS + 1; clientIndex++)
|
|
{
|
|
for (new volumeIndex = 0; volumeIndex < ZR_VOLUMES_MAX; volumeIndex++)
|
|
{
|
|
VolPlayerCountDown[clientIndex][volumeIndex] = -1.0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates all player locations. Used for initialization.
|
|
*
|
|
* Note: If a client is specified, it's NOT validated. This function assumes
|
|
* the specified client is in game and alive.
|
|
*
|
|
* @param client Optional. Specify single client to be updated. Default is
|
|
* -1.
|
|
*/
|
|
VolUpdatePlayerLocation(client = -1)
|
|
{
|
|
if (client > 0)
|
|
{
|
|
// Assume the client is valid and save location in array.
|
|
GetClientAbsOrigin(client, VolPlayerLoc[client]);
|
|
}
|
|
else
|
|
{
|
|
for (client = 1; client <= MaxClients; client++)
|
|
{
|
|
// Validate client's connection state.
|
|
if (!IsClientConnected(client) || !IsClientInGame(client) || !IsPlayerAlive(client))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Save location in array.
|
|
GetClientAbsOrigin(client, VolPlayerLoc[client]);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates player locations and trigger events for each player that enter or
|
|
* leave a volume.
|
|
*/
|
|
VolUpdatePlayerChanges()
|
|
{
|
|
new bool:volumeStates[ZR_VOLUMES_MAX];
|
|
new bool:volumeNewStates[ZR_VOLUMES_MAX];
|
|
|
|
new bool:newState;
|
|
new bool:oldState;
|
|
|
|
new Float:trigger_delay;
|
|
|
|
// Loop through all players.
|
|
for (new client = 1; client <= MaxClients; client++)
|
|
{
|
|
// Validate client's connection state.
|
|
if (!IsClientConnected(client) || !IsClientInGame(client) || !IsPlayerAlive(client))
|
|
{
|
|
// Skip client.
|
|
continue;
|
|
}
|
|
|
|
// Get the current volume states based on player location cache.
|
|
VolGetPlayerStates(client, volumeStates, sizeof(volumeStates));
|
|
|
|
// Update player location cache.
|
|
GetClientAbsOrigin(client, VolPlayerLoc[client]);
|
|
|
|
// Get new volume states.
|
|
VolGetPlayerStates(client, volumeNewStates, sizeof(volumeNewStates));
|
|
|
|
// Loop through each volume and compare states.
|
|
for (new volumeIndex = 0; volumeIndex < ZR_VOLUMES_MAX; volumeIndex++)
|
|
{
|
|
// Check if the volume is disabled and unused.
|
|
if (!VolInUse(volumeIndex) || !VolIsEnabled(volumeIndex))
|
|
{
|
|
// Skip volume.
|
|
continue;
|
|
}
|
|
|
|
// Check team filtering on the volume.
|
|
if (!VolTeamFilterMatch(client, volumeIndex))
|
|
{
|
|
// Team filter mismatch.
|
|
continue;
|
|
}
|
|
|
|
newState = volumeNewStates[volumeIndex];
|
|
oldState = volumeStates[volumeIndex];
|
|
|
|
// Check for no change.
|
|
if (newState == oldState)
|
|
{
|
|
// No change. Skip to next volume.
|
|
continue;
|
|
}
|
|
|
|
// Check if client entered the volume.
|
|
if (newState && !oldState)
|
|
{
|
|
// Get trigger delay value.
|
|
trigger_delay = Volumes[volumeIndex][Vol_TriggerDelay];
|
|
|
|
// Check if the volume has a trigger delay.
|
|
if (trigger_delay > 0.0)
|
|
{
|
|
// Set count down value.
|
|
VolPlayerCountDown[client][volumeIndex] = trigger_delay;
|
|
}
|
|
else
|
|
{
|
|
// Update cache.
|
|
VolPlayerInVolume[client][volumeIndex] = true;
|
|
|
|
// No trigger delay, trigger event instantly.
|
|
VolOnPlayerEnter(client, volumeIndex);
|
|
}
|
|
}
|
|
|
|
// Check if client left the volume.
|
|
else if (!newState && oldState)
|
|
{
|
|
// Make sure count down value is reset.
|
|
VolPlayerCountDown[client][volumeIndex] = -1.0;
|
|
|
|
// Only trigger left volume event if player already is in the
|
|
// volume, so volumes with trigger delay won't get a left event
|
|
// before the enter event.
|
|
if (VolPlayerInVolume[client][volumeIndex])
|
|
{
|
|
// Update cache.
|
|
VolPlayerInVolume[client][volumeIndex] = false;
|
|
|
|
// Trigger event.
|
|
VolOnPlayerLeave(client, volumeIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback for update timer. This is the main timer in volumetric features.
|
|
*/
|
|
public Action:Event_VolUpdateTimer(Handle:timer)
|
|
{
|
|
VolUpdatePlayerChanges();
|
|
}
|
|
|
|
/**
|
|
* Callback for trigger delay timer.
|
|
*/
|
|
public Action:Event_VolTriggerTimer(Handle:timer)
|
|
{
|
|
new Float:countDown;
|
|
|
|
// 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 volume is in use and enabled.
|
|
if (!VolInUse(volumeIndex) || !VolIsEnabled(volumeIndex))
|
|
{
|
|
// Not in use or enabled, skip volume.
|
|
continue;
|
|
}
|
|
|
|
// Get count down value.
|
|
countDown = VolPlayerCountDown[client][volumeIndex];
|
|
|
|
// Check if volume trigger delay is enabled.
|
|
if (countDown > 0.0)
|
|
{
|
|
// Substract by trigger interval.
|
|
countDown -= VolTriggerInterval;
|
|
|
|
// Check if time is up.
|
|
if (countDown <= 0.0)
|
|
{
|
|
// Update cache.
|
|
VolPlayerInVolume[client][volumeIndex] = true;
|
|
|
|
// Trigger volume enter event.
|
|
VolOnPlayerEnter(client, volumeIndex);
|
|
|
|
// Reset count down value.
|
|
VolPlayerCountDown[client][volumeIndex] = -1.0;
|
|
}
|
|
|
|
// Update count down value and continue.
|
|
VolPlayerCountDown[client][volumeIndex] = countDown;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Called when zr_vol CVAR is changed.
|
|
*/
|
|
public VolEnabledChanged(Handle:cvar, const String:oldvalue[], const String:newvalue[])
|
|
{
|
|
new bool:isEnabled = bool:StringToInt(newvalue);
|
|
|
|
if (isEnabled)
|
|
{
|
|
// Volumetric features is enabled.
|
|
VolEnable();
|
|
}
|
|
else
|
|
{
|
|
// Volumetric features is disabled.
|
|
VolDisable();
|
|
}
|
|
}
|