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

607 lines
17 KiB
SourcePawn

/*
* ============================================================================
*
* Zombie:Reloaded
*
* File: volfeatures.inc
* Type: Module
* Description: Volumetric feature manager.
*
* 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/>.
*
* ============================================================================
*/
/**
* 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 = INVALID_HANDLE;
/**
* The handle for a timer that do count down on trigger delays.
*/
new Handle:hVolTriggerTimer = INVALID_HANDLE;
/**
* 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();
}
}