Raised This Month: $12 Target: $400
 3% 

[TF2][Request] Fix for Dodgeball Speed Announcer


Post New Thread Reply   
 
Thread Tools Display Modes
Author Message
cjieliang
Member
Join Date: Apr 2014
Old 03-15-2015 , 14:41   [TF2][Request] Fix for Dodgeball Speed Announcer
Reply With Quote #1

Hi,
If any kind soul out there can tell me what's the problem and fix. i would be really grateful.
The following plugins doesnt work correctly. It was suppose to work with the Yet Another Dodgeball plugins but it doesnt print out the message. instead it printed this "Usage: tf_dodgeball_announce_speed @dead @speed @name"

This is the SpeedAnnouncer.sp By ATRanko.
PHP Code:
#include <sourcemod>

#define PLUGIN_NAME "YADB Speed Announcer"
#define PLUGIN_VERSION "0.1"
#define PLUGIN_AUTHOR "ATRanko"
#define HAMMER_TO_KILOMETER 0.0686
#define HAMMER_TO_MILES 0.0426

new Handle:g_hCvarSpeedAnnounceModeg_iCvarSpeedAnnounceMode;

public 
Plugin:myinfo 
{
    
name PLUGIN_NAME,
    
author PLUGIN_AUTHOR,
    
description "Announces rocket speed to client",
    
version PLUGIN_VERSION,
    
url "http://www.steamcommunity.com/id/at-09-ranko"
}

public 
OnPluginStart()
{
    
RegServerCmd("tf_dodgeball_announce_speed"SpeedAnnounce)
    
g_hCvarSpeedAnnounceMode CreateConVar("sm_dodgeball_speedannounce""1""Enable Speed Announcing, 1 to all, 2 to client only, 0 to disable."_true0.0true2.0);
    
g_iCvarSpeedAnnounceMode GetConVarInt(g_hCvarSpeedAnnounceMode);
    
HookConVarChange(g_hCvarSpeedAnnounceModeOnConVarChange);
    
}

public 
OnConVarChange(Handle:hConvar, const String:OldValue[], const String:NewValue[])
{
    
g_iCvarSpeedAnnounceMode GetConVarInt(g_hCvarSpeedAnnounceMode);
}

public 
Action:SpeedAnnounce(iArgs)
{
    if(
iArgs != 3)
    {
        
PrintToServer("Usage: tf_dodgeball_announce_speed @dead @speed @name")
        return 
Plugin_Handled;
    }
    new 
String:strBuffer[32], String:strRocketName[64];
    
GetCmdArg(1strBuffersizeof(strBuffer)); new iDead StringToInt(strBuffer10);
    
GetCmdArg(2strBuffersizeof(strBuffer)); new Float:fSpeed StringToFloat(strBuffer); fSpeed *= HAMMER_TO_KILOMETER; new iSpeed RoundToNearest(fSpeed);
    
GetCmdArg(3strRocketNamesizeof(strRocketName));
    if(!
IsClientInGame(iDead))
        return 
Plugin_Handled;
    
    switch(
g_iCvarSpeedAnnounceMode)
    {
        case 
0: return Plugin_Handled;
        case 
1AnnounceToAll(iDeadiSpeedstrRocketName);
        case 
2AnnounceToClient(iDeadiSpeedstrRocketName);
        default: return 
Plugin_Handled;
    }
    
    return 
Plugin_Handled;
}

public 
Action:AnnounceToAll(iDeadiSpeedString:strRocketName[])
{
    new 
String:strDeadName[MAX_NAME_LENGTH], String:strDeadColour[32];
    
GetClientName(iDeadstrDeadNamesizeof(strDeadName));
    
FindTeamColour(iDeadstrDeadColoursizeof(strDeadColour));
    
    
PrintToChatAll("\x070FFF0F[Dodgeball] %s%s \x07FFFFFFwas killed by a %s travelling at %d KPH!"strDeadColourstrDeadNamestrRocketNameiSpeed);
    return 
Plugin_Handled;
}

public 
Action:AnnounceToClient(iDeadiSpeedString:strRocketName[])
{
    
PrintToChat(iDead"\x070FFF0F[Dodgeball] \x07FFFFFFYou were killed by a %s travelling at %d KPH!",strRocketNameiSpeed);
    return 
Plugin_Handled;
}

stock FindTeamColour(iClientString:strBuffer[], MaxLength)
{
    switch (
GetClientTeam(iClient))
    {
        case 
0Format(strBufferMaxLength"\0x7FFFFFF");
        case 
2Format(strBufferMaxLength"\x07B8383B");
        case 
3Format(strBufferMaxLength"\x075885A2");
        default: 
Format(strBufferMaxLength"\x07FFFFFF");
    }
    

This is the sp of the YADB that i am using
PHP Code:
// *********************************************************************************
// PREPROCESSOR
// *********************************************************************************
#pragma semicolon 1                  // Force strict semicolon mode.

// *********************************************************************************
// INCLUDES
// *********************************************************************************
#include <sourcemod>
#include <sdktools> 
#include <tf2>
#include <tf2_stocks>
#include <colors>

// *********************************************************************************
// CONSTANTS 
// *********************************************************************************
// ---- Plugin-related constants ---------------------------------------------------
#define PLUGIN_NAME             "[TF2] Dodgeball"
#define PLUGIN_AUTHOR           "Damizean"
#define PLUGIN_VERSION          "1.0"
#define PLUGIN_CONTACT          "[email protected]"
#define CVAR_FLAGS              FCVAR_PLUGIN

// ---- General settings -----------------------------------------------------------
#define FPS_LOGIC_RATE          20.0
#define FPS_LOGIC_INTERVAL      1.0 / FPS_LOGIC_RATE

// ---- Maximum structure sizes ----------------------------------------------------
#define MAX_ROCKETS             100
#define MAX_ROCKET_CLASSES      50
#define MAX_SPAWNER_CLASSES     50
#define MAX_SPAWN_POINTS        100

// ---- Flags and types constants --------------------------------------------------
enum Musics
{
    
Music_RoundStart,
    
Music_RoundWin,
    
Music_RoundLose,
    
Music_Gameplay,
    
SizeOfMusicsArray
};

enum BehaviourTypes
{
    
Behaviour_Unknown,
    
Behaviour_Homing
};

enum RocketFlags
{
    
RocketFlag_None             0,
    
RocketFlag_PlaySpawnSound   << 0,
    
RocketFlag_PlayBeepSound    << 1,
    
RocketFlag_PlayAlertSound   << 2,
    
RocketFlag_ElevateOnDeflect << 3,
    
RocketFlag_IsNeutral        << 4,
    
RocketFlag_Exploded         << 5,
    
RocketFlag_OnSpawnCmd       << 6,
    
RocketFlag_OnDeflectCmd     << 7,
    
RocketFlag_OnKillCmd        << 8,
    
RocketFlag_OnExplodeCmd     << 9,
    
RocketFlag_CustomModel      << 10,
    
RocketFlag_CustomSpawnSound << 11,
    
RocketFlag_CustomBeepSound  << 12,
    
RocketFlag_CustomAlertSound << 13,
    
RocketFlag_Elevating        << 14,
    
RocketFlag_IsAnimated       << 15
};

enum RocketSound
{
    
RocketSound_Spawn,
    
RocketSound_Beep,
    
RocketSound_Alert
};

enum SpawnerFlags
{
    
SpawnerFlag_Team_Red  1,
    
SpawnerFlag_Team_Blu  2,
    
SpawnerFlag_Team_Both 3
};

#define TestFlags(%1,%2)    (!!((%1) & (%2)))
#define TestFlagsAnd(%1,%2) (((%1) & (%2)) == %2)

// ---- Other resources ------------------------------------------------------------
#define SOUND_DEFAULT_SPAWN     "weapons/sentry_rocket.wav"
#define SOUND_DEFAULT_BEEP      "weapons/sentry_scan.wav"
#define SOUND_DEFAULT_ALERT     "weapons/sentry_spot.wav"
#define SNDCHAN_MUSIC           32
#define PARTICLE_NUKE_1         "fireSmokeExplosion"
#define PARTICLE_NUKE_2         "fireSmokeExplosion1"
#define PARTICLE_NUKE_3         "fireSmokeExplosion2"
#define PARTICLE_NUKE_4         "fireSmokeExplosion3"
#define PARTICLE_NUKE_5         "fireSmokeExplosion4"
#define PARTICLE_NUKE_COLLUMN   "fireSmoke_collumnP"
#define PARTICLE_NUKE_1_ANGLES  Float:{270.0, 0.0, 0.0}
#define PARTICLE_NUKE_2_ANGLES  PARTICLE_NUKE_1_ANGLES
#define PARTICLE_NUKE_3_ANGLES  PARTICLE_NUKE_1_ANGLES
#define PARTICLE_NUKE_4_ANGLES  PARTICLE_NUKE_1_ANGLES
#define PARTICLE_NUKE_5_ANGLES  PARTICLE_NUKE_1_ANGLES
#define PARTICLE_NUKE_COLLUMN_ANGLES  PARTICLE_NUKE_1_ANGLES

// Debug
//#define DEBUG

// *********************************************************************************
// VARIABLES
// *********************************************************************************

// -----<<< Cvars >>>-----
new Handle:g_hCvarEnabled;
new 
Handle:g_hCvarEnableCfgFile;
new 
Handle:g_hCvarDisableCfgFile;

// -----<<< Gameplay >>>-----
new bool:g_bEnabled;                // Is the plugin enabled?
new bool:g_bRoundStarted;           // Has the round started?
new g_iRoundCount;                  // Current round count since map start
new g_iRocketsFired;                // No. of rockets fired since round start
new Handle:g_hLogicTimer;           // Logic timer
new Float:g_fNextSpawnTime;         // Time at wich the next rocket will be able to spawn
new g_iLastDeadTeam;                // The team of the last dead client. If none, it's a random team.
new g_iLastDeadClient;              // The last dead client. If none, it's a random client.
new g_iPlayerCount;

// -----<<< Configuration >>>-----
new bool:g_bMusicEnabled;
new 
bool:g_bMusic[_:SizeOfMusicsArray];
new 
String:g_strMusic[_:SizeOfMusicsArray][PLATFORM_MAX_PATH];
new 
bool:g_bUseWebPlayer;
new 
String:g_strWebPlayerUrl[256];

// -----<<< Structures >>>-----
// Rockets
new        bool:g_bRocketIsValid            [MAX_ROCKETS];
new             
g_iRocketEntity             [MAX_ROCKETS];
new             
g_iRocketTarget             [MAX_ROCKETS];
new             
g_iRocketSpawner            [MAX_ROCKETS];
new             
g_iRocketClass              [MAX_ROCKETS];
new 
RocketFlags:g_iRocketFlags              [MAX_ROCKETS];
new       
Float:g_fRocketSpeed              [MAX_ROCKETS];
new       
Float:g_fRocketDirection          [MAX_ROCKETS][3];
new             
g_iRocketDeflections        [MAX_ROCKETS];
new       
Float:g_fRocketLastDeflectionTime [MAX_ROCKETS];
new       
Float:g_fRocketLastBeepTime       [MAX_ROCKETS];
new             
g_iLastCreatedRocket;
new             
g_iRocketCount;

// Classes
new         String:g_strRocketClassName           [MAX_ROCKET_CLASSES][16];
new         
String:g_strRocketClassLongName       [MAX_ROCKET_CLASSES][32];
new 
BehaviourTypes:g_iRocketClassBehaviour        [MAX_ROCKET_CLASSES];
new         
String:g_strRocketClassModel          [MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
new    
RocketFlags:g_iRocketClassFlags            [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassBeepInterval     [MAX_ROCKET_CLASSES];
new         
String:g_strRocketClassSpawnSound     [MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
new         
String:g_strRocketClassBeepSound      [MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
new         
String:g_strRocketClassAlertSound     [MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
new          
Float:g_fRocketClassCritChance       [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassDamage           [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassDamageIncrement  [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassSpeed            [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassSpeedIncrement   [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassTurnRate         [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassTurnRateIncrement[MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassElevationRate    [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassElevationLimit   [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassRocketsModifier  [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassPlayerModifier   [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassControlDelay     [MAX_ROCKET_CLASSES];
new          
Float:g_fRocketClassTargetWeight     [MAX_ROCKET_CLASSES];
new         
Handle:g_hRocketClassCmdsOnSpawn      [MAX_ROCKET_CLASSES];
new         
Handle:g_hRocketClassCmdsOnDeflect    [MAX_ROCKET_CLASSES];
new         
Handle:g_hRocketClassCmdsOnKill       [MAX_ROCKET_CLASSES];
new         
Handle:g_hRocketClassCmdsOnExplode    [MAX_ROCKET_CLASSES];
new         
Handle:g_hRocketClassTrie;
new                
g_iRocketClassCount;

// Spawner classes
new       String:g_strSpawnersName        [MAX_SPAWNER_CLASSES][32];
//new SpawnerFlags:g_iSpawnersFlags         [MAX_SPAWNER_CLASSES];
new              g_iSpawnersMaxRockets    [MAX_SPAWNER_CLASSES];
new        
Float:g_fSpawnersInterval      [MAX_SPAWNER_CLASSES];
new       
Handle:g_hSpawnersChancesTable  [MAX_SPAWNER_CLASSES];
new       
Handle:g_hSpawnersTrie;
new              
g_iSpawnersCount;

// Array containing the spawn points for the Red team, and
// their associated spawner class.
new g_iCurrentRedSpawn;
new 
g_iSpawnPointsRedCount;
new 
g_iSpawnPointsRedClass  [MAX_SPAWN_POINTS];
new 
g_iSpawnPointsRedEntity [MAX_SPAWN_POINTS];

// Array containing the spawn points for the Blu team, and
// their associated spawner class.
new g_iCurrentBluSpawn;
new 
g_iSpawnPointsBluCount;
new 
g_iSpawnPointsBluClass  [MAX_SPAWN_POINTS];
new 
g_iSpawnPointsBluEntity [MAX_SPAWN_POINTS];

// The default spawner class.
new g_iDefaultRedSpawner;
new 
g_iDefaultBluSpawner;

// *********************************************************************************
// PLUGIN
// *********************************************************************************
public Plugin:myinfo =
{
    
name        PLUGIN_NAME,
    
author      PLUGIN_AUTHOR,
    
description PLUGIN_NAME,
    
version     PLUGIN_VERSION,
    
url         PLUGIN_CONTACT
};

// *********************************************************************************
// METHODS
// *********************************************************************************

/* OnPluginStart()
**
** When the plugin is loaded.
** -------------------------------------------------------------------------- */
public OnPluginStart()
{
    
decl String:strModName[32]; GetGameFolderName(strModNamesizeof(strModName));
    if (!
StrEqual(strModName"tf")) SetFailState("This plugin is only for Team Fortress 2.");
    
    
CreateConVar("tf_dodgeball_version"PLUGIN_VERSIONPLUGIN_NAMEFCVAR_PLUGIN);
    
g_hCvarEnabled CreateConVar("tf_dodgeball_enabled""1""Enable Dodgeball on TFDB maps?"_true0.0true1.0);
    
g_hCvarEnableCfgFile CreateConVar("tf_dodgeball_enablecfg""sourcemod/dodgeball_enable.cfg""Config file to execute when enabling the Dodgeball game mode.");
    
g_hCvarDisableCfgFile CreateConVar("tf_dodgeball_disablecfg""sourcemod/dodgeball_disable.cfg""Config file to execute when disabling the Dodgeball game mode.");
    
    
g_hRocketClassTrie CreateTrie();
    
g_hSpawnersTrie CreateTrie();
    
    
RegisterCommands();
}

/* OnConfigsExecuted()
**
** When all the configuration files have been executed, try to enable the
** Dodgeball.
** -------------------------------------------------------------------------- */
public OnConfigsExecuted()
{
    if (
GetConVarBool(g_hCvarEnabled) && IsDodgeBallMap())
    {
        
EnableDodgeBall();
    }
}

/* OnMapEnd()
**
** When the map ends, disable DodgeBall.
** -------------------------------------------------------------------------- */
public OnMapEnd()
{
    
DisableDodgeBall();
}

/*
**••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
**     __  ___                                                  __ 
**    /  |/  /___ _____  ____ _____ ____  ____ ___  ___  ____  / /_
**   / /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ __ `__ \/ _ \/ __ \/ __/
**  / /  / / /_/ / / / / /_/ / /_/ /  __/ / / / / /  __/ / / / /_  
** /_/  /_/\__,_/_/ /_/\__,_/\__, /\___/_/ /_/ /_/\___/_/ /_/\__/  
**                          /____/                                 
**••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
*/

//   ___                       _ 
//  / __|___ _ _  ___ _ _ __ _| |
// | (_ / -_) ' \/ -_) '_/ _` | |
//  \___\___|_||_\___|_| \__,_|_|

/* IsDodgeBallMap()
**
** Checks if the current map is a dodgeball map.
** -------------------------------------------------------------------------- */
bool:IsDodgeBallMap()
{
    
decl String:strMap[64];
    
GetCurrentMap(strMapsizeof(strMap));
    return 
StrContains(strMap"tfdb_"false) == 0;
}

/* EnableDodgeBall()
**
** Enables and hooks all the required events.
** -------------------------------------------------------------------------- */
EnableDodgeBall()
{
    if (
g_bEnabled == false)
    {
        
// Parse configuration files
        
decl String:strMapName[64]; GetCurrentMap(strMapNamesizeof(strMapName));
        
decl String:strMapFile[PLATFORM_MAX_PATH]; Format(strMapFilesizeof(strMapFile), "%s.cfg"strMapName);
        
ParseConfigurations();
        
ParseConfigurations(strMapFile);
        
        
// Check if we have all the required information
        
if (g_iRocketClassCount == 0)   SetFailState("No rocket class defined.");
        if (
g_iSpawnersCount == 0)      SetFailState("No spawner class defined.");
        if (
g_iDefaultRedSpawner == -1SetFailState("No spawner class definition for the Red spawners exists in the config file.");
        if (
g_iDefaultBluSpawner == -1SetFailState("No spawner class definition for the Blu spawners exists in the config file.");
        
        
// Hook events and info_target outputs.
        
HookEvent("teamplay_round_start"OnRoundStartEventHookMode_PostNoCopy);
        
HookEvent("teamplay_setup_finished"OnSetupFinishedEventHookMode_PostNoCopy);
        
HookEvent("teamplay_round_win"OnRoundEndEventHookMode_PostNoCopy);
        
HookEvent("player_spawn"OnPlayerSpawnEventHookMode_Post);
        
HookEvent("player_death"OnPlayerDeathEventHookMode_Pre);
        
HookEvent("post_inventory_application"OnPlayerInventoryEventHookMode_Post);
        
HookEvent("teamplay_broadcast_audio"OnBroadcastAudioEventHookMode_Pre);
        
        
// Precache sounds
        
PrecacheSound(SOUND_DEFAULT_SPAWNtrue);
        
PrecacheSound(SOUND_DEFAULT_BEEPtrue);
        
PrecacheSound(SOUND_DEFAULT_ALERTtrue);
        if (
g_bMusicEnabled == true)
        {
            if (
g_bMusic[Music_RoundStart]) PrecacheSoundEx(g_strMusic[Music_RoundStart], truetrue);
            if (
g_bMusic[Music_RoundWin])   PrecacheSoundEx(g_strMusic[Music_RoundWin], truetrue);
            if (
g_bMusic[Music_RoundLose])  PrecacheSoundEx(g_strMusic[Music_RoundLose], truetrue);
            if (
g_bMusic[Music_Gameplay])   PrecacheSoundEx(g_strMusic[Music_Gameplay], truetrue);
        }
        
        
// Precache particles
        
PrecacheParticle(PARTICLE_NUKE_1);
        
PrecacheParticle(PARTICLE_NUKE_2);
        
PrecacheParticle(PARTICLE_NUKE_3);
        
PrecacheParticle(PARTICLE_NUKE_4);
        
PrecacheParticle(PARTICLE_NUKE_5);
        
PrecacheParticle(PARTICLE_NUKE_COLLUMN);
        
        
// Precache rocket resources
        
for (new 0g_iRocketClassCounti++)
        {
            new 
RocketFlags:iFlags g_iRocketClassFlags[i];
            if (
TestFlags(iFlagsRocketFlag_CustomModel))      PrecacheModelEx(g_strRocketClassModel[i], truetrue);
            if (
TestFlags(iFlagsRocketFlag_CustomSpawnSound)) PrecacheSoundEx(g_strRocketClassSpawnSound[i], truetrue);
            if (
TestFlags(iFlagsRocketFlag_CustomBeepSound))  PrecacheSoundEx(g_strRocketClassBeepSound[i], truetrue);
            if (
TestFlags(iFlagsRocketFlag_CustomAlertSound)) PrecacheSoundEx(g_strRocketClassAlertSound[i], truetrue);
        }
        
        
// Execute enable config file
        
decl String:strCfgFile[64]; GetConVarString(g_hCvarEnableCfgFilestrCfgFilesizeof(strCfgFile));
        
ServerCommand("exec \"%s\""strCfgFile);
        
        
// Done.
        
g_bEnabled        true;
        
g_bRoundStarted   false;
        
g_iRoundCount     0;
    }
}

/* DisableDodgeBall()
**
** Disables all hooks and frees arrays.
** -------------------------------------------------------------------------- */
DisableDodgeBall()
{
    if (
g_bEnabled == true)
    {
        
// Clean up everything
        
DestroyRockets();
        
DestroyRocketClasses();
        
DestroySpawners();
        if (
g_hLogicTimer != INVALID_HANDLEKillTimer(g_hLogicTimer);
        
g_hLogicTimer INVALID_HANDLE;
        
        
// Disable music
        
g_bMusic[Music_RoundStart] =
        
g_bMusic[Music_RoundWin]   = 
        
g_bMusic[Music_RoundLose]  = 
        
g_bMusic[Music_Gameplay]   = false;
        
        
// Unhook events and info_target outputs;
        
UnhookEvent("teamplay_round_start"OnRoundStartEventHookMode_PostNoCopy);
        
UnhookEvent("teamplay_setup_finished"OnSetupFinishedEventHookMode_PostNoCopy);
        
UnhookEvent("teamplay_round_win"OnRoundEndEventHookMode_PostNoCopy);
        
UnhookEvent("player_spawn"OnPlayerSpawnEventHookMode_Post);
        
UnhookEvent("player_death"OnPlayerDeathEventHookMode_Pre);
        
UnhookEvent("post_inventory_application"OnPlayerInventoryEventHookMode_Post);
        
UnhookEvent("teamplay_broadcast_audio"OnBroadcastAudioEventHookMode_Pre);
        
        
// Execute enable config file
        
decl String:strCfgFile[64]; GetConVarString(g_hCvarDisableCfgFilestrCfgFilesizeof(strCfgFile));
        
ServerCommand("exec \"%s\""strCfgFile);
        
        
// Done.
        
g_bEnabled        false;
        
g_bRoundStarted   false;
        
g_iRoundCount     0;
    }
}

//   ___                     _           
//  / __|__ _ _ __  ___ _ __| |__ _ _  _ 
// | (_ / _` | '  \/ -_) '_ \ / _` | || |
//  \___\__,_|_|_|_\___| .__/_\__,_|\_, |
//                     |_|          |__/ 

/* OnRoundStart()
**
** At round start, do something?
** -------------------------------------------------------------------------- */
public Action:OnRoundStart(Handle:hEventString:strEventName[], bool:bDontBroadcast)
{
    if (
g_bMusic[Music_RoundStart])
    {
        
EmitSoundToAll(g_strMusic[Music_RoundStart]);
    }
}

/* OnSetupFinished()
**
** When the setup finishes, populate the spawn points arrays and create the
** Dodgeball game logic timer.
** -------------------------------------------------------------------------- */
public Action:OnSetupFinished(Handle:hEventString:strEventName[], bool:bDontBroadcast)
{   
    if ((
g_bEnabled == true) && (BothTeamsPlaying() == true))
    {
        
PopulateSpawnPoints();
        
        if (
g_iLastDeadTeam == 0g_iLastDeadTeam GetURandomIntRange(_:TFTeam_Red_:TFTeam_Blue);
        if (!
IsValidClient(g_iLastDeadClient)) g_iLastDeadClient 0;
        
        
g_hLogicTimer      CreateTimer(FPS_LOGIC_INTERVALOnDodgeBallGameFrame_TIMER_REPEAT);
        
g_iPlayerCount     CountAlivePlayers();
        
g_iRocketsFired    0;
        
g_iCurrentRedSpawn 0;
        
g_iCurrentBluSpawn 0;
        
g_fNextSpawnTime   GetGameTime();
        
g_bRoundStarted    true;
        
g_iRoundCount++;
    }
}

/* OnRoundEnd()
**
** At round end, stop the Dodgeball game logic timer and destroy the remaining
** rockets.
** -------------------------------------------------------------------------- */
public Action:OnRoundEnd(Handle:hEventString:strEventName[], bool:bDontBroadcast)
{
    if (
g_hLogicTimer != INVALID_HANDLE)
    {
        
KillTimer(g_hLogicTimer);
        
g_hLogicTimer INVALID_HANDLE;
    }
    
    if (
g_bMusicEnabled == true)
    {
        if (
g_bUseWebPlayer)
        {
            for (new 
iClient 1iClient <= MaxClientsiClient++)
            {
                if (
IsValidClient(iClient))
                {
                    
ShowHiddenMOTDPanel(iClient"MusicPlayerStop""http://0.0.0.0/");
                }
            }
        }
        else if (
g_bMusic[Music_Gameplay])
        {
            
StopSoundToAll(SNDCHAN_MUSICg_strMusic[Music_Gameplay]);
        }
    }
    
    
DestroyRockets();
    
g_bRoundStarted false;
}

/* OnPlayerSpawn()
**
** When the player spawns, force class to Pyro.
** -------------------------------------------------------------------------- */
public Action:OnPlayerSpawn(Handle:hEventString:strEventName[], bool:bDontBroadcast)
{
    new 
iClient GetClientOfUserId(GetEventInt(hEvent"userid"));
    if (!
IsValidClient(iClient)) return;
    
    new 
TFClassType:iClass TF2_GetPlayerClass(iClient);
    if (!(
iClass == TFClass_Pyro || iClass == TFClassType:TFClass_Unknown))
    {
        
TF2_SetPlayerClass(iClientTFClass_Pyrofalsetrue);
        
TF2_RespawnPlayer(iClient);
    }
}

/* OnPlayerDeath()
**
** When the player dies, set the last dead team to determine the next
** rocket's team.
** -------------------------------------------------------------------------- */
public Action:OnPlayerDeath(Handle:hEventString:strEventName[], bool:bDontBroadcast)
{
    if (
g_bRoundStarted == false) return;
    new 
iAttacker GetClientOfUserId(GetEventInt(hEvent"userid"));
    new 
iVictim GetClientOfUserId(GetEventInt(hEvent"userid"));
    
    if (
IsValidClient(iVictim))
    {
        
g_iLastDeadClient iVictim;
        
g_iLastDeadTeam GetClientTeam(iVictim);
        
        new 
iInflictor GetEventInt(hEvent"inflictor_entindex");
        new 
iIndex FindRocketByEntity(iInflictor);
        
        if (
iIndex != -1)
        {
            new 
iClass g_iRocketClass[iIndex];
            new 
iTarget EntRefToEntIndex(g_iRocketTarget[iIndex]);
            new 
Float:fSpeed g_fRocketSpeed[iIndex];
            new 
iDeflections g_iRocketDeflections[iIndex];
            
            if ((
g_iRocketFlags[iIndex] & RocketFlag_OnExplodeCmd) && !(g_iRocketFlags[iIndex] & RocketFlag_Exploded))
            {
                
ExecuteCommands(g_hRocketClassCmdsOnExplode[iClass], iClassiInflictoriAttackeriTargetg_iLastDeadClientfSpeediDeflections);
                
g_iRocketFlags[iIndex] |= RocketFlag_Exploded;
            }
            
            if (
TestFlags(g_iRocketFlags[iIndex], RocketFlag_OnKillCmd))
                
ExecuteCommands(g_hRocketClassCmdsOnKill[iClass], iClassiInflictoriAttackeriTargetg_iLastDeadClientfSpeediDeflections);
        }
    }
    
    
SetRandomSeed(_:GetGameTime());
}

/* OnPlayerInventory()
**
** Make sure the client only has the flamethrower equipped.
** -------------------------------------------------------------------------- */
public Action:OnPlayerInventory(Handle:hEventString:strEventName[], bool:bDontBroadcast)
{
    new 
iClient GetClientOfUserId(GetEventInt(hEvent"userid"));
    if (!
IsValidClient(iClient)) return;
    
    for (new 
iSlot 1iSlot 5iSlot++)
    {
        new 
iEntity GetPlayerWeaponSlot(iClientiSlot);
        if (
iEntity != -1RemoveEdict(iEntity);
    }
}

/* OnPlayerRunCmd()
**
** Block flamethrower's Mouse1 attack.
** -------------------------------------------------------------------------- */
public Action:OnPlayerRunCmd(iClient, &iButtons, &iImpulseFloat:fVelocity[3], Float:fAngles[3], &iWeapon)
{
    if (
g_bEnabled == trueiButtons &= ~IN_ATTACK;
    return 
Plugin_Continue;
}

/* OnBroadcastAudio()
**
** Replaces the broadcasted audio for our custom music files.
** -------------------------------------------------------------------------- */
public Action:OnBroadcastAudio(Handle:hEventString:strEventName[], bool:bDontBroadcast)
{
    if (
g_bMusicEnabled == true)
    {
        
decl String:strSound[PLATFORM_MAX_PATH];
        
GetEventString(hEvent"sound"strSoundsizeof(strSound));
        new 
iTeam GetEventInt(hEvent"team");
        
        if (
StrEqual(strSound"Announcer.AM_RoundStartRandom") == true)
        {
            if (
g_bUseWebPlayer == false)
            {
                if (
g_bMusic[Music_Gameplay])
                {
                    
EmitSoundToAll(g_strMusic[Music_Gameplay], SOUND_FROM_PLAYERSNDCHAN_MUSIC);
                    return 
Plugin_Handled;
                }
            }
            else
            {
                for (new 
iClient 1iClient <= MaxClientsiClient++)
                    if (
IsValidClient(iClient))
                        
ShowHiddenMOTDPanel(iClient"MusicPlayerStart"g_strWebPlayerUrl);
                    
                return 
Plugin_Handled;
            }
        }
        else if (
StrEqual(strSound"Game.YourTeamWon") == true)
        {
            if (
g_bMusic[Music_RoundWin])
            {
                for (new 
iClient 1iClient <= MaxClientsiClient++)
                    if (
IsValidClient(iClient) && (iTeam == GetClientTeam(iClient)))
                        
EmitSoundToClient(iClientg_strMusic[Music_RoundWin]);
                    
                return 
Plugin_Handled;
            }
        }
        else if (
StrEqual(strSound"Game.YourTeamLost") == true)
        {
            if (
g_bMusic[Music_RoundLose])
            {
                for (new 
iClient 1iClient <= MaxClientsiClient++)
                    if (
IsValidClient(iClient) && (iTeam == GetClientTeam(iClient)))
                        
EmitSoundToClient(iClientg_strMusic[Music_RoundLose]);
                    
                return 
Plugin_Handled;
            }
            return 
Plugin_Handled;
        }
    }
    return 
Plugin_Continue;
}

/* OnDodgeBallGameFrame()
**
** Every tick of the Dodgeball logic.
** -------------------------------------------------------------------------- */
public Action:OnDodgeBallGameFrame(Handle:hTimerany:Data)
{
    
// Only if both teams are playing
    
if (BothTeamsPlaying() == false) return;
    
    
// Check if we need to fire more rockets.
    
if (GetGameTime() >= g_fNextSpawnTime)
    {
        if (
g_iLastDeadTeam == _:TFTeam_Red)
        {
            new 
iSpawnerEntity g_iSpawnPointsRedEntity[g_iCurrentRedSpawn];
            new 
iSpawnerClass  g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
            if (
g_iRocketCount g_iSpawnersMaxRockets[iSpawnerClass])
            {
                
CreateRocket(iSpawnerEntityiSpawnerClass_:TFTeam_Red);
                
g_iCurrentRedSpawn = (g_iCurrentRedSpawn 1) % g_iSpawnPointsRedCount;
            }
        }
        else
        {
            new 
iSpawnerEntity g_iSpawnPointsBluEntity[g_iCurrentBluSpawn];
            new 
iSpawnerClass  g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
            if (
g_iRocketCount g_iSpawnersMaxRockets[iSpawnerClass])
            {
                
CreateRocket(iSpawnerEntityiSpawnerClass_:TFTeam_Blue);
                
g_iCurrentBluSpawn = (g_iCurrentBluSpawn 1) % g_iSpawnPointsBluCount;
            }
        }
    }
    
    
// Manage the active rockets
    
new iIndex = -1;
    while ((
iIndex FindNextValidRocket(iIndex)) != -1)
    {
        switch (
g_iRocketClassBehaviour[g_iRocketClass[iIndex]])
        {
            case 
Behaviour_Unknown: {}
            case 
Behaviour_Homing:  { HomingRocketThink(iIndex); }
        }
    }
}

//  ___         _       _      
// | _ \___  __| |_____| |_ ___
// |   / _ \/ _| / / -_)  _(_-<
// |_|_\___/\__|_\_\___|\__/__/

/* CreateRocket()
**
** Fires a new rocket entity from the spawner's position.
** -------------------------------------------------------------------------- */
public CreateRocket(iSpawnerEntityiSpawnerClassiTeam)
{    
    new 
iIndex FindFreeRocketSlot();
    if (
iIndex != -1)
    {
        
// Fetch a random rocket class and it's parameters.
        
new iClass GetRandomRocketClass(iSpawnerClass);
        new 
RocketFlags:iFlags g_iRocketClassFlags[iClass];
        
        
// Create rocket entity.
        
new iEntity CreateEntityByName(TestFlags(iFlagsRocketFlag_IsAnimated)? "tf_projectile_sentryrocket" "tf_projectile_rocket");
        if (
iEntity && IsValidEntity(iEntity))
        {
            
// Fetch spawn point's location and angles.
            
new Float:fPosition[3], Float:fAngles[3], Float:fDirection[3];
            
GetEntPropVector(iSpawnerEntityProp_Send"m_vecOrigin"fPosition);
            
GetEntPropVector(iSpawnerEntityProp_Send"m_angRotation"fAngles);
            
GetAngleVectors(fAnglesfDirectionNULL_VECTORNULL_VECTOR);
            
            
// Setup rocket entity.
            
SetEntPropEnt(iEntityProp_Send"m_hOwnerEntity"0);
            
SetEntProp(iEntity,    Prop_Send"m_bCritical",    (GetURandomFloatRange(0.0100.0) <= g_fRocketClassCritChance[iClass])? 01);
            
SetEntProp(iEntity,    Prop_Send"m_iTeamNum",     iTeam1);
            
SetEntProp(iEntity,    Prop_Send"m_iDeflected",   1);
            
TeleportEntity(iEntityfPositionfAnglesFloat:{0.00.00.0});
            
            
// Setup rocket structure with the newly created entity.
            
new iTargetTeam     = (TestFlags(iFlagsRocketFlag_IsNeutral))? GetAnalogueTeam(iTeam);
            new 
iTarget         SelectTarget(iTargetTeam);
            new 
Float:fModifier CalculateModifier(iClass0);
            
g_bRocketIsValid[iIndex]            = true;
            
g_iRocketFlags[iIndex]              = iFlags;
            
g_iRocketEntity[iIndex]             = EntIndexToEntRef(iEntity);
            
g_iRocketTarget[iIndex]             = EntIndexToEntRef(iTarget);    
            
g_iRocketSpawner[iIndex]            = iSpawnerClass;
            
g_iRocketClass[iIndex]              = iClass;
            
g_iRocketDeflections[iIndex]        = 0;
            
g_fRocketLastDeflectionTime[iIndex] = GetGameTime();
            
g_fRocketLastBeepTime[iIndex]       = GetGameTime();
            
g_fRocketSpeed[iIndex]              = CalculateRocketSpeed(iClassfModifier);
            
CopyVectors(fDirectiong_fRocketDirection[iIndex]);
            
SetEntDataFloat(iEntityFindSendPropOffs("CTFProjectile_Rocket""m_iDeflected") + 4CalculateRocketDamage(iClassfModifier), true);
            
DispatchSpawn(iEntity);
                        
            
// Apply custom model, if specified on the flags.
            
if (TestFlags(iFlagsRocketFlag_CustomModel))
            {
                
SetEntityModel(iEntityg_strRocketClassModel[iClass]);
                
UpdateRocketSkin(iEntityiTeamTestFlags(iFlagsRocketFlag_IsNeutral));
            }
            
            
// Execute commands on spawn.
            
if (TestFlags(iFlagsRocketFlag_OnSpawnCmd))
            {
                
ExecuteCommands(g_hRocketClassCmdsOnSpawn[iClass], iClassiEntity0iTargetg_iLastDeadClientg_fRocketSpeed[iIndex], 0);
            }
            
            
// Emit required sounds.
            
EmitRocketSound(RocketSound_SpawniClassiEntityiTargetiFlags);
            
EmitRocketSound(RocketSound_AlertiClassiEntityiTargetiFlags);
            
            
// Done
            
g_iRocketCount++;
            
g_iRocketsFired++;
            
g_fNextSpawnTime GetGameTime() + g_fSpawnersInterval[iSpawnerClass];
        }
    }
}

/* DestroyRocket()
**
** Destroys the rocket at the given index.
** -------------------------------------------------------------------------- */
DestroyRocket(iIndex)
{
    if (
IsValidRocket(iIndex) == true)
    {
        new 
iEntity EntRefToEntIndex(g_iRocketEntity[iIndex]);
        if (
iEntity && IsValidEntity(iEntity)) RemoveEdict(iEntity);
        
g_bRocketIsValid[iIndex] = false;
        
g_iRocketCount--;
    }
}

/* DestroyRockets()
**
** Destroys all the rockets that are currently active.
** -------------------------------------------------------------------------- */
DestroyRockets()
{
    for (new 
iIndex 0iIndex MAX_ROCKETSiIndex++)
    {
        
DestroyRocket(iIndex);
    }
    
g_iRocketCount 0;
}

/* IsValidRocket()
**
** Checks if a rocket structure is valid.
** -------------------------------------------------------------------------- */
bool:IsValidRocket(iIndex)
{
    if ((
iIndex >= 0) && (g_bRocketIsValid[iIndex] == true))
    {
        if (
EntRefToEntIndex(g_iRocketEntity[iIndex]) == -1)
        {
            
g_bRocketIsValid[iIndex] = false;
            
g_iRocketCount--;
            return 
false;
        }
        return 
true;
    }
    return 
false;
}

/* FindNextValidRocket()
**
** Retrieves the index of the next valid rocket from the current offset.
** -------------------------------------------------------------------------- */
FindNextValidRocket(iIndexbool:bWrap false)
{
    for (new 
iCurrent iIndex 1iCurrent MAX_ROCKETSiCurrent++)
        if (
IsValidRocket(iCurrent))
            return 
iCurrent;
        
    return (
bWrap == true)? FindNextValidRocket(-1false) : -1;
}

/* FindFreeRocketSlot()
**
** Retrieves the next free rocket slot since the current one. If all of them
** are full, returns -1.
** -------------------------------------------------------------------------- */
FindFreeRocketSlot()
{
    new 
iIndex g_iLastCreatedRocket;
    new 
iCurrent iIndex;
    
    do
    {
        if (!
IsValidRocket(iCurrent)) return iCurrent;
        if ((++
iCurrent) == MAX_ROCKETSiCurrent 0;
    } while (
iCurrent != iIndex);
    
    return -
1;
}

/* FindRocketByEntity()
**
** Finds a rocket index from it's entity.
** -------------------------------------------------------------------------- */
FindRocketByEntity(iEntity)
{
    new 
iIndex = -1;
    while ((
iIndex FindNextValidRocket(iIndex)) != -1)
        if (
EntRefToEntIndex(g_iRocketEntity[iIndex]) == iEntity)
            return 
iIndex;
        
    return -
1;
}

/* HomingRocketThinkg()
**
** Logic process for the Behaviour_Homing type rockets, wich is simply a
** follower rocket, picking a random target.
** -------------------------------------------------------------------------- */
HomingRocketThink(iIndex)
{
    
// Retrieve the rocket's attributes.
    
new iEntity            EntRefToEntIndex(g_iRocketEntity[iIndex]);
    new 
iClass             g_iRocketClass[iIndex];
    new 
RocketFlags:iFlags g_iRocketFlags[iIndex];
    new 
iTarget            EntRefToEntIndex(g_iRocketTarget[iIndex]);
    new 
iTeam              GetEntProp(iEntityProp_Send"m_iTeamNum"1);
    new 
iTargetTeam        = (TestFlags(iFlagsRocketFlag_IsNeutral))? GetAnalogueTeam(iTeam);
    new 
iDeflectionCount   GetEntProp(iEntityProp_Send"m_iDeflected") - 1;
    new 
Float:fModifier    CalculateModifier(iClassiDeflectionCount);
    
    
// Check if the target is available
    
if (!IsValidClient(iTargettrue))
    {
        
iTarget SelectTarget(iTargetTeam);
        if (!
IsValidClient(iTargettrue)) return;
        
g_iRocketTarget[iIndex] = EntIndexToEntRef(iTarget);
    }
    
// Has the rocket been deflected recently? If so, set new target.
    
else if ((iDeflectionCount g_iRocketDeflections[iIndex]))
    {
        
// Calculate new direction from the player's forward
        
new iClient GetEntPropEnt(iEntityProp_Send"m_hOwnerEntity");
        if (
IsValidClient(iClient))
        {
            new 
Float:fViewAngles[3], Float:fDirection[3];
            
GetClientEyeAngles(iClientfViewAngles);
            
GetAngleVectors(fViewAnglesfDirectionNULL_VECTORNULL_VECTOR);
            
CopyVectors(fDirectiong_fRocketDirection[iIndex]);
            
UpdateRocketSkin(iEntityiTeamTestFlags(iFlagsRocketFlag_IsNeutral));
        }
        
        
// Set new target & deflection count
        
iTarget SelectTarget(iTargetTeamiIndex);
        
g_iRocketTarget[iIndex]             = EntIndexToEntRef(iTarget);
        
g_iRocketDeflections[iIndex]        = iDeflectionCount;
        
g_fRocketLastDeflectionTime[iIndex] = GetGameTime();
        
g_fRocketSpeed[iIndex]              = CalculateRocketSpeed(iClassfModifier);
        
SetEntDataFloat(iEntityFindSendPropOffs("CTFProjectile_Rocket""m_iDeflected") + 4CalculateRocketDamage(iClassfModifier), true);
        if (
TestFlags(iFlagsRocketFlag_ElevateOnDeflect)) g_iRocketFlags[iIndex] |= RocketFlag_Elevating;
        
EmitRocketSound(RocketSound_AlertiClassiEntityiTargetiFlags);
        
        
// Execute appropiate command
        
if (TestFlags(iFlagsRocketFlag_OnDeflectCmd))
        {
            
ExecuteCommands(g_hRocketClassCmdsOnDeflect[iClass], iClassiEntityiClientiTargetg_iLastDeadClientg_fRocketSpeed[iIndex], iDeflectionCount);
        }
    }
    else
    {
        
// If the delay time since the last reflection has been elapsed, rotate towards the client.
        
if ((GetGameTime() - g_fRocketLastDeflectionTime[iIndex]) >= g_fRocketClassControlDelay[iClass])
        {
            
// Calculate turn rate and retrieve directions.
            
new Float:fTurnRate CalculateRocketTurnRate(iClassfModifier);
            
decl Float:fDirectionToTarget[3]; CalculateDirectionToClient(iEntityiTargetfDirectionToTarget);
            
            
// Elevate the rocket after a deflection (if it's enabled on the class definition, of course.)
            
if (g_iRocketFlags[iIndex] & RocketFlag_Elevating)
            {
                if (
g_fRocketDirection[iIndex][2] < g_fRocketClassElevationLimit[iClass])
                {
                    
g_fRocketDirection[iIndex][2] = FMin(g_fRocketDirection[iIndex][2] + g_fRocketClassElevationRate[iClass], g_fRocketClassElevationLimit[iClass]);
                    
fDirectionToTarget[2] = g_fRocketDirection[iIndex][2];
                }
                else
                {
                    
g_iRocketFlags[iIndex] &= ~RocketFlag_Elevating;
                }
            }
            
            
// Smoothly change the orientation to the new one.
            
LerpVectors(g_fRocketDirection[iIndex], fDirectionToTargetg_fRocketDirection[iIndex], fTurnRate);
        }
        
        
// If it's a nuke, beep every some time
        
if ((GetGameTime() - g_fRocketLastBeepTime[iIndex]) >= g_fRocketClassBeepInterval[iClass])
        {
            
EmitRocketSound(RocketSound_BeepiClassiEntityiTargetiFlags);
            
g_fRocketLastBeepTime[iIndex] = GetGameTime();
        }
    }
    
    
// Done
    
ApplyRocketParameters(iIndex);
}

/* CalculateModifier()
**
** Gets the modifier for the damage/speed/rotation calculations.
** -------------------------------------------------------------------------- */
Float:CalculateModifier(iClassiDeflections)
{
    return  
iDeflections 
            (
g_iRocketsFired g_fRocketClassRocketsModifier[iClass]) + 
            (
g_iPlayerCount g_fRocketClassPlayerModifier[iClass]);
}

/* CalculateRocketDamage()
**
** Calculates the damage of the rocket based on it's type and deflection count.
** -------------------------------------------------------------------------- */
Float:CalculateRocketDamage(iClassFloat:fModifier)
{
    return 
g_fRocketClassDamage[iClass] + g_fRocketClassDamageIncrement[iClass] * fModifier;
}

/* CalculateRocketSpeed()
**
** Calculates the speed of the rocket based on it's type and deflection count.
** -------------------------------------------------------------------------- */
Float:CalculateRocketSpeed(iClassFloat:fModifier)
{
    return 
g_fRocketClassSpeed[iClass] + g_fRocketClassSpeedIncrement[iClass] * fModifier;
}

/* CalculateRocketTurnRate()
**
** Calculates the rocket's turn rate based upon it's type and deflection count.
** -------------------------------------------------------------------------- */
Float:CalculateRocketTurnRate(iClassFloat:fModifier)
{
    return 
g_fRocketClassTurnRate[iClass] + g_fRocketClassTurnRateIncrement[iClass] * fModifier;
}

/* CalculateDirectionToClient()
**
** As the name indicates, calculates the orientation for the rocket to move
** towards the specified client.
** -------------------------------------------------------------------------- */
CalculateDirectionToClient(iEntityiClientFloat:fOut[3])
{
    
decl Float:fRocketPosition[3]; GetEntPropVector(iEntityProp_Send"m_vecOrigin"fRocketPosition);
    
GetClientEyePosition(iClientfOut);
    
MakeVectorFromPoints(fRocketPositionfOutfOut);
    
NormalizeVector(fOutfOut);
}

/* ApplyRocketParameters()
**
** Transforms and applies the speed, direction and angles for the rocket
** entity.
** -------------------------------------------------------------------------- */
ApplyRocketParameters(iIndex)
{
    new 
iEntity EntRefToEntIndex(g_iRocketEntity[iIndex]);
    
decl Float:fAngles[3]; GetVectorAngles(g_fRocketDirection[iIndex], fAngles);
    
decl Float:fVelocity[3]; CopyVectors(g_fRocketDirection[iIndex], fVelocity);
    
ScaleVector(fVelocityg_fRocketSpeed[iIndex]);
    
SetEntPropVector(iEntityProp_Data"m_vecAbsVelocity"fVelocity);
    
SetEntPropVector(iEntityProp_Send"m_angRotation"fAngles);
}

/* UpdateRocketSkin()
**
** Changes the skin of the rocket based on it's team.
** -------------------------------------------------------------------------- */
UpdateRocketSkin(iEntityiTeambool:bNeutral)
{
    if (
bNeutral == trueSetEntProp(iEntityProp_Send"m_nSkin"2);
    else                  
SetEntProp(iEntityProp_Send"m_nSkin", (iTeam == _:TFTeam_Blue)? 1);
}

/* GetRandomRocketClass()
**
** Generates a random value and retrieves a rocket class based upon a chances table.
** -------------------------------------------------------------------------- */
GetRandomRocketClass(iSpawnerClass)
{
    new 
iRandom GetURandomIntRange(0101);
    new 
Handle:hTable g_hSpawnersChancesTable[iSpawnerClass];
    new 
iTableSize GetArraySize(hTable);
    new 
iChancesLower 0;
    new 
iChancesUpper 0;
    
    for (new 
iEntry 0iEntry iTableSizeiEntry++)
    {
        
iChancesLower += iChancesUpper;
        
iChancesUpper  iChancesLower GetArrayCell(hTableiEntry);
        
        if ((
iRandom >= iChancesLower) && (iRandom iChancesUpper))
        {
            return 
iEntry;
        }
    }
    
    return 
0;
}

/* EmitRocketSound()
**
** Emits one of the rocket sounds
** -------------------------------------------------------------------------- */
EmitRocketSound(RocketSound:iSoundiClassiEntityiTargetRocketFlags:iFlags)
{
    switch (
iSound)
    {
        case 
RocketSound_Spawn:
        {
            if (
TestFlags(iFlagsRocketFlag_PlaySpawnSound))
            {
                if (
TestFlags(iFlagsRocketFlag_CustomSpawnSound)) EmitSoundToAll(g_strRocketClassSpawnSound[iClass], iEntity);
                else                                                
EmitSoundToAll(SOUND_DEFAULT_SPAWNiEntity);
            }
        }
        case 
RocketSound_Beep:
        {
            if (
TestFlags(iFlagsRocketFlag_PlayBeepSound))
            {
                if (
TestFlags(iFlagsRocketFlag_CustomBeepSound)) EmitSoundToAll(g_strRocketClassBeepSound[iClass], iEntity);
                else                                               
EmitSoundToAll(SOUND_DEFAULT_BEEPiEntity);
            }
        }
        case 
RocketSound_Alert:
        {
            if (
TestFlags(iFlagsRocketFlag_PlayAlertSound))
            {
                if (
TestFlags(iFlagsRocketFlag_CustomAlertSound)) EmitSoundToClient(iTargetg_strRocketClassAlertSound[iClass]);
                else                                                
EmitSoundToClient(iTargetSOUND_DEFAULT_ALERT____0.5);
            }
        }
    }
}

//  ___         _       _      ___ _                    
// | _ \___  __| |_____| |_   / __| |__ _ ______ ___ ___
// |   / _ \/ _| / / -_)  _| | (__| / _` (_-<_-</ -_|_-<
// |_|_\___/\__|_\_\___|\__|  \___|_\__,_/__/__/\___/__/
//                                                      

/* DestroyRocketClasses()
**
** Frees up all the rocket classes defined now.
** -------------------------------------------------------------------------- */
DestroyRocketClasses()
{
    for (new 
iIndex 0iIndex g_iRocketClassCountiIndex++)
    {
        new 
Handle:hCmdOnSpawn   g_hRocketClassCmdsOnSpawn[iIndex];
        new 
Handle:hCmdOnKill    g_hRocketClassCmdsOnKill[iIndex];
        new 
Handle:hCmdOnExplode g_hRocketClassCmdsOnExplode[iIndex];
        new 
Handle:hCmdOnDeflect g_hRocketClassCmdsOnDeflect[iIndex];
        if (
hCmdOnSpawn   != INVALID_HANDLECloseHandle(hCmdOnSpawn);
        if (
hCmdOnKill    != INVALID_HANDLECloseHandle(hCmdOnKill);
        if (
hCmdOnExplode != INVALID_HANDLECloseHandle(hCmdOnExplode);
        if (
hCmdOnDeflect != INVALID_HANDLECloseHandle(hCmdOnDeflect);
        
g_hRocketClassCmdsOnSpawn[iIndex]   = INVALID_HANDLE;
        
g_hRocketClassCmdsOnKill[iIndex]    = INVALID_HANDLE;
        
g_hRocketClassCmdsOnExplode[iIndex] = INVALID_HANDLE;
        
g_hRocketClassCmdsOnDeflect[iIndex] = INVALID_HANDLE;
    }
    
g_iRocketClassCount 0;
    
ClearTrie(g_hRocketClassTrie);
}

//  ___                          ___     _     _                     _    ___ _                    
// / __|_ __  __ ___ __ ___ _   | _ \___(_)_ _| |_ ___  __ _ _ _  __| |  / __| |__ _ ______ ___ ___
// \__ \ '_ \/ _` \ V  V / ' \  |  _/ _ \ | ' \  _(_-< / _` | ' \/ _` | | (__| / _` (_-<_-</ -_|_-<
// |___/ .__/\__,_|\_/\_/|_||_| |_| \___/_|_||_\__/__/ \__,_|_||_\__,_|  \___|_\__,_/__/__/\___/__/
//     |_|                                                                                         

/* DestroySpawners()
**
** Frees up all the spawner points defined up to now.
** -------------------------------------------------------------------------- */
DestroySpawners()
{
    for (new 
iIndex 0iIndex g_iSpawnersCountiIndex++)
    {
        
CloseHandle(g_hSpawnersChancesTable[iIndex]);
    }
    
g_iSpawnersCount  0;
    
g_iSpawnPointsRedCount 0;
    
g_iSpawnPointsBluCount 0;
    
g_iDefaultRedSpawner = -1;
    
g_iDefaultBluSpawner = -1;
    
ClearTrie(g_hSpawnersTrie);
}

/* PopulateSpawnPoints()
**
** Iterates through all the possible spawn points and assigns them an spawner.
** -------------------------------------------------------------------------- */
PopulateSpawnPoints()
{
    
// Clear the current settings
    
g_iSpawnPointsRedCount 0;
    
g_iSpawnPointsBluCount 0;
    
    
// Iterate through all the info target points and check 'em out.
    
new iEntity = -1;
    while ((
iEntity FindEntityByClassname(iEntity"info_target")) != -1)
    {
        
decl String:strName[32]; GetEntPropString(iEntityProp_Data"m_iName"strNamesizeof(strName));
        if ((
StrContains(strName"rocket_spawn_red") != -1) || (StrContains(strName"tf_dodgeball_red") != -1))
        {
            
// Find most appropiate spawner class for this entity.
            
new iIndex FindSpawnerByName(strName);
            if (!
IsValidRocket(iIndex)) iIndex g_iDefaultRedSpawner;
            
            
// Upload to point list
            
g_iSpawnPointsRedClass [g_iSpawnPointsRedCount] = iIndex;
            
g_iSpawnPointsRedEntity[g_iSpawnPointsRedCount] = iEntity;
            
g_iSpawnPointsRedCount++;
        }
        if ((
StrContains(strName"rocket_spawn_blue") != -1) || (StrContains(strName"tf_dodgeball_blu") != -1))
        {
            
// Find most appropiate spawner class for this entity.
            
new iIndex FindSpawnerByName(strName);
            if (!
IsValidRocket(iIndex)) iIndex g_iDefaultBluSpawner;
            
            
// Upload to point list
            
g_iSpawnPointsBluClass [g_iSpawnPointsBluCount] = iIndex;
            
g_iSpawnPointsBluEntity[g_iSpawnPointsBluCount] = iEntity;
            
g_iSpawnPointsBluCount++;
        }
    }
    
    
// Check if there exists spawn points
    
if (g_iSpawnPointsRedCount == 0SetFailState("No RED spawn points found on this map.");
    if (
g_iSpawnPointsBluCount == 0SetFailState("No BLU spawn points found on this map.");
}

/* FindSpawnerByName()
**
** Finds the first spawner wich contains the given name.
** -------------------------------------------------------------------------- */
FindSpawnerByName(String:strName[32])
{
    new 
iIndex = -1;
    
GetTrieValue(g_hSpawnersTriestrNameiIndex);
    return 
iIndex;
}        


/*
**••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
**    ______                                          __    
**   / ____/___  ____ ___  ____ ___  ____ _____  ____/ /____
**  / /   / __ \/ __ `__ \/ __ `__ \/ __ `/ __ \/ __  / ___/
** / /___/ /_/ / / / / / / / / / / / /_/ / / / / /_/ (__  ) 
** \____/\____/_/ /_/ /_/_/ /_/ /_/\__,_/_/ /_/\__,_/____/  
**                                                          
**••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
*/

/* RegisterCommands()
**
** Creates helper server commands to use with the plugin's events system.
** -------------------------------------------------------------------------- */
RegisterCommands()
{
    
RegServerCmd("tf_dodgeball_explosion"CmdExplosion);
    
RegServerCmd("tf_dodgeball_shockwave"CmdShockwave);
}

/* CmdExplosion()
**
** Creates a huge explosion at the location of the client.
** -------------------------------------------------------------------------- */
public Action:CmdExplosion(iArgs)
{
    if (
iArgs == 1)
    {
        
decl String:strBuffer[8], iClient;
        
GetCmdArg(1strBuffersizeof(strBuffer));
        
iClient StringToInt(strBuffer);
        if (
IsValidEntity(iClient))
        {
            
decl Float:fPosition[3]; GetClientAbsOrigin(iClientfPosition);
            switch (
GetURandomIntRange(04))
            {
                case 
0: { PlayParticle(fPositionPARTICLE_NUKE_1_ANGLESPARTICLE_NUKE_1); }
                case 
1: { PlayParticle(fPositionPARTICLE_NUKE_2_ANGLESPARTICLE_NUKE_2); }
                case 
2: { PlayParticle(fPositionPARTICLE_NUKE_3_ANGLESPARTICLE_NUKE_3); }
                case 
3: { PlayParticle(fPositionPARTICLE_NUKE_4_ANGLESPARTICLE_NUKE_4); }
                case 
4: { PlayParticle(fPositionPARTICLE_NUKE_5_ANGLESPARTICLE_NUKE_5); }
            }
            
PlayParticle(fPositionPARTICLE_NUKE_COLLUMN_ANGLESPARTICLE_NUKE_COLLUMN);
        }
    }
    else
    {
        
PrintToServer("Usage: tf_dodgeball_explosion <client index>");
    }
    
    return 
Plugin_Handled;
}

/* CmdShockwave()
**
** Creates a huge shockwave at the location of the client, with the given
** parameters.
** -------------------------------------------------------------------------- */
public Action:CmdShockwave(iArgs)
{
    if (
iArgs == 5)
    {
        
decl String:strBuffer[8], iClientiTeamFloat:fPosition[3], iDamageFloat:fPushStrengthFloat:fRadiusFloat:fFalloffRadius;
        
GetCmdArg(1strBuffersizeof(strBuffer)); iClient        StringToInt(strBuffer);
        
GetCmdArg(2strBuffersizeof(strBuffer)); iDamage        StringToInt(strBuffer);
        
GetCmdArg(3strBuffersizeof(strBuffer)); fPushStrength  StringToFloat(strBuffer);
        
GetCmdArg(4strBuffersizeof(strBuffer)); fRadius        StringToFloat(strBuffer);
        
GetCmdArg(5strBuffersizeof(strBuffer)); fFalloffRadius StringToFloat(strBuffer);
        
        if (
IsValidClient(iClient))
        {
            
iTeam GetClientTeam(iClient);
            
GetClientAbsOrigin(iClientfPosition);
            
            for (
iClient 1iClient <= MaxClientsiClient++)
            {
                if ((
IsValidClient(iClienttrue) == true) && (GetClientTeam(iClient) == iTeam))
                {
                    
decl Float:fPlayerPosition[3]; GetClientEyePosition(iClientfPlayerPosition);
                    new 
Float:fDistanceToShockwave GetVectorDistance(fPositionfPlayerPosition);
                    
                    if (
fDistanceToShockwave fRadius)
                    {
                        
decl Float:fImpulse[3], Float:fFinalPushiFinalDamage;
                        
fImpulse[0] = fPlayerPosition[0] - fPosition[0];
                        
fImpulse[1] = fPlayerPosition[1] - fPosition[1];
                        
fImpulse[2] = fPlayerPosition[2] - fPosition[2];
                        
NormalizeVector(fImpulsefImpulse);
                        if (
fImpulse[2] < 0.4) { fImpulse[2] = 0.4NormalizeVector(fImpulsefImpulse); }
                        
                        if (
fDistanceToShockwave fFalloffRadius)
                        {
                            
fFinalPush fPushStrength;
                            
iFinalDamage iDamage;
                        }
                        else
                        {
                            new 
Float:fImpact = (1.0 - ((fDistanceToShockwave fFalloffRadius) / (fRadius fFalloffRadius)));
                            
fFinalPush   fImpact fPushStrength;
                            
iFinalDamage RoundToFloor(fImpact iDamage);
                        }
                        
ScaleVector(fImpulsefFinalPush);
                        
SetEntPropVector(iClientProp_Data"m_vecAbsVelocity"fImpulse);
                        
                        new 
Handle:hDamage CreateDataPack();
                        
WritePackCell(hDamageiClient);
                        
WritePackCell(hDamageiFinalDamage);
                        
CreateTimer(0.1ApplyDamagehDamageTIMER_FLAG_NO_MAPCHANGE);
                    }
                }
            }
        }
    }
    else
    {
        
PrintToServer("Usage: tf_dodgeball_shockwave <client index> <damage> <push strength> <radius> <falloff>");
    }
    
    return 
Plugin_Handled;
}

/* ExecuteCommands()
**
** The core of the plugin's event system, unpacks and correctly formats the
** given command strings to be executed.
** -------------------------------------------------------------------------- */
ExecuteCommands(Handle:hDataPackiClassiRocketiOwneriTargetiLastDeadFloat:fSpeediNumDeflections)
{
    
ResetPack(hDataPackfalse);
    new 
iNumCommands ReadPackCell(hDataPack);
    while (
iNumCommands-- > 0)
    {
        
decl String:strCmd[256], String:strBuffer[8];
        
ReadPackString(hDataPackstrCmdsizeof(strCmd));
        
ReplaceString(strCmdsizeof(strCmd), "@name"g_strRocketClassLongName[iClass]);
        
Format(strBuffersizeof(strBuffer), "%i"iRocket);         ReplaceString(strCmdsizeof(strCmd), "@rocket"strBuffer);
        
Format(strBuffersizeof(strBuffer), "%i"iOwner);          ReplaceString(strCmdsizeof(strCmd), "@owner"strBuffer);
        
Format(strBuffersizeof(strBuffer), "%i"iTarget);         ReplaceString(strCmdsizeof(strCmd), "@target"strBuffer);
        
Format(strBuffersizeof(strBuffer), "%i"iLastDead);       ReplaceString(strCmdsizeof(strCmd), "@dead"strBuffer);
        
Format(strBuffersizeof(strBuffer), "%f"fSpeed);          ReplaceString(strCmdsizeof(strCmd), "@speed"strBuffer);
        
Format(strBuffersizeof(strBuffer), "%i"iNumDeflections); ReplaceString(strCmdsizeof(strCmd), "@deflections"strBuffer);
        
ServerCommand(strCmd);
    }
}

/*
**••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
**    ______            _____      
**   / ____/___  ____  / __(_)___ _
**  / /   / __ \/ __ \/ /_/ / __ `/
** / /___/ /_/ / / / / __/ / /_/ / 
** \____/\____/_/ /_/_/ /_/\__, /  
**                        /____/   
**••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
*/

/* ParseConfiguration()
**
** Parses a Dodgeball configuration file. It doesn't clear any of the previous
** data, so multiple files can be parsed.
** -------------------------------------------------------------------------- */
bool:ParseConfigurations(String:strConfigFile[] = "general.cfg")
{
    
// Parse configuration
    
decl String:strPath[PLATFORM_MAX_PATH];
    
decl String:strFileName[PLATFORM_MAX_PATH];
    
Format(strFileNamesizeof(strFileName), "configs/dodgeball/%s"strConfigFile);
    
BuildPath(Path_SMstrPathsizeof(strPath), strFileName);
       
    
// Try to parse if it exists
    
LogMessage("Executing configuration file %s"strPath);    
    if (
FileExists(strPathtrue))
    {
        new 
Handle:kvConfig CreateKeyValues("TF2_Dodgeball");
        if (
FileToKeyValues(kvConfigstrPath) == falseSetFailState("Error while parsing the configuration file.");
        
KvGotoFirstSubKey(kvConfig);
        
        
// Parse the subsections
        
do
        {
            
decl String:strSection[64]; KvGetSectionName(kvConfigstrSectionsizeof(strSection));
            
            if (
StrEqual(strSection"general"))       ParseGeneral(kvConfig);
            else if (
StrEqual(strSection"classes"))  ParseClasses(kvConfig);
            else if (
StrEqual(strSection"spawners")) ParseSpawners(kvConfig);
        }
        while (
KvGotoNextKey(kvConfig));
            
        
CloseHandle(kvConfig);
    }
}

/* ParseGeneral()
**
** Parses general settings, such as the music, urls, etc.
** -------------------------------------------------------------------------- */
ParseGeneral(Handle:kvConfig)
{
    
g_bMusicEnabled bool:KvGetNum(kvConfig"music"0);
    if (
g_bMusicEnabled == true)
    {
        
g_bUseWebPlayer bool:KvGetNum(kvConfig"use web player"0);
        
KvGetString(kvConfig"web player url",   g_strWebPlayerUrlsizeof(g_strWebPlayerUrl));
        
        
g_bMusic[Music_RoundStart] = KvGetString(kvConfig"round start",      g_strMusic[Music_RoundStart], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundStart]);
        
g_bMusic[Music_RoundWin]   = KvGetString(kvConfig"round end (win)",  g_strMusic[Music_RoundWin],   PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundWin]);
        
g_bMusic[Music_RoundLose]  = KvGetString(kvConfig"round end (lose)"g_strMusic[Music_RoundLose],  PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundLose]);
        
g_bMusic[Music_Gameplay]   = KvGetString(kvConfig"gameplay",         g_strMusic[Music_Gameplay],   PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_Gameplay]);
    }
}

/* ParseClasses()
**
** Parses the rocket classes data from the given configuration file.
** -------------------------------------------------------------------------- */
ParseClasses(Handle:kvConfig)
{
    
decl String:strName[64];
    
decl String:strBuffer[256];
    
    
KvGotoFirstSubKey(kvConfig);
    do
    {
        new 
iIndex g_iRocketClassCount;
        new 
RocketFlags:iFlags;
        
        
// Basic parameters
        
KvGetSectionName(kvConfigstrNamesizeof(strName));         strcopy(g_strRocketClassName[iIndex], 16strName);
        
KvGetString(kvConfig"name"strBuffersizeof(strBuffer));  strcopy(g_strRocketClassLongName[iIndex], 32strBuffer);
        if (
KvGetString(kvConfig"model"strBuffersizeof(strBuffer)))
        {
            
strcopy(g_strRocketClassModel[iIndex], PLATFORM_MAX_PATHstrBuffer);
            if (
strlen(g_strRocketClassModel[iIndex]) != 0)
            {
                
iFlags |= RocketFlag_CustomModel;
                if (
KvGetNum(kvConfig"is animated"0)) iFlags |= RocketFlag_IsAnimated;
            }
        }
        
        
KvGetString(kvConfig"behaviour"strBuffersizeof(strBuffer), "homing");
        if (
StrEqual(strBuffer"homing")) g_iRocketClassBehaviour[iIndex] = Behaviour_Homing;
        else                               
g_iRocketClassBehaviour[iIndex] = Behaviour_Unknown;
        
        if (
KvGetNum(kvConfig"play spawn sound"0) == 1)
        {
            
iFlags |= RocketFlag_PlaySpawnSound;
            if (
KvGetString(kvConfig"spawn sound"g_strRocketClassSpawnSound[iIndex], PLATFORM_MAX_PATH) && (strlen(g_strRocketClassSpawnSound[iIndex]) != 0))
            {
                
iFlags |= RocketFlag_CustomSpawnSound;
            }
        }
        
        if (
KvGetNum(kvConfig"play beep sound"0) == 1)
        {
            
iFlags |= RocketFlag_PlayBeepSound;
            
g_fRocketClassBeepInterval[iIndex] = KvGetFloat(kvConfig"beep interval"0.5);
            if (
KvGetString(kvConfig"beep sound"g_strRocketClassBeepSound[iIndex], PLATFORM_MAX_PATH) && (strlen(g_strRocketClassBeepSound[iIndex]) != 0))
            {
                
iFlags |= RocketFlag_CustomBeepSound;
            }
        }
        
        if (
KvGetNum(kvConfig"play alert sound"0) == 1)
        {
            
iFlags |= RocketFlag_PlayAlertSound;
            if (
KvGetString(kvConfig"alert sound"g_strRocketClassAlertSound[iIndex], PLATFORM_MAX_PATH) && strlen(g_strRocketClassAlertSound[iIndex]) != 0)
            {
                
iFlags |= RocketFlag_CustomAlertSound;
            }    
        }
        
        
// Behaviour modifiers
        
if (KvGetNum(kvConfig"elevate on deflect"1) == 1iFlags |= RocketFlag_ElevateOnDeflect;
        if (
KvGetNum(kvConfig"neutral rocket"0) == 1)     iFlags |= RocketFlag_IsNeutral;
        
        
// Movement parameters
        
g_fRocketClassDamage[iIndex]            = KvGetFloat(kvConfig"damage");
        
g_fRocketClassDamageIncrement[iIndex]   = KvGetFloat(kvConfig"damage increment");
        
g_fRocketClassCritChance[iIndex]        = KvGetFloat(kvConfig"critical chance");
        
g_fRocketClassSpeed[iIndex]             = KvGetFloat(kvConfig"speed");
        
g_fRocketClassSpeedIncrement[iIndex]    = KvGetFloat(kvConfig"speed increment");
        
g_fRocketClassTurnRate[iIndex]          = KvGetFloat(kvConfig"turn rate");
        
g_fRocketClassTurnRateIncrement[iIndex] = KvGetFloat(kvConfig"turn rate increment");
        
g_fRocketClassElevationRate[iIndex]     = KvGetFloat(kvConfig"elevation rate");
        
g_fRocketClassElevationLimit[iIndex]    = KvGetFloat(kvConfig"elevation limit");
        
g_fRocketClassControlDelay[iIndex]      = KvGetFloat(kvConfig"control delay");
        
g_fRocketClassPlayerModifier[iIndex]    = KvGetFloat(kvConfig"no. players modifier");
        
g_fRocketClassRocketsModifier[iIndex]   = KvGetFloat(kvConfig"no. rockets modifier");
        
g_fRocketClassTargetWeight[iIndex]      = KvGetFloat(kvConfig"direction to target weight");
        
        
// Events
        
new Handle:hCmds INVALID_HANDLE;
        
KvGetString(kvConfig"on spawn"strBuffersizeof(strBuffer));
        if ((
hCmds ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnSpawnCmdg_hRocketClassCmdsOnSpawn[iIndex] = hCmds; }
        
KvGetString(kvConfig"on deflect"strBuffersizeof(strBuffer));
        if ((
hCmds ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnDeflectCmdg_hRocketClassCmdsOnDeflect[iIndex] = hCmds; }
        
KvGetString(kvConfig"on kill"strBuffersizeof(strBuffer));
        if ((
hCmds ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnKillCmdg_hRocketClassCmdsOnKill[iIndex] = hCmds; }
        
KvGetString(kvConfig"on explode"strBuffersizeof(strBuffer));
        if ((
hCmds ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnExplodeCmdg_hRocketClassCmdsOnExplode[iIndex] = hCmds; }
        
        
// Done
        
SetTrieValue(g_hRocketClassTriestrNameiIndex);
        
g_iRocketClassFlags[iIndex] = iFlags;
        
g_iRocketClassCount++;
    }
    while (
KvGotoNextKey(kvConfig));
    
KvGoBack(kvConfig); 
}

/* ParseSpawners()
**
** Parses the spawn points classes data from the given configuration file.
** -------------------------------------------------------------------------- */
ParseSpawners(Handle:kvConfig)
{
    
decl String:strBuffer[256];
    
KvGotoFirstSubKey(kvConfig);
        
    do
    {
        new 
iIndex g_iSpawnersCount;
        
        
// Basic parameters
        
KvGetSectionName(kvConfigstrBuffersizeof(strBuffer)); strcopy(g_strSpawnersName[iIndex], 32strBuffer);
        
g_iSpawnersMaxRockets[iIndex] = KvGetNum(kvConfig"max rockets"1);
        
g_fSpawnersInterval[iIndex]   = KvGetFloat(kvConfig"interval"1.0);
        
        
// Chances table
        
g_hSpawnersChancesTable[iIndex] = CreateArray();
        for (new 
iClassIndex 0iClassIndex g_iRocketClassCountiClassIndex++)
        {
            
Format(strBuffersizeof(strBuffer), "%s%%"g_strRocketClassName[iClassIndex]);
            
PushArrayCell(g_hSpawnersChancesTable[iIndex], KvGetNum(kvConfigstrBuffer0));
        }
        
        
// Done.
        
SetTrieValue(g_hSpawnersTrieg_strSpawnersName[iIndex], iIndex);
        
g_iSpawnersCount++;
    }
    while (
KvGotoNextKey(kvConfig));
    
KvGoBack(kvConfig);
    
    
GetTrieValue(g_hSpawnersTrie"red"g_iDefaultRedSpawner);
    
GetTrieValue(g_hSpawnersTrie"blu"g_iDefaultBluSpawner);
}

/* ParseCommands()
**
** Part of the event system, parses the given command strings and packs them
** to a Datapack.
** -------------------------------------------------------------------------- */
Handle:ParseCommands(String:strLine[])
{
    
TrimString(strLine);
    if (
strlen(strLine) == 0)
    {
        return 
INVALID_HANDLE;
    }
    else
    {
        new 
String:strStrings[8][255];
        new 
iNumStrings ExplodeString(strLine";"strStrings8255);
        
        new 
Handle:hDataPack CreateDataPack();
        
WritePackCell(hDataPackiNumStrings);
        for (new 
0iNumStringsi++)
        {
            
WritePackString(hDataPackstrStrings[i]);
        }
        
        return 
hDataPack;
    }
}

/*
**••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
**   ______            __    
**  /_  __/___  ____  / /____
**   / / / __ \/ __ \/ / ___/
**  / / / /_/ / /_/ / (__  ) 
** /_/  \____/\____/_/____/  
**
**••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••••
*/

/* ApplyDamage()
**
** Applies a damage to a player.
** -------------------------------------------------------------------------- */
public Action:ApplyDamage(Handle:hTimerany:hDataPack)
{
    
ResetPack(hDataPackfalse);
    new 
iClient ReadPackCell(hDataPack);
    new 
iDamage ReadPackCell(hDataPack);
    
CloseHandle(hDataPack);
    
SlapPlayer(iClientiDamagetrue);
}

/* CopyVectors()
**
** Copies the contents from a vector to another.
** -------------------------------------------------------------------------- */
stock CopyVectors(Float:fFrom[3], Float:fTo[3])
{
    
fTo[0] = fFrom[0];
    
fTo[1] = fFrom[1];
    
fTo[2] = fFrom[2];
}

/* LerpVectors()
**
** Calculates the linear interpolation of the two given vectors and stores
** it on the third one.
** -------------------------------------------------------------------------- */
stock LerpVectors(Float:fA[3], Float:fB[3], Float:fC[3], Float:t)
{
    if (
0.00.0;
    if (
1.01.0;
    
    
fC[0] = fA[0] + (fB[0] - fA[0]) * t;
    
fC[1] = fA[1] + (fB[1] - fA[1]) * t;
    
fC[2] = fA[2] + (fB[2] - fA[2]) * t;
}

/* IsValidClient()
**
** Checks if the given client index is valid, and if it's alive or not.
** -------------------------------------------------------------------------- */
stock bool:IsValidClient(iClientbool:bAlive false)
{
    if (
iClient >= &&
    
iClient <= MaxClients &&
    
IsClientConnected(iClient) &&
    
IsClientInGame(iClient) &&
    (
bAlive == false || IsPlayerAlive(iClient)))
    {
        return 
true;
    }
    
    return 
false;
}

/* BothTeamsPlaying()
**
** Checks if there are players on both teams.
** -------------------------------------------------------------------------- */
stock bool:BothTeamsPlaying()
{
    new 
bool:bRedFoundbool:bBluFound;
    for (new 
iClient 1iClient <= MaxClientsiClient++)
    {
        if (
IsValidClient(iClienttrue) == false) continue;
        new 
iTeam GetClientTeam(iClient);
        if (
iTeam == _:TFTeam_RedbRedFound true;
        if (
iTeam == _:TFTeam_BluebBluFound true;
    }
    return 
bRedFound && bBluFound;
}

/* CountAlivePlayers()
**
** Retrieves the number of players alive.
** -------------------------------------------------------------------------- */
stock CountAlivePlayers()
{
    new 
iCount 0;
    for (new 
iClient 1iClient <= MaxClientsiClient++)
    {
        if (
IsValidClient(iClienttrue)) iCount++;
    }
    return 
iCount;
}

/* SelectTarget()
**
** Determines a random target of the given team for the homing rocket.
** -------------------------------------------------------------------------- */
stock SelectTarget(iTeamiRocket = -1)
{
    new 
iTarget             = -1;
    new 
Float:fTargetWeight 0.0;
    
decl Float:fRocketPosition[3];
    
decl Float:fRocketDirection[3];
    
decl Float:fWeight;
    new 
bool:bUseRocket;
    
    if (
iRocket != -1)
    {
        new 
iClass g_iRocketClass[iRocket];
        new 
iEntity EntRefToEntIndex(g_iRocketEntity[iRocket]);
        
        
GetEntPropVector(iEntityProp_Send"m_vecOrigin"fRocketPosition);
        
CopyVectors(g_fRocketDirection[iRocket], fRocketDirection);
        
fWeight g_fRocketClassTargetWeight[iClass];
        
        
bUseRocket true;
    }
    
    for (new 
iClient 1iClient <= MaxClientsiClient++)
    {    
        
// If the client isn't connected, skip.
        
if (!IsValidClient(iClienttrue)) continue;
        if (
iTeam && GetClientTeam(iClient) != iTeam) continue;
        
        
// Determine if this client should be the target.
        
new Float:fNewWeight GetURandomFloatRange(0.0100.0);
        
        if (
bUseRocket == true)
        {
            
decl Float:fClientPosition[3]; GetClientEyePosition(iClientfClientPosition);
            
decl Float:fDirectionToClient[3]; MakeVectorFromPoints(fRocketPositionfClientPositionfDirectionToClient);
            
fNewWeight += GetVectorDotProduct(fRocketDirectionfDirectionToClient) * fWeight;
        }
        
        if ((
iTarget == -1) || fNewWeight >= fTargetWeight)
        {
            
iTarget iClient;
            
fTargetWeight fNewWeight;
        }
    }
    
    return 
iTarget;
}

/* StopSoundToAll()
**
** Stops a sound for all the clients on the given channel.
** -------------------------------------------------------------------------- */
stock StopSoundToAll(iChannel, const String:strSound[])
{
    for (new 
iClient 1iClient <= MaxClientsiClient++)
    {
        if (
IsValidClient(iClient)) StopSound(iClientiChannelstrSound);
    }
}

/* PlayParticle()
**
** Plays a particle system at the given location & angles.
** -------------------------------------------------------------------------- */
stock PlayParticle(Float:fPosition[3], Float:fAngles[3], String:strParticleName[], Float:fEffectTime 5.0Float:fLifeTime 9.0)
{
    new 
iEntity CreateEntityByName("info_particle_system");
    if (
iEntity && IsValidEdict(iEntity))
    {
        
TeleportEntity(iEntityfPositionfAnglesNULL_VECTOR);
        
DispatchKeyValue(iEntity"effect_name"strParticleName);
        
ActivateEntity(iEntity);
        
AcceptEntityInput(iEntity"Start");
        
CreateTimer(fEffectTimeStopParticleEntIndexToEntRef(iEntity));
        
CreateTimer(fLifeTimeKillParticleEntIndexToEntRef(iEntity));
    }
    else
    {
        
LogError("ShowParticle: could not create info_particle_system");
    }    
}

/* StopParticle()
**
** Turns of the particle system. Automatically called by PlayParticle
** -------------------------------------------------------------------------- */
public Action:StopParticle(Handle:hTimerany:iEntityRef)
{
    if (
iEntityRef != INVALID_ENT_REFERENCE)
    {
        new 
iEntity EntRefToEntIndex(iEntityRef);
        if (
iEntity && IsValidEntity(iEntity))
        {
            
AcceptEntityInput(iEntity"Stop");
        }
    }
}

/* KillParticle()
**
** Destroys the particle system. Automatically called by PlayParticle
** -------------------------------------------------------------------------- */
public Action:KillParticle(Handle:hTimerany:iEntityRef)
{
    if (
iEntityRef != INVALID_ENT_REFERENCE)
    {
        new 
iEntity EntRefToEntIndex(iEntityRef);
        if (
iEntity && IsValidEntity(iEntity))
        {
            
RemoveEdict(iEntity);
        }
    }
}

/* PrecacheParticle()
**
** Forces the client to precache a particle system.
** -------------------------------------------------------------------------- */
stock PrecacheParticle(String:strParticleName[])
{
    
PlayParticle(Float:{0.00.00.0}, Float:{0.00.00.0}, strParticleName0.10.1);
}

/* FindEntityByClassnameSafe()
**
** Used to iterate through entity types, avoiding problems in cases where
** the entity may not exist anymore.
** -------------------------------------------------------------------------- */
stock FindEntityByClassnameSafe(iStart, const String:strClassname[])
{
    while (
iStart > -&& !IsValidEntity(iStart))
    {
        
iStart--;
    }
    return 
FindEntityByClassname(iStartstrClassname);
}

/* GetAnalogueTeam()
**
** Gets the analogue team for this. In case of Red, it's Blue, and viceversa.
** -------------------------------------------------------------------------- */
stock GetAnalogueTeam(iTeam)
{
    if (
iTeam == _:TFTeam_Red) return _:TFTeam_Blue;
    return 
_:TFTeam_Red;
}

/* ShowHiddenMOTDPanel()
**
** Shows a hidden MOTD panel, useful for streaming music.
** -------------------------------------------------------------------------- */
stock ShowHiddenMOTDPanel(iClientString:strTitle[], String:strMsg[], String:strType[] = "2")
{
    new 
Handle:hPanel CreateKeyValues("data");
    
KvSetString(hPanel"title"strTitle);
    
KvSetString(hPanel"type"strType);
    
KvSetString(hPanel"msg"strMsg);
    
ShowVGUIPanel(iClient"info"hPanelfalse);
    
CloseHandle(hPanel);
}

/* PrecacheSoundEx()
**
** Precaches a sound and adds it to the download table.
** -------------------------------------------------------------------------- */
stock PrecacheSoundEx(String:strFileName[], bool:bPreload=falsebool:bAddToDownloadTable=false)
{
    new 
String:strFinalPath[PLATFORM_MAX_PATH]; 
    
Format(strFinalPathsizeof(strFinalPath), "sound/%s"strFileName);
    
PrecacheSound(strFileNamebPreload);
    if (
bAddToDownloadTable == trueAddFileToDownloadsTable(strFinalPath);
}

/* PrecacheModelEx()
**
** Precaches a models and adds it to the download table.
** -------------------------------------------------------------------------- */
stock PrecacheModelEx(String:strFileName[], bool:bPreload=falsebool:bAddToDownloadTable=false)
{
    
PrecacheModel(strFileNamebPreload);
    if (
bAddToDownloadTable)
    {
        
decl String:strDepFileName[PLATFORM_MAX_PATH];
        
Format(strDepFileNamesizeof(strDepFileName), "%s.res"strFileName);
        
        if (
FileExists(strDepFileName))
        {
            
// Open stream, if possible
            
new Handle:hStream OpenFile(strDepFileName"r");
            if (
hStream == INVALID_HANDLE) { LogMessage("Error, can't read file containing model dependencies."); return; }
            
            while(!
IsEndOfFile(hStream))
            {
                
decl String:strBuffer[PLATFORM_MAX_PATH];
                
ReadFileLine(hStreamstrBuffersizeof(strBuffer));
                
CleanString(strBuffer);
                
                
// If file exists...
                
if (FileExists(strBuffertrue))
                {
                    
// Precache depending on type, and add to download table
                    
if (StrContains(strBuffer".vmt"false) != -1)      PrecacheDecal(strBuffertrue);
                    else if (
StrContains(strBuffer".mdl"false) != -1PrecacheModel(strBuffertrue);
                    else if (
StrContains(strBuffer".pcf"false) != -1PrecacheGeneric(strBuffertrue);
                    
AddFileToDownloadsTable(strBuffer);
                }
            }
            
            
// Close file
            
CloseHandle(hStream);
        }
    }
}

/* CleanString()
**
** Cleans the given string from any illegal character.
** -------------------------------------------------------------------------- */
stock CleanString(String:strBuffer[])
{
    
// Cleanup any illegal characters
    
new Length strlen(strBuffer);
    for (new 
iPos=0iPos<LengthiPos++)
    {
        switch(
strBuffer[iPos])
        {
            case 
'\r'strBuffer[iPos] = ' ';
            case 
'\n'strBuffer[iPos] = ' ';
            case 
'\t'strBuffer[iPos] = ' ';
        }
    }
    
    
// Trim string
    
TrimString(strBuffer);
}

/* FMax()
**
** Returns the maximum of the two values given.
** -------------------------------------------------------------------------- */
stock Float:FMax(Float:aFloat:b)
{
    return (
b)? a:b;
}

/* FMin()
**
** Returns the minimum of the two values given.
** -------------------------------------------------------------------------- */
stock Float:FMin(Float:aFloat:b)
{
    return (
b)? a:b;
}

/* GetURandomIntRange()
**
** 
** -------------------------------------------------------------------------- */
stock GetURandomIntRange(iMiniMax)
{
    return 
iMin + (GetURandomInt() % (iMax iMin 1));
}

/* GetURandomFloatRange()
**
** 
** -------------------------------------------------------------------------- */
stock Float:GetURandomFloatRange(Float:fMinFloat:fMax)
{
    return 
fMin + (GetURandomFloat() * (fMax fMin));
}

// EOF 
Please help me out. I really need this plugins for my server.
Attached Files
File Type: sp Get Plugin or Get Source (TF2_Dodgeball.sp - 56 views - 76.5 KB)
File Type: sp Get Plugin or Get Source (SpeedAnnouncer.sp - 201 views - 2.8 KB)

Last edited by cjieliang; 03-15-2015 at 16:56.
cjieliang is offline
cjieliang
Member
Join Date: Apr 2014
Old 03-16-2015 , 13:30   Re: [TF2][Request] Fix for Dodgeball Speed Announcer
Reply With Quote #2

..
cjieliang is offline
cjieliang
Member
Join Date: Apr 2014
Old 03-17-2015 , 02:34   Re: [TF2][Request] Fix for Dodgeball Speed Announcer
Reply With Quote #3

Anyone?
cjieliang is offline
cjieliang
Member
Join Date: Apr 2014
Old 03-17-2015 , 13:35   Re: [TF2][Request] Fix for Dodgeball Speed Announcer
Reply With Quote #4

..
cjieliang is offline
ReFlexPoison
☠☠☠
Join Date: Jul 2011
Location: ☠☠☠
Old 03-17-2015 , 14:42   Re: [TF2][Request] Fix for Dodgeball Speed Announcer
Reply With Quote #5

plz post moar
ReFlexPoison is offline
cjieliang
Member
Join Date: Apr 2014
Old 03-18-2015 , 08:06   Re: [TF2][Request] Fix for Dodgeball Speed Announcer
Reply With Quote #6

As in?
cjieliang is offline
cjieliang
Member
Join Date: Apr 2014
Old 03-20-2015 , 15:45   Re: [TF2][Request] Fix for Dodgeball Speed Announcer
Reply With Quote #7

wrwe
cjieliang is offline
cjieliang
Member
Join Date: Apr 2014
Old 03-26-2015 , 13:23   Re: [TF2][Request] Fix for Dodgeball Speed Announcer
Reply With Quote #8

up
cjieliang is offline
Newbie1992
Senior Member
Join Date: Jan 2013
Location: Germany
Old 03-26-2015 , 14:40   Re: [TF2][Request] Fix for Dodgeball Speed Announcer
Reply With Quote #9

( ͡° ͜ʖ ͡°)
__________________
Newbie1992 is offline
cjieliang
Member
Join Date: Apr 2014
Old 03-26-2015 , 17:43   Re: [TF2][Request] Fix for Dodgeball Speed Announcer
Reply With Quote #10

awww
cjieliang is offline
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -4. The time now is 08:07.


Powered by vBulletin®
Copyright ©2000 - 2024, vBulletin Solutions, Inc.
Theme made by Freecode