2009-05-16 01:37:23 +02:00
/*
* ============================================================================
*
2009-05-30 04:17:01 +02:00
* Zombie : Reloaded
2009-05-16 01:37:23 +02:00
*
2009-05-30 04:17:01 +02:00
* File : volfeatures . inc
* Type : Module
2009-10-08 00:49:31 +02:00
* Description : Volumetric feature manager .
2009-05-30 04:17:01 +02:00
*
* 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 />.
2009-05-16 01:37:23 +02:00
*
* ============================================================================
*/
/**
* Total volumes that can be created in a map .
*/
2009-10-08 00:49:31 +02:00
#define ZR_VOLUMES_MAX 32
2009-05-16 01:37:23 +02:00
/**
* Represent a rectangular volume .
*/
enum VolumeAttributes
{
/* General */
2009-09-27 23:45:17 +02:00
bool : Vol_Enabled , /** Volume state. */
bool : Vol_InUse , /** Marks if the volume is used. */
2009-05-16 01:37:23 +02:00
/* Location */
2009-09-27 23:45:17 +02:00
Float : Vol_xMin , /** Minimum x position. */
Float : Vol_xMax , /** Maximum x position. */
2009-05-16 01:37:23 +02:00
2009-09-27 23:45:17 +02:00
Float : Vol_yMin , /** Minimum y position. */
Float : Vol_yMax , /** Maximum y position. */
2009-05-16 01:37:23 +02:00
2009-09-27 23:45:17 +02:00
Float : Vol_zMin , /** Minimum z position. */
Float : Vol_zMax , /** Maximum z position. */
2009-05-30 04:17:01 +02:00
/* Style */
2009-09-27 23:45:17 +02:00
VolumeEffects : Vol_Effect , /** Visual effect to apply on the volume. */
Vol_EffectColor [ 3 ], /** Render color of the effect. RGB colors. */
2009-05-16 01:37:23 +02:00
/* Data */
2009-09-27 23:45:17 +02:00
VolumeFeatureTypes : Vol_Type , /** The volumetric feature type. */
Vol_DataIndex , /** Index in remote feature array. */
2009-05-30 04:17:01 +02:00
/* Behaviour */
2009-09-27 23:45:17 +02:00
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. */
2009-10-08 02:59:42 +02:00
VolumeConflictActions : Vol_ConflictAction , /** What to do if volumes of same type overlap. */
2009-09-27 23:45:17 +02:00
Vol_Priority , /** Volume priority. */
2009-05-16 01:37:23 +02:00
}
/**
* Available volumetric feature types .
*/
enum VolumeFeatureTypes
{
2009-07-16 10:05:40 +02:00
VolFeature_Invalid = 0 ,
VolFeature_Anticamp ,
2009-09-27 23:45:17 +02:00
VolFeature_ClassEdit
2009-05-16 01:37:23 +02:00
}
2009-05-30 04:17:01 +02:00
/**
* Effects that can be applied on a volume . ( Currently no effects . )
*/
2009-05-16 01:37:23 +02:00
enum VolumeEffects
{
VolEffect_None = 0 ,
VolEffect_Wireframe ,
VolEffect_Smoke
}
2009-05-30 04:17:01 +02:00
/**
* Available team filter settings .
*/
enum VolumeTeamFilters
{
VolTeam_All = 0 ,
VolTeam_Humans ,
VolTeam_Zombies
}
2009-09-27 23:45:17 +02:00
/**
* Conflict actions . What to do with overlapping volumes of same type .
*/
enum VolumeConflictActions
{
VolPriority = 0 , /** Use the volume with highest priority, ignore others. */
2009-10-08 02:59:42 +02:00
VolMerge /** Try to merge volume settings/attributes, or use priority if there's a merge conflict. */
2009-09-27 23:45:17 +02:00
}
2009-05-16 01:37:23 +02:00
/**
* 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 ];
2009-07-16 10:05:40 +02:00
/**
* Cache that specifies if a player is in a volume or not .
*/
new bool : VolPlayerInVolume [ MAXPLAYERS + 1 ][ ZR_VOLUMES_MAX ];
2009-05-16 01:37:23 +02:00
/**
2009-05-30 04:17:01 +02:00
* Specifies whether the volumetric features module is enabled or not . Synced
* with zr_vol CVAR .
2009-05-16 01:37:23 +02:00
*/
new bool : VolEnabled ;
/**
2009-05-30 04:17:01 +02:00
* 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
2009-05-16 01:37:23 +02:00
* and any feature events can ' t be updated faster than this interval .
*
2009-07-16 10:05:40 +02:00
* Note : Some features may have its own timer for actions on players .
2009-05-16 01:37:23 +02:00
*/
new Handle : hVolUpdateTimer ;
2009-05-30 04:17:01 +02:00
/**
* The handle for a timer that do count down on trigger delays .
*/
new Handle : hVolTriggerTimer ;
/**
* Cached interval value for trigger timer .
*/
new Float : VolTriggerInterval ;
2009-10-08 00:49:31 +02:00
#include "zr/volfeatures/voltools"
2009-05-16 01:37:23 +02:00
#include "zr/volfeatures/volevents"
2009-05-30 04:17:01 +02:00
#include "zr/volfeatures/volgenericattributes"
#include "zr/volfeatures/volcommands"
2009-07-16 10:05:40 +02:00
// Sub features.
2009-05-16 01:37:23 +02:00
#include "zr/volfeatures/volanticamp"
2009-09-27 23:45:17 +02:00
#include "zr/volfeatures/volclassedit"
2009-05-16 01:37:23 +02:00
2009-05-30 04:17:01 +02:00
/**
* Initialize volumetric features .
*/
2009-09-27 23:45:17 +02:00
VolInit ()
2009-05-30 04:17:01 +02:00
{
2009-09-27 23:45:17 +02:00
// Clear all volumes.
VolClearAll ();
2009-07-16 10:05:40 +02:00
// Initialize sub features.
VolAnticampInit ();
2009-09-27 23:45:17 +02:00
VolClassEditInit ();
}
/**
* Initialize volumetric feature settings .
*/
VolLoad ()
{
// Cache CVAR value.
VolEnabled = GetConVarBool ( g_hCvarsList [ CVAR_VOL ]);
2009-05-30 04:17:01 +02:00
}
/**
* Function alias for fully stopping volumetric features .
*/
VolDisable ()
{
VolEnabled = false ;
VolStopUpdateTimer ();
2009-07-16 10:05:40 +02:00
VolDisableVolumes ();
2009-05-30 04:17:01 +02:00
2009-07-16 10:05:40 +02:00
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 ])
{
2009-07-17 18:15:44 +02:00
// 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.
2009-07-16 10:05:40 +02:00
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 ])
{
2009-07-17 18:15:44 +02:00
Volumes [ volindex ][ Vol_Enabled ] = true ;
2009-07-16 10:05:40 +02:00
VolOnEnabled ( volindex );
}
}
2009-05-30 04:17:01 +02:00
}
2009-05-16 01:37:23 +02:00
/**
* Starts the update timer .
2009-05-30 04:17:01 +02:00
*
* @ return True if timer is started , false otherwise .
2009-05-16 01:37:23 +02:00
*/
bool : VolStartUpdateTimer ()
{
2009-05-30 04:17:01 +02:00
// Check if volumetric features is enabled.
2009-05-16 01:37:23 +02:00
if ( ! VolEnabled )
{
// Volumetric features disabled.
return false ;
}
2009-05-30 04:17:01 +02:00
// Stop timer if it exist.
VolStopUpdateTimer ();
// Get update interval.
new Float : interval = GetConVarFloat ( g_hCvarsList [ CVAR_VOL_UPDATE_INTERVAL ]);
2009-05-16 01:37:23 +02:00
// Validate interval.
if ( interval > 0.0 )
{
// Create a new timer.
2009-10-31 03:39:00 +01:00
hVolUpdateTimer = CreateTimer ( interval , Event_VolUpdateTimer , _ , TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE );
2009-05-16 01:37:23 +02:00
2009-05-30 04:17:01 +02:00
// Also start the trigger delay timer.
VolStartTriggerTimer ();
2009-05-16 01:37:23 +02:00
// Volumetric features started.
return true ;
}
else
{
2009-07-16 10:05:40 +02:00
// Volumetric features disabled.
2009-09-27 23:45:17 +02:00
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. " );
2009-05-16 01:37:23 +02:00
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 ;
}
2009-05-30 04:17:01 +02:00
// 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.
2009-10-31 03:39:00 +01:00
hVolTriggerTimer = CreateTimer ( VolTriggerInterval , Event_VolTriggerTimer , _ , TIMER_REPEAT | TIMER_FLAG_NO_MAPCHANGE );
2009-05-30 04:17:01 +02:00
// Trigger timer started.
return true ;
}
else
{
// Trigger timer not running. Either disabled or invalid interval.
2009-09-27 23:45:17 +02:00
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. " );
2009-05-30 04:17:01 +02:00
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.
2009-06-03 01:37:37 +02:00
for ( new clientIndex = 0 ; clientIndex < MAXPLAYERS + 1 ; clientIndex ++ )
2009-05-30 04:17:01 +02:00
{
for ( new volumeIndex = 0 ; volumeIndex < ZR_VOLUMES_MAX ; volumeIndex ++ )
{
VolPlayerCountDown [ clientIndex ][ volumeIndex ] = - 1.0 ;
}
}
}
2009-05-16 01:37:23 +02:00
}
/**
* 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 )
{
2009-07-16 10:05:40 +02:00
if ( client > 0 )
2009-05-16 01:37:23 +02:00
{
// Assume the client is valid and save location in array.
GetClientAbsOrigin ( client , VolPlayerLoc [ client ]);
}
else
{
2009-05-30 04:17:01 +02:00
for ( client = 1 ; client <= MaxClients ; client ++ )
2009-05-16 01:37:23 +02:00
{
2009-07-17 18:15:44 +02:00
// Validate client's connection state.
2009-05-16 01:37:23 +02:00
if ( ! IsClientConnected ( client ) || ! IsClientInGame ( client ) || ! IsPlayerAlive ( client ))
{
2009-07-17 18:15:44 +02:00
continue ;
2009-05-16 01:37:23 +02:00
}
// 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 ;
2009-05-30 04:17:01 +02:00
new Float : trigger_delay ;
// Loop through all players.
2009-05-16 01:37:23 +02:00
for ( new client = 1 ; client <= MaxClients ; client ++ )
{
2009-07-17 18:15:44 +02:00
// Validate client's connection state.
2009-05-16 01:37:23 +02:00
if ( ! IsClientConnected ( client ) || ! IsClientInGame ( client ) || ! IsPlayerAlive ( client ))
{
2009-07-16 10:05:40 +02:00
// Skip client.
continue ;
2009-05-16 01:37:23 +02:00
}
2009-07-16 10:05:40 +02:00
// Get the current volume states based on player location cache.
2009-05-16 01:37:23 +02:00
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.
2009-07-16 10:05:40 +02:00
for ( new volumeIndex = 0 ; volumeIndex < ZR_VOLUMES_MAX ; volumeIndex ++ )
2009-05-16 01:37:23 +02:00
{
2009-07-16 10:05:40 +02:00
// 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 ;
}
2009-05-16 01:37:23 +02:00
newState = volumeNewStates [ volumeIndex ];
oldState = volumeStates [ volumeIndex ];
2009-07-16 10:05:40 +02:00
// Check for no change.
2009-05-16 01:37:23 +02:00
if ( newState == oldState )
{
// No change. Skip to next volume.
2009-07-16 10:05:40 +02:00
continue ;
2009-05-16 01:37:23 +02:00
}
2009-05-30 04:17:01 +02:00
// Check if client entered the volume.
2009-07-16 10:05:40 +02:00
if ( newState && ! oldState )
2009-05-16 01:37:23 +02:00
{
2009-05-30 04:17:01 +02:00
// Get trigger delay value.
2009-07-16 10:05:40 +02:00
trigger_delay = Volumes [ volumeIndex ][ Vol_TriggerDelay ];
2009-05-30 04:17:01 +02:00
// Check if the volume has a trigger delay.
if ( trigger_delay > 0.0 )
{
// Set count down value.
VolPlayerCountDown [ client ][ volumeIndex ] = trigger_delay ;
}
else
{
2009-07-16 10:05:40 +02:00
// Update cache.
VolPlayerInVolume [ client ][ volumeIndex ] = true ;
2009-05-30 04:17:01 +02:00
// No trigger delay, trigger event instantly.
VolOnPlayerEnter ( client , volumeIndex );
}
2009-05-16 01:37:23 +02:00
}
2009-05-30 04:17:01 +02:00
// Check if client left the volume.
2009-07-16 10:05:40 +02:00
else if ( ! newState && oldState )
2009-05-16 01:37:23 +02:00
{
2009-05-30 04:17:01 +02:00
// Make sure count down value is reset.
VolPlayerCountDown [ client ][ volumeIndex ] = - 1.0 ;
2009-07-17 18:15:44 +02:00
// 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 );
}
2009-05-16 01:37:23 +02:00
}
}
}
}
/**
* Callback for update timer . This is the main timer in volumetric features .
*/
public Action : Event_VolUpdateTimer ( Handle : timer )
{
VolUpdatePlayerChanges ();
}
2009-05-30 04:17:01 +02:00
/**
* 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 ++ )
{
2009-07-16 10:05:40 +02:00
// Check if volume is in use and enabled.
if ( ! VolInUse ( volumeIndex ) || ! VolIsEnabled ( volumeIndex ))
{
// Not in use or enabled, skip volume.
continue ;
}
2009-05-30 04:17:01 +02:00
// 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 ;
2009-07-16 10:05:40 +02:00
// Check if time is up.
2009-05-30 04:17:01 +02:00
if ( countDown <= 0.0 )
{
2009-07-16 10:05:40 +02:00
// Update cache.
VolPlayerInVolume [ client ][ volumeIndex ] = true ;
2009-05-30 04:17:01 +02:00
// Trigger volume enter event.
VolOnPlayerEnter ( client , volumeIndex );
// Reset count down value.
VolPlayerCountDown [ client ][ volumeIndex ] = - 1.0 ;
}
2009-07-16 10:05:40 +02:00
// Update count down value and continue.
VolPlayerCountDown [ client ][ volumeIndex ] = countDown ;
2009-05-30 04:17:01 +02:00
}
}
}
}
/**
* 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.
2009-07-16 10:05:40 +02:00
VolEnable ();
2009-05-30 04:17:01 +02:00
}
else
{
// Volumetric features is disabled.
VolDisable ();
}
}