From fcafe6b4286027c1aff3bd72b89b41be678945ad Mon Sep 17 00:00:00 2001 From: richard Date: Sat, 16 May 2009 01:37:23 +0200 Subject: [PATCH] Started working on volumetric features module. Added some base functions for working with modules. It's not tested, but it compiles. CVARs are not created yet. Implemented volume events. Function VolUpdatePlayerChanges with help from VolGetPlayerStates detects if a player leave or enter a volume between main timer intervals. This solution is not final and might be expensive. Needs to be optimized later! --- src/zombiereloaded.sp | 1 + src/zr/event.inc | 2 + src/zr/volfeatures/volanticamp.inc | 26 +++ src/zr/volfeatures/volevents.inc | 70 ++++++ src/zr/volfeatures/volfeatures.inc | 356 +++++++++++++++++++++++++++++ 5 files changed, 455 insertions(+) create mode 100644 src/zr/volfeatures/volanticamp.inc create mode 100644 src/zr/volfeatures/volevents.inc create mode 100644 src/zr/volfeatures/volfeatures.inc diff --git a/src/zombiereloaded.sp b/src/zombiereloaded.sp index d0becc7..2f8fd7b 100644 --- a/src/zombiereloaded.sp +++ b/src/zombiereloaded.sp @@ -57,6 +57,7 @@ #include "zr/zhp" #include "zr/jumpboost" #include "zr/anticamp" +#include "zr/volfeatures/volfeatures" // Almost replaced! :) #include "zr/zombie" diff --git a/src/zr/event.inc b/src/zr/event.inc index fba8390..aa6a990 100644 --- a/src/zr/event.inc +++ b/src/zr/event.inc @@ -74,6 +74,7 @@ public Action:EventRoundStart(Handle:event, const String:name[], bool:dontBroadc SEffectsOnRoundStart(); AntiStickOnRoundStart(); ZSpawnOnRoundStart(); + VolOnRoundStart(); } /** @@ -113,6 +114,7 @@ public Action:EventRoundEnd(Handle:event, const String:name[], bool:dontBroadcas SEffectsOnRoundEnd(); RespawnOnRoundEnd(); ZSpawnOnRoundEnd(); + VolOnRoundEnd(); } /** diff --git a/src/zr/volfeatures/volanticamp.inc b/src/zr/volfeatures/volanticamp.inc new file mode 100644 index 0000000..5af4e3b --- /dev/null +++ b/src/zr/volfeatures/volanticamp.inc @@ -0,0 +1,26 @@ +/* + * ============================================================================ + * + * Zombie:Reloaded + * + * File: volanticamp.inc + * Type: Module + * Description: Anti-camp handler. + * + * ============================================================================ + */ + +/** + * Data structure for a anti-camp volume. + */ +enum VolTypeAnticamp +{ + anticamp_damage, + anticamp_interval, + Handle:anticamp_timer +} + +/** + * Anti-camp data. + */ +new AnticampData[ZR_VOLUMES_MAX][VolTypeAnticamp]; diff --git a/src/zr/volfeatures/volevents.inc b/src/zr/volfeatures/volevents.inc new file mode 100644 index 0000000..5889542 --- /dev/null +++ b/src/zr/volfeatures/volevents.inc @@ -0,0 +1,70 @@ +/* + * ============================================================================ + * + * Zombie:Reloaded + * + * File: volevents.inc + * Type: Module + * Description: Handles volumetric feature events. + * + * ============================================================================ + */ + +/** + * Called when a player enters a volume. + * + * @param client The client index. + * @param volumeIndex The volume index. + */ +VolOnPlayerEnter(client, volumeIndex) +{ + // TODO: Check if volfeatures is enabled first. + + // Forward event to features. + // VolAnticampStart(client, volume); +} + +/** + * Called when a player leaves a volume. + * + * @param client The client index. + * @param volumeIndex The volume index. + */ +VolOnPlayerLeave(client, volumeIndex) +{ + // TODO: Check if volfeatures is enabled first. + + // Forward event to features. + // VolAnticampStop(client, volume); +} + +/** + * Called when a player spawned. Used for initializing player data. + * + * @param client The client index. + */ +VolOnPlayerSpawn(client) +{ + VolUpdatePlayerLocation(client); +} + +/** + * Called when the round starts. Main enable event for volumetric features. + */ +VolOnRoundStart() +{ + // Start main timer. + VolStartUpdateTimer(); +} + +/** + * Called when the round ends. Main disable event for volumetric features. + */ +VolOnRoundEnd() +{ + // Stop main timer. + VolStopUpdateTimer(); + + // Forward stop event to features. + // VolAnticampStop(); +} diff --git a/src/zr/volfeatures/volfeatures.inc b/src/zr/volfeatures/volfeatures.inc new file mode 100644 index 0000000..d8b1d1e --- /dev/null +++ b/src/zr/volfeatures/volfeatures.inc @@ -0,0 +1,356 @@ +/* + * ============================================================================ + * + * Zombie:Reloaded + * + * File: volfeatures.inc + * Type: Module + * Description: Provides functions for managing volumetric features. + * + * ============================================================================ + */ + +/** + * Total volumes that can be created in a map. + */ +#define ZR_VOLUMES_MAX 64 + +/** + * Represent a rectangular volume. + */ +enum VolumeAttributes +{ + /* General */ + bool:vol_enabled, /** Volume state. */ + bool:vol_in_use, /** Marks if the volume is used. */ + + /* Location */ + Float:vol_x_min, /** Minimum x position. */ + Float:vol_x_max, /** Maximum x position. */ + + Float:vol_y_min, /** Minimum y position. */ + Float:vol_y_max, /** Maximum y position. */ + + Float:vol_z_min, /** Minimum z position. */ + Float:vol_z_max, /** Maximum z position. */ + + /* Data */ + VolumeEffects:vol_effect, /** Visual effect to apply on the volume. */ + VolumeFeatureTypes:vol_type, /** The volumetric feature type. */ + vol_data_index /** Index in remote feature array. */ +} + +/** + * Available volumetric feature types. + */ +enum VolumeFeatureTypes +{ + VolFeature_Anticamp = 0, + VolFeature_Knockback +} + +enum VolumeEffects +{ + VolEffect_None = 0, + VolEffect_Wireframe, + VolEffect_Smoke +} + +/** + * 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]; + +/** + * Specifies whether the volumetric features module is enabled or not. To be + * synced with zr_volfeatures_enabled CVAR. + */ +new bool:VolEnabled; + +/** + * Timer handle for 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. + */ +new Handle:hVolUpdateTimer; + +#include "zr/volfeatures/volevents" +#include "zr/volfeatures/volanticamp" + +/** + * Starts the update timer. + */ +bool:VolStartUpdateTimer() +{ + // TODO: Read from CVAR (zr_volfeatures_enabled). + VolEnabled = true; + + if (!VolEnabled) + { + // Volumetric features disabled. + return false; + } + + // TODO: Read from CVAR (zr_volfeatures_interval). + new Float:interval = 1.0; + + // Validate interval. + if (interval > 0.0) + { + // Stop timer if it exist. + VolStopUpdateTimer(); + + // Create a new timer. + hVolUpdateTimer = CreateTimer(interval, Event_VolUpdateTimer, _, TIMER_REPEAT); + + // Volumetric features started. + return true; + } + else + { + // Volumetric features disabled. Do explicit stop. + VolStopUpdateTimer(); + + 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; + } +} + +/** + * 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 (new client = 1; client <= MaxClients; client++) + { + // Check if client is in game and alive. + if (!IsClientConnected(client) || !IsClientInGame(client) || !IsPlayerAlive(client)) + { + return; + } + + // 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; + + for (new client = 1; client <= MaxClients; client++) + { + // Check if client is in game and alive. + if (!IsClientConnected(client) || !IsClientInGame(client) || !IsPlayerAlive(client)) + { + return; + } + + // Get the current volume states. + 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 < VolumeCount; volumeIndex++) + { + newState = volumeNewStates[volumeIndex]; + oldState = volumeStates[volumeIndex]; + + // Compare new states with old states. + if (newState == oldState) + { + // No change. Skip to next volume. + break; + } + else if (!newState && oldState) + { + // Client entered volume. Trigger event. + VolOnPlayerEnter(client, volumeIndex); + } + else if (newState && !oldState) + { + // Client left the volume. Trigger event. + VolOnPlayerLeave(client, volumeIndex); + } + } + } +} + +/** + * Returns wether a position is within a certain location. + * + * @param loc The position to check. + * @param min Minimum x, y and z values of the location. + * @param max Maximum x, y and z values of the location. + * @return True if the position is within min and max values. False + * otherwise. + */ +bool:IsPositionInLocation(Float:pos[3], Float:min[3], Float:max[3]) +{ + // Cache location to avoid re-indexing arrays. + new Float:posX = pos[0]; + new Float:posY = pos[1]; + new Float:posZ = pos[2]; + + // Check if within x boundaries. + if ((posX >= min[0]) && (posX <= max[0])) + { + // Check if within y boundaries. + if ((posY >= min[1]) && (posY <= max[1])) + { + // Check if within x boundaries. + if ((posZ >= min[2]) && (posZ <= max[2])) + { + // The player is within the location boundaries. + return true; + } + } + } + + // The player is outside the location boundaries. + return false; +} + +/** + * Returns wether a volume is marked as in use. + * + * @param volumeIndex The volume index. + * @return True if in use, false otherwise. + */ +bool:VolIsInUse(volumeIndex) +{ + return Volumes[volumeIndex][vol_in_use]; +} + +/** + * Gets the first free volume index. + * + * @return The first free volume index if successful, or -1 if there are + * no free volumes. + */ +VolGetFreeVolume() +{ + // Loop through all volumes. + for (new volumeIndex = 0; volumeIndex < ZR_VOLUMES_MAX; volumeIndex++) + { + // Check if it's free. + if (!VolIsInUse(volumeIndex)) + { + return volumeIndex; + } + } + + // No free volumes found. + return -1; +} + +/** + * Gets wether a client is within volumes or not. Result is stored in a boolean + * array. + * + * @param client The client index. + * @param buffer Destination buffer. + * @param maxlen Size of destination buffer. + * @return Number of volumes the client is within. + */ +VolGetPlayerStates(client, bool:buffer[], maxlen) +{ + new volumeBuffer[VolumeAttributes]; + new volCount; + + new Float:volMinBuffer[3]; + new Float:volMaxBuffer[3]; + + // Loop through all available volumes. + for (new volumeIndex = 0; volumeIndex < VolumeCount && volumeIndex < maxlen; volumeIndex++) + { + if (VolIsInUse(volumeIndex)) + { + // Chache volume to avoid re-indexing. + volumeBuffer = Volumes[volumeIndex]; + + // Get min positions. + volMinBuffer[0] = volumeBuffer[vol_x_min]; + volMinBuffer[1] = volumeBuffer[vol_y_min]; + volMinBuffer[2] = volumeBuffer[vol_z_min]; + + // Get max positions. + volMaxBuffer[0] = volumeBuffer[vol_x_min]; + volMaxBuffer[1] = volumeBuffer[vol_y_min]; + volMaxBuffer[2] = volumeBuffer[vol_z_min]; + + // Check the cached player location. + if (IsPositionInLocation(VolPlayerLoc[client], volMinBuffer, volMaxBuffer)) + { + // Mark player as in volume. + buffer[volumeIndex] = true; + volCount++; + } + else + { + // Do explicit reset. + buffer[volumeIndex] = false; + } + } + } + + return volCount; +} + +/** + * Callback for update timer. This is the main timer in volumetric features. + */ +public Action:Event_VolUpdateTimer(Handle:timer) +{ + VolUpdatePlayerChanges(); +}