· 5 years ago · Feb 28, 2020, 01:44 AM
1// *********************************************************************************
2// PREPROCESSOR
3// *********************************************************************************
4#pragma semicolon 1 // Force strict semicolon mode.
5
6// *********************************************************************************
7// INCLUDES
8// *********************************************************************************
9#include <sourcemod>
10#include <sdktools>
11#include <sdkhooks>
12#include <tf2>
13#include <tf2_stocks>
14#include <tf2items>
15#include <tf2attributes>
16#include <morecolors>
17
18
19// *********************************************************************************
20// CONSTANTS
21// *********************************************************************************
22// ---- Plugin-related constants ---------------------------------------------------
23#define PLUGIN_NAME "[TF2] Zonas' Custom Dodgeball Plugin"
24#define PLUGIN_AUTHOR "Damizean, Edited by blood and soul, Modifications from Walgrim's, edit combined by DeadSworn"
25#define PLUGIN_VERSION "2.0 - Mod"
26#define PLUGIN_CONTACT "https://savita-gaming.com"
27
28// ---- General settings -----------------------------------------------------------
29
30#define FPS_LOGIC_RATE 20.0
31#define FPS_LOGIC_INTERVAL 1.0 / FPS_LOGIC_RATE
32
33// ---- Maximum structure sizes ----------------------------------------------------
34#define MAX_ROCKETS 100
35#define MAX_ROCKET_CLASSES 50
36#define MAX_SPAWNER_CLASSES 50
37#define MAX_SPAWN_POINTS 100
38
39// ---- 1V1 ------------------------------------------------------------------------
40
41#define TEAM_RED 2
42#define TEAM_BLUE 3
43
44// ---- PyroVision Stuff -----------------------------------------------------------
45#define PYROVISION_ATTRIBUTE "vision opt in flags"
46
47#define USAGE "Usage: sm_ab [0/1]"
48int abPrevention[MAXPLAYERS + 1];
49int firstJoined[MAXPLAYERS + 1];
50
51// ---- Asherkin's RocketBounce Stuff ----------------------------------------------
52#define MAX_EDICT_BITS 11
53#define MAX_EDICTS (1 << MAX_EDICT_BITS)
54
55int g_nBounces[MAX_EDICTS];
56
57Handle g_hMaxBouncesConVar;
58int g_config_iMaxBounces = 2;
59
60// ---- Airblast -------------------------------------------------------------------
61bool Airblast[MAXPLAYERS + 1] = { true, ... };
62
63// Particlessss
64//int g_RocketParticle[MAXPLAYERS + 1];
65
66// ---- Flags and types constants --------------------------------------------------
67enum Musics
68{
69 Music_RoundStart,
70 Music_RoundWin,
71 Music_RoundLose,
72 Music_Gameplay,
73 SizeOfMusicsArray
74};
75
76
77enum BehaviourTypes
78{
79 Behaviour_Unknown,
80 Behaviour_Homing
81};
82
83enum RocketFlags
84{
85 RocketFlag_None = 0,
86 RocketFlag_PlaySpawnSound = 1 << 0,
87 RocketFlag_PlayBeepSound = 1 << 1,
88 RocketFlag_PlayAlertSound = 1 << 2,
89 RocketFlag_ElevateOnDeflect = 1 << 3,
90 RocketFlag_IsNeutral = 1 << 4,
91 RocketFlag_Exploded = 1 << 5,
92 RocketFlag_OnSpawnCmd = 1 << 6,
93 RocketFlag_OnDeflectCmd = 1 << 7,
94 RocketFlag_OnKillCmd = 1 << 8,
95 RocketFlag_OnExplodeCmd = 1 << 9,
96 RocketFlag_CustomModel = 1 << 10,
97 RocketFlag_CustomSpawnSound = 1 << 11,
98 RocketFlag_CustomBeepSound = 1 << 12,
99 RocketFlag_CustomAlertSound = 1 << 13,
100 RocketFlag_Elevating = 1 << 14,
101 RocketFlag_IsAnimated = 1 << 15
102};
103
104enum RocketSound
105{
106 RocketSound_Spawn,
107 RocketSound_Beep,
108 RocketSound_Alert
109};
110
111enum SpawnerFlags
112{
113 SpawnerFlag_Team_Red = 1,
114 SpawnerFlag_Team_Blu = 2,
115 SpawnerFlag_Team_Both = 3
116};
117
118#define TestFlags(%1,%2) (!!((%1) & (%2)))
119#define TestFlagsAnd(%1,%2) (((%1) & (%2)) == %2)
120
121// ---- Other resources ------------------------------------------------------------
122#define SOUND_DEFAULT_SPAWN "weapons/sentry_rocket.wav"
123#define SOUND_DEFAULT_BEEP "weapons/sentry_scan.wav"
124#define SOUND_DEFAULT_ALERT "weapons/sentry_spot.wav"
125#define SOUND_DEFAULT_SPEEDUPALERT "misc/doomsday_lift_warning.wav"
126#define SNDCHAN_MUSIC 32
127#define PARTICLE_NUKE_1 "fireSmokeExplosion"
128#define PARTICLE_NUKE_2 "fireSmokeExplosion1"
129#define PARTICLE_NUKE_3 "fireSmokeExplosion2"
130#define PARTICLE_NUKE_4 "fireSmokeExplosion3"
131#define PARTICLE_NUKE_5 "fireSmokeExplosion4"
132#define PARTICLE_NUKE_COLLUMN "fireSmoke_collumnP"
133#define PARTICLE_NUKE_1_ANGLES view_as<float> ({270.0, 0.0, 0.0})
134#define PARTICLE_NUKE_2_ANGLES PARTICLE_NUKE_1_ANGLES
135#define PARTICLE_NUKE_3_ANGLES PARTICLE_NUKE_1_ANGLES
136#define PARTICLE_NUKE_4_ANGLES PARTICLE_NUKE_1_ANGLES
137#define PARTICLE_NUKE_5_ANGLES PARTICLE_NUKE_1_ANGLES
138#define PARTICLE_NUKE_COLLUMN_ANGLES PARTICLE_NUKE_1_ANGLES
139
140#define MAXMULTICOLORHUD 5
141
142// Debug
143//#define DEBUG
144
145// *********************************************************************************
146// VARIABLES
147// *********************************************************************************
148
149// -----<<< Cvars >>>-----
150Handle g_hCvarEnabled;
151Handle g_hCvarEnableCfgFile;
152Handle g_hCvarDisableCfgFile;
153Handle g_hCvarSpeedo;
154Handle g_hCvarAnnounce;
155Handle g_hCvarPyroVisionEnabled = INVALID_HANDLE;
156Handle g_hCvarAirBlastCommandEnabled;
157Handle g_hCvarDeflectCountAnnounce;
158Handle g_hCvarRedirectBeep;
159Handle g_hCvarPreventTauntKillEnabled;
160Handle g_hCvarStealPrevention;
161Handle g_hCvarStealPreventionNumber;
162
163Handle g_hCvarDelayPrevention;
164Handle g_hCvarDelayPreventionTime;
165Handle g_hCvarDelayPreventionSpeedup;
166Handle g_hCvarVoteRocketClassCommandEnabled;
167Handle g_hCvarVoteClassPercentage;
168Handle g_hCvarVoteClassCooldown;
169Handle g_hCvarServerChatTag;
170Handle g_hCvarMainChatColor;
171Handle g_hCvarKeywordChatColor;
172Handle g_hCvarClientChatColor;
173Handle g_hCvarNameHud;
174
175
176ConVar g_hTouchAnnounce;
177
178// -----<<< Gameplay >>>-----
179bool IsOnTouch;
180bool IsOnGameFrame;
181//int g_stolen[MAXPLAYERS + 1];
182bool g_bEnabled; // Is the plugin enabled?
183bool g_bRoundStarted; // Has the round started?
184int g_iRoundCount; // Current round count since map start
185int g_iRocketsFired; // No. of rockets fired since round start
186//Handle g_hLogicTimer; // Logic timer
187float g_fLastSpawnTime; // Time at which the last rocket had spawned
188float g_fNextSpawnTime; // Time at which the next rocket will be able to spawn
189int g_iLastDeadTeam; // The team of the last dead client. If none, it's a random team.
190int g_iLastDeadClient; // The last dead client. If none, it's a random client.
191int g_iPlayerCount;
192int gDeflector;
193Handle g_hHud;
194Handle g_h1v1Hud;
195Handle g_hNameHud;
196int g_iRocketSpeed;
197int g_hRocketSpeed;
198int xDeflections;
199Handle g_hTimerHud;
200
201enum eRocketSteal
202{
203 stoleRocket = false,
204 rocketsStolen
205};
206
207int bStealArray[MAXPLAYERS + 1][eRocketSteal];
208
209// -----<<< Configuration >>>-----
210bool g_bMusicEnabled;
211bool g_bMusic[view_as<int>(SizeOfMusicsArray)];
212char g_strMusic[view_as<int>(SizeOfMusicsArray)][PLATFORM_MAX_PATH];
213bool g_bUseWebPlayer;
214char g_strWebPlayerUrl[256];
215
216char g_strServerChatTag[256];
217char g_strMainChatColor[256];
218char g_strKeywordChatColor[256];
219char g_strClientChatColor[256];
220char g_strNameHud[256];
221// -----<<< Structures >>>-----
222// Rockets
223bool g_bRocketIsValid[MAX_ROCKETS];
224bool g_bRocketIsNuke[MAX_ROCKETS];
225bool g_bPreventingDelay;
226int g_iRocketEntity[MAX_ROCKETS];
227int g_iRocketTarget[MAX_ROCKETS];
228int g_iRocketSpawner[MAX_ROCKETS];
229int g_iRocketClass[MAX_ROCKETS];
230RocketFlags g_iRocketFlags[MAX_ROCKETS];
231float g_fRocketSpeed[MAX_ROCKETS];
232float g_fRocketDirection[MAX_ROCKETS][3];
233int g_iRocketDeflections[MAX_ROCKETS];
234float g_fRocketLastDeflectionTime[MAX_ROCKETS];
235float g_fRocketLastBeepTime[MAX_ROCKETS];
236int g_iLastCreatedRocket;
237int g_iRocketCount;
238float g_fSavedSpeed;
239float g_fSavedSpeedIncrement;
240
241// Classes
242char g_strRocketClassName[MAX_ROCKET_CLASSES][16];
243char g_strRocketClassLongName[MAX_ROCKET_CLASSES][32];
244char g_strSavedClassName[32];
245BehaviourTypes g_iRocketClassBehaviour[MAX_ROCKET_CLASSES];
246char g_strRocketClassModel[MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
247RocketFlags g_iRocketClassFlags[MAX_ROCKET_CLASSES];
248float g_fRocketClassBeepInterval[MAX_ROCKET_CLASSES];
249char g_strRocketClassSpawnSound[MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
250char g_strRocketClassBeepSound[MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
251char g_strRocketClassAlertSound[MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
252float g_fRocketClassCritChance[MAX_ROCKET_CLASSES];
253float g_fRocketClassDamage[MAX_ROCKET_CLASSES];
254float g_fRocketClassDamageIncrement[MAX_ROCKET_CLASSES];
255float g_fRocketClassSpeed[MAX_ROCKET_CLASSES];
256float g_fRocketClassSpeedIncrement[MAX_ROCKET_CLASSES];
257float g_fRocketClassTurnRate[MAX_ROCKET_CLASSES];
258float g_fRocketClassTurnRateIncrement[MAX_ROCKET_CLASSES];
259float g_fRocketClassElevationRate[MAX_ROCKET_CLASSES];
260float g_fRocketClassElevationLimit[MAX_ROCKET_CLASSES];
261float g_fRocketClassRocketsModifier[MAX_ROCKET_CLASSES];
262float g_fRocketClassPlayerModifier[MAX_ROCKET_CLASSES];
263float g_fRocketClassControlDelay[MAX_ROCKET_CLASSES];
264float g_fRocketClassTargetWeight[MAX_ROCKET_CLASSES];
265Handle g_hRocketClassCmdsOnSpawn[MAX_ROCKET_CLASSES];
266Handle g_hRocketClassCmdsOnDeflect[MAX_ROCKET_CLASSES];
267Handle g_hRocketClassCmdsOnKill[MAX_ROCKET_CLASSES];
268Handle g_hRocketClassCmdsOnExplode[MAX_ROCKET_CLASSES];
269Handle g_hRocketClassTrie;
270char g_iRocketClassCount;
271
272// Spawner classes
273char g_strSpawnersName[MAX_SPAWNER_CLASSES][32];
274int g_iSpawnersMaxRockets[MAX_SPAWNER_CLASSES];
275float g_fSpawnersInterval[MAX_SPAWNER_CLASSES];
276Handle g_hSpawnersChancesTable[MAX_SPAWNER_CLASSES];
277Handle g_hSpawnersTrie;
278int g_iSpawnersCount;
279
280// Array containing the spawn points for the Red team, and
281// their associated spawner class.
282int g_iCurrentRedSpawn;
283int g_iSpawnPointsRedCount;
284int g_iSpawnPointsRedClass[MAX_SPAWN_POINTS];
285int g_iSpawnPointsRedEntity[MAX_SPAWN_POINTS];
286
287// Array containing the spawn points for the Blu team, and
288// their associated spawner class.
289int g_iCurrentBluSpawn;
290int g_iSpawnPointsBluCount;
291int g_iSpawnPointsBluClass[MAX_SPAWN_POINTS];
292int g_iSpawnPointsBluEntity[MAX_SPAWN_POINTS];
293
294// The default spawner class.
295int g_iDefaultRedSpawner;
296int g_iDefaultBluSpawner;
297
298//Observer
299int g_observer;
300int g_op_rocket;
301
302//Voting
303
304bool g_bVoteClassEnabled; // check if players are allowed to vote for a rocket class change
305bool g_bCanVoteClass; // check if voting for rocket class is on cooldown
306bool g_bClassVoted[MAXPLAYERS + 1] = {false, ...}; // check which players have voted for a rocket class change
307int g_iClassVoters = 0; // maximum number of votes
308int g_iClassVotes = 0; // how many rocket class votes recieved
309int g_iClassVotesRequired; // how many rocket class votes are needed
310int g_iTimeVoted = 0;
311int iVotes;
312bool iVote[MAXPLAYERS + 1] = false;
313Handle g_percentVote = null;
314bool bounceActivated = true;
315bool godmode = false;
316ConVar cV_enabled;
317
318// *********************************************************************************
319// PLUGIN
320// *********************************************************************************
321public Plugin myinfo =
322{
323 name = PLUGIN_NAME,
324 author = PLUGIN_AUTHOR,
325 description = PLUGIN_NAME,
326 version = PLUGIN_VERSION,
327 url = PLUGIN_CONTACT
328};
329
330// *********************************************************************************
331// METHODS
332// *********************************************************************************
333
334/* OnPluginStart()
335**
336** When the plugin is loaded.
337** -------------------------------------------------------------------------- */
338public void OnPluginStart()
339{
340 char strModName[32]; GetGameFolderName(strModName, sizeof(strModName));
341 if (!StrEqual(strModName, "tf"))SetFailState("This plugin is only for Team Fortress 2.");
342
343 CreateConVar("tf_dodgeballupdated_version", PLUGIN_VERSION, PLUGIN_NAME, FCVAR_SPONLY | FCVAR_UNLOGGED | FCVAR_DONTRECORD | FCVAR_REPLICATED | FCVAR_NOTIFY);
344 g_percentVote = CreateConVar("sm_percentageVotes", "50.0", "Needed percentage to activate the superbot", _, true, 0.0, true, 100.0);
345 g_hCvarEnabled = CreateConVar("tf_dodgeball_enabled", "1", "Enable Dodgeball on TFDB maps?", _, true, 0.0, true, 1.0);
346 g_hCvarEnableCfgFile = CreateConVar("tf_dodgeball_enablecfg", "sourcemod/dodgeball_enable.cfg", "Config file to execute when enabling the Dodgeball game mode.", FCVAR_PROTECTED);
347 g_hCvarDisableCfgFile = CreateConVar("tf_dodgeball_disablecfg", "sourcemod/dodgeball_disable.cfg", "Config file to execute when disabling the Dodgeball game mode.", FCVAR_PROTECTED);
348 g_hCvarSpeedo = CreateConVar("tf_dodgeball_speedo", "1", "Enable HUD speedometer", FCVAR_PROTECTED);
349 g_hCvarAnnounce = CreateConVar("tf_dodgeball_announce", "1", "Enable kill announces in chat", FCVAR_PROTECTED);
350 g_hCvarPyroVisionEnabled = CreateConVar("tf_dodgeball_pyrovision", "0", "Enable pyrovision for everyone", FCVAR_PROTECTED);
351 g_hMaxBouncesConVar = CreateConVar("tf_dodgeball_rbmax", "10000", "Max number of times a rocket will bounce.", FCVAR_PROTECTED, true, 0.0, false);
352 g_hCvarAirBlastCommandEnabled = CreateConVar("tf_dodgeball_airblast", "1", "Enable if airblast is enabled or not", FCVAR_PROTECTED);
353 g_hCvarDeflectCountAnnounce = CreateConVar("tf_dodgeball_count_deflect", "1", "Enable number of deflections in kill announce", FCVAR_PROTECTED);
354 g_hCvarRedirectBeep = CreateConVar("tf_dodgeball_rdrbeep", "1", "Do redirects beep?", FCVAR_PROTECTED);
355 g_hCvarPreventTauntKillEnabled = CreateConVar("tf_dodgeball_block_tauntkill", "0", "Block taunt kills?", FCVAR_PROTECTED);
356 g_hCvarStealPrevention = CreateConVar("tf_dodgeball_steal_prevention", "0", "Enable steal prevention?", FCVAR_PROTECTED);
357 g_hCvarStealPreventionNumber = CreateConVar("tf_dodgeball_sp_number", "3", "How many steals before you get slayed?", FCVAR_PROTECTED);
358
359 g_hCvarDelayPrevention = CreateConVar("tf_dodgeball_delay_prevention", "0", "Enable delay prevention?", FCVAR_PROTECTED);
360 g_hCvarDelayPreventionTime = CreateConVar("tf_dodgeball_dp_time", "5", "How much time (in seconds) before delay prevention activates?", FCVAR_PROTECTED, true, 0.0, false);
361 g_hCvarDelayPreventionSpeedup = CreateConVar("tf_dodgeball_dp_speedup", "100", "How much speed (in hammer units per second) should the rocket gain (20 Refresh Rate for every 0.1 seconds) for delay prevention? Multiply by (15/352) for mph.", FCVAR_NONE, true, 0.0, false);
362 g_hCvarVoteRocketClassCommandEnabled = CreateConVar("tf_dodgeball_voteclass", "1", "Should voting for rocket class be enabled or not?", FCVAR_PROTECTED);
363 g_hCvarVoteClassPercentage = CreateConVar("tf_dodgeball_voteclass_percentage", "0.60", "Percentage of votes required to change rocket class.", FCVAR_PROTECTED, true, 0.0, true, 1.0);
364 g_hCvarVoteClassCooldown = CreateConVar("tf_dodgeball_voteclass_cooldown", "30", "Seconds before another vote for rocket class can be initated.", FCVAR_PROTECTED, true, 0.0);
365
366 g_hCvarServerChatTag = CreateConVar("tf_dodgeball_servertag", "{DARKOLIVEGREEN}[TFDB]", "Tag that appears at the start of each chat announcement.", FCVAR_PROTECTED);
367 g_hCvarMainChatColor = CreateConVar("tf_dodgeball_maincolor", "{WHITE}", "Color assigned to the majority of the words in chat announcements.");
368 g_hCvarKeywordChatColor = CreateConVar("tf_dodgeball_keywordcolor", "{DARKOLIVEGREEN}", "Color assigned to the most important words in chat announcements.", FCVAR_PROTECTED);
369 g_hCvarClientChatColor = CreateConVar("tf_dodgeball_clientwordcolor", "{ORANGE}", "Color assigned to the client in chat announcements.", FCVAR_PROTECTED);
370 g_hCvarNameHud = CreateConVar("tf_dodgeball_serverhud", "Extale Studio", "This is the server's name'", FCVAR_PROTECTED);
371
372
373 g_hTouchAnnounce = CreateConVar("tf_dodgeball_touchannounce", "1", "Announce speeds on touch instead of on death.", FCVAR_NONE, true, 0.0, true, 1.0);
374
375 // Commands
376 RegConsoleCmd("sm_ab", Command_ToggleAirblast, USAGE);
377 RegAdminCmd("sm_tfdb", Command_DodgeballAdminMenu, ADMFLAG_GENERIC, "A menu for admins to modify things inside the plugin.");
378
379 RegConsoleCmd("sm_currentrocket", Command_PostCurrentRocketClass, "Posts a chat message of the name of the current main rocket class.");
380 RegConsoleCmd("sm_voterocket", Command_VoteRocketClass, "Vote to change the current rocket class.");
381 RegAdminCmd("sm_class", Command_SetRocketClass, ADMFLAG_GENERIC, "Set the main rocket class via name.");
382 RegAdminCmd("sm_reload", Command_Reload, ADMFLAG_GENERIC, "sm_reload - Reload Dodgeball yesyes.");
383 RegAdminCmd("sm_unload", Command_Unload, ADMFLAG_GENERIC, "sm_unload - unload Dodgeball yesyes.");
384 RegAdminCmd("sm_load", Command_Load, ADMFLAG_GENERIC, "sm_load - load Dodgeball yesyes.");
385 RegConsoleCmd("sm_votebounce", Command_VotePVB, "Vote for the bounce");
386
387 ServerCommand("tf_arena_use_queue 0");
388
389 HookConVarChange(g_hMaxBouncesConVar, tf2dodgeball_hooks);
390 HookConVarChange(g_hCvarPyroVisionEnabled, tf2dodgeball_hooks);
391
392 HookConVarChange(g_hCvarVoteRocketClassCommandEnabled, tf2dodgeball_hooks);
393 HookConVarChange(g_hCvarVoteClassPercentage, tf2dodgeball_hooks);
394 HookConVarChange(g_hCvarVoteClassCooldown, tf2dodgeball_hooks);
395
396 g_bVoteClassEnabled = GetConVarBool(g_hCvarVoteRocketClassCommandEnabled);
397
398
399 HookConVarChange(g_hCvarServerChatTag, tf2dodgeball_hooks);
400 HookConVarChange(g_hCvarMainChatColor, tf2dodgeball_hooks);
401 HookConVarChange(g_hCvarKeywordChatColor, tf2dodgeball_hooks);
402 HookConVarChange(g_hCvarClientChatColor, tf2dodgeball_hooks);
403
404 GetConVarString(g_hCvarServerChatTag, g_strServerChatTag, sizeof(g_strServerChatTag));
405 GetConVarString(g_hCvarMainChatColor, g_strMainChatColor, sizeof(g_strMainChatColor));
406 GetConVarString(g_hCvarKeywordChatColor, g_strKeywordChatColor, sizeof(g_strKeywordChatColor));
407 GetConVarString(g_hCvarClientChatColor, g_strClientChatColor, sizeof(g_strClientChatColor));
408 GetConVarString(g_hCvarNameHud, g_strNameHud, sizeof(g_strNameHud));
409
410 cV_enabled = FindConVar("sm_ovo_enabled");
411 cV_enabled = CreateConVar("sm_ovo_enabled", "1.0", "Enabled or disabled", _, true, 0.0, true, 1.0);
412 if(cV_enabled != null) {
413 HookConVarChange(cV_enabled, CVarChanged);
414 }
415
416 g_hRocketClassTrie = CreateTrie();
417 g_hSpawnersTrie = CreateTrie();
418
419 g_bCanVoteClass = true;
420
421 g_hHud = CreateHudSynchronizer();
422 g_h1v1Hud = CreateHudSynchronizer();
423 g_hNameHud = CreateHudSynchronizer();
424
425
426 LoadTranslations("tf2dodgeball.phrases");
427 AutoExecConfig(true, "tf2_dodgeball");
428
429 RegisterCommands();
430
431 for (int i = 1; i <= MaxClients; i++)
432 {
433 if (IsClientInGame(i))
434 {
435 SDKHook(i, SDKHook_OnTakeDamage, OnTakeDamage);
436 }
437 }
438 SetGodmodeState();
439}
440
441
442
443
444bool PluginDisabled() {
445 return !GetConVarBool(cV_enabled);
446}
447
448public void CVarChanged(ConVar convar, const char[] oldValue, const char[] newValue) {
449 cV_enabled = FindConVar("sm_ovo_enabled");
450}
451public bool SetGodmodeState()
452{
453 if(PluginDisabled()) {
454 return false;
455 }
456
457 int playerCount = GetTeamClientCount(3) + GetTeamClientCount(2);
458 if (playerCount == 2)
459 {
460 if (godmode != true)
461 {
462 CPrintToChatAll("{DARKOLIVEGREEN}[{DEFAULT}TFDB{DARKOLIVEGREEN}] {DEFAULT}2 players found, {TURQUOISE}Enabling {DEFAULT}1{ORANGE}v{DEFAULT}1 Mode");
463 }
464 godmode = true;
465 }
466 else
467 {
468 if (godmode)
469 {
470 CPrintToChatAll("{DARKOLIVEGREEN}[{DEFAULT}TFDB{DARKOLIVEGREEN}] {DEFAULT}More than 2 players found, Disabling {DEFAULT}1{ORANGE}v{DEFAULT}1 Mode");
471 }
472 godmode = false;
473 }
474 return godmode;
475}
476
477public void OnRoundActive(Handle event, char[] name, bool dontBroadcast)
478{
479 SetGodmodeState();
480 CreateTimer(1.0, Truc);
481}
482
483public void OnTeamSwitch(Event event, char[] name, bool dontBroadcast)
484{
485 SetGodmodeState();
486}
487
488public Action OnTakeDamage(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom)
489{
490 if(PluginDisabled()) {
491 return Plugin_Continue;
492 }
493
494 SetGodmodeState();
495 if (godmode)
496 {
497 damage = 0.0;
498 return Plugin_Changed;
499 }
500 return Plugin_Continue;
501}
502
503
504
505/*
506** ▄▀ ▄▀▄ █░█ █▄░█ ▀█▀
507** █░ █░█ █░█ █░▀█ ░█░
508** ░▀ ░▀░ ░▀░ ▀░░▀ ░▀░
509***********************************/
510stock int GetAllClientCount() {
511 int count = 0;
512 for (int i = 1; i <= MaxClients; i++) {
513 if (IsClientInGame(i) && !IsFakeClient(i) && GetClientTeam(i) > 1) {
514 count += 1;
515 }
516 }
517 return count;
518}
519
520///////////////////////
521
522public Action Command_VotePVB(int client, int args) {
523 int iNeededVotes = RoundToCeil((GetAllClientCount() * GetConVarFloat(g_percentVote)) / 100.0);
524 if (!iVote[client] && IsValidClient(client))
525 {
526 iVotes++;
527 if (!bounceActivated)
528 {
529 CPrintToChatAll("%s %s%N %swants to enable Rocket Bounce! (%s%i%s/%s%i%s).", g_strServerChatTag, g_strClientChatColor, client, g_strMainChatColor, g_strKeywordChatColor, iVotes, g_strMainChatColor, g_strKeywordChatColor, iNeededVotes, g_strMainChatColor);
530 }
531 else
532 {
533 CPrintToChatAll("%s %s%N %swants to disable Rocket Bounce! (%s%i%s/%s%i%s).", g_strServerChatTag, g_strClientChatColor, client, g_strMainChatColor, g_strKeywordChatColor, iVotes, g_strMainChatColor, g_strKeywordChatColor, iNeededVotes, g_strMainChatColor);
534 }
535 iVote[client] = true;
536 } else if (iVote[client] && IsValidClient(client)) {
537 CPrintToChat(client, "%s %s%N %syou can't vote twice!", g_strServerChatTag, g_strClientChatColor, client, g_strMainChatColor);
538 }
539 if (iVotes >= iNeededVotes) {
540 if (!bounceActivated) {
541 CPrintToChatAll("%s %sRocket Bounce is now %sactivated%s!", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, g_strMainChatColor);
542 EnableMode();
543 }
544 else {
545 CPrintToChatAll("%s %sRocket Bounce is now %sdisabled%s!", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, g_strMainChatColor);
546 DisableMode();
547 }
548 iVote[client] = false;
549 iVotes = 0;
550 }
551 return Plugin_Handled;
552}
553
554/*
555**█▀▀ █▄░█ ▄▀▄ █▀▄ █░░ █▀▀ ▄▀▄ █▀▀▄ █▀▄ ▀ ▄▀▀ ▄▀▄ █▀▄ █░░ █▀▀
556**█▀▀ █░▀█ █▀█ █▀█ █░▄ █▀▀ █░█ █▐█▀ █░█ █ ░▀▄ █▀█ █▀█ █░▄ █▀▀
557**▀▀▀ ▀░░▀ ▀░▀ ▀▀░ ▀▀▀ ▀▀▀ ░▀░ ▀░▀▀ ▀▀░ ▀ ▀▀░ ▀░▀ ▀▀░ ▀▀▀ ▀▀▀
558***********************************************************************************/
559
560stock void EnableMode()
561{
562 EnableBounce();
563 bounceActivated = true;
564}
565
566stock void EnableBounce()
567{
568 ServerCommand("sm_cvar tf_dodgeball_rbmax 999");
569}
570
571stock void DisableMode()
572{
573 DisableBounce();
574 bounceActivated = false;
575}
576
577stock void DisableBounce()
578{
579 ServerCommand("sm_cvar tf_dodgeball_rbmax 0");
580}
581
582public Action Command_Reload(int client, int args)
583 {
584 ServerCommand("sm plugins reload Dodgeballtouch");
585 CPrintToChatAll("%s %sPlugin has been %sreloaded%s.", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, g_strMainChatColor);
586 }
587
588public Action Command_Unload(int client, int args)
589 {
590 ServerCommand("sm plugins unload Dodgeballtouch");
591 CPrintToChatAll("%s %sPlugin has been %sunloaded%s.", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, g_strMainChatColor);
592 }
593
594public Action Command_Load(int client, int args)
595 {
596 ServerCommand("sm plugins load Dodgeballtouch");
597 CPrintToChatAll("%s %sPlugin has been %sloaded%s.", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, g_strMainChatColor);
598 }
599
600/* OnConfigsExecuted()
601**
602** When all the configuration files have been executed, try to enable the
603** Dodgeball.
604** -------------------------------------------------------------------------- */
605public void OnConfigsExecuted()
606{
607 if (GetConVarBool(g_hCvarEnabled) && IsDodgeBallMap())
608 {
609 EnableDodgeBall();
610 }
611}
612
613/* OnMapStart()
614**
615** When the map starts, clear vote variables.
616** -------------------------------------------------------------------------- */
617
618public void OnMapStart()
619{
620 g_iClassVoters = 0;
621 g_iClassVotes = 0;
622 g_iClassVotesRequired = 0;
623}
624
625/* OnMapEnd()
626**
627** When the map ends, disable DodgeBall.
628** -------------------------------------------------------------------------- */
629public void OnMapEnd()
630{
631 DisableDodgeBall();
632
633 g_bVoteClassEnabled = false;
634}
635
636public Action Command_SetRocketClass(int client, int args)
637{
638 if (args < 1)
639 {
640 ReplyToCommand(client, "[SM] Usage: sm_class <rocketclassname>");
641 return Plugin_Handled;
642 }
643
644 char class[PLATFORM_MAX_PATH];
645 GetCmdArg(1, class, sizeof(class));
646
647 int classIndex = FindRocketClass(class);
648
649 if (classIndex == -1)
650 {
651 ReplyToCommand(client, "%t", "Rocket class was not found", class);
652 return Plugin_Handled;
653 }
654
655 ShowActivity2(client, "", "%t", "Changed rocket class", class);
656 LogAction(client, -1, "\"%L\" changed rocket class to \"%s\"", client, class);
657
658 SetMainRocketClass(classIndex, false);
659
660 return Plugin_Handled;
661}
662
663public Action Command_DodgeballAdminMenu(int client, int args)
664{
665 Menu menu = new Menu(DodgeballAdmin_Handler, MENU_ACTIONS_ALL);
666
667 menu.SetTitle("Dodgeball Admin Menu");
668
669 menu.AddItem("0", "Max Rocket Count");
670 menu.AddItem("1", "Speed Multiplier");
671 menu.AddItem("2", "Main Rocket Class");
672 menu.AddItem("3", "Refresh Configurations");
673 menu.AddItem("4", "Reload Plugin");
674 menu.AddItem("5", "Enable Rocket Bounce");
675 menu.ExitButton = true;
676 menu.Display(client, MENU_TIME_FOREVER);
677
678 return Plugin_Handled;
679}
680
681public int DodgeballAdmin_Handler(Menu menu, MenuAction action, int param1, int param2)
682{
683 switch (action)
684 {
685 case MenuAction_Start:
686 {
687 // It's important to log anything in any way, the best is printtoserver, but if you just want to log to client to make it easier to get progress done, feel free.
688 PrintToServer("Displaying menu"); // Log it
689 }
690
691 case MenuAction_Display:
692 {
693 PrintToServer("Client %d was sent menu with panel %x", param1, param2); // Log so you can check if it gets sent.
694 }
695
696 case MenuAction_Select:
697 {
698 char sInfo[32];
699 menu.GetItem(param2, sInfo, sizeof(sInfo));
700
701 switch (param2)
702 {
703 case 0:
704 {
705 DrawMaxRocketCountMenu(param1);
706 }
707 case 1:
708 {
709 DrawRocketSpeedMenu(param1);
710 }
711 case 2:
712 {
713 if (!g_strSavedClassName[0]) {
714 CPrintToChat(param1, "%s %sNo main rocket class detected, %saborting%s...", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, g_strMainChatColor);
715 return;
716 }
717 DrawRocketClassMenu(param1);
718 }
719 case 3:
720 {
721 // Clean up everything
722 DestroyRocketClasses();
723 DestroySpawners();
724 // Then reinitialize
725 char strMapName[64]; GetCurrentMap(strMapName, sizeof(strMapName));
726 char strMapFile[PLATFORM_MAX_PATH]; Format(strMapFile, sizeof(strMapFile), "%s.cfg", strMapName);
727 ParseConfigurations();
728 ParseConfigurations(strMapFile);
729 CPrintToChatAll("%s %s%N %srefreshed the %sdodgeball configs%s.", g_strServerChatTag, g_strClientChatColor, param1, g_strMainChatColor, g_strKeywordChatColor, g_strMainChatColor);
730 }
731 case 4:
732 {
733 ServerCommand("sm plugins reload Dodgeballtouch");
734 CPrintToChatAll("%s %sPlugin has been %sreloaded%s.", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, g_strMainChatColor);
735 }
736 case 5:
737 {
738 DrawRocketBounceMenu(param1);
739 }
740 }
741 }
742
743 case MenuAction_Cancel:
744 {
745 PrintToServer("Client %d's menu was cancelled for reason %d", param1, param2); // Logging once again.
746 }
747
748 case MenuAction_End:
749 {
750 delete menu;
751 }
752
753 case MenuAction_DrawItem:
754 {
755 int style;
756 char info[32];
757 menu.GetItem(param2, info, sizeof(info), style);
758 }
759
760 case MenuAction_DisplayItem:
761 {
762 char info[32];
763 menu.GetItem(param2, info, sizeof(info));
764 }
765 }
766}
767
768void DrawRocketBounceMenu(int client)
769{
770 Menu menu = new Menu(DodgeballRocketBounce_Handler, MENU_ACTIONS_ALL);
771
772 menu.SetTitle("Enable / Disable Bounce?");
773
774 menu.AddItem("1", "Enable");
775 menu.AddItem("2", "Disable");
776
777 menu.ExitButton = true;
778 menu.Display(client, MENU_TIME_FOREVER);
779}
780
781void DrawMaxRocketCountMenu(int client)
782{
783 Menu menu = new Menu(DodgeballAdminRocketCount_Handler, MENU_ACTIONS_ALL);
784
785 menu.SetTitle("How many rockets?");
786
787 menu.AddItem("1", "One");
788 menu.AddItem("2", "Two");
789 menu.AddItem("3", "Three");
790 menu.AddItem("4", "Four");
791 menu.AddItem("5", "Five");
792
793 menu.ExitButton = true;
794 menu.Display(client, MENU_TIME_FOREVER);
795}
796
797void DrawRocketSpeedMenu(int client)
798{
799 Menu menu = new Menu(DodgeballAdminRocketSpeed_Handler, MENU_ACTIONS_ALL);
800
801 menu.SetTitle("How fast should the rockets go?");
802
803 menu.AddItem("1", "25% (Slow)");
804 menu.AddItem("2", "50% (Normal)");
805 menu.AddItem("3", "75% (Fast)");
806 menu.AddItem("4", "100% (Silly Fast)");
807
808 menu.ExitButton = true;
809 menu.Display(client, MENU_TIME_FOREVER);
810}
811
812void DrawRocketClassMenu(int client)
813{
814 Menu menu = new Menu(DodgeballAdminRocketClass_Handler, MENU_ACTIONS_ALL);
815
816 menu.SetTitle("Which class should the rocket be set to?");
817
818 for (int currentClass = 0; currentClass < g_iRocketClassCount; currentClass++)
819 {
820 char classNumber[16];
821 IntToString(currentClass, classNumber, sizeof(classNumber));
822 if (StrEqual(g_strSavedClassName, g_strRocketClassLongName[currentClass]))
823 {
824 char currentClassName[32];
825 strcopy(currentClassName, sizeof(currentClassName), "[Current] ");
826 StrCat(currentClassName, sizeof(currentClassName), g_strSavedClassName);
827 menu.AddItem(classNumber, currentClassName);
828 }
829 else menu.AddItem(classNumber, g_strRocketClassLongName[currentClass]);
830 }
831
832 menu.ExitButton = true;
833 menu.Display(client, MENU_TIME_FOREVER);
834}
835
836public int DodgeballRocketBounce_Handler(Menu menu, MenuAction action, int param1, int param2)
837{
838 switch (action)
839 {
840 case MenuAction_Start:
841 {
842 // It's important to log anything in any way, the best is printtoserver, but if you just want to log to client to make it easier to get progress done, feel free.
843 PrintToServer("Displaying menu"); // Log it
844 }
845
846 case MenuAction_Display:
847 {
848 PrintToServer("Client %d was sent menu with panel %x", param1, param2); // Log so you can check if it gets sent.
849 }
850
851 case MenuAction_Select:
852 {
853 char sInfo[32];
854 menu.GetItem(param2, sInfo, sizeof(sInfo));
855
856 switch (param2)
857 {
858 case 0:
859 {
860 ServerCommand("sm_cvar tf_dodgeball_rbmax 999");
861 CPrintToChatAll("%s %s%N %sEnabled Rocket %sBounce.", g_strServerChatTag, g_strClientChatColor, param1, g_strMainChatColor, g_strKeywordChatColor);
862 }
863 case 1:
864 {
865 ServerCommand("sm_cvar tf_dodgeball_rbmax 0");
866 CPrintToChatAll("%s %s%N %sDisabled Rocket %sBounce.", g_strServerChatTag, g_strClientChatColor, param1, g_strMainChatColor, g_strKeywordChatColor);
867 }
868 }
869 }
870 case MenuAction_Cancel:
871 {
872 PrintToServer("Client %d's menu was cancelled for reason %d", param1, param2); // Logging once again.
873 }
874
875 case MenuAction_End:
876 {
877 delete menu;
878 }
879
880 case MenuAction_DrawItem:
881 {
882 int style;
883 char info[32];
884 menu.GetItem(param2, info, sizeof(info), style);
885 }
886
887 case MenuAction_DisplayItem:
888 {
889 char info[32];
890 menu.GetItem(param2, info, sizeof(info));
891 }
892 }
893}
894
895public int DodgeballAdminRocketCount_Handler(Menu menu, MenuAction action, int param1, int param2)
896{
897 switch (action)
898 {
899 case MenuAction_Start:
900 {
901 // It's important to log anything in any way, the best is printtoserver, but if you just want to log to client to make it easier to get progress done, feel free.
902 PrintToServer("Displaying menu"); // Log it
903 }
904
905 case MenuAction_Display:
906 {
907 PrintToServer("Client %d was sent menu with panel %x", param1, param2); // Log so you can check if it gets sent.
908 }
909
910 case MenuAction_Select:
911 {
912 char sInfo[32];
913 menu.GetItem(param2, sInfo, sizeof(sInfo));
914
915 switch (param2)
916 {
917 case 0:
918 {
919 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
920 g_iSpawnersMaxRockets[iSpawnerClassBlu] = 1;
921
922 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
923 g_iSpawnersMaxRockets[iSpawnerClassRed] = 1;
924
925 CPrintToChatAll("%s %s%N %schanged the max rockets to %s1.", g_strServerChatTag, g_strClientChatColor, param1, g_strMainChatColor, g_strKeywordChatColor);
926 }
927 case 1:
928 {
929 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
930 g_iSpawnersMaxRockets[iSpawnerClassBlu] = 2;
931
932 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
933 g_iSpawnersMaxRockets[iSpawnerClassRed] = 2;
934
935 CPrintToChatAll("%s %s%N %schanged the max rockets to %s2.", g_strServerChatTag, g_strClientChatColor, param1, g_strMainChatColor, g_strKeywordChatColor);
936 }
937 case 2:
938 {
939 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
940 g_iSpawnersMaxRockets[iSpawnerClassBlu] = 3;
941
942 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
943 g_iSpawnersMaxRockets[iSpawnerClassRed] = 3;
944
945 CPrintToChatAll("%s %s%N %schanged the max rockets to %s3.", g_strServerChatTag, g_strClientChatColor, param1, g_strMainChatColor, g_strKeywordChatColor);
946 }
947 case 3:
948 {
949 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
950 g_iSpawnersMaxRockets[iSpawnerClassBlu] = 4;
951
952 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
953 g_iSpawnersMaxRockets[iSpawnerClassRed] = 4;
954
955 CPrintToChatAll("%s %s%N %schanged the max rockets to %s4.", g_strServerChatTag, g_strKeywordChatColor, param1, g_strMainChatColor, g_strKeywordChatColor);
956 }
957 case 4:
958 {
959 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
960 g_iSpawnersMaxRockets[iSpawnerClassBlu] = 5;
961
962 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
963 g_iSpawnersMaxRockets[iSpawnerClassRed] = 5;
964
965 CPrintToChatAll("%s %s%N %schanged the max rockets to %s5.", g_strServerChatTag, g_strKeywordChatColor, param1, g_strMainChatColor, g_strKeywordChatColor);
966 }
967 }
968 }
969
970 case MenuAction_Cancel:
971 {
972 PrintToServer("Client %d's menu was cancelled for reason %d", param1, param2); // Logging once again.
973 }
974
975 case MenuAction_End:
976 {
977 delete menu;
978 }
979
980 case MenuAction_DrawItem:
981 {
982 int style;
983 char info[32];
984 menu.GetItem(param2, info, sizeof(info), style);
985 }
986
987 case MenuAction_DisplayItem:
988 {
989 char info[32];
990 menu.GetItem(param2, info, sizeof(info));
991 }
992 }
993}
994
995public int DodgeballAdminRocketSpeed_Handler(Menu menu, MenuAction action, int param1, int param2)
996{
997 switch (action)
998 {
999 case MenuAction_Start:
1000 {
1001 // It's important to log anything in any way, the best is printtoserver, but if you just want to log to client to make it easier to get progress done, feel free.
1002 PrintToServer("Displaying menu"); // Log it
1003 }
1004
1005 case MenuAction_Display:
1006 {
1007 PrintToServer("Client %d was sent menu with panel %x", param1, param2); // Log so you can check if it gets sent.
1008 }
1009
1010 case MenuAction_Select:
1011 {
1012 char sInfo[32];
1013 menu.GetItem(param2, sInfo, sizeof(sInfo));
1014 float kvSpeed = g_fSavedSpeed;
1015 float kvSpeedIncrement = g_fSavedSpeedIncrement;
1016
1017 switch (param2)
1018 {
1019 case 0:
1020 {
1021 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
1022 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
1023 int iClassRed = GetRandomRocketClass(iSpawnerClassRed);
1024 int iClassBlu = GetRandomRocketClass(iSpawnerClassBlu);
1025
1026 g_fRocketSpeed[iClassRed] = kvSpeed / 2;
1027 g_fRocketClassSpeedIncrement[iClassRed] = kvSpeedIncrement / 2;
1028
1029 g_fRocketSpeed[iClassBlu] = kvSpeed / 2;
1030 g_fRocketClassSpeedIncrement[iClassBlu] = kvSpeedIncrement / 2;
1031
1032 CPrintToChatAll("%s %s%N %schanged the rocket speed to %s25%% (Slow)", g_strServerChatTag, g_strClientChatColor, param1, g_strMainChatColor, g_strKeywordChatColor);
1033 }
1034 case 1:
1035 {
1036 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
1037 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
1038 int iClassRed = GetRandomRocketClass(iSpawnerClassRed);
1039 int iClassBlu = GetRandomRocketClass(iSpawnerClassBlu);
1040
1041 g_fRocketSpeed[iClassRed] = kvSpeed;
1042 g_fRocketClassSpeedIncrement[iClassRed] = kvSpeedIncrement;
1043
1044 g_fRocketSpeed[iClassBlu] = kvSpeed;
1045 g_fRocketClassSpeedIncrement[iClassBlu] = kvSpeedIncrement;
1046
1047 CPrintToChatAll("%s %s%N %schanged the rocket speed to %s50%% (Normal)", g_strServerChatTag, g_strClientChatColor, param1, g_strMainChatColor, g_strKeywordChatColor);
1048 }
1049 case 2:
1050 {
1051 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
1052 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
1053 int iClassRed = GetRandomRocketClass(iSpawnerClassRed);
1054 int iClassBlu = GetRandomRocketClass(iSpawnerClassBlu);
1055
1056 g_fRocketSpeed[iClassRed] = kvSpeed * 2;
1057 g_fRocketClassSpeedIncrement[iClassRed] = kvSpeedIncrement * 2;
1058
1059 g_fRocketSpeed[iClassBlu] = kvSpeed * 2;
1060 g_fRocketClassSpeedIncrement[iClassBlu] = kvSpeedIncrement * 2;
1061
1062 CPrintToChatAll("%s %s%N %schanged the rocket speed to %s75%% (Fast)", g_strServerChatTag, g_strClientChatColor, param1, g_strMainChatColor, g_strKeywordChatColor);
1063 }
1064 case 3:
1065 {
1066 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
1067 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
1068 int iClassRed = GetRandomRocketClass(iSpawnerClassRed);
1069 int iClassBlu = GetRandomRocketClass(iSpawnerClassBlu);
1070
1071 g_fRocketSpeed[iClassRed] = kvSpeed * 3;
1072 g_fRocketClassSpeedIncrement[iClassRed] = kvSpeedIncrement * 3;
1073
1074 g_fRocketSpeed[iClassBlu] = kvSpeed * 3;
1075 g_fRocketClassSpeedIncrement[iClassBlu] = kvSpeedIncrement * 3;
1076
1077 CPrintToChatAll("%s %s%N %schanged the rocket speed to %s100%% (Silly Fast)", g_strServerChatTag, g_strClientChatColor, param1, g_strMainChatColor, g_strKeywordChatColor);
1078 }
1079 }
1080 }
1081
1082 case MenuAction_Cancel:
1083 {
1084 PrintToServer("Client %d's menu was cancelled for reason %d", param1, param2); // Logging once again.
1085 }
1086
1087 case MenuAction_End:
1088 {
1089 delete menu;
1090 }
1091
1092 case MenuAction_DrawItem:
1093 {
1094 int style;
1095 char info[32];
1096 menu.GetItem(param2, info, sizeof(info), style);
1097 }
1098
1099 case MenuAction_DisplayItem:
1100 {
1101 char info[32];
1102 menu.GetItem(param2, info, sizeof(info));
1103 }
1104 }
1105}
1106
1107public int DodgeballAdminRocketClass_Handler(Menu menu, MenuAction action, int param1, int param2)
1108{
1109 switch (action)
1110 {
1111 case MenuAction_Start:
1112 {
1113 // It's important to log anything in any way, the best is printtoserver, but if you just want to log to client to make it easier to get progress done, feel free.
1114 PrintToServer("Displaying menu"); // Log it
1115 }
1116
1117 case MenuAction_Display:
1118 {
1119 PrintToServer("Client %d was sent menu with panel %x", param1, param2); // Log so you can check if it gets sent.
1120 }
1121
1122 case MenuAction_Select:
1123 {
1124 char sInfo[32];
1125 menu.GetItem(param2, sInfo, sizeof(sInfo));
1126
1127 SetMainRocketClass(param2, false, param1);
1128 }
1129
1130 case MenuAction_Cancel:
1131 {
1132 PrintToServer("Client %d's menu was cancelled for reason %d", param1, param2); // Logging once again.
1133 }
1134
1135 case MenuAction_End:
1136 {
1137 delete menu;
1138 }
1139
1140 case MenuAction_DrawItem:
1141 {
1142 int style;
1143 char info[32];
1144 menu.GetItem(param2, info, sizeof(info), style);
1145 }
1146
1147 case MenuAction_DisplayItem:
1148 {
1149 char info[32];
1150 menu.GetItem(param2, info, sizeof(info));
1151 }
1152 }
1153}
1154
1155//__ __ _
1156//\ \ / /__| |_ ___ ___
1157// \ V / _ \ _/ -_|_-<
1158// \_/\___/\__\___/__/
1159
1160public Action Command_VoteRocketClass(int client, int args) {
1161 if (!g_strSavedClassName[0])
1162 {
1163 g_bVoteClassEnabled = false;
1164 CPrintToChat(client, "%s %sNo main rocket class detected, voting for rocket class %sdisabled.", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor);
1165 return Plugin_Handled;
1166 }
1167 else g_bVoteClassEnabled = true;
1168
1169 if(!g_bVoteClassEnabled) {
1170 CReplyToCommand(client, "%s %sVoting for rocket class is %snot enabled.", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor);
1171 return Plugin_Handled;
1172 }
1173
1174 if(!g_bCanVoteClass) {
1175 CReplyToCommand(client,"%s %sYou cannot vote at this time (%s%ds %sleft).", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, GetConVarInt(g_hCvarVoteClassCooldown) - (GetTime() - g_iTimeVoted), g_strMainChatColor);
1176 return Plugin_Handled;
1177 }
1178
1179 g_iClassVotesRequired = RoundToCeil(GetTotalClientCount() * GetConVarFloat(g_hCvarVoteClassPercentage));
1180
1181 if(g_bClassVoted[client]) {
1182 CReplyToCommand(client, "%s %sYou have %salready %svoted", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, g_strMainChatColor);
1183 return Plugin_Handled;
1184 }
1185
1186 if(IsVoteInProgress()) {
1187 CReplyToCommand(client, "%s %sA vote is currently %sin progress", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor);
1188 return Plugin_Handled;
1189 }
1190
1191 g_iClassVotes++;
1192 g_bClassVoted[client] = true;
1193
1194 CPrintToChatAll("%s %s%N %swants to change the rocket class (%s%d/%d%s votes)", g_strServerChatTag, g_strClientChatColor, client, g_strMainChatColor, g_strKeywordChatColor, g_iClassVotes, g_iClassVotesRequired, g_strMainChatColor);
1195
1196 if(g_iClassVotes >= g_iClassVotesRequired) {
1197 CreateTimer(2.0, Timer_StartRocketClassVote);
1198 g_bCanVoteClass = false;
1199
1200 g_iClassVotes = 0;
1201 for(int i = 0; i < sizeof(g_bClassVoted); i++) {
1202 g_bClassVoted[i] = false;
1203 }
1204
1205 CreateTimer(GetConVarFloat(g_hCvarVoteClassCooldown), Timer_AllowRocketClassVote);
1206 }
1207
1208 return Plugin_Handled;
1209}
1210
1211/*
1212**����������������������������������������������������������������������������������
1213** __ ___ __
1214** / |/ /___ _____ ____ _____ ____ ____ ___ ___ ____ / /_
1215** / /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ __ `__ \/ _ \/ __ \/ __/
1216** / / / / /_/ / / / / /_/ / /_/ / __/ / / / / / __/ / / / /_
1217** /_/ /_/\__,_/_/ /_/\__,_/\__, /\___/_/ /_/ /_/\___/_/ /_/\__/
1218** /____/
1219**����������������������������������������������������������������������������������
1220*/
1221
1222// ___ _
1223// / __|___ _ _ ___ _ _ __ _| |
1224// | (_ / -_) ' \/ -_) '_/ _` | |
1225// \___\___|_||_\___|_| \__,_|_|
1226
1227/* IsDodgeBallMap()
1228**
1229** Checks if the current map is a dodgeball map.
1230** -------------------------------------------------------------------------- */
1231bool IsDodgeBallMap()
1232{
1233 char strMap[64];
1234 GetCurrentMap(strMap, sizeof(strMap));
1235 return StrContains(strMap, "tfdb_", false) == 0;
1236}
1237
1238/* EnableDodgeBall()
1239**
1240** Enables and hooks all the required events.
1241** -------------------------------------------------------------------------- */
1242void EnableDodgeBall()
1243{
1244 if (g_bEnabled == false)
1245 {
1246 // Parse configuration files
1247 char strMapName[64]; GetCurrentMap(strMapName, sizeof(strMapName));
1248 char strMapFile[PLATFORM_MAX_PATH]; Format(strMapFile, sizeof(strMapFile), "%s.cfg", strMapName);
1249 ParseConfigurations();
1250 ParseConfigurations(strMapFile);
1251
1252 ServerCommand("tf_dodgeball_rbmax %f", GetConVarFloat(g_hMaxBouncesConVar));
1253
1254 // Check if we have all the required information
1255 if (g_iRocketClassCount == 0)
1256 SetFailState("No rocket class defined.");
1257
1258 if (g_iSpawnersCount == 0)
1259 SetFailState("No spawner class defined.");
1260
1261 if (g_iDefaultRedSpawner == -1)
1262 SetFailState("No spawner class definition for the Red spawners exists in the config file.");
1263
1264 if (g_iDefaultBluSpawner == -1)
1265 SetFailState("No spawner class definition for the Blu spawners exists in the config file.");
1266
1267 // Hook events and info_target outputs.
1268 HookEvent("object_deflected", Event_ObjectDeflected);
1269 HookEvent("teamplay_round_start", OnRoundStart, EventHookMode_PostNoCopy);
1270 HookEvent("teamplay_setup_finished", OnSetupFinished, EventHookMode_PostNoCopy);
1271 HookEvent("teamplay_round_win", OnRoundEnd, EventHookMode_PostNoCopy);
1272 HookEvent("player_spawn", OnPlayerSpawn, EventHookMode_Post);
1273 HookEvent("player_death", OnPlayerDeath, EventHookMode_Pre);
1274 HookEvent("post_inventory_application", OnPlayerInventory, EventHookMode_Post);
1275 HookEvent("teamplay_broadcast_audio", OnBroadcastAudio, EventHookMode_Pre);
1276
1277
1278
1279 // Precache sounds
1280 PrecacheSound(SOUND_DEFAULT_SPAWN, true);
1281 PrecacheSound(SOUND_DEFAULT_BEEP, true);
1282 PrecacheSound(SOUND_DEFAULT_ALERT, true);
1283 PrecacheSound(SOUND_DEFAULT_SPEEDUPALERT, true);
1284 if (g_bMusicEnabled == true)
1285 {
1286 if (g_bMusic[Music_RoundStart])PrecacheSoundEx(g_strMusic[Music_RoundStart], true, true);
1287 if (g_bMusic[Music_RoundWin])PrecacheSoundEx(g_strMusic[Music_RoundWin], true, true);
1288 if (g_bMusic[Music_RoundLose])PrecacheSoundEx(g_strMusic[Music_RoundLose], true, true);
1289 if (g_bMusic[Music_Gameplay])PrecacheSoundEx(g_strMusic[Music_Gameplay], true, true);
1290 }
1291
1292 // Precache particles
1293 PrecacheParticle(PARTICLE_NUKE_1);
1294 PrecacheParticle(PARTICLE_NUKE_2);
1295 PrecacheParticle(PARTICLE_NUKE_3);
1296 PrecacheParticle(PARTICLE_NUKE_4);
1297 PrecacheParticle(PARTICLE_NUKE_5);
1298 PrecacheParticle(PARTICLE_NUKE_COLLUMN);
1299
1300 // Precache rocket resources
1301 for (int i = 0; i < g_iRocketClassCount; i++)
1302 {
1303 RocketFlags iFlags = g_iRocketClassFlags[i];
1304 if (TestFlags(iFlags, RocketFlag_CustomModel))PrecacheModelEx(g_strRocketClassModel[i], true, true);
1305 if (TestFlags(iFlags, RocketFlag_CustomSpawnSound))PrecacheSoundEx(g_strRocketClassSpawnSound[i], true, true);
1306 if (TestFlags(iFlags, RocketFlag_CustomBeepSound))PrecacheSoundEx(g_strRocketClassBeepSound[i], true, true);
1307 if (TestFlags(iFlags, RocketFlag_CustomAlertSound))PrecacheSoundEx(g_strRocketClassAlertSound[i], true, true);
1308 }
1309
1310 // Execute enable config file
1311 char strCfgFile[64]; GetConVarString(g_hCvarEnableCfgFile, strCfgFile, sizeof(strCfgFile));
1312 ServerCommand("exec \"%s\"", strCfgFile);
1313
1314 // Done.
1315 g_bEnabled = true;
1316 g_bRoundStarted = false;
1317 g_iRoundCount = 0;
1318 }
1319}
1320
1321/* DisableDodgeBall()
1322**
1323** Disables all hooks and frees arrays.
1324** -------------------------------------------------------------------------- */
1325void DisableDodgeBall()
1326{
1327 if (g_bEnabled == true)
1328 {
1329 // Clean up everything
1330 DestroyRockets();
1331 DestroyRocketClasses();
1332 DestroySpawners();
1333 // if (g_hLogicTimer != INVALID_HANDLE)KillTimer(g_hLogicTimer);
1334 // g_hLogicTimer = INVALID_HANDLE;
1335 IsOnGameFrame = false;
1336
1337 // Disable music
1338 g_bMusic[Music_RoundStart] =
1339 g_bMusic[Music_RoundWin] =
1340 g_bMusic[Music_RoundLose] =
1341 g_bMusic[Music_Gameplay] = false;
1342
1343 // Unhook events and info_target outputs;
1344 UnhookEvent("teamplay_round_start", OnRoundStart, EventHookMode_PostNoCopy);
1345 UnhookEvent("teamplay_setup_finished", OnSetupFinished, EventHookMode_PostNoCopy);
1346 UnhookEvent("teamplay_round_win", OnRoundEnd, EventHookMode_PostNoCopy);
1347 UnhookEvent("player_spawn", OnPlayerSpawn, EventHookMode_Post);
1348 UnhookEvent("player_death", OnPlayerDeath, EventHookMode_Pre);
1349 UnhookEvent("post_inventory_application", OnPlayerInventory, EventHookMode_Post);
1350 UnhookEvent("teamplay_broadcast_audio", OnBroadcastAudio, EventHookMode_Pre);
1351
1352 // Execute enable config file
1353 char strCfgFile[64]; GetConVarString(g_hCvarDisableCfgFile, strCfgFile, sizeof(strCfgFile));
1354 ServerCommand("exec \"%s\"", strCfgFile);
1355
1356 // Done.
1357 g_bEnabled = false;
1358 g_bRoundStarted = false;
1359 g_iRoundCount = 0;
1360 }
1361}
1362public void OnClientPutInServer(int clientId)
1363{
1364 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
1365 {
1366 firstJoined[clientId] = true;
1367 }
1368 if (GetConVarBool(g_hCvarPreventTauntKillEnabled))
1369 {
1370 SDKHook(clientId, SDKHook_OnTakeDamage, TauntCheck);
1371 }
1372
1373
1374 SDKHook(clientId, SDKHook_OnTakeDamage, OnTakeDamage);
1375 SetGodmodeState();
1376
1377
1378}
1379
1380
1381
1382public void OnClientConnected(int client)
1383{
1384 if (IsFakeClient(client)) return;
1385
1386 g_bClassVoted[client] = false;
1387 g_iClassVoters++;
1388 g_iClassVotesRequired = RoundToCeil(GetTotalClientCount() * GetConVarFloat(g_hCvarVoteClassPercentage));
1389 SDKHook(client, SDKHook_OnTakeDamage, OnTakeDamage);
1390 SetGodmodeState();
1391}
1392
1393public void OnClientDisconnect(int client)
1394{
1395 if(IsFakeClient(client)) return;
1396
1397 if (GetConVarBool(g_hCvarPreventTauntKillEnabled))
1398 {
1399 SDKUnhook(client, SDKHook_OnTakeDamage, TauntCheck);
1400 }
1401
1402 if (GetConVarBool(g_hCvarStealPrevention))
1403 {
1404 bStealArray[client][stoleRocket] = false;
1405 bStealArray[client][rocketsStolen] = 0;
1406 }
1407
1408 if(g_bClassVoted[client])
1409 {
1410 g_iClassVotes--;
1411 }
1412
1413 g_iClassVoters--;
1414 g_iClassVotesRequired = RoundToCeil(GetTotalClientCount() * GetConVarFloat(g_hCvarVoteClassPercentage));
1415
1416 if (g_iClassVotes >= g_iClassVotesRequired && g_bVoteClassEnabled && g_iClassVoters != 0)
1417 {
1418 CreateTimer(2.0, Timer_StartRocketClassVote);
1419 }
1420 SDKUnhook(client, SDKHook_OnTakeDamage, OnTakeDamage);
1421 SetGodmodeState();
1422}
1423
1424/* OnObjectDeflected
1425**
1426**
1427** Check if client is human, don't airblast if bool is false
1428** -------------------------------------------------------------------------- */
1429public Action Event_ObjectDeflected(Handle event, const char[] name, bool dontBroadcast)
1430{
1431 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
1432 {
1433 int object1 = GetEventInt(event, "object_entindex");
1434 if ((object1 >= 1) && (object1 <= MaxClients))
1435 {
1436 if (Airblast[object1])
1437 {
1438 float Vel[3];
1439 TeleportEntity(object1, NULL_VECTOR, NULL_VECTOR, Vel); // Stops knockback
1440 TF2_RemoveCondition(object1, TFCond_Dazed); // Stops slowdown
1441 SetEntPropVector(object1, Prop_Send, "m_vecPunchAngle", Vel);
1442 SetEntPropVector(object1, Prop_Send, "m_vecPunchAngleVel", Vel); // Stops screen shake
1443 }
1444 }
1445 }
1446}
1447
1448/* IsValidAliveClient()
1449**
1450** Check if the client is valid and alive/ingame
1451** -------------------------------------------------------------------------- */
1452stock bool IsValidAliveClient(int client)
1453{
1454 if(client <= 0 || client > MaxClients || !IsClientConnected(client) || !IsClientInGame(client) || !IsPlayerAlive(client))
1455 {
1456 return false;
1457 }
1458 return true;
1459}
1460
1461/* GetAlivePlayersCount()
1462**
1463** Get alive players of a team (ignoring one)
1464** -------------------------------------------------------------------------- */
1465stock GetAlivePlayersCount(team,ignore=-1)
1466{
1467 int count = 0, i;
1468
1469 for( i = 1; i <= MaxClients; i++ )
1470 {
1471 if(IsValidAliveClient(i) && GetClientTeam(i) == team && i != ignore)
1472 {
1473 count++;
1474 }
1475 }
1476 return count;
1477}
1478
1479/* GetAlivePlayersCount()
1480**
1481** Get last player of a team (ignoring one), asuming that GetAlivePlayersCountwas used before.
1482** -------------------------------------------------------------------------- */
1483stock GetLastPlayer(team,ignore=-1)
1484{
1485 for(int i = 1; i <= MaxClients; i++ )
1486 {
1487 if(IsValidAliveClient(i) && GetClientTeam(i) == team && i != ignore)
1488 {
1489 return i;
1490 }
1491 }
1492 return -1;
1493}
1494
1495public Action Timer_HudName(Handle hTimer)
1496{
1497 SetHudTextParams(0, 0, 0.5, 255, 20, 40, 255, 0, 6.0, 0.1, 0.2);
1498 for (int iClient = 1; iClient <= MaxClients; iClient++)
1499 {
1500 if (IsValidClient(iClient) && !IsFakeClient(iClient))
1501 {
1502 ShowSyncHudText(iClient, g_hNameHud, "%s", g_strNameHud);
1503 }
1504 }
1505}
1506
1507public Action Timer_Hud1v1(Handle hTimer)
1508{
1509 SetHudTextParams(-1.0, 0.7,7.0,0,255,255,255, 1, 3.0, 1.5, 1.5);
1510 for (int iClient = 1; iClient <= MaxClients; iClient++)
1511 {
1512 if (IsValidClient(iClient) && !IsFakeClient(iClient) && g_iRocketSpeed != 0 && GetAlivePlayersCount(TEAM_BLUE,-1) == 1 && GetAlivePlayersCount(TEAM_RED,-1) == 1)
1513 {
1514 ShowSyncHudText(iClient, g_h1v1Hud, "%N and %N are the last players standing !", GetLastPlayer(TEAM_RED), GetLastPlayer(TEAM_BLUE));
1515 }
1516 }
1517
1518}
1519
1520
1521
1522public Action Truc(Handle timer)
1523 {
1524 CloseHandle(g_h1v1Hud);
1525 }
1526
1527// ___ _
1528// / __|__ _ _ __ ___ _ __| |__ _ _ _
1529// | (_ / _` | ' \/ -_) '_ \ / _` | || |
1530// \___\__,_|_|_|_\___| .__/_\__,_|\_, |
1531// |_| |__/
1532
1533/* OnRoundStart()
1534**
1535** At round start, do something?
1536** -------------------------------------------------------------------------- */
1537public Action OnRoundStart(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1538{
1539 SetGodmodeState();
1540 if (GetConVarBool(g_hCvarStealPrevention))
1541 {
1542 for (int i = 0; i <= MaxClients; i++)
1543 {
1544 bStealArray[i][stoleRocket] = false;
1545 bStealArray[i][rocketsStolen] = 0;
1546 }
1547 }
1548
1549 if (g_bMusic[Music_RoundStart])
1550 {
1551 EmitSoundToAll(g_strMusic[Music_RoundStart]);
1552 }
1553 g_iRocketSpeed = 0;
1554 if (g_hTimerHud != INVALID_HANDLE)
1555 {
1556 KillTimer(g_hTimerHud);
1557 g_hTimerHud = INVALID_HANDLE;
1558 }
1559 g_hTimerHud = CreateTimer(0.3, Timer_HudSpeed, _, TIMER_REPEAT);
1560 CreateTimer(0.1, Timer_Hud1v1, _, TIMER_REPEAT);
1561 CreateTimer(0.9, Timer_HudName, _, TIMER_REPEAT);
1562
1563
1564
1565
1566
1567}
1568
1569
1570/* OnSetupFinished()
1571**
1572** When the setup finishes, populate the spawn points arrays and create the
1573** Dodgeball game logic timer.
1574** -------------------------------------------------------------------------- */
1575public Action OnSetupFinished(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1576{
1577 if ((g_bEnabled == true) && (BothTeamsPlaying() == true))
1578 {
1579 PopulateSpawnPoints();
1580
1581 if (g_iLastDeadTeam == 0)
1582 {
1583 g_iLastDeadTeam = GetURandomIntRange(view_as<int>(TFTeam_Red), view_as<int>(TFTeam_Blue));
1584 }
1585 if (!IsValidClient(g_iLastDeadClient))g_iLastDeadClient = 0;
1586
1587 //g_hLogicTimer = CreateTimer(FPS_LOGIC_INTERVAL, OnDodgeBallGameFrame, _, TIMER_REPEAT);
1588 IsOnGameFrame = true;
1589 g_iPlayerCount = CountAlivePlayers();
1590 g_iRocketsFired = 0;
1591 g_iCurrentRedSpawn = 0;
1592 g_iCurrentBluSpawn = 0;
1593 g_fNextSpawnTime = GetGameTime();
1594 g_bRoundStarted = true;
1595 g_iRoundCount++;
1596 }
1597}
1598
1599/* OnRoundEnd()
1600**
1601** At round end, stop the Dodgeball game logic timer and destroy the remaining
1602** rockets.
1603** -------------------------------------------------------------------------- */
1604public Action OnRoundEnd(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1605{
1606 if (g_hTimerHud != INVALID_HANDLE)
1607 {
1608 KillTimer(g_hTimerHud);
1609 g_hTimerHud = INVALID_HANDLE;
1610 }
1611 // if (g_hLogicTimer != INVALID_HANDLE)
1612 // {
1613 // KillTimer(g_hLogicTimer);
1614 // g_hLogicTimer = INVALID_HANDLE;
1615 // }
1616
1617 IsOnGameFrame = false;
1618
1619
1620 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
1621 {
1622 for (int i = 0; i < MAXPLAYERS + 1; i++)
1623 {
1624 firstJoined[i] = false;
1625 }
1626 }
1627 if (g_bMusicEnabled == true)
1628 {
1629 if (g_bUseWebPlayer)
1630 {
1631 for (int iClient = 1; iClient <= MaxClients; iClient++)
1632 {
1633 if (IsValidClient(iClient))
1634 {
1635 ShowHiddenMOTDPanel(iClient, "MusicPlayerStop", "http://0.0.0.0/");
1636 if (!IsFakeClient(iClient))
1637 {
1638 ClearSyncHud(iClient, g_hHud);
1639 }
1640 }
1641 }
1642 }
1643 else if (g_bMusic[Music_Gameplay])
1644 {
1645 StopSoundToAll(SNDCHAN_MUSIC, g_strMusic[Music_Gameplay]);
1646 }
1647 }
1648
1649 DestroyRockets();
1650 g_bRoundStarted = false;
1651}
1652
1653public Action Command_ToggleAirblast(int clientId, int args)
1654{
1655 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
1656 {
1657 char arg[128];
1658
1659 if (args > 1)
1660 {
1661 ReplyToCommand(clientId, "[SM] %s", USAGE);
1662 return Plugin_Handled;
1663 }
1664
1665 if (args == 0)
1666 {
1667 preventAirblast(clientId, !abPrevention[clientId]);
1668 }
1669 else if (args == 1)
1670 {
1671 GetCmdArg(1, arg, sizeof(arg));
1672
1673 if (strcmp(arg, "0") == 0)
1674 {
1675 preventAirblast(clientId, false);
1676 }
1677 else if (strcmp(arg, "1") == 0)
1678 {
1679 preventAirblast(clientId, true);
1680 }
1681 else
1682 {
1683 ReplyToCommand(clientId, "[SM] %s", USAGE);
1684 return Plugin_Handled;
1685 }
1686 }
1687
1688 if (abPrevention[clientId])
1689 {
1690 ReplyToCommand(clientId, "[SM] %s", "Airblast Prevention Enabled");
1691 }
1692 else
1693 {
1694 ReplyToCommand(clientId, "[SM] %s", "Airblast Prevention Disabled");
1695 }
1696 }
1697
1698 if (!GetConVarBool(g_hCvarAirBlastCommandEnabled))
1699 {
1700 ReplyToCommand(clientId, "[SM] %s", "Airblast Prevention is disabled on this server.");
1701 preventAirblast(clientId, false);
1702 }
1703 return Plugin_Handled;
1704}
1705
1706public Action Command_PostCurrentRocketClass(int client, int args)
1707{
1708 if (args > 1)
1709 {
1710 ReplyToCommand(client, "[SM] %s", "Usage: sm_currentrocket");
1711 return Plugin_Handled;
1712 }
1713
1714 if (!g_strSavedClassName[0])
1715 {
1716 CPrintToChat(client, "%s %sCurrent Rocket: %sMultiple", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor);
1717 return Plugin_Handled;
1718 }
1719 CPrintToChatAll("%s %sCurrent Rocket: %s%s", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, g_strSavedClassName);
1720
1721 return Plugin_Handled;
1722}
1723
1724
1725/* OnPlayerSpawn()
1726**
1727** When the player spawns, force class to Pyro.
1728** -------------------------------------------------------------------------- */
1729public Action OnPlayerSpawn(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1730{
1731 int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
1732 int clientId = GetClientOfUserId(GetEventInt(hEvent, "userid"));
1733 //g_stolen[iClient] = 0;
1734
1735 if (!IsValidClient(iClient)) return;
1736
1737 TFClassType iClass = TF2_GetPlayerClass(iClient);
1738 if (!(iClass == TFClass_Pyro || iClass == view_as<TFClassType>(TFClass_Unknown)))
1739 {
1740 TF2_SetPlayerClass(iClient, TFClass_Pyro, false, true);
1741 TF2_RespawnPlayer(iClient);
1742 }
1743
1744 for (int i = MaxClients; i; --i)
1745 {
1746 if (IsClientInGame(i) && IsPlayerAlive(i))
1747 SetEntPropEnt(i, Prop_Data, "m_hActiveWeapon", GetPlayerWeaponSlot(i, TFWeaponSlot_Primary));
1748 }
1749
1750 if (!GetConVarBool(g_hCvarPyroVisionEnabled))
1751 {
1752 return;
1753 }
1754 TF2Attrib_SetByName(iClient, PYROVISION_ATTRIBUTE, 1.0);
1755
1756 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
1757 {
1758 if (firstJoined[clientId])
1759 {
1760 //Enable ab prevention when a player joins the server
1761 abPrevention[clientId] = true;
1762 }
1763
1764 preventAirblast(clientId, true);
1765 }
1766}
1767
1768/* OnPlayerDeath()
1769**
1770** When the player dies, set the last dead team to determine the next
1771** rocket's team.
1772** -------------------------------------------------------------------------- */
1773public Action OnPlayerDeath(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1774{
1775 if (g_bRoundStarted == false)
1776 {
1777 return;
1778 }
1779 int iAttacker = GetClientOfUserId(GetEventInt(hEvent, "attacker"));
1780 int iVictim = GetClientOfUserId(GetEventInt(hEvent, "userid"));
1781
1782 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
1783 {
1784 int clientId = GetClientOfUserId(GetEventInt(hEvent, "userid"));
1785 firstJoined[clientId] = false;
1786 }
1787 if (IsValidClient(iVictim))
1788 {
1789 if (GetConVarBool(g_hCvarStealPrevention))
1790 {
1791 bStealArray[iVictim][stoleRocket] = false;
1792 bStealArray[iVictim][rocketsStolen] = 0;
1793 }
1794
1795 g_iLastDeadClient = iVictim;
1796 g_iLastDeadTeam = GetClientTeam(iVictim);
1797
1798 int iInflictor = GetEventInt(hEvent, "inflictor_entindex");
1799 int iIndex = FindRocketByEntity(iInflictor);
1800
1801 if (iIndex != -1)
1802 {
1803 int iClass = g_iRocketClass[iIndex];
1804 int iTarget = EntRefToEntIndex(g_iRocketTarget[iIndex]);
1805 float fSpeed = g_fRocketSpeed[iIndex];
1806 int iDeflections = g_iRocketDeflections[iIndex];
1807
1808 if(!GetConVarBool(g_hTouchAnnounce)) {
1809 if (GetConVarBool(g_hCvarAnnounce)) {
1810 if (GetConVarBool(g_hCvarDeflectCountAnnounce)) {
1811 if (iVictim == iTarget) {
1812 //CPrintToChatAll("%s %s%N %sdied to their rocket travelling %s%i %smph with %s%i %sdeflections!", g_strServerChatTag, g_strKeywordChatColor, g_iLastDeadClient, g_strMainChatColor, g_strKeywordChatColor, g_iRocketSpeed, g_strMainChatColor, g_strKeywordChatColor, iDeflections, g_strMainChatColor);
1813 CPrintToChatAll("%s %s%N : Speed: %s%i %smph || deflections: %s%i", g_strServerChatTag, iTarget, g_strKeywordChatColor, g_iRocketSpeed, g_strMainChatColor, g_strKeywordChatColor, xDeflections);
1814 } else {
1815 CPrintToChatAll("{Orange}%N {DEFAULT}- {Orange}%.15N{DEFAULT}: Speed: {TURQUOISE}%i {DEFAULT}mph || deflections: {TURQUOISE}%i", iTarget, g_iLastDeadClient, g_iRocketSpeed, xDeflections);
1816 //CPrintToChatAll("%s %s%N %sdied to %s%.15N's %srocket travelling %s%i %smph with %s%i %sdeflections!", g_strServerChatTag, g_strKeywordChatColor, g_iLastDeadClient, g_strMainChatColor, g_strKeywordChatColor, iTarget, g_strMainChatColor, g_strKeywordChatColor, g_iRocketSpeed, g_strMainChatColor, g_strKeywordChatColor, iDeflections, g_strMainChatColor);
1817 }
1818 } else {
1819 //CPrintToChatAll("%s %s%N %sdied to a rocket travelling %s%i %smph!", g_strServerChatTag, g_strKeywordChatColor, g_iLastDeadClient, g_strMainChatColor, g_strKeywordChatColor, g_iRocketSpeed, g_strMainChatColor);
1820 CPrintToChatAll("{Orange}%N {DEFAULT}- {Orange}%.15N's{DEFAULT}: Speed: {TURQUOISE}%i mph", iTarget, g_iLastDeadClient, g_iRocketSpeed);
1821 }
1822 }
1823 }
1824
1825 if ((g_iRocketFlags[iIndex] & RocketFlag_OnExplodeCmd) && !(g_iRocketFlags[iIndex] & RocketFlag_Exploded))
1826 {
1827 ExecuteCommands(g_hRocketClassCmdsOnExplode[iClass], iClass, iInflictor, iAttacker, iTarget, g_iLastDeadClient, fSpeed, iDeflections);
1828 g_iRocketFlags[iIndex] |= RocketFlag_Exploded;
1829 }
1830
1831 if (TestFlags(g_iRocketFlags[iIndex], RocketFlag_OnKillCmd))
1832 ExecuteCommands(g_hRocketClassCmdsOnKill[iClass], iClass, iInflictor, iAttacker, iTarget, g_iLastDeadClient, fSpeed, iDeflections);
1833 }
1834 }
1835
1836 SetRandomSeed(view_as<int>(GetGameTime()));
1837}
1838
1839/* OnPlayerInventory()
1840**
1841** Make sure the client only has the flamethrower equipped.
1842** -------------------------------------------------------------------------- */
1843public Action OnPlayerInventory(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1844{
1845 int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
1846 if (!IsValidClient(iClient))return;
1847
1848 for (int iSlot = 1; iSlot < 5; iSlot++)
1849 {
1850 int iEntity = GetPlayerWeaponSlot(iClient, iSlot);
1851 if (iEntity != -1)RemoveEdict(iEntity);
1852 }
1853}
1854
1855/* OnPlayerRunCmd()
1856**
1857** Block flamethrower's Mouse1 attack.
1858** -------------------------------------------------------------------------- */
1859public Action OnPlayerRunCmd(int iClient, int &iButtons, int &iImpulse, float fVelocity[3], float fAngles[3], int &iWeapon)
1860{
1861 if (g_bEnabled == true)iButtons &= ~IN_ATTACK;
1862 return Plugin_Continue;
1863}
1864
1865/* OnBroadcastAudio()
1866**
1867** Replaces the broadcasted audio for our custom music files.
1868** -------------------------------------------------------------------------- */
1869public Action OnBroadcastAudio(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1870{
1871 if (g_bMusicEnabled == true)
1872 {
1873 char strSound[PLATFORM_MAX_PATH];
1874 GetEventString(hEvent, "sound", strSound, sizeof(strSound));
1875 int iTeam = GetEventInt(hEvent, "team");
1876
1877 if (StrEqual(strSound, "Announcer.AM_RoundStartRandom") == true)
1878 {
1879 if (g_bUseWebPlayer == false)
1880 {
1881 if (g_bMusic[Music_Gameplay])
1882 {
1883 EmitSoundToAll(g_strMusic[Music_Gameplay], SOUND_FROM_PLAYER, SNDCHAN_MUSIC);
1884 return Plugin_Handled;
1885 }
1886 }
1887 else
1888 {
1889 for (int iClient = 1; iClient <= MaxClients; iClient++)
1890 if (IsValidClient(iClient))
1891 ShowHiddenMOTDPanel(iClient, "MusicPlayerStart", g_strWebPlayerUrl);
1892
1893 return Plugin_Handled;
1894 }
1895 }
1896 else if (StrEqual(strSound, "Game.YourTeamWon") == true)
1897 {
1898 if (g_bMusic[Music_RoundWin])
1899 {
1900 for (int iClient = 1; iClient <= MaxClients; iClient++)
1901 if (IsValidClient(iClient) && (iTeam == GetClientTeam(iClient)))
1902 EmitSoundToClient(iClient, g_strMusic[Music_RoundWin]);
1903
1904 return Plugin_Handled;
1905 }
1906 }
1907 else if (StrEqual(strSound, "Game.YourTeamLost") == true)
1908 {
1909 if (g_bMusic[Music_RoundLose])
1910 {
1911 for (int iClient = 1; iClient <= MaxClients; iClient++)
1912 if (IsValidClient(iClient) && (iTeam == GetClientTeam(iClient)))
1913 EmitSoundToClient(iClient, g_strMusic[Music_RoundLose]);
1914
1915 return Plugin_Handled;
1916 }
1917 return Plugin_Handled;
1918 }
1919 }
1920 return Plugin_Continue;
1921}
1922
1923/* OnGameFrame()
1924**
1925** Every tick of the server logic.
1926** -------------------------------------------------------------------------- */
1927public void OnGameFrame() {
1928 if (IsOnGameFrame) {
1929 // Only if both teams are playing
1930 if (BothTeamsPlaying() == false) return;
1931
1932 // Check if we need to fire more rockets.
1933 if (GetGameTime() >= g_fNextSpawnTime)
1934 {
1935 if (g_iLastDeadTeam == view_as<int>(TFTeam_Red))
1936 {
1937 int iSpawnerEntity = g_iSpawnPointsRedEntity[g_iCurrentRedSpawn];
1938 int iSpawnerClass = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
1939 if (g_iRocketCount < g_iSpawnersMaxRockets[iSpawnerClass])
1940 {
1941 CreateRocket(iSpawnerEntity, iSpawnerClass, view_as<int>(TFTeam_Red));
1942 g_iCurrentRedSpawn = (g_iCurrentRedSpawn + 1) % g_iSpawnPointsRedCount;
1943 }
1944 }
1945 else
1946 {
1947 int iSpawnerEntity = g_iSpawnPointsBluEntity[g_iCurrentBluSpawn];
1948 int iSpawnerClass = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
1949 if (g_iRocketCount < g_iSpawnersMaxRockets[iSpawnerClass])
1950 {
1951 CreateRocket(iSpawnerEntity, iSpawnerClass, view_as<int>(TFTeam_Blue));
1952 g_iCurrentBluSpawn = (g_iCurrentBluSpawn + 1) % g_iSpawnPointsBluCount;
1953 }
1954 }
1955 }
1956 // Manage the active rockets
1957 int iIndex = -1;
1958 while ((iIndex = FindNextValidRocket(iIndex)) != -1)
1959 {
1960 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
1961 int iDeflectionCount = GetEntProp(iEntity, Prop_Send, "m_iDeflected") - 1;
1962 if ((iDeflectionCount > g_iRocketDeflections[iIndex]) || IsOnTouch) {
1963 IsOnGameFrame = false;
1964 CreateTimer(GetTickInterval(), OnDodgeBallGameFrame);
1965 CreateTimer(GetTickInterval(), SetBack);
1966 }
1967 else {
1968 switch (g_iRocketClassBehaviour[g_iRocketClass[iIndex]])
1969 {
1970 case Behaviour_Unknown: { }
1971 case Behaviour_Homing: { HomingRocketThink(iIndex); }
1972 }
1973 }
1974 }
1975 }
1976}
1977
1978/* OnDodgeBallGameFrame()
1979**
1980** Every tick of the Dodgeball logic.
1981** -------------------------------------------------------------------------- */
1982public Action OnDodgeBallGameFrame(Handle hTimer, any Data)
1983{
1984 // Manage the active rockets
1985 int iIndex = -1;
1986 while ((iIndex = FindNextValidRocket(iIndex)) != -1)
1987 {
1988 switch (g_iRocketClassBehaviour[g_iRocketClass[iIndex]])
1989 {
1990 case Behaviour_Unknown: { }
1991 case Behaviour_Homing: { HomingRocketThink(iIndex); }
1992 }
1993 }
1994}
1995
1996/*public Action ShowToTarget(int iIndex, int iClient)
1997{
1998 int iParticle = EntRefToEntIndex(g_RocketParticle[iIndex]);
1999 int iTarget = EntRefToEntIndex(g_iRocketTarget[iIndex]);
2000
2001 if (!IsValidEntity(iParticle))
2002 return Plugin_Handled;
2003
2004 if (!IsValidClient(iTarget))
2005 return Plugin_Handled;
2006
2007 if (iClient != iTarget)
2008 return Plugin_Handled;
2009
2010 return Plugin_Continue;
2011}*/
2012
2013public Action Timer_HudSpeed(Handle hTimer)
2014{
2015 if (GetConVarBool(g_hCvarSpeedo))
2016 {
2017 SetHudTextParams(-1.0, 0.9, 1.1, 255, 255, 255, 255);
2018 for (int iClient = 1; iClient <= MaxClients; iClient++)
2019 {
2020 if (IsValidClient(iClient) && !IsFakeClient(iClient) && g_iRocketSpeed != 0)
2021 {
2022 ShowSyncHudText(iClient, g_hHud, "Speed: %i mph", g_iRocketSpeed);
2023 }
2024 }
2025 }
2026}
2027
2028public Action Timer_StartRocketClassVote(Handle timer)
2029{
2030 if(!g_bVoteClassEnabled)
2031 return;
2032
2033 g_iTimeVoted = GetTime();
2034
2035 CPrintToChatAll("%s %sVoting for Rocket Class %sstarted!", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor);
2036
2037 Menu menu = new Menu(Handler_RocketClassVoteMenu);
2038 menu.SetTitle("Set the Rocket Class:");
2039
2040 for (int currentClass = 0; currentClass < g_iRocketClassCount; currentClass++)
2041 {
2042 char classNumber[16];
2043 IntToString(currentClass, classNumber, sizeof(classNumber));
2044 if (StrEqual(g_strSavedClassName, g_strRocketClassLongName[currentClass]))
2045 {
2046 char currentClassName[32];
2047 strcopy(currentClassName, sizeof(currentClassName), "[Current] ");
2048 StrCat(currentClassName, sizeof(currentClassName), g_strSavedClassName);
2049 menu.AddItem(classNumber, currentClassName);
2050 }
2051 else menu.AddItem(classNumber, g_strRocketClassLongName[currentClass]);
2052 }
2053
2054 char nochange[64];
2055 Format(nochange, 64, "Don't Change");
2056 char classCount[sizeof(g_iRocketClassCount)];
2057 IntToString(g_iRocketClassCount + 1, classCount, sizeof(classCount));
2058 menu.AddItem(classCount, nochange);
2059
2060 menu.ExitButton = false;
2061 menu.DisplayVoteToAll(20);
2062
2063 LogMessage("[VRC] Voting for rocket class has successfully started.");
2064}
2065
2066public int Handler_RocketClassVoteMenu(Menu menu, MenuAction action, int param1, int param2)
2067{
2068 switch (action)
2069 {
2070 case MenuAction_End:
2071 {
2072 delete menu;
2073 }
2074 case MenuAction_Select:
2075 {
2076 char voter[64], choice[64];
2077 GetClientName(param1, voter, sizeof(voter));
2078 menu.GetItem(param2, choice, sizeof(choice));
2079 LogMessage("[VRC] %s Selected Rocket Class %s", voter, choice);
2080 }
2081 case MenuAction_VoteEnd:
2082 {
2083 char classNum[64];
2084 char className[64];
2085 int votes, totalVotes;
2086 GetMenuVoteInfo(param2, votes, totalVotes);
2087
2088 if (totalVotes < 1)
2089 {
2090 CPrintToChatAll("%s %sVoting ended with %sno Votes!", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor);
2091 return;
2092 }
2093
2094 menu.GetItem(param1, classNum, sizeof(classNum));
2095
2096 strcopy(className, sizeof(className), g_strRocketClassLongName[param1]);
2097
2098 if (StrContains(className, "[Current]") != -1 || StringToInt(classNum) == (g_iRocketClassCount + 1))
2099 {
2100 CPrintToChatAll("%s %sCurrent Rocket Class %sStays", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor);
2101 LogMessage("[VRC] Voting for Rocket Class has ended, current class kept.");
2102 }
2103 else
2104 {
2105 LogMessage("[VRC] Voting for Rocket Class has ended, changing class to %s.", className);
2106 SetMainRocketClass(param1, true);
2107 }
2108 }
2109 }
2110}
2111
2112public Action Timer_AllowRocketClassVote(Handle timer, Handle hndl) {
2113 g_bCanVoteClass = true;
2114}
2115
2116// ___ _ _
2117// | _ \___ __| |_____| |_ ___
2118// | / _ \/ _| / / -_) _(_-<
2119// |_|_\___/\__|_\_\___|\__/__/
2120
2121/* CreateRocket()
2122**
2123** Fires a new rocket entity from the spawner's position.
2124** -------------------------------------------------------------------------- */
2125public void CreateRocket(int iSpawnerEntity, int iSpawnerClass, int iTeam)
2126{
2127 int iIndex = FindFreeRocketSlot();
2128 if (iIndex != -1)
2129 {
2130 // Fetch a random rocket class and it's parameters.
2131 int iClass = GetRandomRocketClass(iSpawnerClass);
2132 RocketFlags iFlags = g_iRocketClassFlags[iClass];
2133
2134 // Create rocket entity.
2135 int iEntity = CreateEntityByName(TestFlags(iFlags, RocketFlag_IsAnimated) ? "tf_projectile_sentryrocket" : "tf_projectile_rocket");
2136 if (iEntity && IsValidEntity(iEntity))
2137 {
2138 // Fetch spawn point's location and angles.
2139 float fPosition[3];
2140 float fAngles[3];
2141 float fDirection[3];
2142 GetEntPropVector(iSpawnerEntity, Prop_Send, "m_vecOrigin", fPosition);
2143 GetEntPropVector(iSpawnerEntity, Prop_Send, "m_angRotation", fAngles);
2144 GetAngleVectors(fAngles, fDirection, NULL_VECTOR, NULL_VECTOR);
2145
2146 // Setup rocket entity.
2147 SetEntPropEnt(iEntity, Prop_Send, "m_hOwnerEntity", 0);
2148 SetEntProp(iEntity, Prop_Send, "m_bCritical", (GetURandomFloatRange(0.0, 100.0) <= g_fRocketClassCritChance[iClass]) ? 1 : 0, 1);
2149 SetEntProp(iEntity, Prop_Send, "m_iTeamNum", iTeam, 1);
2150 SetEntProp(iEntity, Prop_Send, "m_iDeflected", 1);
2151 TeleportEntity(iEntity, fPosition, fAngles, view_as<float>( { 0.0, 0.0, 0.0 } ));
2152
2153 // Setup rocket structure with the newly created entity.
2154 int iTargetTeam = (TestFlags(iFlags, RocketFlag_IsNeutral)) ? 0 : GetAnalogueTeam(iTeam);
2155 int iTarget = SelectTarget(iTargetTeam);
2156 float fModifier = CalculateModifier(iClass, 0);
2157 g_bRocketIsValid[iIndex] = true;
2158 g_iRocketFlags[iIndex] = iFlags;
2159 g_iRocketEntity[iIndex] = EntIndexToEntRef(iEntity);
2160 g_iRocketTarget[iIndex] = EntIndexToEntRef(iTarget);
2161 g_iRocketSpawner[iIndex] = iSpawnerClass;
2162 g_iRocketClass[iIndex] = iClass;
2163 g_iRocketDeflections[iIndex] = 0;
2164 g_fRocketLastDeflectionTime[iIndex] = GetGameTime();
2165 g_fRocketLastBeepTime[iIndex] = GetGameTime();
2166 g_fRocketSpeed[iIndex] = CalculateRocketSpeed(iClass, fModifier);
2167 g_iRocketSpeed = RoundFloat(g_fRocketSpeed[iIndex] * 0.042614);
2168
2169 CopyVectors(fDirection, g_fRocketDirection[iIndex]);
2170 SetEntDataFloat(iEntity, FindSendPropInfo("CTFProjectile_Rocket", "m_iDeflected") + 4, CalculateRocketDamage(iClass, fModifier), true);
2171 DispatchSpawn(iEntity);
2172
2173 // Apply custom model, if specified on the flags.
2174 if (TestFlags(iFlags, RocketFlag_CustomModel))
2175 {
2176 SetEntityModel(iEntity, g_strRocketClassModel[iClass]);
2177 UpdateRocketSkin(iEntity, iTeam, TestFlags(iFlags, RocketFlag_IsNeutral));
2178 }
2179
2180 // Execute commands on spawn.
2181 if (TestFlags(iFlags, RocketFlag_OnSpawnCmd))
2182 {
2183 ExecuteCommands(g_hRocketClassCmdsOnSpawn[iClass], iClass, iEntity, 0, iTarget, g_iLastDeadClient, g_fRocketSpeed[iIndex], 0);
2184 }
2185
2186 // Emit required sounds.
2187 EmitRocketSound(RocketSound_Spawn, iClass, iEntity, iTarget, iFlags);
2188 EmitRocketSound(RocketSound_Alert, iClass, iEntity, iTarget, iFlags);
2189
2190 // Done
2191 g_iRocketCount++;
2192 g_iRocketsFired++;
2193 g_fLastSpawnTime = GetGameTime();
2194 g_fNextSpawnTime = GetGameTime() + g_fSpawnersInterval[iSpawnerClass];
2195 g_bRocketIsNuke[iIndex] = false;
2196
2197 //AttachParticle(iEntity, "burningplayer_rainbow_glow");
2198 //AttachParticle(iEntity, "burningplayer_rainbow_glow_old");
2199 //CreateTempParticle("superrare_greenenergy", iEntity, _, _, true);
2200 //SDKHook(iEntity, SDKHook_SetTransmit, ShowToTarget);
2201
2202 //Observer
2203 if (IsValidEntity(g_observer))
2204 {
2205 g_op_rocket = iEntity;
2206 TeleportEntity(g_observer, fPosition, fAngles, view_as<float>( { 0.0, 0.0, 0.0 } ));
2207 SetVariantString("!activator");
2208 AcceptEntityInput(g_observer, "SetParent", g_op_rocket);
2209 }
2210 }
2211 }
2212}
2213
2214public void OnEntityDestroyed(int entity)
2215{
2216 if (entity == -1)
2217 {
2218 return;
2219 }
2220
2221 if (entity == g_op_rocket && g_bEnabled == true && IsValidEntity(g_observer) && IsValidEntity(g_op_rocket))
2222 {
2223 SetVariantString("");
2224 AcceptEntityInput(g_observer, "ClearParent");
2225 g_op_rocket = -1;
2226
2227 float opPos[3];
2228 float opAng[3];
2229
2230 int spawner = GetRandomInt(0, 1);
2231 if (spawner == 0)
2232 spawner = g_iSpawnPointsRedEntity[0];
2233 else
2234 spawner = g_iSpawnPointsBluEntity[0];
2235
2236 if (IsValidEntity(spawner) && spawner > MAXPLAYERS)
2237 {
2238 GetEntPropVector(spawner, Prop_Data, "m_vecOrigin", opPos);
2239 GetEntPropVector(spawner, Prop_Data, "m_angAbsRotation", opAng);
2240 TeleportEntity(g_observer, opPos, opAng, NULL_VECTOR);
2241 }
2242 }
2243}
2244
2245
2246/* DestroyRocket()
2247**
2248** Destroys the rocket at the given index.
2249** -------------------------------------------------------------------------- */
2250void DestroyRocket(int iIndex)
2251{
2252 if (IsValidRocket(iIndex) == true)
2253 {
2254 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
2255 if (iEntity && IsValidEntity(iEntity))RemoveEdict(iEntity);
2256 g_bRocketIsValid[iIndex] = false;
2257 g_iRocketCount--;
2258 }
2259}
2260
2261/* DestroyRockets()
2262**
2263** Destroys all the rockets that are currently active.
2264** -------------------------------------------------------------------------- */
2265void DestroyRockets()
2266{
2267 for (int iIndex = 0; iIndex < MAX_ROCKETS; iIndex++)
2268 {
2269 DestroyRocket(iIndex);
2270 }
2271 g_iRocketCount = 0;
2272}
2273
2274/* IsValidRocket()
2275**
2276** Checks if a rocket structure is valid.
2277** -------------------------------------------------------------------------- */
2278bool IsValidRocket(int iIndex)
2279{
2280 if ((iIndex >= 0) && (g_bRocketIsValid[iIndex] == true))
2281 {
2282 if (EntRefToEntIndex(g_iRocketEntity[iIndex]) == -1)
2283 {
2284 g_bRocketIsValid[iIndex] = false;
2285 g_iRocketCount--;
2286 return false;
2287 }
2288 return true;
2289 }
2290 return false;
2291}
2292
2293/* FindNextValidRocket()
2294**
2295** Retrieves the index of the next valid rocket from the current offset.
2296** -------------------------------------------------------------------------- */
2297int FindNextValidRocket(int iIndex, bool bWrap = false)
2298{
2299 for (int iCurrent = iIndex + 1; iCurrent < MAX_ROCKETS; iCurrent++)
2300 if (IsValidRocket(iCurrent))
2301 return iCurrent;
2302
2303 return (bWrap == true) ? FindNextValidRocket(-1, false) : -1;
2304}
2305
2306/* FindFreeRocketSlot()
2307**
2308** Retrieves the next free rocket slot since the current one. If all of them
2309** are full, returns -1.
2310** -------------------------------------------------------------------------- */
2311int FindFreeRocketSlot()
2312{
2313 int iIndex = g_iLastCreatedRocket;
2314 int iCurrent = iIndex;
2315
2316 do
2317 {
2318 if (!IsValidRocket(iCurrent))return iCurrent;
2319 if ((++iCurrent) == MAX_ROCKETS)iCurrent = 0;
2320 } while (iCurrent != iIndex);
2321
2322 return -1;
2323}
2324
2325/* FindRocketByEntity()
2326**
2327** Finds a rocket index from it's entity.
2328** -------------------------------------------------------------------------- */
2329int FindRocketByEntity(int iEntity)
2330{
2331 int iIndex = -1;
2332 while ((iIndex = FindNextValidRocket(iIndex)) != -1)
2333 if (EntRefToEntIndex(g_iRocketEntity[iIndex]) == iEntity)
2334 return iIndex;
2335
2336 return -1;
2337}
2338
2339/* HomingRocketThinkg()
2340**
2341** Logic process for the Behaviour_Homing type rockets, wich is simply a
2342** follower rocket, picking a random target.
2343** -------------------------------------------------------------------------- */
2344void HomingRocketThink(int iIndex)
2345{
2346 // Retrieve the rocket's attributes.
2347 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
2348 int iClass = g_iRocketClass[iIndex];
2349 RocketFlags iFlags = g_iRocketFlags[iIndex];
2350 int iTarget = EntRefToEntIndex(g_iRocketTarget[iIndex]);
2351 int iTeam = GetEntProp(iEntity, Prop_Send, "m_iTeamNum", 1);
2352 int iTargetTeam = (TestFlags(iFlags, RocketFlag_IsNeutral)) ? 0 : GetAnalogueTeam(iTeam);
2353 int iDeflectionCount = GetEntProp(iEntity, Prop_Send, "m_iDeflected") - 1;
2354 float fModifier = CalculateModifier(iClass, iDeflectionCount);
2355 g_hRocketSpeed = RoundFloat(g_fRocketSpeed[iIndex]);
2356 xDeflections = g_iRocketDeflections[iIndex];
2357
2358 // Check if the target is available
2359 if (!IsValidClient(iTarget, true))
2360 {
2361 iTarget = SelectTarget(iTargetTeam);
2362 if (!IsValidClient(iTarget, true))return;
2363 g_iRocketTarget[iIndex] = EntIndexToEntRef(iTarget);
2364
2365 if (GetConVarBool(g_hCvarRedirectBeep))
2366 {
2367 EmitRocketSound(RocketSound_Alert, iClass, iEntity, iTarget, iFlags);
2368 }
2369 }
2370 // Has the rocket been deflected recently? If so, set new target.
2371 else if ((iDeflectionCount > g_iRocketDeflections[iIndex]))
2372 {
2373 // Calculate new direction from the player's forward
2374 int iClient = GetEntPropEnt(iEntity, Prop_Send, "m_hOwnerEntity");
2375 if (IsValidClient(iClient))
2376 {
2377 float fViewAngles[3];
2378 float fDirection[3];
2379 GetClientEyeAngles(iClient, fViewAngles);
2380 GetAngleVectors(fViewAngles, fDirection, NULL_VECTOR, NULL_VECTOR);
2381 CopyVectors(fDirection, g_fRocketDirection[iIndex]);
2382 UpdateRocketSkin(iEntity, iTeam, TestFlags(iFlags, RocketFlag_IsNeutral));
2383 if (GetConVarBool(g_hCvarStealPrevention))
2384 {
2385 checkStolenRocket(iClient, iIndex);
2386 }
2387 }
2388
2389 gDeflector = iClient;
2390
2391 // Set new target & deflection count
2392 iTarget = SelectTarget(iTargetTeam, iIndex);
2393 g_iRocketTarget[iIndex] = EntIndexToEntRef(iTarget);
2394 g_iRocketDeflections[iIndex] = iDeflectionCount;
2395 g_fRocketLastDeflectionTime[iIndex] = GetGameTime();
2396 g_fRocketSpeed[iIndex] = CalculateRocketSpeed(iClass, fModifier);
2397 g_iRocketSpeed = RoundFloat(g_fRocketSpeed[iIndex] * 0.042614);
2398 g_bPreventingDelay = false;
2399
2400 SetEntDataFloat(iEntity, FindSendPropInfo("CTFProjectile_Rocket", "m_iDeflected") + 4, CalculateRocketDamage(iClass, fModifier), true);
2401 if (TestFlags(iFlags, RocketFlag_ElevateOnDeflect))g_iRocketFlags[iIndex] |= RocketFlag_Elevating;
2402 EmitRocketSound(RocketSound_Alert, iClass, iEntity, iTarget, iFlags);
2403 //Send out temp entity to target
2404 //SendTempEnt(iTarget, "superrare_greenenergy", iEntity, _, _, true);
2405
2406 // Execute appropiate command
2407 if (TestFlags(iFlags, RocketFlag_OnDeflectCmd))
2408 {
2409 ExecuteCommands(g_hRocketClassCmdsOnDeflect[iClass], iClass, iEntity, iClient, iTarget, g_iLastDeadClient, g_fRocketSpeed[iIndex], iDeflectionCount);
2410 }
2411 }
2412 else
2413 {
2414 // If the delay time since the last reflection has been elapsed, rotate towards the client.
2415 if ((GetGameTime() - g_fRocketLastDeflectionTime[iIndex]) >= g_fRocketClassControlDelay[iClass])
2416 {
2417 // Calculate turn rate and retrieve directions.
2418 float fTurnRate = CalculateRocketTurnRate(iClass, fModifier);
2419 float fDirectionToTarget[3]; CalculateDirectionToClient(iEntity, iTarget, fDirectionToTarget);
2420
2421 // Elevate the rocket after a deflection (if it's enabled on the class definition, of course.)
2422 if (g_iRocketFlags[iIndex] & RocketFlag_Elevating)
2423 {
2424 if (g_fRocketDirection[iIndex][2] < g_fRocketClassElevationLimit[iClass])
2425 {
2426 g_fRocketDirection[iIndex][2] = FMin(g_fRocketDirection[iIndex][2] + g_fRocketClassElevationRate[iClass], g_fRocketClassElevationLimit[iClass]);
2427 fDirectionToTarget[2] = g_fRocketDirection[iIndex][2];
2428 }
2429 else
2430 {
2431 g_iRocketFlags[iIndex] &= ~RocketFlag_Elevating;
2432 }
2433 }
2434
2435 // Smoothly change the orientation to the new one.
2436 LerpVectors(g_fRocketDirection[iIndex], fDirectionToTarget, g_fRocketDirection[iIndex], fTurnRate);
2437 }
2438
2439 // If it's a nuke, beep every some time
2440 if ((GetGameTime() - g_fRocketLastBeepTime[iIndex]) >= g_fRocketClassBeepInterval[iClass])
2441 {
2442 g_bRocketIsNuke[iIndex] = true;
2443 EmitRocketSound(RocketSound_Beep, iClass, iEntity, iTarget, iFlags);
2444 g_fRocketLastBeepTime[iIndex] = GetGameTime();
2445 }
2446
2447 if (GetConVarBool(g_hCvarDelayPrevention))
2448 {
2449 checkRoundDelays(iIndex);
2450 }
2451 }
2452
2453 // Done
2454 ApplyRocketParameters(iIndex);
2455}
2456
2457public Action SetBack(Handle timer) {
2458 IsOnTouch = false;
2459 IsOnGameFrame = true;
2460}
2461
2462/* CalculateModifier()
2463**
2464** Gets the modifier for the damage/speed/rotation calculations.
2465** -------------------------------------------------------------------------- */
2466float CalculateModifier(int iClass, int iDeflections)
2467{
2468 return iDeflections +
2469 (g_iRocketsFired * g_fRocketClassRocketsModifier[iClass]) +
2470 (g_iPlayerCount * g_fRocketClassPlayerModifier[iClass]);
2471}
2472
2473/* CalculateRocketDamage()
2474**
2475** Calculates the damage of the rocket based on it's type and deflection count.
2476** -------------------------------------------------------------------------- */
2477float CalculateRocketDamage(int iClass, float fModifier)
2478{
2479 return g_fRocketClassDamage[iClass] + g_fRocketClassDamageIncrement[iClass] * fModifier;
2480}
2481
2482/* CalculateRocketSpeed()
2483**
2484** Calculates the speed of the rocket based on it's type and deflection count.
2485** -------------------------------------------------------------------------- */
2486float CalculateRocketSpeed(int iClass, float fModifier)
2487{
2488 return g_fRocketClassSpeed[iClass] + g_fRocketClassSpeedIncrement[iClass] * fModifier;
2489}
2490
2491/* CalculateRocketTurnRate()
2492**
2493** Calculates the rocket's turn rate based upon it's type and deflection count.
2494** -------------------------------------------------------------------------- */
2495float CalculateRocketTurnRate(int iClass, float fModifier)
2496{
2497 return g_fRocketClassTurnRate[iClass] + g_fRocketClassTurnRateIncrement[iClass] * fModifier;
2498}
2499
2500/* CalculateDirectionToClient()
2501**
2502** As the name indicates, calculates the orientation for the rocket to move
2503** towards the specified client.
2504** -------------------------------------------------------------------------- */
2505void CalculateDirectionToClient(int iEntity, int iClient, float fOut[3])
2506{
2507 float fRocketPosition[3]; GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fRocketPosition);
2508 GetClientEyePosition(iClient, fOut);
2509 MakeVectorFromPoints(fRocketPosition, fOut, fOut);
2510 NormalizeVector(fOut, fOut);
2511}
2512
2513/* ApplyRocketParameters()
2514**
2515** Transforms and applies the speed, direction and angles for the rocket
2516** entity.
2517** -------------------------------------------------------------------------- */
2518void ApplyRocketParameters(int iIndex)
2519{
2520 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
2521 float fAngles[3]; GetVectorAngles(g_fRocketDirection[iIndex], fAngles);
2522 float fVelocity[3]; CopyVectors(g_fRocketDirection[iIndex], fVelocity);
2523 ScaleVector(fVelocity, g_fRocketSpeed[iIndex]);
2524 SetEntPropVector(iEntity, Prop_Data, "m_vecAbsVelocity", fVelocity);
2525 SetEntPropVector(iEntity, Prop_Send, "m_angRotation", fAngles);
2526}
2527
2528/* UpdateRocketSkin()
2529**
2530** Changes the skin of the rocket based on it's team.
2531** -------------------------------------------------------------------------- */
2532void UpdateRocketSkin(int iEntity, int iTeam, bool bNeutral)
2533{
2534 if (bNeutral == true)SetEntProp(iEntity, Prop_Send, "m_nSkin", 2);
2535 else SetEntProp(iEntity, Prop_Send, "m_nSkin", (iTeam == view_as<int>(TFTeam_Blue)) ? 0 : 1);
2536}
2537
2538/* GetRandomRocketClass()
2539**
2540** Generates a random value and retrieves a rocket class based upon a chances table.
2541** -------------------------------------------------------------------------- */
2542int GetRandomRocketClass(int iSpawnerClass)
2543{
2544 int iRandom = GetURandomIntRange(0, 101);
2545 Handle hTable = g_hSpawnersChancesTable[iSpawnerClass];
2546 int iTableSize = GetArraySize(hTable);
2547 int iChancesLower = 0;
2548 int iChancesUpper = 0;
2549
2550 for (int iEntry = 0; iEntry < iTableSize; iEntry++)
2551 {
2552 iChancesLower += iChancesUpper;
2553 iChancesUpper = iChancesLower + GetArrayCell(hTable, iEntry);
2554
2555 if ((iRandom >= iChancesLower) && (iRandom < iChancesUpper))
2556 {
2557 return iEntry;
2558 }
2559 }
2560
2561 return 0;
2562}
2563
2564/* EmitRocketSound()
2565**
2566** Emits one of the rocket sounds
2567** -------------------------------------------------------------------------- */
2568void EmitRocketSound(RocketSound iSound, int iClass, int iEntity, int iTarget, RocketFlags iFlags)
2569{
2570 switch (iSound)
2571 {
2572 case RocketSound_Spawn:
2573 {
2574 if (TestFlags(iFlags, RocketFlag_PlaySpawnSound))
2575 {
2576 if (TestFlags(iFlags, RocketFlag_CustomSpawnSound))EmitSoundToAll(g_strRocketClassSpawnSound[iClass], iEntity);
2577 else EmitSoundToAll(SOUND_DEFAULT_SPAWN, iEntity);
2578 }
2579 }
2580 case RocketSound_Beep:
2581 {
2582 if (TestFlags(iFlags, RocketFlag_PlayBeepSound))
2583 {
2584 if (TestFlags(iFlags, RocketFlag_CustomBeepSound))EmitSoundToAll(g_strRocketClassBeepSound[iClass], iEntity);
2585 else EmitSoundToAll(SOUND_DEFAULT_BEEP, iEntity);
2586 }
2587 }
2588 case RocketSound_Alert:
2589 {
2590 if (TestFlags(iFlags, RocketFlag_PlayAlertSound))
2591 {
2592 if (TestFlags(iFlags, RocketFlag_CustomAlertSound))EmitSoundToClient(iTarget, g_strRocketClassAlertSound[iClass]);
2593 else EmitSoundToClient(iTarget, SOUND_DEFAULT_ALERT, _, _, _, _, 0.5);
2594 }
2595 }
2596 }
2597}
2598
2599// ___ _ _ ___ _
2600// | _ \___ __| |_____| |_ / __| |__ _ ______ ___ ___
2601// | / _ \/ _| / / -_) _| | (__| / _` (_-<_-</ -_|_-<
2602// |_|_\___/\__|_\_\___|\__| \___|_\__,_/__/__/\___/__/
2603//
2604
2605/* DestroyRocketClasses()
2606**
2607** Frees up all the rocket classes defined now.
2608** -------------------------------------------------------------------------- */
2609void DestroyRocketClasses()
2610{
2611 for (int iIndex = 0; iIndex < g_iRocketClassCount; iIndex++)
2612 {
2613 Handle hCmdOnSpawn = g_hRocketClassCmdsOnSpawn[iIndex];
2614 Handle hCmdOnKill = g_hRocketClassCmdsOnKill[iIndex];
2615 Handle hCmdOnExplode = g_hRocketClassCmdsOnExplode[iIndex];
2616 Handle hCmdOnDeflect = g_hRocketClassCmdsOnDeflect[iIndex];
2617 if (hCmdOnSpawn != INVALID_HANDLE)CloseHandle(hCmdOnSpawn);
2618 if (hCmdOnKill != INVALID_HANDLE)CloseHandle(hCmdOnKill);
2619 if (hCmdOnExplode != INVALID_HANDLE)CloseHandle(hCmdOnExplode);
2620 if (hCmdOnDeflect != INVALID_HANDLE)CloseHandle(hCmdOnDeflect);
2621 g_hRocketClassCmdsOnSpawn[iIndex] = INVALID_HANDLE;
2622 g_hRocketClassCmdsOnKill[iIndex] = INVALID_HANDLE;
2623 g_hRocketClassCmdsOnExplode[iIndex] = INVALID_HANDLE;
2624 g_hRocketClassCmdsOnDeflect[iIndex] = INVALID_HANDLE;
2625 }
2626 g_iRocketClassCount = 0;
2627 ClearTrie(g_hRocketClassTrie);
2628}
2629
2630// ___ ___ _ _ _ ___ _
2631// / __|_ __ __ ___ __ ___ _ | _ \___(_)_ _| |_ ___ __ _ _ _ __| | / __| |__ _ ______ ___ ___
2632// \__ \ '_ \/ _` \ V V / ' \ | _/ _ \ | ' \ _(_-< / _` | ' \/ _` | | (__| / _` (_-<_-</ -_|_-<
2633// |___/ .__/\__,_|\_/\_/|_||_| |_| \___/_|_||_\__/__/ \__,_|_||_\__,_| \___|_\__,_/__/__/\___/__/
2634// |_|
2635
2636/* DestroySpawners()
2637**
2638** Frees up all the spawner points defined up to now.
2639** -------------------------------------------------------------------------- */
2640void DestroySpawners()
2641{
2642 for (int iIndex = 0; iIndex < g_iSpawnersCount; iIndex++)
2643 {
2644 CloseHandle(g_hSpawnersChancesTable[iIndex]);
2645 }
2646 g_iSpawnersCount = 0;
2647 g_iSpawnPointsRedCount = 0;
2648 g_iSpawnPointsBluCount = 0;
2649 g_iDefaultRedSpawner = -1;
2650 g_iDefaultBluSpawner = -1;
2651 g_strSavedClassName[0] = '\0';
2652 ClearTrie(g_hSpawnersTrie);
2653}
2654
2655/* PopulateSpawnPoints()
2656**
2657** Iterates through all the possible spawn points and assigns them an spawner.
2658** -------------------------------------------------------------------------- */
2659void PopulateSpawnPoints()
2660{
2661 // Clear the current settings
2662 g_iSpawnPointsRedCount = 0;
2663 g_iSpawnPointsBluCount = 0;
2664
2665 // Iterate through all the info target points and check 'em out.
2666 int iEntity = -1;
2667 while ((iEntity = FindEntityByClassname(iEntity, "info_target")) != -1)
2668 {
2669 char strName[32]; GetEntPropString(iEntity, Prop_Data, "m_iName", strName, sizeof(strName));
2670 if ((StrContains(strName, "rocket_spawn_red") != -1) || (StrContains(strName, "tf_dodgeball_red") != -1))
2671 {
2672 // Find most appropiate spawner class for this entity.
2673 int iIndex = FindSpawnerByName(strName);
2674 if (!IsValidRocket(iIndex)) iIndex = g_iDefaultRedSpawner;
2675
2676 // Upload to point list
2677 g_iSpawnPointsRedClass[g_iSpawnPointsRedCount] = iIndex;
2678 g_iSpawnPointsRedEntity[g_iSpawnPointsRedCount] = iEntity;
2679 g_iSpawnPointsRedCount++;
2680 }
2681 if ((StrContains(strName, "rocket_spawn_blue") != -1) || (StrContains(strName, "tf_dodgeball_blu") != -1))
2682 {
2683 // Find most appropiate spawner class for this entity.
2684 int iIndex = FindSpawnerByName(strName);
2685 if (!IsValidRocket(iIndex))iIndex = g_iDefaultBluSpawner;
2686
2687 // Upload to point list
2688 g_iSpawnPointsBluClass[g_iSpawnPointsBluCount] = iIndex;
2689 g_iSpawnPointsBluEntity[g_iSpawnPointsBluCount] = iEntity;
2690 g_iSpawnPointsBluCount++;
2691 }
2692 }
2693
2694 // Check if there exists spawn points
2695 if (g_iSpawnPointsRedCount == 0)
2696 SetFailState("No RED spawn points found on this map.");
2697
2698 if (g_iSpawnPointsBluCount == 0)
2699 SetFailState("No BLU spawn points found on this map.");
2700
2701
2702 //ObserverPoint
2703 float opPos[3];
2704 float opAng[3];
2705
2706 int spawner = GetRandomInt(0, 1);
2707 if (spawner == 0)
2708 spawner = g_iSpawnPointsRedEntity[0];
2709 else
2710 spawner = g_iSpawnPointsBluEntity[0];
2711
2712 if (IsValidEntity(spawner) && spawner > MAXPLAYERS)
2713 {
2714 GetEntPropVector(spawner, Prop_Data, "m_vecOrigin", opPos);
2715 GetEntPropVector(spawner, Prop_Data, "m_angAbsRotation", opAng);
2716 g_observer = CreateEntityByName("info_observer_point");
2717 DispatchKeyValue(g_observer, "Angles", "90 0 0");
2718 DispatchKeyValue(g_observer, "TeamNum", "0");
2719 DispatchKeyValue(g_observer, "StartDisabled", "0");
2720 DispatchSpawn(g_observer);
2721 AcceptEntityInput(g_observer, "Enable");
2722 TeleportEntity(g_observer, opPos, opAng, NULL_VECTOR);
2723 }
2724 else
2725 {
2726 g_observer = -1;
2727 }
2728
2729}
2730
2731/* FindSpawnerByName()
2732**
2733** Finds the first spawner wich contains the given name.
2734** -------------------------------------------------------------------------- */
2735int FindSpawnerByName(char strName[32])
2736{
2737 int iIndex = -1;
2738 GetTrieValue(g_hSpawnersTrie, strName, iIndex);
2739 return iIndex;
2740}
2741
2742
2743/*
2744**����������������������������������������������������������������������������������
2745** ______ __
2746** / ____/___ ____ ___ ____ ___ ____ _____ ____/ /____
2747** / / / __ \/ __ `__ \/ __ `__ \/ __ `/ __ \/ __ / ___/
2748** / /___/ /_/ / / / / / / / / / / / /_/ / / / / /_/ (__ )
2749** \____/\____/_/ /_/ /_/_/ /_/ /_/\__,_/_/ /_/\__,_/____/
2750**
2751**����������������������������������������������������������������������������������
2752*/
2753
2754/* RegisterCommands()
2755**
2756** Creates helper server commands to use with the plugin's events system.
2757** -------------------------------------------------------------------------- */
2758void RegisterCommands()
2759{
2760 RegServerCmd("tf_dodgeball_explosion", CmdExplosion);
2761 RegServerCmd("tf_dodgeball_shockwave", CmdShockwave);
2762 RegServerCmd("tf_dodgeball_resize", CmdResize);
2763}
2764
2765public Action CmdResize(int iIndex)
2766{
2767 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
2768 if (iEntity && IsValidEntity(iEntity) && g_bRocketIsNuke[iEntity])
2769 {
2770 SetEntPropFloat(iEntity, Prop_Send, "m_flModelScale", (4.0));
2771 }
2772}
2773
2774/* CmdExplosion()
2775**
2776** Creates a huge explosion at the location of the client.
2777** -------------------------------------------------------------------------- */
2778public Action CmdExplosion(int iArgs)
2779{
2780 if (iArgs == 1)
2781 {
2782 char strBuffer[8], iClient;
2783 GetCmdArg(1, strBuffer, sizeof(strBuffer));
2784 iClient = StringToInt(strBuffer);
2785 if (IsValidEntity(iClient))
2786 {
2787 float fPosition[3];
2788 GetClientAbsOrigin(iClient, fPosition);
2789 switch (GetURandomIntRange(0, 4))
2790 {
2791 case 0:
2792 {
2793 PlayParticle(fPosition, PARTICLE_NUKE_1_ANGLES, PARTICLE_NUKE_1);
2794 }
2795 case 1:
2796 {
2797 PlayParticle(fPosition, PARTICLE_NUKE_2_ANGLES, PARTICLE_NUKE_2);
2798 }
2799 case 2:
2800 {
2801 PlayParticle(fPosition, PARTICLE_NUKE_3_ANGLES, PARTICLE_NUKE_3);
2802 }
2803 case 3:
2804 {
2805 PlayParticle(fPosition, PARTICLE_NUKE_4_ANGLES, PARTICLE_NUKE_4);
2806 }
2807 case 4:
2808 {
2809 PlayParticle(fPosition, PARTICLE_NUKE_5_ANGLES, PARTICLE_NUKE_5);
2810 }
2811 }
2812 PlayParticle(fPosition, PARTICLE_NUKE_COLLUMN_ANGLES, PARTICLE_NUKE_COLLUMN);
2813 }
2814 }
2815 else
2816 {
2817 PrintToServer("Usage: tf_dodgeball_explosion <client index>");
2818 }
2819
2820 return Plugin_Handled;
2821}
2822
2823/* CmdShockwave()
2824**
2825** Creates a huge shockwave at the location of the client, with the given
2826** parameters.
2827** -------------------------------------------------------------------------- */
2828public Action CmdShockwave(int iArgs)
2829{
2830 if (iArgs == 5)
2831 {
2832 char strBuffer[8];
2833 int iClient;
2834 int iTeam;
2835 float fPosition[3];
2836 int iDamage;
2837 float fPushStrength;
2838 float fRadius;
2839 float fFalloffRadius;
2840 GetCmdArg(1, strBuffer, sizeof(strBuffer)); iClient = StringToInt(strBuffer);
2841 GetCmdArg(2, strBuffer, sizeof(strBuffer)); iDamage = StringToInt(strBuffer);
2842 GetCmdArg(3, strBuffer, sizeof(strBuffer)); fPushStrength = StringToFloat(strBuffer);
2843 GetCmdArg(4, strBuffer, sizeof(strBuffer)); fRadius = StringToFloat(strBuffer);
2844 GetCmdArg(5, strBuffer, sizeof(strBuffer)); fFalloffRadius = StringToFloat(strBuffer);
2845
2846 if (IsValidClient(iClient))
2847 {
2848 iTeam = GetClientTeam(iClient);
2849 GetClientAbsOrigin(iClient, fPosition);
2850
2851 for (iClient = 1; iClient <= MaxClients; iClient++)
2852 {
2853 if ((IsValidClient(iClient, true) == true) && (GetClientTeam(iClient) == iTeam))
2854 {
2855 float fPlayerPosition[3]; GetClientEyePosition(iClient, fPlayerPosition);
2856 float fDistanceToShockwave = GetVectorDistance(fPosition, fPlayerPosition);
2857
2858 if (fDistanceToShockwave < fRadius)
2859 {
2860 float fImpulse[3];
2861 float fFinalPush;
2862 int iFinalDamage;
2863 fImpulse[0] = fPlayerPosition[0] - fPosition[0];
2864 fImpulse[1] = fPlayerPosition[1] - fPosition[1];
2865 fImpulse[2] = fPlayerPosition[2] - fPosition[2];
2866 NormalizeVector(fImpulse, fImpulse);
2867 if (fImpulse[2] < 0.4) { fImpulse[2] = 0.4; NormalizeVector(fImpulse, fImpulse); }
2868
2869 if (fDistanceToShockwave < fFalloffRadius)
2870 {
2871 fFinalPush = fPushStrength;
2872 iFinalDamage = iDamage;
2873 }
2874 else
2875 {
2876 float fImpact = (1.0 - ((fDistanceToShockwave - fFalloffRadius) / (fRadius - fFalloffRadius)));
2877 fFinalPush = fImpact * fPushStrength;
2878 iFinalDamage = RoundToFloor(fImpact * iDamage);
2879 }
2880 ScaleVector(fImpulse, fFinalPush);
2881 SetEntPropVector(iClient, Prop_Data, "m_vecAbsVelocity", fImpulse);
2882
2883 Handle hDamage = CreateDataPack();
2884 WritePackCell(hDamage, iClient);
2885 WritePackCell(hDamage, iFinalDamage);
2886 CreateTimer(0.1, ApplyDamage, hDamage, TIMER_FLAG_NO_MAPCHANGE);
2887 }
2888 }
2889 }
2890 }
2891 }
2892 else
2893 {
2894 PrintToServer("Usage: tf_dodgeball_shockwave <client index> <damage> <push strength> <radius> <falloff>");
2895 }
2896
2897 return Plugin_Handled;
2898}
2899
2900/* ExecuteCommands()
2901**
2902** The core of the plugin's event system, unpacks and correctly formats the
2903** given command strings to be executed.
2904** -------------------------------------------------------------------------- */
2905void ExecuteCommands(Handle hDataPack, int iClass, int iRocket, int iOwner, int iTarget, int iLastDead, float fSpeed, int iNumDeflections)
2906{
2907 ResetPack(hDataPack, false);
2908 int iNumCommands = ReadPackCell(hDataPack);
2909 while (iNumCommands-- > 0)
2910 {
2911 char strCmd[256];
2912 char strBuffer[8];
2913 ReadPackString(hDataPack, strCmd, sizeof(strCmd));
2914 ReplaceString(strCmd, sizeof(strCmd), "@name", g_strRocketClassLongName[iClass]);
2915 Format(strBuffer, sizeof(strBuffer), "%i", iRocket); ReplaceString(strCmd, sizeof(strCmd), "@rocket", strBuffer);
2916 Format(strBuffer, sizeof(strBuffer), "%i", iOwner); ReplaceString(strCmd, sizeof(strCmd), "@owner", strBuffer);
2917 Format(strBuffer, sizeof(strBuffer), "%i", iTarget); ReplaceString(strCmd, sizeof(strCmd), "@target", strBuffer);
2918 Format(strBuffer, sizeof(strBuffer), "%i", iLastDead); ReplaceString(strCmd, sizeof(strCmd), "@dead", strBuffer);
2919 Format(strBuffer, sizeof(strBuffer), "%f", fSpeed); ReplaceString(strCmd, sizeof(strCmd), "@speed", strBuffer);
2920 Format(strBuffer, sizeof(strBuffer), "%i", iNumDeflections); ReplaceString(strCmd, sizeof(strCmd), "@deflections", strBuffer);
2921 ServerCommand(strCmd);
2922 }
2923}
2924
2925/*
2926**����������������������������������������������������������������������������������
2927** ______ _____
2928** / ____/___ ____ / __(_)___ _
2929** / / / __ \/ __ \/ /_/ / __ `/
2930** / /___/ /_/ / / / / __/ / /_/ /
2931** \____/\____/_/ /_/_/ /_/\__, /
2932** /____/
2933**����������������������������������������������������������������������������������
2934*/
2935
2936/* ParseConfiguration()
2937**
2938** Parses a Dodgeball configuration file. It doesn't clear any of the previous
2939** data, so multiple files can be parsed.
2940** -------------------------------------------------------------------------- */
2941bool ParseConfigurations(char strConfigFile[] = "general.cfg")
2942{
2943 // Parse configuration
2944 char strPath[PLATFORM_MAX_PATH];
2945 char strFileName[PLATFORM_MAX_PATH];
2946 Format(strFileName, sizeof(strFileName), "configs/dodgeball/%s", strConfigFile);
2947 BuildPath(Path_SM, strPath, sizeof(strPath), strFileName);
2948
2949 // Try to parse if it exists
2950 LogMessage("Executing configuration file %s", strPath);
2951 if (FileExists(strPath, true))
2952 {
2953 KeyValues kvConfig = CreateKeyValues("TF2_Dodgeball");
2954
2955 if (FileToKeyValues(kvConfig, strPath) == false)
2956 SetFailState("Error while parsing the configuration file.");
2957
2958 kvConfig.GotoFirstSubKey();
2959
2960 // Parse the subsections
2961 do
2962 {
2963 char strSection[64];
2964 KvGetSectionName(kvConfig, strSection, sizeof(strSection));
2965
2966 if (StrEqual(strSection, "general"))
2967 ParseGeneral(kvConfig);
2968 else if (StrEqual(strSection, "classes"))
2969 ParseClasses(kvConfig);
2970 else if (StrEqual(strSection, "spawners"))
2971 ParseSpawners(kvConfig);
2972 }
2973 while (KvGotoNextKey(kvConfig));
2974
2975 CloseHandle(kvConfig);
2976 }
2977}
2978
2979/* ParseGeneral()
2980**
2981** Parses general settings, such as the music, urls, etc.
2982** -------------------------------------------------------------------------- */
2983void ParseGeneral(Handle kvConfig)
2984{
2985 g_bMusicEnabled = view_as<bool>(KvGetNum(kvConfig, "music", 0));
2986 if (g_bMusicEnabled == true)
2987 {
2988 g_bUseWebPlayer = view_as<bool>(KvGetNum(kvConfig, "use web player", 0));
2989 KvGetString(kvConfig, "web player url", g_strWebPlayerUrl, sizeof(g_strWebPlayerUrl));
2990
2991 g_bMusic[Music_RoundStart] = KvGetString(kvConfig, "round start", g_strMusic[Music_RoundStart], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundStart]);
2992 g_bMusic[Music_RoundWin] = KvGetString(kvConfig, "round end (win)", g_strMusic[Music_RoundWin], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundWin]);
2993 g_bMusic[Music_RoundLose] = KvGetString(kvConfig, "round end (lose)", g_strMusic[Music_RoundLose], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundLose]);
2994 g_bMusic[Music_Gameplay] = KvGetString(kvConfig, "gameplay", g_strMusic[Music_Gameplay], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_Gameplay]);
2995 }
2996}
2997
2998/* ParseClasses()
2999**
3000** Parses the rocket classes data from the given configuration file.
3001** -------------------------------------------------------------------------- */
3002void ParseClasses(Handle kvConfig)
3003{
3004 char strName[64];
3005 char strBuffer[256];
3006
3007 KvGotoFirstSubKey(kvConfig);
3008 do
3009 {
3010 int iIndex = g_iRocketClassCount;
3011 RocketFlags iFlags;
3012
3013 // Basic parameters
3014 KvGetSectionName(kvConfig, strName, sizeof(strName)); strcopy(g_strRocketClassName[iIndex], 16, strName);
3015 KvGetString(kvConfig, "name", strBuffer, sizeof(strBuffer)); strcopy(g_strRocketClassLongName[iIndex], 32, strBuffer);
3016 if (KvGetString(kvConfig, "model", strBuffer, sizeof(strBuffer)))
3017 {
3018 strcopy(g_strRocketClassModel[iIndex], PLATFORM_MAX_PATH, strBuffer);
3019 if (strlen(g_strRocketClassModel[iIndex]) != 0)
3020 {
3021 iFlags |= RocketFlag_CustomModel;
3022 if (KvGetNum(kvConfig, "is animated", 0))iFlags |= RocketFlag_IsAnimated;
3023 }
3024 }
3025
3026 KvGetString(kvConfig, "behaviour", strBuffer, sizeof(strBuffer), "homing");
3027 if (StrEqual(strBuffer, "homing"))g_iRocketClassBehaviour[iIndex] = Behaviour_Homing;
3028 else g_iRocketClassBehaviour[iIndex] = Behaviour_Unknown;
3029
3030 if (KvGetNum(kvConfig, "play spawn sound", 0) == 1)
3031 {
3032 iFlags |= RocketFlag_PlaySpawnSound;
3033 if (KvGetString(kvConfig, "spawn sound", g_strRocketClassSpawnSound[iIndex], PLATFORM_MAX_PATH) && (strlen(g_strRocketClassSpawnSound[iIndex]) != 0))
3034 {
3035 iFlags |= RocketFlag_CustomSpawnSound;
3036 }
3037 }
3038
3039 if (KvGetNum(kvConfig, "play beep sound", 0) == 1)
3040 {
3041 iFlags |= RocketFlag_PlayBeepSound;
3042 g_fRocketClassBeepInterval[iIndex] = KvGetFloat(kvConfig, "beep interval", 0.5);
3043 if (KvGetString(kvConfig, "beep sound", g_strRocketClassBeepSound[iIndex], PLATFORM_MAX_PATH) && (strlen(g_strRocketClassBeepSound[iIndex]) != 0))
3044 {
3045 iFlags |= RocketFlag_CustomBeepSound;
3046 }
3047 }
3048
3049 if (KvGetNum(kvConfig, "play alert sound", 0) == 1)
3050 {
3051 iFlags |= RocketFlag_PlayAlertSound;
3052 if (KvGetString(kvConfig, "alert sound", g_strRocketClassAlertSound[iIndex], PLATFORM_MAX_PATH) && strlen(g_strRocketClassAlertSound[iIndex]) != 0)
3053 {
3054 iFlags |= RocketFlag_CustomAlertSound;
3055 }
3056 }
3057
3058 // Behaviour modifiers
3059 if (KvGetNum(kvConfig, "elevate on deflect", 1) == 1)iFlags |= RocketFlag_ElevateOnDeflect;
3060 if (KvGetNum(kvConfig, "neutral rocket", 0) == 1)iFlags |= RocketFlag_IsNeutral;
3061
3062 // Movement parameters
3063 g_fRocketClassDamage[iIndex] = KvGetFloat(kvConfig, "damage");
3064 g_fRocketClassDamageIncrement[iIndex] = KvGetFloat(kvConfig, "damage increment");
3065 g_fRocketClassCritChance[iIndex] = KvGetFloat(kvConfig, "critical chance");
3066 g_fRocketClassSpeed[iIndex] = KvGetFloat(kvConfig, "speed");
3067 g_fSavedSpeed = g_fRocketClassSpeed[iIndex];
3068 g_fRocketClassSpeedIncrement[iIndex] = KvGetFloat(kvConfig, "speed increment");
3069 g_fSavedSpeedIncrement = g_fRocketClassSpeedIncrement[iIndex];
3070 g_fRocketClassTurnRate[iIndex] = KvGetFloat(kvConfig, "turn rate");
3071 g_fRocketClassTurnRateIncrement[iIndex] = KvGetFloat(kvConfig, "turn rate increment");
3072 g_fRocketClassElevationRate[iIndex] = KvGetFloat(kvConfig, "elevation rate");
3073 g_fRocketClassElevationLimit[iIndex] = KvGetFloat(kvConfig, "elevation limit");
3074 g_fRocketClassControlDelay[iIndex] = KvGetFloat(kvConfig, "control delay");
3075 g_fRocketClassPlayerModifier[iIndex] = KvGetFloat(kvConfig, "no. players modifier");
3076 g_fRocketClassRocketsModifier[iIndex] = KvGetFloat(kvConfig, "no. rockets modifier");
3077 g_fRocketClassTargetWeight[iIndex] = KvGetFloat(kvConfig, "direction to target weight");
3078
3079 // Events
3080 Handle hCmds = INVALID_HANDLE;
3081 KvGetString(kvConfig, "on spawn", strBuffer, sizeof(strBuffer));
3082 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnSpawnCmd; g_hRocketClassCmdsOnSpawn[iIndex] = hCmds; }
3083 KvGetString(kvConfig, "on deflect", strBuffer, sizeof(strBuffer));
3084 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnDeflectCmd; g_hRocketClassCmdsOnDeflect[iIndex] = hCmds; }
3085 KvGetString(kvConfig, "on kill", strBuffer, sizeof(strBuffer));
3086 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnKillCmd; g_hRocketClassCmdsOnKill[iIndex] = hCmds; }
3087 KvGetString(kvConfig, "on explode", strBuffer, sizeof(strBuffer));
3088 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnExplodeCmd; g_hRocketClassCmdsOnExplode[iIndex] = hCmds; }
3089
3090 // Done
3091 SetTrieValue(g_hRocketClassTrie, strName, iIndex);
3092 g_iRocketClassFlags[iIndex] = iFlags;
3093 g_iRocketClassCount++;
3094 }
3095 while (KvGotoNextKey(kvConfig));
3096 KvGoBack(kvConfig);
3097}
3098
3099/* ParseSpawners()
3100**
3101** Parses the spawn points classes data from the given configuration file.
3102** -------------------------------------------------------------------------- */
3103void ParseSpawners(KeyValues kvConfig)
3104{
3105 kvConfig.JumpToKey("spawners"); //jump to spawners section
3106 char strBuffer[256];
3107 kvConfig.GotoFirstSubKey(); //goto to first subkey of "spawners" section
3108
3109 do
3110 {
3111 int iIndex = g_iSpawnersCount;
3112
3113 // Basic parameters
3114 kvConfig.GetSectionName(strBuffer, sizeof(strBuffer)); //okay, here we got section name, as example, red
3115 strcopy(g_strSpawnersName[iIndex], 32, strBuffer); //here we copied it to the g_strSpawnersName array
3116 g_iSpawnersMaxRockets[iIndex] = kvConfig.GetNum("max rockets", 1); //get some values...
3117 g_fSpawnersInterval[iIndex] = kvConfig.GetFloat("interval", 1.0);
3118
3119 // Chances table
3120 g_hSpawnersChancesTable[iIndex] = CreateArray(); //not interested in this
3121 for (int iClassIndex = 0; iClassIndex < g_iRocketClassCount; iClassIndex++)
3122 {
3123 Format(strBuffer, sizeof(strBuffer), "%s%%", g_strRocketClassName[iClassIndex]);
3124 PushArrayCell(g_hSpawnersChancesTable[iIndex], KvGetNum(kvConfig, strBuffer, 0));
3125 if (KvGetNum(kvConfig, strBuffer, 0) == 100) strcopy(g_strSavedClassName, sizeof(g_strSavedClassName), g_strRocketClassLongName[iClassIndex]);
3126 }
3127
3128 // Done.
3129 SetTrieValue(g_hSpawnersTrie, g_strSpawnersName[iIndex], iIndex); //okay, push section name to g_hSpawnersTrie
3130 g_iSpawnersCount++;
3131 } while (kvConfig.GotoNextKey());
3132
3133 kvConfig.Rewind(); //rewind
3134
3135 GetTrieValue(g_hSpawnersTrie, "Red", g_iDefaultRedSpawner); //get value by section name, section name exists in the g_hSpawnersTrie, everything should work
3136 GetTrieValue(g_hSpawnersTrie, "Blue", g_iDefaultBluSpawner);
3137}
3138
3139/* ParseCommands()
3140**
3141** Part of the event system, parses the given command strings and packs them
3142** to a Datapack.
3143** -------------------------------------------------------------------------- */
3144Handle ParseCommands(char[] strLine)
3145{
3146 TrimString(strLine);
3147 if (strlen(strLine) == 0)
3148 {
3149 return INVALID_HANDLE;
3150 }
3151 else
3152 {
3153 char strStrings[8][255];
3154 int iNumStrings = ExplodeString(strLine, ";", strStrings, 8, 255);
3155
3156 Handle hDataPack = CreateDataPack();
3157 WritePackCell(hDataPack, iNumStrings);
3158 for (int i = 0; i < iNumStrings; i++)
3159 {
3160 WritePackString(hDataPack, strStrings[i]);
3161 }
3162
3163 return hDataPack;
3164 }
3165}
3166
3167/*
3168**����������������������������������������������������������������������������������
3169** ______ __
3170** /_ __/___ ____ / /____
3171** / / / __ \/ __ \/ / ___/
3172** / / / /_/ / /_/ / (__ )
3173** /_/ \____/\____/_/____/
3174**
3175**����������������������������������������������������������������������������������
3176*/
3177
3178/* ApplyDamage()
3179**
3180** Applies a damage to a player.
3181** -------------------------------------------------------------------------- */
3182public Action ApplyDamage(Handle hTimer, any hDataPack)
3183{
3184 ResetPack(hDataPack, false);
3185 int iClient = ReadPackCell(hDataPack);
3186 int iDamage = ReadPackCell(hDataPack);
3187 CloseHandle(hDataPack);
3188 SlapPlayer(iClient, iDamage, true);
3189}
3190
3191/* CopyVectors()
3192**
3193** Copies the contents from a vector to another.
3194** -------------------------------------------------------------------------- */
3195stock void CopyVectors(float fFrom[3], float fTo[3])
3196{
3197 fTo[0] = fFrom[0];
3198 fTo[1] = fFrom[1];
3199 fTo[2] = fFrom[2];
3200}
3201
3202/* LerpVectors()
3203**
3204** Calculates the linear interpolation of the two given vectors and stores
3205** it on the third one.
3206** -------------------------------------------------------------------------- */
3207stock void LerpVectors(float fA[3], float fB[3], float fC[3], float t)
3208{
3209 if (t < 0.0)t = 0.0;
3210 if (t > 1.0)t = 1.0;
3211
3212 fC[0] = fA[0] + (fB[0] - fA[0]) * t;
3213 fC[1] = fA[1] + (fB[1] - fA[1]) * t;
3214 fC[2] = fA[2] + (fB[2] - fA[2]) * t;
3215}
3216
3217/* IsValidClient()
3218**
3219** Checks if the given client index is valid, and if it's alive or not.
3220** -------------------------------------------------------------------------- */
3221stock bool IsValidClient(int iClient, bool bAlive = false)
3222{
3223 if (iClient >= 1 &&
3224 iClient <= MaxClients &&
3225 IsClientConnected(iClient) &&
3226 IsClientInGame(iClient) &&
3227 (bAlive == false || IsPlayerAlive(iClient)))
3228 {
3229 return true;
3230 }
3231
3232 return false;
3233}
3234
3235/* BothTeamsPlaying()
3236**
3237** Checks if there are players on both teams.
3238** -------------------------------------------------------------------------- */
3239stock bool BothTeamsPlaying()
3240{
3241 bool bRedFound;
3242 bool bBluFound;
3243 for (int iClient = 1; iClient <= MaxClients; iClient++)
3244 {
3245 if (IsValidClient(iClient, true) == false)continue;
3246 int iTeam = GetClientTeam(iClient);
3247 if (iTeam == view_as<int>(TFTeam_Red))bRedFound = true;
3248 if (iTeam == view_as<int>(TFTeam_Blue))bBluFound = true;
3249 }
3250 return bRedFound && bBluFound;
3251}
3252
3253/* CountAlivePlayers()
3254**
3255** Retrieves the number of players alive.
3256** -------------------------------------------------------------------------- */
3257stock int CountAlivePlayers()
3258{
3259 int iCount = 0;
3260 for (int iClient = 1; iClient <= MaxClients; iClient++)
3261 {
3262 if (IsValidClient(iClient, true))iCount++;
3263 }
3264 return iCount;
3265}
3266
3267/* GetTotalClientCount()
3268**
3269** Retrieves the number of real players connected.
3270** -------------------------------------------------------------------------- */
3271stock int GetTotalClientCount() {
3272 int count = 0;
3273 for (int i = 1; i <= MaxClients; i++) {
3274 if (IsClientInGame(i) && !IsFakeClient(i) && GetClientTeam(i) > 1) {
3275 count += 1;
3276 }
3277 }
3278 return count;
3279}
3280
3281/* SelectTarget()
3282**
3283** Determines a random target of the given team for the homing rocket.
3284** -------------------------------------------------------------------------- */
3285stock int SelectTarget(int iTeam, int iRocket = -1)
3286{
3287 int iTarget = -1;
3288 float fTargetWeight = 0.0;
3289 float fRocketPosition[3];
3290 float fRocketDirection[3];
3291 float fWeight;
3292 bool bUseRocket;
3293
3294 if (iRocket != -1)
3295 {
3296 int iClass = g_iRocketClass[iRocket];
3297 int iEntity = EntRefToEntIndex(g_iRocketEntity[iRocket]);
3298
3299 GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fRocketPosition);
3300 CopyVectors(g_fRocketDirection[iRocket], fRocketDirection);
3301 fWeight = g_fRocketClassTargetWeight[iClass];
3302
3303 bUseRocket = true;
3304 }
3305
3306 for (int iClient = 1; iClient <= MaxClients; iClient++)
3307 {
3308 // If the client isn't connected, skip.
3309 if (!IsValidClient(iClient, true))continue;
3310 if (iTeam && GetClientTeam(iClient) != iTeam)continue;
3311
3312 // Determine if this client should be the target.
3313 float fNewWeight = GetURandomFloatRange(0.0, 100.0);
3314
3315 if (bUseRocket == true)
3316 {
3317 float fClientPosition[3]; GetClientEyePosition(iClient, fClientPosition);
3318 float fDirectionToClient[3]; MakeVectorFromPoints(fRocketPosition, fClientPosition, fDirectionToClient);
3319 fNewWeight += GetVectorDotProduct(fRocketDirection, fDirectionToClient) * fWeight;
3320 }
3321
3322 if ((iTarget == -1) || fNewWeight >= fTargetWeight)
3323 {
3324 iTarget = iClient;
3325 fTargetWeight = fNewWeight;
3326 }
3327 }
3328
3329 return iTarget;
3330}
3331
3332/* StopSoundToAll()
3333**
3334** Stops a sound for all the clients on the given channel.
3335** -------------------------------------------------------------------------- */
3336stock void StopSoundToAll(int iChannel, const char[] strSound)
3337{
3338 for (int iClient = 1; iClient <= MaxClients; iClient++)
3339 {
3340 if (IsValidClient(iClient))StopSound(iClient, iChannel, strSound);
3341 }
3342}
3343
3344/* PlayParticle()
3345**
3346** Plays a particle system at the given location & angles.
3347** -------------------------------------------------------------------------- */
3348stock void PlayParticle(float fPosition[3], float fAngles[3], char[] strParticleName, float fEffectTime = 5.0, float fLifeTime = 9.0)
3349{
3350 int iEntity = CreateEntityByName("info_particle_system");
3351 if (iEntity && IsValidEdict(iEntity))
3352 {
3353 TeleportEntity(iEntity, fPosition, fAngles, NULL_VECTOR);
3354 DispatchKeyValue(iEntity, "effect_name", strParticleName);
3355 ActivateEntity(iEntity);
3356 AcceptEntityInput(iEntity, "Start");
3357 CreateTimer(fEffectTime, StopParticle, EntIndexToEntRef(iEntity));
3358 CreateTimer(fLifeTime, KillParticle, EntIndexToEntRef(iEntity));
3359 }
3360 else
3361 {
3362 LogError("ShowParticle: could not create info_particle_system");
3363 }
3364}
3365
3366/* StopParticle()
3367**
3368** Turns of the particle system. Automatically called by PlayParticle
3369** -------------------------------------------------------------------------- */
3370public Action StopParticle(Handle hTimer, any iEntityRef)
3371{
3372 if (iEntityRef != INVALID_ENT_REFERENCE)
3373 {
3374 int iEntity = EntRefToEntIndex(iEntityRef);
3375 if (iEntity && IsValidEntity(iEntity))
3376 {
3377 AcceptEntityInput(iEntity, "Stop");
3378 }
3379 }
3380}
3381
3382/* KillParticle()
3383**
3384** Destroys the particle system. Automatically called by PlayParticle
3385** -------------------------------------------------------------------------- */
3386public Action KillParticle(Handle hTimer, any iEntityRef)
3387{
3388 if (iEntityRef != INVALID_ENT_REFERENCE)
3389 {
3390 int iEntity = EntRefToEntIndex(iEntityRef);
3391 if (iEntity && IsValidEntity(iEntity))
3392 {
3393 RemoveEdict(iEntity);
3394 }
3395 }
3396}
3397
3398/* PrecacheParticle()
3399**
3400** Forces the client to precache a particle system.
3401** -------------------------------------------------------------------------- */
3402stock void PrecacheParticle(char[] strParticleName)
3403{
3404 PlayParticle(view_as<float>( { 0.0, 0.0, 0.0 } ), view_as<float>( { 0.0, 0.0, 0.0 } ), strParticleName, 0.1, 0.1);
3405}
3406
3407/* FindEntityByClassnameSafe()
3408**
3409** Used to iterate through entity types, avoiding problems in cases where
3410** the entity may not exist anymore.
3411** -------------------------------------------------------------------------- */
3412stock void FindEntityByClassnameSafe(int iStart, const char[] strClassname)
3413{
3414 while (iStart > -1 && !IsValidEntity(iStart))
3415 {
3416 iStart--;
3417 }
3418 return FindEntityByClassname(iStart, strClassname);
3419}
3420
3421/* GetAnalogueTeam()
3422**
3423** Gets the analogue team for this. In case of Red, it's Blue, and viceversa.
3424** -------------------------------------------------------------------------- */
3425stock int GetAnalogueTeam(int iTeam)
3426{
3427 if (iTeam == view_as<int>(TFTeam_Red))return view_as<int>(TFTeam_Blue);
3428 return view_as<int>(TFTeam_Red);
3429}
3430
3431/* ShowHiddenMOTDPanel()
3432**
3433** Shows a hidden MOTD panel, useful for streaming music.
3434** -------------------------------------------------------------------------- */
3435stock void ShowHiddenMOTDPanel(int iClient, char[] strTitle, char[] strMsg, char[] strType = "2")
3436{
3437 Handle hPanel = CreateKeyValues("data");
3438 KvSetString(hPanel, "title", strTitle);
3439 KvSetString(hPanel, "type", strType);
3440 KvSetString(hPanel, "msg", strMsg);
3441 ShowVGUIPanel(iClient, "info", hPanel, false);
3442 CloseHandle(hPanel);
3443}
3444
3445/* PrecacheSoundEx()
3446**
3447** Precaches a sound and adds it to the download table.
3448** -------------------------------------------------------------------------- */
3449stock void PrecacheSoundEx(char[] strFileName, bool bPreload = false, bool bAddToDownloadTable = false)
3450{
3451 char strFinalPath[PLATFORM_MAX_PATH];
3452 Format(strFinalPath, sizeof(strFinalPath), "sound/%s", strFileName);
3453 PrecacheSound(strFileName, bPreload);
3454 if (bAddToDownloadTable == true)AddFileToDownloadsTable(strFinalPath);
3455}
3456
3457/* PrecacheModelEx()
3458**
3459** Precaches a models and adds it to the download table.
3460** -------------------------------------------------------------------------- */
3461stock void PrecacheModelEx(char[] strFileName, bool bPreload = false, bool bAddToDownloadTable = false)
3462{
3463 PrecacheModel(strFileName, bPreload);
3464 if (bAddToDownloadTable)
3465 {
3466 char strDepFileName[PLATFORM_MAX_PATH];
3467 Format(strDepFileName, sizeof(strDepFileName), "%s.res", strFileName);
3468
3469 if (FileExists(strDepFileName))
3470 {
3471 // Open stream, if possible
3472 Handle hStream = OpenFile(strDepFileName, "r");
3473 if (hStream == INVALID_HANDLE) { LogMessage("Error, can't read file containing model dependencies."); return; }
3474
3475 while (!IsEndOfFile(hStream))
3476 {
3477 char strBuffer[PLATFORM_MAX_PATH];
3478 ReadFileLine(hStream, strBuffer, sizeof(strBuffer));
3479 CleanString(strBuffer);
3480
3481 // If file exists...
3482 if (FileExists(strBuffer, true))
3483 {
3484 // Precache depending on type, and add to download table
3485 if (StrContains(strBuffer, ".vmt", false) != -1)PrecacheDecal(strBuffer, true);
3486 else if (StrContains(strBuffer, ".mdl", false) != -1)PrecacheModel(strBuffer, true);
3487 else if (StrContains(strBuffer, ".pcf", false) != -1)PrecacheGeneric(strBuffer, true);
3488 AddFileToDownloadsTable(strBuffer);
3489 }
3490 }
3491
3492 // Close file
3493 CloseHandle(hStream);
3494 }
3495 }
3496}
3497
3498/* CleanString()
3499**
3500** Cleans the given string from any illegal character.
3501** -------------------------------------------------------------------------- */
3502stock void CleanString(char[] strBuffer)
3503{
3504 // Cleanup any illegal characters
3505 int Length = strlen(strBuffer);
3506 for (int iPos = 0; iPos < Length; iPos++)
3507 {
3508 switch (strBuffer[iPos])
3509 {
3510 case '\r':strBuffer[iPos] = ' ';
3511 case '\n':strBuffer[iPos] = ' ';
3512 case '\t':strBuffer[iPos] = ' ';
3513 }
3514 }
3515
3516 // Trim string
3517 TrimString(strBuffer);
3518}
3519
3520/* FMax()
3521**
3522** Returns the maximum of the two values given.
3523** -------------------------------------------------------------------------- */
3524stock float FMax(float a, float b)
3525{
3526 return (a > b) ? a:b;
3527}
3528
3529/* FMin()
3530**
3531** Returns the minimum of the two values given.
3532** -------------------------------------------------------------------------- */
3533stock float FMin(float a, float b)
3534{
3535 return (a < b) ? a:b;
3536}
3537
3538/* GetURandomIntRange()
3539**
3540**
3541** -------------------------------------------------------------------------- */
3542stock int GetURandomIntRange(const int iMin, const int iMax)
3543{
3544 return iMin + (GetURandomInt() % (iMax - iMin + 1));
3545}
3546
3547/* GetURandomFloatRange()
3548**
3549**
3550** -------------------------------------------------------------------------- */
3551stock float GetURandomFloatRange(float fMin, float fMax)
3552{
3553 return fMin + (GetURandomFloat() * (fMax - fMin));
3554}
3555
3556// Pyro vision
3557public void tf2dodgeball_hooks(Handle convar, const char[] oldValue, const char[] newValue)
3558{
3559 if (GetConVarBool(g_hCvarPyroVisionEnabled))
3560 {
3561 for (int i = 1; i <= MaxClients; ++i)
3562 {
3563 if (IsClientInGame(i))
3564 {
3565 TF2Attrib_SetByName(i, PYROVISION_ATTRIBUTE, 1.0);
3566 }
3567 }
3568 }
3569 else
3570 {
3571 for (int i = 1; i <= MaxClients; ++i)
3572 {
3573 if (IsClientInGame(i))
3574 {
3575 TF2Attrib_RemoveByName(i, PYROVISION_ATTRIBUTE);
3576 }
3577 }
3578 }
3579 if(convar == g_hMaxBouncesConVar)
3580 g_config_iMaxBounces = StringToInt(newValue);
3581 if(convar == g_hCvarVoteRocketClassCommandEnabled)
3582 g_bVoteClassEnabled = view_as<bool>(StringToInt(newValue));
3583 if(convar == g_hCvarServerChatTag)
3584 strcopy(g_strServerChatTag, sizeof(g_strServerChatTag), newValue);
3585 if(convar == g_hCvarMainChatColor)
3586 strcopy(g_strMainChatColor, sizeof(g_strMainChatColor), newValue);
3587 if(convar == g_hCvarKeywordChatColor)
3588 strcopy(g_strKeywordChatColor, sizeof(g_strKeywordChatColor), newValue);
3589}
3590
3591// Asherkins RocketBounce
3592
3593public void OnEntityCreated(int entity, const char[] classname)
3594{
3595 if (!StrEqual(classname, "tf_projectile_rocket", false))
3596 return;
3597
3598 if (StrEqual(classname, "tf_projectile_rocket") || StrEqual(classname, "tf_projectile_sentryrocket"))
3599 {
3600 if (IsValidEntity(entity))
3601 {
3602 SetEntPropEnt(entity, Prop_Send, "m_hOriginalLauncher", entity);
3603 SetEntPropEnt(entity, Prop_Send, "m_hLauncher", entity);
3604 }
3605 }
3606
3607 g_nBounces[entity] = 0;
3608 SDKHook(entity, SDKHook_StartTouch, OnStartTouch);
3609}
3610
3611public Action OnStartTouch(int entity, int other)
3612{
3613 if (other > 0 && other <= MaxClients) {
3614 if(GetConVarBool(g_hTouchAnnounce)) {
3615 if (GetConVarBool(g_hCvarAnnounce)) {
3616 if (GetConVarBool(g_hCvarDeflectCountAnnounce)) {
3617 if (gDeflector == other || gDeflector == 0) {
3618 //CPrintToChatAll("%s %s%N %sdied to their rocket travelling %s%i %smph with %s%i %sdeflections!", g_strServerChatTag, g_strKeywordChatColor, g_iLastDeadClient, g_strMainChatColor, g_strKeywordChatColor, g_iRocketSpeed, g_strMainChatColor, g_strKeywordChatColor, iDeflections, g_strMainChatColor);
3619 CPrintToChatAll("{ORANGE}%N {DEFAULT}: Speed: {TURQUOISE}%i {DEFAULT}mph || deflections: {TURQUOISE}%i", other, g_hRocketSpeed, xDeflections);
3620 } else {
3621 CPrintToChatAll("%s %s%N %s- %s%.15N: Speed: %s%i %smph || deflections: %s%i", g_strServerChatTag, g_strMainChatColor, gDeflector, g_strClientChatColor, g_strMainChatColor, other, g_strKeywordChatColor, g_iRocketSpeed, g_strMainChatColor, g_strKeywordChatColor, xDeflections);
3622 //CPrintToChatAll("%s %s%N %sdied to %s%.15N's %srocket travelling %s%i %smph with %s%i %sdeflections!", g_strServerChatTag, g_strKeywordChatColor, g_iLastDeadClient, g_strMainChatColor, g_strKeywordChatColor, iTarget, g_strMainChatColor, g_strKeywordChatColor, g_iRocketSpeed, g_strMainChatColor, g_strKeywordChatColor, iDeflections, g_strMainChatColor);
3623 }
3624 } else {
3625 //CPrintToChatAll("%s %s%N %sdied to a rocket travelling %s%i %smph!", g_strServerChatTag, g_strKeywordChatColor, g_iLastDeadClient, g_strMainChatColor, g_strKeywordChatColor, g_iRocketSpeed, g_strMainChatColor);
3626 CPrintToChatAll("%s %s%N %s- %s%.15N's: Speed: %s%i %smph", g_strServerChatTag, g_strMainChatColor, gDeflector, g_strClientChatColor, other, g_strKeywordChatColor, g_iRocketSpeed, g_strMainChatColor);
3627 }
3628 }
3629 }
3630 return Plugin_Continue;
3631 }
3632
3633 // Only allow a rocket to bounce x times.
3634 if (g_nBounces[entity] >= g_config_iMaxBounces)
3635 return Plugin_Continue;
3636
3637
3638 IsOnTouch = true;
3639 SDKHook(entity, SDKHook_Touch, OnTouch);
3640 return Plugin_Handled;
3641}
3642
3643public Action OnTouch(int entity, int other)
3644{
3645 float vOrigin[3];
3646 GetEntPropVector(entity, Prop_Data, "m_vecOrigin", vOrigin);
3647
3648 float vAngles[3];
3649 GetEntPropVector(entity, Prop_Data, "m_angRotation", vAngles);
3650
3651 float vVelocity[3];
3652 GetEntPropVector(entity, Prop_Data, "m_vecAbsVelocity", vVelocity);
3653
3654 Handle trace = TR_TraceRayFilterEx(vOrigin, vAngles, MASK_SHOT, RayType_Infinite, TEF_ExcludeEntity, entity);
3655
3656 if (!TR_DidHit(trace))
3657 {
3658 CloseHandle(trace);
3659 return Plugin_Continue;
3660 }
3661
3662 float vNormal[3];
3663 TR_GetPlaneNormal(trace, vNormal);
3664
3665 //PrintToServer("Surface Normal: [%.2f, %.2f, %.2f]", vNormal[0], vNormal[1], vNormal[2]);
3666
3667 CloseHandle(trace);
3668
3669 float dotProduct = GetVectorDotProduct(vNormal, vVelocity);
3670
3671 ScaleVector(vNormal, dotProduct);
3672 ScaleVector(vNormal, 2.0);
3673
3674 float vBounceVec[3];
3675 SubtractVectors(vVelocity, vNormal, vBounceVec);
3676
3677 float vNewAngles[3];
3678 GetVectorAngles(vBounceVec, vNewAngles);
3679
3680 //PrintToServer("Angles: [%.2f, %.2f, %.2f] -> [%.2f, %.2f, %.2f]", vAngles[0], vAngles[1], vAngles[2], vNewAngles[0], vNewAngles[1], vNewAngles[2]);
3681 //PrintToServer("Velocity: [%.2f, %.2f, %.2f] |%.2f| -> [%.2f, %.2f, %.2f] |%.2f|", vVelocity[0], vVelocity[1], vVelocity[2], GetVectorLength(vVelocity), vBounceVec[0], vBounceVec[1], vBounceVec[2], GetVectorLength(vBounceVec));
3682 TeleportEntity(entity, NULL_VECTOR, vNewAngles, vBounceVec);
3683 g_nBounces[entity]++;
3684
3685 SDKUnhook(entity, SDKHook_Touch, OnTouch);
3686 return Plugin_Handled;
3687}
3688
3689public bool TEF_ExcludeEntity(int entity, int contentsMask, any data)
3690{
3691 return (entity != data);
3692}
3693
3694void preventAirblast(int clientId, bool prevent)
3695{
3696 int flags;
3697
3698 if (prevent == true)
3699 {
3700 abPrevention[clientId] = true;
3701 flags = GetEntityFlags(clientId) | FL_NOTARGET;
3702 }
3703 else
3704 {
3705 abPrevention[clientId] = false;
3706 flags = GetEntityFlags(clientId) & ~FL_NOTARGET;
3707 }
3708
3709 SetEntityFlags(clientId, flags);
3710}
3711
3712public Action TauntCheck(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom)
3713{
3714 switch (damagecustom)
3715 {
3716 case TF_CUSTOM_TAUNT_ARMAGEDDON:
3717 {
3718 damage = 0.0;
3719 return Plugin_Changed;
3720 }
3721
3722 }
3723 return Plugin_Continue;
3724}
3725
3726void checkStolenRocket(int clientId, int entId)
3727{
3728 if (EntRefToEntIndex(g_iRocketTarget[entId]) != clientId && !bStealArray[clientId][stoleRocket])
3729 {
3730 bStealArray[clientId][stoleRocket] = true;
3731 if (bStealArray[clientId][rocketsStolen] < GetConVarInt(g_hCvarStealPreventionNumber))
3732 {
3733 bStealArray[clientId][rocketsStolen]++;
3734 CreateTimer(0.1, tStealTimer, GetClientUserId(clientId), TIMER_FLAG_NO_MAPCHANGE);
3735 SlapPlayer(clientId, 0, true);
3736 CPrintToChat(clientId, "%s %sDo not steal rockets. [Warning %s%i%s/%s%i%s]", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, bStealArray[clientId][rocketsStolen], g_strMainChatColor, g_strKeywordChatColor, GetConVarInt(g_hCvarStealPreventionNumber), g_strMainChatColor);
3737 }
3738 else
3739 {
3740 ForcePlayerSuicide(clientId);
3741 CPrintToChat(clientId, "%s %sYou have been slain for stealing rockets.", g_strServerChatTag, g_strMainChatColor);
3742 CPrintToChatAll("%s %s%N %swas slain for stealing rockets.", g_strServerChatTag, g_strClientChatColor, clientId, g_strMainChatColor);
3743 }
3744 }
3745}
3746
3747void checkRoundDelays(int entId)
3748{
3749 int iEntity = EntRefToEntIndex(g_iRocketEntity[entId]);
3750 int iTarget = EntRefToEntIndex(g_iRocketTarget[entId]);
3751 float timeToCheck;
3752 if (g_iRocketDeflections[entId] == 0)
3753 timeToCheck = g_fLastSpawnTime;
3754 else
3755 timeToCheck = g_fRocketLastDeflectionTime[entId];
3756
3757 if (iTarget != INVALID_ENT_REFERENCE && (GetGameTime() - timeToCheck) >= GetConVarFloat(g_hCvarDelayPreventionTime))
3758 {
3759 g_fRocketSpeed[entId] += GetConVarFloat(g_hCvarDelayPreventionSpeedup);
3760 if (!g_bPreventingDelay)
3761 {
3762 CPrintToChatAll("%s %s%N %sis delaying, the rocket will now speed up.", g_strServerChatTag, g_strClientChatColor, iTarget, g_strMainChatColor);
3763 EmitSoundToAll(SOUND_DEFAULT_SPEEDUPALERT, iEntity, SNDCHAN_AUTO, SNDLEVEL_GUNFIRE);
3764 }
3765 g_bPreventingDelay = true;
3766 }
3767}
3768
3769/* SetMainRocketClass()
3770**
3771** Takes a specified rocket class name and outputs its respective index.
3772** -------------------------------------------------------------------------- */
3773stock int FindRocketClass(char[] class)
3774{
3775 for (int i = 0; i < g_iRocketClassCount; i++)
3776 {
3777 if (StrContains(g_strRocketClassLongName[i], class, false) != -1)
3778 {
3779 return i;
3780 }
3781 }
3782 return -1;
3783}
3784
3785/* SetMainRocketClass()
3786**
3787** Takes a specified rocket class index and sets it as the only rocket class able to spawn.
3788** -------------------------------------------------------------------------- */
3789stock void SetMainRocketClass(int Index, bool isVote, int client = 0)
3790{
3791 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
3792 char strBufferRed[256];
3793 strcopy(strBufferRed, sizeof(strBufferRed), "Red");
3794
3795 Format(strBufferRed, sizeof(strBufferRed), "%s%%", g_strRocketClassName[Index]);
3796 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassRed], Index, 100);
3797
3798 for (int iClassIndex = 0; iClassIndex < g_iRocketClassCount; iClassIndex++)
3799 {
3800 if (!(iClassIndex == Index))
3801 {
3802 Format(strBufferRed, sizeof(strBufferRed), "%s%%", g_strRocketClassName[iClassIndex]);
3803 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassRed], iClassIndex, 0);
3804 }
3805 }
3806
3807 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
3808 char strBufferBlue[256];
3809 strcopy(strBufferBlue, sizeof(strBufferBlue), "Blue");
3810
3811 Format(strBufferBlue, sizeof(strBufferBlue), "%s%%", g_strRocketClassName[Index]);
3812 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassBlu], Index, 100);
3813
3814 char strSelectionBlue[256];
3815 strcopy(strSelectionBlue, sizeof(strBufferBlue), strBufferBlue);
3816
3817 for (int iClassIndex = 0; iClassIndex < g_iRocketClassCount; iClassIndex++)
3818 {
3819 if (!(iClassIndex == Index))
3820 {
3821 Format(strBufferBlue, sizeof(strBufferBlue), "%s%%", g_strRocketClassName[iClassIndex]);
3822 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassBlu], iClassIndex, 0);
3823 }
3824 }
3825
3826 int iClass = GetRandomRocketClass(iSpawnerClassRed);
3827 strcopy(g_strSavedClassName, sizeof(g_strSavedClassName), g_strRocketClassLongName[iClass]);
3828
3829 if (isVote)
3830 CPrintToChatAll("%s %sVote %sfinished! %sRocket class changed to %s%s", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, g_strMainChatColor, g_strKeywordChatColor, g_strSavedClassName);
3831 else CPrintToChatAll("%s %s%N %schanged the rocket class to %s%s", g_strServerChatTag, g_strClientChatColor, client, g_strMainChatColor, g_strKeywordChatColor, g_strRocketClassLongName[iClass]);
3832}
3833
3834public Action tStealTimer(Handle hTimer, int iClientUid)
3835{
3836 int iClient = GetClientOfUserId(iClientUid);
3837 bStealArray[iClient][stoleRocket] = false;
3838}
3839
3840/*void AttachParticle(int iEntity, char[] strParticleType)
3841{
3842 int iParticle = CreateEntityByName("info_particle_system");
3843
3844 char strName[128];
3845 if (IsValidEdict(iParticle))
3846 {
3847 float fPos[3];
3848 GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fPos);
3849 fPos[2] += 10;
3850 TeleportEntity(iParticle, fPos, NULL_VECTOR, NULL_VECTOR);
3851
3852 Format(strName, sizeof(strName), "target%i", iEntity);
3853 DispatchKeyValue(iEntity, "targetname", strName);
3854
3855 DispatchKeyValue(iParticle, "targetname", "tf2particle");
3856 DispatchKeyValue(iParticle, "parentname", strName);
3857 DispatchKeyValue(iParticle, "effect_name", strParticleType);
3858 DispatchSpawn(iParticle);
3859 SetVariantString(strName);
3860 AcceptEntityInput(iParticle, "SetParent", iParticle, iParticle, 0);
3861 SetVariantString("");
3862 AcceptEntityInput(iParticle, "SetParentAttachment", iParticle, iParticle, 0);
3863 ActivateEntity(iParticle);
3864 AcceptEntityInput(iParticle, "start");
3865
3866 g_RocketParticle[iEntity] = iParticle;
3867 }
3868}*/
3869
3870stock void CreateTempParticle(char[] particle, int entity = -1, float origin[3] = NULL_VECTOR, float angles[3] = { 0.0, 0.0, 0.0 }, bool resetparticles = false)
3871{
3872 int tblidx = FindStringTable("ParticleEffectNames");
3873
3874 char tmp[256];
3875 int stridx = INVALID_STRING_INDEX;
3876
3877 for (int i = 0; i < GetStringTableNumStrings(tblidx); i++)
3878 {
3879 ReadStringTable(tblidx, i, tmp, sizeof(tmp));
3880 if (StrEqual(tmp, particle, false))
3881 {
3882 stridx = i;
3883 break;
3884 }
3885 }
3886
3887 TE_Start("TFParticleEffect");
3888 TE_WriteFloat("m_vecOrigin[0]", origin[0]);
3889 TE_WriteFloat("m_vecOrigin[1]", origin[1]);
3890 TE_WriteFloat("m_vecOrigin[2]", origin[2]);
3891 TE_WriteVector("m_vecAngles", angles);
3892 TE_WriteNum("m_iParticleSystemIndex", stridx);
3893 TE_WriteNum("entindex", entity);
3894 TE_WriteNum("m_iAttachType", 1);
3895 TE_WriteNum("m_bResetParticles", resetparticles);
3896 TE_SendToAll();
3897}
3898
3899stock void ClearTempParticles(int client)
3900{
3901 float empty[3];
3902 CreateTempParticle("sandwich_fx", client, empty, empty, true);
3903}
3904
3905/*void StolenRocket(int iClient, int iTarget)
3906{
3907 if (iTarget != iClient && GetClientTeam(iClient) == GetClientTeam(iTarget))
3908 {
3909 PrintToChatAll("\x03%N\x01 stole \x03%N\x01's rocket!", iClient, iTarget);
3910 g_stolen[iClient]++;
3911 if (g_stolen[iClient] >= GetConVarInt(g_hCvarStealPreventionNumber))
3912 {
3913 g_stolen[iClient] = 0;
3914 ForcePlayerSuicide(iClient);
3915 PrintToChat(iClient, "\x03You stole %d rockets and was slayed.", g_hCvarStealPreventionNumber);
3916 }
3917 }
3918}*/
3919
3920
3921// EOF