· 6 years ago · Jul 09, 2019, 06:56 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 <tf2attributes>
15#include <morecolors>
16
17#pragma newdecls required
18
19// *********************************************************************************
20// CONSTANTS
21// *********************************************************************************
22// ---- Plugin-related constants ---------------------------------------------------
23#define PLUGIN_NAME "[TF2] Yet Another Dodgeball Plugin (SVH Server)"
24#define PLUGIN_AUTHOR "Damizean, Edited by blood and soul"
25#define PLUGIN_VERSION "1.4.2_SVH"
26#define PLUGIN_CONTACT "https://savita-gaming.com"
27
28// ---- General settings -----------------------------------------------------------
29#define FPS_LOGIC_RATE 20.0
30#define FPS_LOGIC_INTERVAL 1.0 / FPS_LOGIC_RATE
31
32// ---- Maximum structure sizes ----------------------------------------------------
33#define MAX_ROCKETS 100
34#define MAX_ROCKET_CLASSES 50
35#define MAX_SPAWNER_CLASSES 50
36#define MAX_SPAWN_POINTS 100
37
38// ---- PyroVision Stuff -----------------------------------------------------------
39#define PYROVISION_ATTRIBUTE "vision opt in flags"
40
41#define USAGE "Usage: sm_ab [0/1]"
42int abPrevention[MAXPLAYERS + 1];
43int firstJoined[MAXPLAYERS + 1];
44
45// ---- Asherkin's RocketBounce Stuff ----------------------------------------------
46#define MAX_EDICT_BITS 11
47#define MAX_EDICTS (1 << MAX_EDICT_BITS)
48
49int g_nBounces[MAX_EDICTS];
50
51Handle g_hMaxBouncesConVar;
52int g_config_iMaxBounces = 2;
53
54// ---- Airblast -------------------------------------------------------------------
55bool Airblast[MAXPLAYERS + 1] = { true, ... };
56
57// Particlessss
58//int g_RocketParticle[MAXPLAYERS + 1];
59
60// ---- Flags and types constants --------------------------------------------------
61enum Musics
62{
63 Music_RoundStart,
64 Music_RoundWin,
65 Music_RoundLose,
66 Music_Gameplay,
67 SizeOfMusicsArray
68};
69
70enum BehaviourTypes
71{
72 Behaviour_Unknown,
73 Behaviour_Homing
74};
75
76enum RocketFlags
77{
78 RocketFlag_None = 0,
79 RocketFlag_PlaySpawnSound = 1 << 0,
80 RocketFlag_PlayBeepSound = 1 << 1,
81 RocketFlag_PlayAlertSound = 1 << 2,
82 RocketFlag_ElevateOnDeflect = 1 << 3,
83 RocketFlag_IsNeutral = 1 << 4,
84 RocketFlag_Exploded = 1 << 5,
85 RocketFlag_OnSpawnCmd = 1 << 6,
86 RocketFlag_OnDeflectCmd = 1 << 7,
87 RocketFlag_OnKillCmd = 1 << 8,
88 RocketFlag_OnExplodeCmd = 1 << 9,
89 RocketFlag_CustomModel = 1 << 10,
90 RocketFlag_CustomSpawnSound = 1 << 11,
91 RocketFlag_CustomBeepSound = 1 << 12,
92 RocketFlag_CustomAlertSound = 1 << 13,
93 RocketFlag_Elevating = 1 << 14,
94 RocketFlag_IsAnimated = 1 << 15
95};
96
97enum RocketSound
98{
99 RocketSound_Spawn,
100 RocketSound_Beep,
101 RocketSound_Alert
102};
103
104enum SpawnerFlags
105{
106 SpawnerFlag_Team_Red = 1,
107 SpawnerFlag_Team_Blu = 2,
108 SpawnerFlag_Team_Both = 3
109};
110
111#define TestFlags(%1,%2) (!!((%1) & (%2)))
112#define TestFlagsAnd(%1,%2) (((%1) & (%2)) == %2)
113
114// ---- Other resources ------------------------------------------------------------
115#define SOUND_DEFAULT_SPAWN "weapons/sentry_rocket.wav"
116#define SOUND_DEFAULT_BEEP "weapons/sentry_scan.wav"
117#define SOUND_DEFAULT_ALERT "weapons/sentry_spot.wav"
118#define SNDCHAN_MUSIC 32
119#define PARTICLE_NUKE_1 "fireSmokeExplosion"
120#define PARTICLE_NUKE_2 "fireSmokeExplosion1"
121#define PARTICLE_NUKE_3 "fireSmokeExplosion2"
122#define PARTICLE_NUKE_4 "fireSmokeExplosion3"
123#define PARTICLE_NUKE_5 "fireSmokeExplosion4"
124#define PARTICLE_NUKE_COLLUMN "fireSmoke_collumnP"
125#define PARTICLE_NUKE_1_ANGLES view_as<float> ({270.0, 0.0, 0.0})
126#define PARTICLE_NUKE_2_ANGLES PARTICLE_NUKE_1_ANGLES
127#define PARTICLE_NUKE_3_ANGLES PARTICLE_NUKE_1_ANGLES
128#define PARTICLE_NUKE_4_ANGLES PARTICLE_NUKE_1_ANGLES
129#define PARTICLE_NUKE_5_ANGLES PARTICLE_NUKE_1_ANGLES
130#define PARTICLE_NUKE_COLLUMN_ANGLES PARTICLE_NUKE_1_ANGLES
131
132// Debug
133//#define DEBUG
134
135// *********************************************************************************
136// VARIABLES
137// *********************************************************************************
138
139// -----<<< Cvars >>>-----
140Handle g_hCvarEnabled;
141Handle g_hCvarEnableCfgFile;
142Handle g_hCvarDisableCfgFile;
143Handle g_hCvarSpeedo;
144Handle g_hCvarAnnounce;
145Handle g_hCvarPyroVisionEnabled = INVALID_HANDLE;
146Handle g_hCvarAirBlastCommandEnabled;
147Handle g_hCvarDeflectCountAnnounce;
148Handle g_hCvarRedirectBeep;
149Handle g_hCvarPreventTauntKillEnabled;
150Handle g_hCvarStealPrevention;
151Handle g_hCvarStealPreventionNumber;
152
153// -----<<< Gameplay >>>-----
154//int g_stolen[MAXPLAYERS + 1];
155bool g_bEnabled; // Is the plugin enabled?
156bool g_bRoundStarted; // Has the round started?
157int g_iRoundCount; // Current round count since map start
158int g_iRocketsFired; // No. of rockets fired since round start
159Handle g_hLogicTimer; // Logic timer
160float g_fNextSpawnTime; // Time at wich the next rocket will be able to spawn
161int g_iLastDeadTeam; // The team of the last dead client. If none, it's a random team.
162int g_iLastDeadClient; // The last dead client. If none, it's a random client.
163int g_iPlayerCount;
164Handle g_hHud;
165int g_iRocketSpeed;
166Handle g_hTimerHud;
167
168enum eRocketSteal
169{
170 stoleRocket = false,
171 rocketsStolen
172};
173
174int bStealArray[MAXPLAYERS + 1][eRocketSteal];
175
176// -----<<< Configuration >>>-----
177bool g_bMusicEnabled;
178bool g_bMusic[view_as<int>(SizeOfMusicsArray)];
179char g_strMusic[view_as<int>(SizeOfMusicsArray)][PLATFORM_MAX_PATH];
180bool g_bUseWebPlayer;
181char g_strWebPlayerUrl[256];
182
183// -----<<< Structures >>>-----
184// Rockets
185bool g_bRocketIsValid[MAX_ROCKETS];
186bool g_bRocketIsNuke[MAX_ROCKETS];
187int g_iRocketEntity[MAX_ROCKETS];
188int g_iRocketTarget[MAX_ROCKETS];
189int g_iRocketSpawner[MAX_ROCKETS];
190int g_iRocketClass[MAX_ROCKETS];
191RocketFlags g_iRocketFlags[MAX_ROCKETS];
192float g_fRocketSpeed[MAX_ROCKETS];
193float g_fRocketDirection[MAX_ROCKETS][3];
194int g_iRocketDeflections[MAX_ROCKETS];
195float g_fRocketLastDeflectionTime[MAX_ROCKETS];
196float g_fRocketLastBeepTime[MAX_ROCKETS];
197int g_iLastCreatedRocket;
198int g_iRocketCount;
199float g_fSavedSpeed;
200float g_fSavedSpeedIncrement;
201
202// Classes
203char g_strRocketClassName[MAX_ROCKET_CLASSES][16];
204char g_strRocketClassLongName[MAX_ROCKET_CLASSES][32];
205char g_strSavedClassName[32];
206BehaviourTypes g_iRocketClassBehaviour[MAX_ROCKET_CLASSES];
207char g_strRocketClassModel[MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
208RocketFlags g_iRocketClassFlags[MAX_ROCKET_CLASSES];
209float g_fRocketClassBeepInterval[MAX_ROCKET_CLASSES];
210char g_strRocketClassSpawnSound[MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
211char g_strRocketClassBeepSound[MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
212char g_strRocketClassAlertSound[MAX_ROCKET_CLASSES][PLATFORM_MAX_PATH];
213float g_fRocketClassCritChance[MAX_ROCKET_CLASSES];
214float g_fRocketClassDamage[MAX_ROCKET_CLASSES];
215float g_fRocketClassDamageIncrement[MAX_ROCKET_CLASSES];
216float g_fRocketClassSpeed[MAX_ROCKET_CLASSES];
217float g_fRocketClassSpeedIncrement[MAX_ROCKET_CLASSES];
218float g_fRocketClassTurnRate[MAX_ROCKET_CLASSES];
219float g_fRocketClassTurnRateIncrement[MAX_ROCKET_CLASSES];
220float g_fRocketClassElevationRate[MAX_ROCKET_CLASSES];
221float g_fRocketClassElevationLimit[MAX_ROCKET_CLASSES];
222float g_fRocketClassRocketsModifier[MAX_ROCKET_CLASSES];
223float g_fRocketClassPlayerModifier[MAX_ROCKET_CLASSES];
224float g_fRocketClassControlDelay[MAX_ROCKET_CLASSES];
225float g_fRocketClassTargetWeight[MAX_ROCKET_CLASSES];
226Handle g_hRocketClassCmdsOnSpawn[MAX_ROCKET_CLASSES];
227Handle g_hRocketClassCmdsOnDeflect[MAX_ROCKET_CLASSES];
228Handle g_hRocketClassCmdsOnKill[MAX_ROCKET_CLASSES];
229Handle g_hRocketClassCmdsOnExplode[MAX_ROCKET_CLASSES];
230Handle g_hRocketClassTrie;
231char g_iRocketClassCount;
232
233// Spawner classes
234char g_strSpawnersName[MAX_SPAWNER_CLASSES][32];
235int g_iSpawnersMaxRockets[MAX_SPAWNER_CLASSES];
236float g_fSpawnersInterval[MAX_SPAWNER_CLASSES];
237Handle g_hSpawnersChancesTable[MAX_SPAWNER_CLASSES];
238Handle g_hSpawnersTrie;
239int g_iSpawnersCount;
240
241// Array containing the spawn points for the Red team, and
242// their associated spawner class.
243int g_iCurrentRedSpawn;
244int g_iSpawnPointsRedCount;
245int g_iSpawnPointsRedClass[MAX_SPAWN_POINTS];
246int g_iSpawnPointsRedEntity[MAX_SPAWN_POINTS];
247
248// Array containing the spawn points for the Blu team, and
249// their associated spawner class.
250int g_iCurrentBluSpawn;
251int g_iSpawnPointsBluCount;
252int g_iSpawnPointsBluClass[MAX_SPAWN_POINTS];
253int g_iSpawnPointsBluEntity[MAX_SPAWN_POINTS];
254
255// The default spawner class.
256int g_iDefaultRedSpawner;
257int g_iDefaultBluSpawner;
258
259//Observer
260int g_observer;
261int g_op_rocket;
262
263// *********************************************************************************
264// PLUGIN
265// *********************************************************************************
266public Plugin myinfo =
267{
268 name = PLUGIN_NAME,
269 author = PLUGIN_AUTHOR,
270 description = PLUGIN_NAME,
271 version = PLUGIN_VERSION,
272 url = PLUGIN_CONTACT
273};
274
275// *********************************************************************************
276// METHODS
277// *********************************************************************************
278
279/* OnPluginStart()
280**
281** When the plugin is loaded.
282** -------------------------------------------------------------------------- */
283public void OnPluginStart()
284{
285 char strModName[32]; GetGameFolderName(strModName, sizeof(strModName));
286 if (!StrEqual(strModName, "tf"))SetFailState("This plugin is only for Team Fortress 2.");
287
288 CreateConVar("tf_dodgeballupdated_version", PLUGIN_VERSION, PLUGIN_NAME, FCVAR_SPONLY | FCVAR_UNLOGGED | FCVAR_DONTRECORD | FCVAR_REPLICATED | FCVAR_NOTIFY);
289 g_hCvarEnabled = CreateConVar("tf_dodgeball_enabled", "1", "Enable Dodgeball on TFDB maps?", _, true, 0.0, true, 1.0);
290 g_hCvarEnableCfgFile = CreateConVar("tf_dodgeball_enablecfg", "sourcemod/dodgeball_enable.cfg", "Config file to execute when enabling the Dodgeball game mode.");
291 g_hCvarDisableCfgFile = CreateConVar("tf_dodgeball_disablecfg", "sourcemod/dodgeball_disable.cfg", "Config file to execute when disabling the Dodgeball game mode.");
292 g_hCvarSpeedo = CreateConVar("tf_dodgeball_speedo", "1", "Enable HUD speedometer");
293 g_hCvarAnnounce = CreateConVar("tf_dodgeball_announce", "1", "Enable kill announces in chat");
294 g_hCvarPyroVisionEnabled = CreateConVar("tf_dodgeball_pyrovision", "0", "Enable pyrovision for everyone");
295 g_hMaxBouncesConVar = CreateConVar("tf_dodgeball_rbmax", "10000", "Max number of times a rocket will bounce.", FCVAR_NONE, true, 0.0, false);
296 g_hCvarAirBlastCommandEnabled = CreateConVar("tf_dodgeball_airblast", "1", "Enable if airblast is enabled or not");
297 g_hCvarDeflectCountAnnounce = CreateConVar("tf_dodgeball_count_deflect", "1", "Enable number of deflections in kill announce");
298 g_hCvarRedirectBeep = CreateConVar("tf_dodgeball_rdrbeep", "1", "Do redirects beep?");
299 g_hCvarPreventTauntKillEnabled = CreateConVar("tf_dodgeball_block_tauntkill", "0", "Block taunt kills?");
300 g_hCvarStealPrevention = CreateConVar("tf_dodgeball_steal_prevention", "0", "Enable steal prevention?");
301 g_hCvarStealPreventionNumber = CreateConVar("tf_dodgeball_sp_number", "3", "How many steals before you get slayed?");
302
303 // Commands
304 RegConsoleCmd("sm_ab", Command_ToggleAirblast, USAGE);
305 RegAdminCmd("sm_tfdb", Command_DodgeballAdminMenu, ADMFLAG_GENERIC, "A menu for admins to modify things inside the plugin.");
306 RegConsoleCmd("sm_currentrocket", Command_PostCurrentRocketClass, "Posts a chat message of the name of the current main rocket class.");
307
308 ServerCommand("tf_arena_use_queue 0");
309
310 HookConVarChange(g_hMaxBouncesConVar, tf2dodgeball_hooks);
311 HookConVarChange(g_hCvarPyroVisionEnabled, tf2dodgeball_hooks);
312
313 g_hRocketClassTrie = CreateTrie();
314 g_hSpawnersTrie = CreateTrie();
315
316 g_hHud = CreateHudSynchronizer();
317
318 AutoExecConfig(true, "tf2_dodgeball");
319
320 RegisterCommands();
321}
322
323/* OnConfigsExecuted()
324**
325** When all the configuration files have been executed, try to enable the
326** Dodgeball.
327** -------------------------------------------------------------------------- */
328public void OnConfigsExecuted()
329{
330 if (GetConVarBool(g_hCvarEnabled) && IsDodgeBallMap())
331 {
332 EnableDodgeBall();
333 }
334}
335
336/* OnMapEnd()
337**
338** When the map ends, disable DodgeBall.
339** -------------------------------------------------------------------------- */
340public void OnMapEnd()
341{
342 DisableDodgeBall();
343}
344
345public Action Command_DodgeballAdminMenu(int client, int args)
346{
347 Menu menu = new Menu(DodgeballAdmin_Handler, MENU_ACTIONS_ALL);
348
349 menu.SetTitle("Dodgeball Admin Menu");
350
351 menu.AddItem("0", "Max Rocket Count");
352 menu.AddItem("1", "Speed Multiplier");
353 menu.AddItem("2", "Main Rocket Class");
354
355 menu.ExitButton = true;
356 menu.Display(client, MENU_TIME_FOREVER);
357
358 return Plugin_Handled;
359}
360
361public int DodgeballAdmin_Handler(Menu menu, MenuAction action, int param1, int param2)
362{
363 switch (action)
364 {
365 case MenuAction_Start:
366 {
367 // 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.
368 PrintToServer("Displaying menu"); // Log it
369 }
370
371 case MenuAction_Display:
372 {
373 PrintToServer("Client %d was sent menu with panel %x", param1, param2); // Log so you can check if it gets sent.
374 }
375
376 case MenuAction_Select:
377 {
378 char sInfo[32];
379 menu.GetItem(param2, sInfo, sizeof(sInfo));
380
381 switch (param2)
382 {
383 case 0:
384 {
385 DrawMaxRocketCountMenu(param1);
386 }
387 case 1:
388 {
389 DrawRocketSpeedMenu(param1);
390 }
391 case 2:
392 {
393 if (!g_strSavedClassName[0]) {
394 CPrintToChat(param1, "No main class detected, aborting...");
395 return;
396 }
397 DrawRocketClassMenu(param1);
398 }
399 }
400 }
401
402 case MenuAction_Cancel:
403 {
404 PrintToServer("Client %d's menu was cancelled for reason %d", param1, param2); // Logging once again.
405 }
406
407 case MenuAction_End:
408 {
409 delete menu;
410 }
411
412 case MenuAction_DrawItem:
413 {
414 int style;
415 char info[32];
416 menu.GetItem(param2, info, sizeof(info), style);
417 }
418
419 case MenuAction_DisplayItem:
420 {
421 char info[32];
422 menu.GetItem(param2, info, sizeof(info));
423 }
424 }
425}
426
427void DrawMaxRocketCountMenu(int client)
428{
429 Menu menu = new Menu(DodgeballAdminRocketCount_Handler, MENU_ACTIONS_ALL);
430
431 menu.SetTitle("How many rockets?");
432
433 menu.AddItem("1", "One");
434 menu.AddItem("2", "Two");
435 menu.AddItem("3", "Three");
436 menu.AddItem("4", "Four");
437 menu.AddItem("5", "Five");
438
439 menu.ExitButton = true;
440 menu.Display(client, MENU_TIME_FOREVER);
441}
442
443void DrawRocketSpeedMenu(int client)
444{
445 Menu menu = new Menu(DodgeballAdminRocketSpeed_Handler, MENU_ACTIONS_ALL);
446
447 menu.SetTitle("How fast should the rockets go?");
448
449 menu.AddItem("1", "25% (Slow)");
450 menu.AddItem("2", "50% (Normal)");
451 menu.AddItem("3", "75% (Fast)");
452 menu.AddItem("4", "100% (Silly Fast)");
453
454 menu.ExitButton = true;
455 menu.Display(client, MENU_TIME_FOREVER);
456}
457
458void DrawRocketClassMenu(int client)
459{
460 Menu menu = new Menu(DodgeballAdminRocketClass_Handler, MENU_ACTIONS_ALL);
461
462 menu.SetTitle("Which class should the rocket be set to?");
463
464 for (int currentClass = 0; currentClass < g_iRocketClassCount; currentClass++)
465 {
466 char classNumber[16];
467 IntToString(currentClass, classNumber, sizeof(classNumber));
468 if (StrEqual(g_strSavedClassName, g_strRocketClassLongName[currentClass]))
469 {
470 char currentClassName[32];
471 strcopy(currentClassName, sizeof(currentClassName), "[Current] ");
472 StrCat(currentClassName, sizeof(currentClassName), g_strSavedClassName);
473 menu.AddItem(classNumber, currentClassName);
474 }
475 else menu.AddItem(classNumber, g_strRocketClassLongName[currentClass]);
476 }
477
478 menu.ExitButton = true;
479 menu.Display(client, MENU_TIME_FOREVER);
480}
481
482public int DodgeballAdminRocketCount_Handler(Menu menu, MenuAction action, int param1, int param2)
483{
484 switch (action)
485 {
486 case MenuAction_Start:
487 {
488 // 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.
489 PrintToServer("Displaying menu"); // Log it
490 }
491
492 case MenuAction_Display:
493 {
494 PrintToServer("Client %d was sent menu with panel %x", param1, param2); // Log so you can check if it gets sent.
495 }
496
497 case MenuAction_Select:
498 {
499 char sInfo[32];
500 menu.GetItem(param2, sInfo, sizeof(sInfo));
501
502 switch (param2)
503 {
504 case 0:
505 {
506 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
507 g_iSpawnersMaxRockets[iSpawnerClassBlu] = 1;
508
509 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
510 g_iSpawnersMaxRockets[iSpawnerClassRed] = 1;
511
512 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}changed the max rockets to {TURQUOISE}1.", param1);
513 }
514 case 1:
515 {
516 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
517 g_iSpawnersMaxRockets[iSpawnerClassBlu] = 2;
518
519 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
520 g_iSpawnersMaxRockets[iSpawnerClassRed] = 2;
521
522 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}changed the max rockets to {TURQUOISE}2.", param1);
523 }
524 case 2:
525 {
526 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
527 g_iSpawnersMaxRockets[iSpawnerClassBlu] = 3;
528
529 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
530 g_iSpawnersMaxRockets[iSpawnerClassRed] = 3;
531
532 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}changed the max rockets to {TURQUOISE}3.", param1);
533 }
534 case 3:
535 {
536 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
537 g_iSpawnersMaxRockets[iSpawnerClassBlu] = 4;
538
539 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
540 g_iSpawnersMaxRockets[iSpawnerClassRed] = 4;
541
542 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}changed the max rockets to {TURQUOISE}4.", param1);
543 }
544 case 4:
545 {
546 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
547 g_iSpawnersMaxRockets[iSpawnerClassBlu] = 5;
548
549 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
550 g_iSpawnersMaxRockets[iSpawnerClassRed] = 5;
551
552 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}changed the max rockets to {TURQUOISE}5.", param1);
553 }
554 }
555 }
556
557 case MenuAction_Cancel:
558 {
559 PrintToServer("Client %d's menu was cancelled for reason %d", param1, param2); // Logging once again.
560 }
561
562 case MenuAction_End:
563 {
564 delete menu;
565 }
566
567 case MenuAction_DrawItem:
568 {
569 int style;
570 char info[32];
571 menu.GetItem(param2, info, sizeof(info), style);
572 }
573
574 case MenuAction_DisplayItem:
575 {
576 char info[32];
577 menu.GetItem(param2, info, sizeof(info));
578 }
579 }
580}
581
582public int DodgeballAdminRocketSpeed_Handler(Menu menu, MenuAction action, int param1, int param2)
583{
584 switch (action)
585 {
586 case MenuAction_Start:
587 {
588 // 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.
589 PrintToServer("Displaying menu"); // Log it
590 }
591
592 case MenuAction_Display:
593 {
594 PrintToServer("Client %d was sent menu with panel %x", param1, param2); // Log so you can check if it gets sent.
595 }
596
597 case MenuAction_Select:
598 {
599 char sInfo[32];
600 menu.GetItem(param2, sInfo, sizeof(sInfo));
601 float kvSpeed = g_fSavedSpeed;
602 float kvSpeedIncrement = g_fSavedSpeedIncrement;
603
604 switch (param2)
605 {
606 case 0:
607 {
608 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
609 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
610 int iClassRed = GetRandomRocketClass(iSpawnerClassRed);
611 int iClassBlu = GetRandomRocketClass(iSpawnerClassBlu);
612
613 g_fRocketSpeed[iClassRed] = kvSpeed / 2;
614 g_fRocketClassSpeedIncrement[iClassRed] = kvSpeedIncrement / 2;
615
616 g_fRocketSpeed[iClassBlu] = kvSpeed / 2;
617 g_fRocketClassSpeedIncrement[iClassBlu] = kvSpeedIncrement / 2;
618
619 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}changed the rocket speed to {TURQUOISE}25%% (Slow)", param1);
620 }
621 case 1:
622 {
623 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
624 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
625 int iClassRed = GetRandomRocketClass(iSpawnerClassRed);
626 int iClassBlu = GetRandomRocketClass(iSpawnerClassBlu);
627
628 g_fRocketSpeed[iClassRed] = kvSpeed;
629 g_fRocketClassSpeedIncrement[iClassRed] = kvSpeedIncrement;
630
631 g_fRocketSpeed[iClassBlu] = kvSpeed;
632 g_fRocketClassSpeedIncrement[iClassBlu] = kvSpeedIncrement;
633
634 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}changed the rocket speed to {TURQUOISE}50%% (Normal)", param1);
635 }
636 case 2:
637 {
638 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
639 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
640 int iClassRed = GetRandomRocketClass(iSpawnerClassRed);
641 int iClassBlu = GetRandomRocketClass(iSpawnerClassBlu);
642
643 g_fRocketSpeed[iClassRed] = kvSpeed * 2;
644 g_fRocketClassSpeedIncrement[iClassRed] = kvSpeedIncrement * 2;
645
646 g_fRocketSpeed[iClassBlu] = kvSpeed * 2;
647 g_fRocketClassSpeedIncrement[iClassBlu] = kvSpeedIncrement * 2;
648
649 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}changed the rocket speed to {TURQUOISE}75%% (Fast)", param1);
650 }
651 case 3:
652 {
653 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
654 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
655 int iClassRed = GetRandomRocketClass(iSpawnerClassRed);
656 int iClassBlu = GetRandomRocketClass(iSpawnerClassBlu);
657
658 g_fRocketSpeed[iClassRed] = kvSpeed * 3;
659 g_fRocketClassSpeedIncrement[iClassRed] = kvSpeedIncrement * 3;
660
661 g_fRocketSpeed[iClassBlu] = kvSpeed * 3;
662 g_fRocketClassSpeedIncrement[iClassBlu] = kvSpeedIncrement * 3;
663
664 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}changed the rocket speed to {TURQUOISE}100%% (Silly Fast)", param1);
665 }
666 }
667 }
668
669 case MenuAction_Cancel:
670 {
671 PrintToServer("Client %d's menu was cancelled for reason %d", param1, param2); // Logging once again.
672 }
673
674 case MenuAction_End:
675 {
676 delete menu;
677 }
678
679 case MenuAction_DrawItem:
680 {
681 int style;
682 char info[32];
683 menu.GetItem(param2, info, sizeof(info), style);
684 }
685
686 case MenuAction_DisplayItem:
687 {
688 char info[32];
689 menu.GetItem(param2, info, sizeof(info));
690 }
691 }
692}
693
694public int DodgeballAdminRocketClass_Handler(Menu menu, MenuAction action, int param1, int param2)
695{
696 switch (action)
697 {
698 case MenuAction_Start:
699 {
700 // 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.
701 PrintToServer("Displaying menu"); // Log it
702 }
703
704 case MenuAction_Display:
705 {
706 PrintToServer("Client %d was sent menu with panel %x", param1, param2); // Log so you can check if it gets sent.
707 }
708
709 case MenuAction_Select:
710 {
711 char sInfo[32];
712 menu.GetItem(param2, sInfo, sizeof(sInfo));
713
714 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
715 char strBufferRed[256];
716 strcopy(strBufferRed, sizeof(strBufferRed), "Red");
717
718 Format(strBufferRed, sizeof(strBufferRed), "%s%%", g_strRocketClassName[param2]);
719 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassRed], param2, 100);
720
721 for (int iClassIndex = 0; iClassIndex < g_iRocketClassCount; iClassIndex++)
722 {
723 if (!(iClassIndex == param2))
724 {
725 Format(strBufferRed, sizeof(strBufferRed), "%s%%", g_strRocketClassName[iClassIndex]);
726 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassRed], iClassIndex, 0);
727 }
728 }
729
730 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
731 char strBufferBlue[256];
732 strcopy(strBufferBlue, sizeof(strBufferBlue), "Blue");
733
734 Format(strBufferBlue, sizeof(strBufferBlue), "%s%%", g_strRocketClassName[param2]);
735 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassBlu], param2, 100);
736
737 char strSelectionBlue[256];
738 strcopy(strSelectionBlue, sizeof(strBufferBlue), strBufferBlue);
739
740 for (int iClassIndex = 0; iClassIndex < g_iRocketClassCount; iClassIndex++)
741 {
742 if (!(iClassIndex == param2))
743 {
744 Format(strBufferBlue, sizeof(strBufferBlue), "%s%%", g_strRocketClassName[iClassIndex]);
745 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassBlu], iClassIndex, 0);
746 }
747 }
748
749 int iClass = GetRandomRocketClass(iSpawnerClassRed);
750 strcopy(g_strSavedClassName, sizeof(g_strSavedClassName), g_strRocketClassLongName[iClass]);
751
752 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}changed the rocket class to {TURQUOISE}%s.", param1, g_strSavedClassName);
753 }
754
755 case MenuAction_Cancel:
756 {
757 PrintToServer("Client %d's menu was cancelled for reason %d", param1, param2); // Logging once again.
758 }
759
760 case MenuAction_End:
761 {
762 delete menu;
763 }
764
765 case MenuAction_DrawItem:
766 {
767 int style;
768 char info[32];
769 menu.GetItem(param2, info, sizeof(info), style);
770 }
771
772 case MenuAction_DisplayItem:
773 {
774 char info[32];
775 menu.GetItem(param2, info, sizeof(info));
776 }
777 }
778}
779
780/*
781**����������������������������������������������������������������������������������
782** __ ___ __
783** / |/ /___ _____ ____ _____ ____ ____ ___ ___ ____ / /_
784** / /|_/ / __ `/ __ \/ __ `/ __ `/ _ \/ __ `__ \/ _ \/ __ \/ __/
785** / / / / /_/ / / / / /_/ / /_/ / __/ / / / / / __/ / / / /_
786** /_/ /_/\__,_/_/ /_/\__,_/\__, /\___/_/ /_/ /_/\___/_/ /_/\__/
787** /____/
788**����������������������������������������������������������������������������������
789*/
790
791// ___ _
792// / __|___ _ _ ___ _ _ __ _| |
793// | (_ / -_) ' \/ -_) '_/ _` | |
794// \___\___|_||_\___|_| \__,_|_|
795
796/* IsDodgeBallMap()
797**
798** Checks if the current map is a dodgeball map.
799** -------------------------------------------------------------------------- */
800bool IsDodgeBallMap()
801{
802 char strMap[64];
803 GetCurrentMap(strMap, sizeof(strMap));
804 return StrContains(strMap, "tfdb_", false) == 0;
805}
806
807/* EnableDodgeBall()
808**
809** Enables and hooks all the required events.
810** -------------------------------------------------------------------------- */
811void EnableDodgeBall()
812{
813 if (g_bEnabled == false)
814 {
815 // Parse configuration files
816 char strMapName[64]; GetCurrentMap(strMapName, sizeof(strMapName));
817 char strMapFile[PLATFORM_MAX_PATH]; Format(strMapFile, sizeof(strMapFile), "%s.cfg", strMapName);
818 ParseConfigurations();
819 ParseConfigurations(strMapFile);
820
821 ServerCommand("tf_dodgeball_rbmax %f", GetConVarFloat(g_hMaxBouncesConVar));
822
823 // Check if we have all the required information
824 if (g_iRocketClassCount == 0)
825 SetFailState("No rocket class defined.");
826
827 if (g_iSpawnersCount == 0)
828 SetFailState("No spawner class defined.");
829
830 if (g_iDefaultRedSpawner == -1)
831 SetFailState("No spawner class definition for the Red spawners exists in the config file.");
832
833 if (g_iDefaultBluSpawner == -1)
834 SetFailState("No spawner class definition for the Blu spawners exists in the config file.");
835
836 // Hook events and info_target outputs.
837 HookEvent("object_deflected", Event_ObjectDeflected);
838 HookEvent("teamplay_round_start", OnRoundStart, EventHookMode_PostNoCopy);
839 HookEvent("teamplay_setup_finished", OnSetupFinished, EventHookMode_PostNoCopy);
840 HookEvent("teamplay_round_win", OnRoundEnd, EventHookMode_PostNoCopy);
841 HookEvent("player_spawn", OnPlayerSpawn, EventHookMode_Post);
842 HookEvent("player_death", OnPlayerDeath, EventHookMode_Pre);
843 HookEvent("post_inventory_application", OnPlayerInventory, EventHookMode_Post);
844 HookEvent("teamplay_broadcast_audio", OnBroadcastAudio, EventHookMode_Pre);
845
846
847
848 // Precache sounds
849 PrecacheSound(SOUND_DEFAULT_SPAWN, true);
850 PrecacheSound(SOUND_DEFAULT_BEEP, true);
851 PrecacheSound(SOUND_DEFAULT_ALERT, true);
852 if (g_bMusicEnabled == true)
853 {
854 if (g_bMusic[Music_RoundStart])PrecacheSoundEx(g_strMusic[Music_RoundStart], true, true);
855 if (g_bMusic[Music_RoundWin])PrecacheSoundEx(g_strMusic[Music_RoundWin], true, true);
856 if (g_bMusic[Music_RoundLose])PrecacheSoundEx(g_strMusic[Music_RoundLose], true, true);
857 if (g_bMusic[Music_Gameplay])PrecacheSoundEx(g_strMusic[Music_Gameplay], true, true);
858 }
859
860 // Precache particles
861 PrecacheParticle(PARTICLE_NUKE_1);
862 PrecacheParticle(PARTICLE_NUKE_2);
863 PrecacheParticle(PARTICLE_NUKE_3);
864 PrecacheParticle(PARTICLE_NUKE_4);
865 PrecacheParticle(PARTICLE_NUKE_5);
866 PrecacheParticle(PARTICLE_NUKE_COLLUMN);
867
868 // Precache rocket resources
869 for (int i = 0; i < g_iRocketClassCount; i++)
870 {
871 RocketFlags iFlags = g_iRocketClassFlags[i];
872 if (TestFlags(iFlags, RocketFlag_CustomModel))PrecacheModelEx(g_strRocketClassModel[i], true, true);
873 if (TestFlags(iFlags, RocketFlag_CustomSpawnSound))PrecacheSoundEx(g_strRocketClassSpawnSound[i], true, true);
874 if (TestFlags(iFlags, RocketFlag_CustomBeepSound))PrecacheSoundEx(g_strRocketClassBeepSound[i], true, true);
875 if (TestFlags(iFlags, RocketFlag_CustomAlertSound))PrecacheSoundEx(g_strRocketClassAlertSound[i], true, true);
876 }
877
878 // Execute enable config file
879 char strCfgFile[64]; GetConVarString(g_hCvarEnableCfgFile, strCfgFile, sizeof(strCfgFile));
880 ServerCommand("exec \"%s\"", strCfgFile);
881
882 // Done.
883 g_bEnabled = true;
884 g_bRoundStarted = false;
885 g_iRoundCount = 0;
886 }
887}
888
889/* DisableDodgeBall()
890**
891** Disables all hooks and frees arrays.
892** -------------------------------------------------------------------------- */
893void DisableDodgeBall()
894{
895 if (g_bEnabled == true)
896 {
897 // Clean up everything
898 DestroyRockets();
899 DestroyRocketClasses();
900 DestroySpawners();
901 if (g_hLogicTimer != INVALID_HANDLE)KillTimer(g_hLogicTimer);
902 g_hLogicTimer = INVALID_HANDLE;
903
904 // Disable music
905 g_bMusic[Music_RoundStart] =
906 g_bMusic[Music_RoundWin] =
907 g_bMusic[Music_RoundLose] =
908 g_bMusic[Music_Gameplay] = false;
909
910 // Unhook events and info_target outputs;
911 UnhookEvent("teamplay_round_start", OnRoundStart, EventHookMode_PostNoCopy);
912 UnhookEvent("teamplay_setup_finished", OnSetupFinished, EventHookMode_PostNoCopy);
913 UnhookEvent("teamplay_round_win", OnRoundEnd, EventHookMode_PostNoCopy);
914 UnhookEvent("player_spawn", OnPlayerSpawn, EventHookMode_Post);
915 UnhookEvent("player_death", OnPlayerDeath, EventHookMode_Pre);
916 UnhookEvent("post_inventory_application", OnPlayerInventory, EventHookMode_Post);
917 UnhookEvent("teamplay_broadcast_audio", OnBroadcastAudio, EventHookMode_Pre);
918
919 // Execute enable config file
920 char strCfgFile[64]; GetConVarString(g_hCvarDisableCfgFile, strCfgFile, sizeof(strCfgFile));
921 ServerCommand("exec \"%s\"", strCfgFile);
922
923 // Done.
924 g_bEnabled = false;
925 g_bRoundStarted = false;
926 g_iRoundCount = 0;
927 }
928}
929
930
931public void OnClientPutInServer(int clientId)
932{
933 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
934 {
935 firstJoined[clientId] = true;
936 }
937 if (GetConVarBool(g_hCvarPreventTauntKillEnabled))
938 {
939 SDKHook(clientId, SDKHook_OnTakeDamage, TauntCheck);
940 }
941}
942
943
944public void OnClientDisconnect(int client)
945{
946 if (GetConVarBool(g_hCvarPreventTauntKillEnabled))
947 {
948 SDKUnhook(client, SDKHook_OnTakeDamage, TauntCheck);
949 }
950
951 if (GetConVarBool(g_hCvarStealPrevention))
952 {
953 bStealArray[client][stoleRocket] = false;
954 bStealArray[client][rocketsStolen] = 0;
955 }
956}
957
958/* OnObjectDeflected
959**
960**
961** Check if client is human, don't airblast if bool is false
962** -------------------------------------------------------------------------- */
963public Action Event_ObjectDeflected(Handle event, const char[] name, bool dontBroadcast)
964{
965 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
966 {
967 int object1 = GetEventInt(event, "object_entindex");
968 if ((object1 >= 1) && (object1 <= MaxClients))
969 {
970 if (Airblast[object1])
971 {
972 float Vel[3];
973 TeleportEntity(object1, NULL_VECTOR, NULL_VECTOR, Vel); // Stops knockback
974 TF2_RemoveCondition(object1, TFCond_Dazed); // Stops slowdown
975 SetEntPropVector(object1, Prop_Send, "m_vecPunchAngle", Vel);
976 SetEntPropVector(object1, Prop_Send, "m_vecPunchAngleVel", Vel); // Stops screen shake
977 }
978 }
979 }
980}
981
982
983// ___ _
984// / __|__ _ _ __ ___ _ __| |__ _ _ _
985// | (_ / _` | ' \/ -_) '_ \ / _` | || |
986// \___\__,_|_|_|_\___| .__/_\__,_|\_, |
987// |_| |__/
988
989/* OnRoundStart()
990**
991** At round start, do something?
992** -------------------------------------------------------------------------- */
993public Action OnRoundStart(Handle hEvent, char[] strEventName, bool bDontBroadcast)
994{
995 if (GetConVarBool(g_hCvarStealPrevention))
996 {
997 for (int i = 0; i <= MaxClients; i++)
998 {
999 bStealArray[i][stoleRocket] = false;
1000 bStealArray[i][rocketsStolen] = 0;
1001 }
1002 }
1003
1004 if (g_bMusic[Music_RoundStart])
1005 {
1006 EmitSoundToAll(g_strMusic[Music_RoundStart]);
1007 }
1008 g_iRocketSpeed = 0;
1009 if (g_hTimerHud != INVALID_HANDLE)
1010 {
1011 KillTimer(g_hTimerHud);
1012 g_hTimerHud = INVALID_HANDLE;
1013 }
1014 g_hTimerHud = CreateTimer(1.0, Timer_HudSpeed, _, TIMER_REPEAT);
1015}
1016
1017/* OnSetupFinished()
1018**
1019** When the setup finishes, populate the spawn points arrays and create the
1020** Dodgeball game logic timer.
1021** -------------------------------------------------------------------------- */
1022public Action OnSetupFinished(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1023{
1024 if ((g_bEnabled == true) && (BothTeamsPlaying() == true))
1025 {
1026 PopulateSpawnPoints();
1027
1028 if (g_iLastDeadTeam == 0)
1029 {
1030 g_iLastDeadTeam = GetURandomIntRange(view_as<int>(TFTeam_Red), view_as<int>(TFTeam_Blue));
1031 }
1032 if (!IsValidClient(g_iLastDeadClient))g_iLastDeadClient = 0;
1033
1034 g_hLogicTimer = CreateTimer(FPS_LOGIC_INTERVAL, OnDodgeBallGameFrame, _, TIMER_REPEAT);
1035 g_iPlayerCount = CountAlivePlayers();
1036 g_iRocketsFired = 0;
1037 g_iCurrentRedSpawn = 0;
1038 g_iCurrentBluSpawn = 0;
1039 g_fNextSpawnTime = GetGameTime();
1040 g_bRoundStarted = true;
1041 g_iRoundCount++;
1042 }
1043}
1044
1045/* OnRoundEnd()
1046**
1047** At round end, stop the Dodgeball game logic timer and destroy the remaining
1048** rockets.
1049** -------------------------------------------------------------------------- */
1050public Action OnRoundEnd(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1051{
1052 if (g_hTimerHud != INVALID_HANDLE)
1053 {
1054 KillTimer(g_hTimerHud);
1055 g_hTimerHud = INVALID_HANDLE;
1056 }
1057 if (g_hLogicTimer != INVALID_HANDLE)
1058 {
1059 KillTimer(g_hLogicTimer);
1060 g_hLogicTimer = INVALID_HANDLE;
1061 }
1062
1063 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
1064 {
1065 for (int i = 0; i < MAXPLAYERS + 1; i++)
1066 {
1067 firstJoined[i] = false;
1068 }
1069 }
1070 if (g_bMusicEnabled == true)
1071 {
1072 if (g_bUseWebPlayer)
1073 {
1074 for (int iClient = 1; iClient <= MaxClients; iClient++)
1075 {
1076 if (IsValidClient(iClient))
1077 {
1078 ShowHiddenMOTDPanel(iClient, "MusicPlayerStop", "http://0.0.0.0/");
1079 if (!IsFakeClient(iClient))
1080 {
1081 ClearSyncHud(iClient, g_hHud);
1082 }
1083 }
1084 }
1085 }
1086 else if (g_bMusic[Music_Gameplay])
1087 {
1088 StopSoundToAll(SNDCHAN_MUSIC, g_strMusic[Music_Gameplay]);
1089 }
1090 }
1091
1092 DestroyRockets();
1093 g_bRoundStarted = false;
1094}
1095
1096public Action Command_ToggleAirblast(int clientId, int args)
1097{
1098 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
1099 {
1100 char arg[128];
1101
1102 if (args > 1)
1103 {
1104 ReplyToCommand(clientId, "[SM] %s", USAGE);
1105 return Plugin_Handled;
1106 }
1107
1108 if (args == 0)
1109 {
1110 preventAirblast(clientId, !abPrevention[clientId]);
1111 }
1112 else if (args == 1)
1113 {
1114 GetCmdArg(1, arg, sizeof(arg));
1115
1116 if (strcmp(arg, "0") == 0)
1117 {
1118 preventAirblast(clientId, false);
1119 }
1120 else if (strcmp(arg, "1") == 0)
1121 {
1122 preventAirblast(clientId, true);
1123 }
1124 else
1125 {
1126 ReplyToCommand(clientId, "[SM] %s", USAGE);
1127 return Plugin_Handled;
1128 }
1129 }
1130
1131 if (abPrevention[clientId])
1132 {
1133 ReplyToCommand(clientId, "[SM] %s", "Airblast Prevention Enabled");
1134 }
1135 else
1136 {
1137 ReplyToCommand(clientId, "[SM] %s", "Airblast Prevention Disabled");
1138 }
1139 }
1140
1141 if (!GetConVarBool(g_hCvarAirBlastCommandEnabled))
1142 {
1143 ReplyToCommand(clientId, "[SM] %s", "Airblast Prevention is disabled on this server.");
1144 preventAirblast(clientId, false);
1145 }
1146 return Plugin_Handled;
1147}
1148
1149public Action Command_PostCurrentRocketClass(int client, int args)
1150{
1151 if (args > 1)
1152 {
1153 ReplyToCommand(client, "[SM] %s", "Usage: sm_currentrocket");
1154 return Plugin_Handled;
1155 }
1156
1157 if (!g_strSavedClassName[0])
1158 {
1159 CPrintToChat(client, "{FULLRED}[SVH] {DARKGRAY}Current Rocket: {TURQUOISE}Multiple");
1160 return Plugin_Handled;
1161 }
1162 CPrintToChatAll("{FULLRED}[SVH] {DARKGRAY}Current Rocket: {TURQUOISE}%s", g_strSavedClassName);
1163
1164 return Plugin_Handled;
1165}
1166
1167
1168/* OnPlayerSpawn()
1169**
1170** When the player spawns, force class to Pyro.
1171** -------------------------------------------------------------------------- */
1172public Action OnPlayerSpawn(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1173{
1174 int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
1175 int clientId = GetClientOfUserId(GetEventInt(hEvent, "userid"));
1176 //g_stolen[iClient] = 0;
1177
1178 if (!IsValidClient(iClient))return;
1179
1180 TFClassType iClass = TF2_GetPlayerClass(iClient);
1181 if (!(iClass == TFClass_Pyro || iClass == view_as<TFClassType>(TFClass_Unknown)))
1182 {
1183 TF2_SetPlayerClass(iClient, TFClass_Pyro, false, true);
1184 TF2_RespawnPlayer(iClient);
1185 }
1186
1187 for (int i = MaxClients; i; --i)
1188 {
1189 if (IsClientInGame(i) && IsPlayerAlive(i))
1190 SetEntPropEnt(i, Prop_Data, "m_hActiveWeapon", GetPlayerWeaponSlot(i, TFWeaponSlot_Primary));
1191 }
1192
1193 if (!GetConVarBool(g_hCvarPyroVisionEnabled))
1194 {
1195 return;
1196 }
1197 TF2Attrib_SetByName(iClient, PYROVISION_ATTRIBUTE, 1.0);
1198
1199 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
1200 {
1201 if (firstJoined[clientId])
1202 {
1203 //Enable ab prevention when a player joins the server
1204 abPrevention[clientId] = true;
1205 }
1206
1207 preventAirblast(clientId, true);
1208 }
1209}
1210
1211/* OnPlayerDeath()
1212**
1213** When the player dies, set the last dead team to determine the next
1214** rocket's team.
1215** -------------------------------------------------------------------------- */
1216public Action OnPlayerDeath(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1217{
1218 if (g_bRoundStarted == false)
1219 {
1220 return;
1221 }
1222 int iAttacker = GetClientOfUserId(GetEventInt(hEvent, "attacker"));
1223 int iVictim = GetClientOfUserId(GetEventInt(hEvent, "userid"));
1224
1225 if (GetConVarBool(g_hCvarAirBlastCommandEnabled))
1226 {
1227 int clientId = GetClientOfUserId(GetEventInt(hEvent, "userid"));
1228 firstJoined[clientId] = false;
1229 }
1230 if (IsValidClient(iVictim))
1231 {
1232 if (GetConVarBool(g_hCvarStealPrevention))
1233 {
1234 bStealArray[iVictim][stoleRocket] = false;
1235 bStealArray[iVictim][rocketsStolen] = 0;
1236 }
1237
1238 g_iLastDeadClient = iVictim;
1239 g_iLastDeadTeam = GetClientTeam(iVictim);
1240
1241 int iInflictor = GetEventInt(hEvent, "inflictor_entindex");
1242 int iIndex = FindRocketByEntity(iInflictor);
1243
1244 if (iIndex != -1)
1245 {
1246 int iClass = g_iRocketClass[iIndex];
1247 int iTarget = EntRefToEntIndex(g_iRocketTarget[iIndex]);
1248 float fSpeed = g_fRocketSpeed[iIndex];
1249 int iDeflections = g_iRocketDeflections[iIndex];
1250
1251 if (GetConVarBool(g_hCvarAnnounce))
1252 {
1253 if (GetConVarBool(g_hCvarDeflectCountAnnounce))
1254 {
1255 if (iVictim == iTarget)
1256 {
1257 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}died to their rocket travelling {TURQUOISE}%i {DARKGRAY}mph with {TURQUOISE}%i {DARKGRAY}deflections!", g_iLastDeadClient, g_iRocketSpeed, iDeflections);
1258 }
1259 else
1260 {
1261 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}died to {TURQUOISE}%.15N's {DARKGRAY}rocket travelling {TURQUOISE}%i {DARKGRAY}mph with {TURQUOISE}%i {DARKGRAY}deflections!", g_iLastDeadClient, iTarget, g_iRocketSpeed, iDeflections);
1262 }
1263 }
1264 else
1265 {
1266 CPrintToChatAll("{FULLRED}[SVH] {TURQUOISE}%N {DARKGRAY}died to a rocket travelling {TURQUOISE}%i {DARKGRAY}mph!", g_iLastDeadClient, g_iRocketSpeed);
1267 }
1268 }
1269
1270 if ((g_iRocketFlags[iIndex] & RocketFlag_OnExplodeCmd) && !(g_iRocketFlags[iIndex] & RocketFlag_Exploded))
1271 {
1272 ExecuteCommands(g_hRocketClassCmdsOnExplode[iClass], iClass, iInflictor, iAttacker, iTarget, g_iLastDeadClient, fSpeed, iDeflections);
1273 g_iRocketFlags[iIndex] |= RocketFlag_Exploded;
1274 }
1275
1276 if (TestFlags(g_iRocketFlags[iIndex], RocketFlag_OnKillCmd))
1277 ExecuteCommands(g_hRocketClassCmdsOnKill[iClass], iClass, iInflictor, iAttacker, iTarget, g_iLastDeadClient, fSpeed, iDeflections);
1278 }
1279 }
1280
1281 SetRandomSeed(view_as<int>(GetGameTime()));
1282}
1283
1284/* OnPlayerInventory()
1285**
1286** Make sure the client only has the flamethrower equipped.
1287** -------------------------------------------------------------------------- */
1288public Action OnPlayerInventory(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1289{
1290 int iClient = GetClientOfUserId(GetEventInt(hEvent, "userid"));
1291 if (!IsValidClient(iClient))return;
1292
1293 for (int iSlot = 1; iSlot < 5; iSlot++)
1294 {
1295 int iEntity = GetPlayerWeaponSlot(iClient, iSlot);
1296 if (iEntity != -1)RemoveEdict(iEntity);
1297 }
1298}
1299
1300/* OnPlayerRunCmd()
1301**
1302** Block flamethrower's Mouse1 attack.
1303** -------------------------------------------------------------------------- */
1304public Action OnPlayerRunCmd(int iClient, int &iButtons, int &iImpulse, float fVelocity[3], float fAngles[3], int &iWeapon)
1305{
1306 if (g_bEnabled == true)iButtons &= ~IN_ATTACK;
1307 return Plugin_Continue;
1308}
1309
1310/* OnBroadcastAudio()
1311**
1312** Replaces the broadcasted audio for our custom music files.
1313** -------------------------------------------------------------------------- */
1314public Action OnBroadcastAudio(Handle hEvent, char[] strEventName, bool bDontBroadcast)
1315{
1316 if (g_bMusicEnabled == true)
1317 {
1318 char strSound[PLATFORM_MAX_PATH];
1319 GetEventString(hEvent, "sound", strSound, sizeof(strSound));
1320 int iTeam = GetEventInt(hEvent, "team");
1321
1322 if (StrEqual(strSound, "Announcer.AM_RoundStartRandom") == true)
1323 {
1324 if (g_bUseWebPlayer == false)
1325 {
1326 if (g_bMusic[Music_Gameplay])
1327 {
1328 EmitSoundToAll(g_strMusic[Music_Gameplay], SOUND_FROM_PLAYER, SNDCHAN_MUSIC);
1329 return Plugin_Handled;
1330 }
1331 }
1332 else
1333 {
1334 for (int iClient = 1; iClient <= MaxClients; iClient++)
1335 if (IsValidClient(iClient))
1336 ShowHiddenMOTDPanel(iClient, "MusicPlayerStart", g_strWebPlayerUrl);
1337
1338 return Plugin_Handled;
1339 }
1340 }
1341 else if (StrEqual(strSound, "Game.YourTeamWon") == true)
1342 {
1343 if (g_bMusic[Music_RoundWin])
1344 {
1345 for (int iClient = 1; iClient <= MaxClients; iClient++)
1346 if (IsValidClient(iClient) && (iTeam == GetClientTeam(iClient)))
1347 EmitSoundToClient(iClient, g_strMusic[Music_RoundWin]);
1348
1349 return Plugin_Handled;
1350 }
1351 }
1352 else if (StrEqual(strSound, "Game.YourTeamLost") == true)
1353 {
1354 if (g_bMusic[Music_RoundLose])
1355 {
1356 for (int iClient = 1; iClient <= MaxClients; iClient++)
1357 if (IsValidClient(iClient) && (iTeam == GetClientTeam(iClient)))
1358 EmitSoundToClient(iClient, g_strMusic[Music_RoundLose]);
1359
1360 return Plugin_Handled;
1361 }
1362 return Plugin_Handled;
1363 }
1364 }
1365 return Plugin_Continue;
1366}
1367
1368/* OnDodgeBallGameFrame()
1369**
1370** Every tick of the Dodgeball logic.
1371** -------------------------------------------------------------------------- */
1372public Action OnDodgeBallGameFrame(Handle hTimer, any Data)
1373{
1374 // Only if both teams are playing
1375 if (BothTeamsPlaying() == false)return;
1376
1377 // Check if we need to fire more rockets.
1378 if (GetGameTime() >= g_fNextSpawnTime)
1379 {
1380 if (g_iLastDeadTeam == view_as<int>(TFTeam_Red))
1381 {
1382 int iSpawnerEntity = g_iSpawnPointsRedEntity[g_iCurrentRedSpawn];
1383 int iSpawnerClass = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
1384 if (g_iRocketCount < g_iSpawnersMaxRockets[iSpawnerClass])
1385 {
1386 CreateRocket(iSpawnerEntity, iSpawnerClass, view_as<int>(TFTeam_Red));
1387 g_iCurrentRedSpawn = (g_iCurrentRedSpawn + 1) % g_iSpawnPointsRedCount;
1388 }
1389 }
1390 else
1391 {
1392 int iSpawnerEntity = g_iSpawnPointsBluEntity[g_iCurrentBluSpawn];
1393 int iSpawnerClass = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
1394 if (g_iRocketCount < g_iSpawnersMaxRockets[iSpawnerClass])
1395 {
1396 CreateRocket(iSpawnerEntity, iSpawnerClass, view_as<int>(TFTeam_Blue));
1397 g_iCurrentBluSpawn = (g_iCurrentBluSpawn + 1) % g_iSpawnPointsBluCount;
1398 }
1399 }
1400 }
1401
1402 // Manage the active rockets
1403 int iIndex = -1;
1404 while ((iIndex = FindNextValidRocket(iIndex)) != -1)
1405 {
1406 switch (g_iRocketClassBehaviour[g_iRocketClass[iIndex]])
1407 {
1408 case Behaviour_Unknown: { }
1409 case Behaviour_Homing: { HomingRocketThink(iIndex); }
1410 }
1411 }
1412}
1413
1414/*public Action ShowToTarget(int iIndex, int iClient)
1415{
1416 int iParticle = EntRefToEntIndex(g_RocketParticle[iIndex]);
1417 int iTarget = EntRefToEntIndex(g_iRocketTarget[iIndex]);
1418
1419 if (!IsValidEntity(iParticle))
1420 return Plugin_Handled;
1421
1422 if (!IsValidClient(iTarget))
1423 return Plugin_Handled;
1424
1425 if (iClient != iTarget)
1426 return Plugin_Handled;
1427
1428 return Plugin_Continue;
1429}*/
1430
1431public Action Timer_HudSpeed(Handle hTimer)
1432{
1433 if (GetConVarBool(g_hCvarSpeedo))
1434 {
1435 SetHudTextParams(-1.0, 0.9, 1.1, 255, 255, 255, 255);
1436 for (int iClient = 1; iClient <= MaxClients; iClient++)
1437 {
1438 if (IsValidClient(iClient) && !IsFakeClient(iClient) && g_iRocketSpeed != 0)
1439 {
1440 ShowSyncHudText(iClient, g_hHud, "Speed: %i mph", g_iRocketSpeed);
1441 }
1442 }
1443 }
1444}
1445
1446// ___ _ _
1447// | _ \___ __| |_____| |_ ___
1448// | / _ \/ _| / / -_) _(_-<
1449// |_|_\___/\__|_\_\___|\__/__/
1450
1451/* CreateRocket()
1452**
1453** Fires a new rocket entity from the spawner's position.
1454** -------------------------------------------------------------------------- */
1455public void CreateRocket(int iSpawnerEntity, int iSpawnerClass, int iTeam)
1456{
1457 int iIndex = FindFreeRocketSlot();
1458 if (iIndex != -1)
1459 {
1460 // Fetch a random rocket class and it's parameters.
1461 int iClass = GetRandomRocketClass(iSpawnerClass);
1462 RocketFlags iFlags = g_iRocketClassFlags[iClass];
1463
1464 // Create rocket entity.
1465 int iEntity = CreateEntityByName(TestFlags(iFlags, RocketFlag_IsAnimated) ? "tf_projectile_sentryrocket" : "tf_projectile_rocket");
1466 if (iEntity && IsValidEntity(iEntity))
1467 {
1468 // Fetch spawn point's location and angles.
1469 float fPosition[3];
1470 float fAngles[3];
1471 float fDirection[3];
1472 GetEntPropVector(iSpawnerEntity, Prop_Send, "m_vecOrigin", fPosition);
1473 GetEntPropVector(iSpawnerEntity, Prop_Send, "m_angRotation", fAngles);
1474 GetAngleVectors(fAngles, fDirection, NULL_VECTOR, NULL_VECTOR);
1475
1476 // Setup rocket entity.
1477 SetEntPropEnt(iEntity, Prop_Send, "m_hOwnerEntity", 0);
1478 SetEntProp(iEntity, Prop_Send, "m_bCritical", (GetURandomFloatRange(0.0, 100.0) <= g_fRocketClassCritChance[iClass]) ? 1 : 0, 1);
1479 SetEntProp(iEntity, Prop_Send, "m_iTeamNum", iTeam, 1);
1480 SetEntProp(iEntity, Prop_Send, "m_iDeflected", 1);
1481 TeleportEntity(iEntity, fPosition, fAngles, view_as<float>( { 0.0, 0.0, 0.0 } ));
1482
1483 // Setup rocket structure with the newly created entity.
1484 int iTargetTeam = (TestFlags(iFlags, RocketFlag_IsNeutral)) ? 0 : GetAnalogueTeam(iTeam);
1485 int iTarget = SelectTarget(iTargetTeam);
1486 float fModifier = CalculateModifier(iClass, 0);
1487 g_bRocketIsValid[iIndex] = true;
1488 g_iRocketFlags[iIndex] = iFlags;
1489 g_iRocketEntity[iIndex] = EntIndexToEntRef(iEntity);
1490 g_iRocketTarget[iIndex] = EntIndexToEntRef(iTarget);
1491 g_iRocketSpawner[iIndex] = iSpawnerClass;
1492 g_iRocketClass[iIndex] = iClass;
1493 g_iRocketDeflections[iIndex] = 0;
1494 g_fRocketLastDeflectionTime[iIndex] = GetGameTime();
1495 g_fRocketLastBeepTime[iIndex] = GetGameTime();
1496 g_fRocketSpeed[iIndex] = CalculateRocketSpeed(iClass, fModifier);
1497 g_iRocketSpeed = RoundFloat(g_fRocketSpeed[iIndex] * 0.042614);
1498
1499 CopyVectors(fDirection, g_fRocketDirection[iIndex]);
1500 SetEntDataFloat(iEntity, FindSendPropInfo("CTFProjectile_Rocket", "m_iDeflected") + 4, CalculateRocketDamage(iClass, fModifier), true);
1501 DispatchSpawn(iEntity);
1502
1503 // Apply custom model, if specified on the flags.
1504 if (TestFlags(iFlags, RocketFlag_CustomModel))
1505 {
1506 SetEntityModel(iEntity, g_strRocketClassModel[iClass]);
1507 UpdateRocketSkin(iEntity, iTeam, TestFlags(iFlags, RocketFlag_IsNeutral));
1508 }
1509
1510 // Execute commands on spawn.
1511 if (TestFlags(iFlags, RocketFlag_OnSpawnCmd))
1512 {
1513 ExecuteCommands(g_hRocketClassCmdsOnSpawn[iClass], iClass, iEntity, 0, iTarget, g_iLastDeadClient, g_fRocketSpeed[iIndex], 0);
1514 }
1515
1516 // Emit required sounds.
1517 EmitRocketSound(RocketSound_Spawn, iClass, iEntity, iTarget, iFlags);
1518 EmitRocketSound(RocketSound_Alert, iClass, iEntity, iTarget, iFlags);
1519
1520 // Done
1521 g_iRocketCount++;
1522 g_iRocketsFired++;
1523 g_fNextSpawnTime = GetGameTime() + g_fSpawnersInterval[iSpawnerClass];
1524 g_bRocketIsNuke[iIndex] = false;
1525
1526 //AttachParticle(iEntity, "burningplayer_rainbow_glow");
1527 //AttachParticle(iEntity, "burningplayer_rainbow_glow_old");
1528 //CreateTempParticle("superrare_greenenergy", iEntity, _, _, true);
1529 //SDKHook(iEntity, SDKHook_SetTransmit, ShowToTarget);
1530
1531 //Observer
1532 if (IsValidEntity(g_observer))
1533 {
1534 g_op_rocket = iEntity;
1535 TeleportEntity(g_observer, fPosition, fAngles, view_as<float>( { 0.0, 0.0, 0.0 } ));
1536 SetVariantString("!activator");
1537 AcceptEntityInput(g_observer, "SetParent", g_op_rocket);
1538 }
1539 }
1540 }
1541}
1542
1543public void OnEntityDestroyed(int entity)
1544{
1545 if (entity == -1)
1546 {
1547 return;
1548 }
1549
1550 if (entity == g_op_rocket && g_bEnabled == true && IsValidEntity(g_observer) && IsValidEntity(g_op_rocket))
1551 {
1552 SetVariantString("");
1553 AcceptEntityInput(g_observer, "ClearParent");
1554 g_op_rocket = -1;
1555
1556 float opPos[3];
1557 float opAng[3];
1558
1559 int spawner = GetRandomInt(0, 1);
1560 if (spawner == 0)
1561 spawner = g_iSpawnPointsRedEntity[0];
1562 else
1563 spawner = g_iSpawnPointsBluEntity[0];
1564
1565 if (IsValidEntity(spawner) && spawner > MAXPLAYERS)
1566 {
1567 GetEntPropVector(spawner, Prop_Data, "m_vecOrigin", opPos);
1568 GetEntPropVector(spawner, Prop_Data, "m_angAbsRotation", opAng);
1569 TeleportEntity(g_observer, opPos, opAng, NULL_VECTOR);
1570 }
1571 }
1572}
1573
1574/* DestroyRocket()
1575**
1576** Destroys the rocket at the given index.
1577** -------------------------------------------------------------------------- */
1578void DestroyRocket(int iIndex)
1579{
1580 if (IsValidRocket(iIndex) == true)
1581 {
1582 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
1583 if (iEntity && IsValidEntity(iEntity))RemoveEdict(iEntity);
1584 g_bRocketIsValid[iIndex] = false;
1585 g_iRocketCount--;
1586 }
1587}
1588
1589/* DestroyRockets()
1590**
1591** Destroys all the rockets that are currently active.
1592** -------------------------------------------------------------------------- */
1593void DestroyRockets()
1594{
1595 for (int iIndex = 0; iIndex < MAX_ROCKETS; iIndex++)
1596 {
1597 DestroyRocket(iIndex);
1598 }
1599 g_iRocketCount = 0;
1600}
1601
1602/* IsValidRocket()
1603**
1604** Checks if a rocket structure is valid.
1605** -------------------------------------------------------------------------- */
1606bool IsValidRocket(int iIndex)
1607{
1608 if ((iIndex >= 0) && (g_bRocketIsValid[iIndex] == true))
1609 {
1610 if (EntRefToEntIndex(g_iRocketEntity[iIndex]) == -1)
1611 {
1612 g_bRocketIsValid[iIndex] = false;
1613 g_iRocketCount--;
1614 return false;
1615 }
1616 return true;
1617 }
1618 return false;
1619}
1620
1621/* FindNextValidRocket()
1622**
1623** Retrieves the index of the next valid rocket from the current offset.
1624** -------------------------------------------------------------------------- */
1625int FindNextValidRocket(int iIndex, bool bWrap = false)
1626{
1627 for (int iCurrent = iIndex + 1; iCurrent < MAX_ROCKETS; iCurrent++)
1628 if (IsValidRocket(iCurrent))
1629 return iCurrent;
1630
1631 return (bWrap == true) ? FindNextValidRocket(-1, false) : -1;
1632}
1633
1634/* FindFreeRocketSlot()
1635**
1636** Retrieves the next free rocket slot since the current one. If all of them
1637** are full, returns -1.
1638** -------------------------------------------------------------------------- */
1639int FindFreeRocketSlot()
1640{
1641 int iIndex = g_iLastCreatedRocket;
1642 int iCurrent = iIndex;
1643
1644 do
1645 {
1646 if (!IsValidRocket(iCurrent))return iCurrent;
1647 if ((++iCurrent) == MAX_ROCKETS)iCurrent = 0;
1648 } while (iCurrent != iIndex);
1649
1650 return -1;
1651}
1652
1653/* FindRocketByEntity()
1654**
1655** Finds a rocket index from it's entity.
1656** -------------------------------------------------------------------------- */
1657int FindRocketByEntity(int iEntity)
1658{
1659 int iIndex = -1;
1660 while ((iIndex = FindNextValidRocket(iIndex)) != -1)
1661 if (EntRefToEntIndex(g_iRocketEntity[iIndex]) == iEntity)
1662 return iIndex;
1663
1664 return -1;
1665}
1666
1667/* HomingRocketThinkg()
1668**
1669** Logic process for the Behaviour_Homing type rockets, wich is simply a
1670** follower rocket, picking a random target.
1671** -------------------------------------------------------------------------- */
1672void HomingRocketThink(int iIndex)
1673{
1674 // Retrieve the rocket's attributes.
1675 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
1676 int iClass = g_iRocketClass[iIndex];
1677 RocketFlags iFlags = g_iRocketFlags[iIndex];
1678 int iTarget = EntRefToEntIndex(g_iRocketTarget[iIndex]);
1679 int iTeam = GetEntProp(iEntity, Prop_Send, "m_iTeamNum", 1);
1680 int iTargetTeam = (TestFlags(iFlags, RocketFlag_IsNeutral)) ? 0 : GetAnalogueTeam(iTeam);
1681 int iDeflectionCount = GetEntProp(iEntity, Prop_Send, "m_iDeflected") - 1;
1682 float fModifier = CalculateModifier(iClass, iDeflectionCount);
1683
1684 // Check if the target is available
1685 if (!IsValidClient(iTarget, true))
1686 {
1687 iTarget = SelectTarget(iTargetTeam);
1688 if (!IsValidClient(iTarget, true))return;
1689 g_iRocketTarget[iIndex] = EntIndexToEntRef(iTarget);
1690
1691 if (GetConVarBool(g_hCvarRedirectBeep))
1692 {
1693 EmitRocketSound(RocketSound_Alert, iClass, iEntity, iTarget, iFlags);
1694 }
1695 }
1696 // Has the rocket been deflected recently? If so, set new target.
1697 else if ((iDeflectionCount > g_iRocketDeflections[iIndex]))
1698 {
1699 // Calculate new direction from the player's forward
1700 int iClient = GetEntPropEnt(iEntity, Prop_Send, "m_hOwnerEntity");
1701 if (IsValidClient(iClient))
1702 {
1703 float fViewAngles[3];
1704 float fDirection[3];
1705 GetClientEyeAngles(iClient, fViewAngles);
1706 GetAngleVectors(fViewAngles, fDirection, NULL_VECTOR, NULL_VECTOR);
1707 CopyVectors(fDirection, g_fRocketDirection[iIndex]);
1708 UpdateRocketSkin(iEntity, iTeam, TestFlags(iFlags, RocketFlag_IsNeutral));
1709 if (GetConVarBool(g_hCvarStealPrevention))
1710 {
1711 checkStolenRocket(iClient, iIndex);
1712 }
1713 }
1714
1715 // Set new target & deflection count
1716 iTarget = SelectTarget(iTargetTeam, iIndex);
1717 g_iRocketTarget[iIndex] = EntIndexToEntRef(iTarget);
1718 g_iRocketDeflections[iIndex] = iDeflectionCount;
1719 g_fRocketLastDeflectionTime[iIndex] = GetGameTime();
1720 g_fRocketSpeed[iIndex] = CalculateRocketSpeed(iClass, fModifier);
1721 g_iRocketSpeed = RoundFloat(g_fRocketSpeed[iIndex] * 0.042614);
1722
1723 SetEntDataFloat(iEntity, FindSendPropInfo("CTFProjectile_Rocket", "m_iDeflected") + 4, CalculateRocketDamage(iClass, fModifier), true);
1724 if (TestFlags(iFlags, RocketFlag_ElevateOnDeflect))g_iRocketFlags[iIndex] |= RocketFlag_Elevating;
1725 EmitRocketSound(RocketSound_Alert, iClass, iEntity, iTarget, iFlags);
1726 //Send out temp entity to target
1727 //SendTempEnt(iTarget, "superrare_greenenergy", iEntity, _, _, true);
1728
1729 // Execute appropiate command
1730 if (TestFlags(iFlags, RocketFlag_OnDeflectCmd))
1731 {
1732 ExecuteCommands(g_hRocketClassCmdsOnDeflect[iClass], iClass, iEntity, iClient, iTarget, g_iLastDeadClient, g_fRocketSpeed[iIndex], iDeflectionCount);
1733 }
1734 }
1735 else
1736 {
1737 // If the delay time since the last reflection has been elapsed, rotate towards the client.
1738 if ((GetGameTime() - g_fRocketLastDeflectionTime[iIndex]) >= g_fRocketClassControlDelay[iClass])
1739 {
1740 // Calculate turn rate and retrieve directions.
1741 float fTurnRate = CalculateRocketTurnRate(iClass, fModifier);
1742 float fDirectionToTarget[3]; CalculateDirectionToClient(iEntity, iTarget, fDirectionToTarget);
1743
1744 // Elevate the rocket after a deflection (if it's enabled on the class definition, of course.)
1745 if (g_iRocketFlags[iIndex] & RocketFlag_Elevating)
1746 {
1747 if (g_fRocketDirection[iIndex][2] < g_fRocketClassElevationLimit[iClass])
1748 {
1749 g_fRocketDirection[iIndex][2] = FMin(g_fRocketDirection[iIndex][2] + g_fRocketClassElevationRate[iClass], g_fRocketClassElevationLimit[iClass]);
1750 fDirectionToTarget[2] = g_fRocketDirection[iIndex][2];
1751 }
1752 else
1753 {
1754 g_iRocketFlags[iIndex] &= ~RocketFlag_Elevating;
1755 }
1756 }
1757
1758 // Smoothly change the orientation to the new one.
1759 LerpVectors(g_fRocketDirection[iIndex], fDirectionToTarget, g_fRocketDirection[iIndex], fTurnRate);
1760 }
1761
1762 // If it's a nuke, beep every some time
1763 if ((GetGameTime() - g_fRocketLastBeepTime[iIndex]) >= g_fRocketClassBeepInterval[iClass])
1764 {
1765 g_bRocketIsNuke[iIndex] = true;
1766 EmitRocketSound(RocketSound_Beep, iClass, iEntity, iTarget, iFlags);
1767 g_fRocketLastBeepTime[iIndex] = GetGameTime();
1768 }
1769 }
1770
1771 // Done
1772 ApplyRocketParameters(iIndex);
1773}
1774
1775/* CalculateModifier()
1776**
1777** Gets the modifier for the damage/speed/rotation calculations.
1778** -------------------------------------------------------------------------- */
1779float CalculateModifier(int iClass, int iDeflections)
1780{
1781 return iDeflections +
1782 (g_iRocketsFired * g_fRocketClassRocketsModifier[iClass]) +
1783 (g_iPlayerCount * g_fRocketClassPlayerModifier[iClass]);
1784}
1785
1786/* CalculateRocketDamage()
1787**
1788** Calculates the damage of the rocket based on it's type and deflection count.
1789** -------------------------------------------------------------------------- */
1790float CalculateRocketDamage(int iClass, float fModifier)
1791{
1792 return g_fRocketClassDamage[iClass] + g_fRocketClassDamageIncrement[iClass] * fModifier;
1793}
1794
1795/* CalculateRocketSpeed()
1796**
1797** Calculates the speed of the rocket based on it's type and deflection count.
1798** -------------------------------------------------------------------------- */
1799float CalculateRocketSpeed(int iClass, float fModifier)
1800{
1801 return g_fRocketClassSpeed[iClass] + g_fRocketClassSpeedIncrement[iClass] * fModifier;
1802}
1803
1804/* CalculateRocketTurnRate()
1805**
1806** Calculates the rocket's turn rate based upon it's type and deflection count.
1807** -------------------------------------------------------------------------- */
1808float CalculateRocketTurnRate(int iClass, float fModifier)
1809{
1810 return g_fRocketClassTurnRate[iClass] + g_fRocketClassTurnRateIncrement[iClass] * fModifier;
1811}
1812
1813/* CalculateDirectionToClient()
1814**
1815** As the name indicates, calculates the orientation for the rocket to move
1816** towards the specified client.
1817** -------------------------------------------------------------------------- */
1818void CalculateDirectionToClient(int iEntity, int iClient, float fOut[3])
1819{
1820 float fRocketPosition[3]; GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fRocketPosition);
1821 GetClientEyePosition(iClient, fOut);
1822 MakeVectorFromPoints(fRocketPosition, fOut, fOut);
1823 NormalizeVector(fOut, fOut);
1824}
1825
1826/* ApplyRocketParameters()
1827**
1828** Transforms and applies the speed, direction and angles for the rocket
1829** entity.
1830** -------------------------------------------------------------------------- */
1831void ApplyRocketParameters(int iIndex)
1832{
1833 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
1834 float fAngles[3]; GetVectorAngles(g_fRocketDirection[iIndex], fAngles);
1835 float fVelocity[3]; CopyVectors(g_fRocketDirection[iIndex], fVelocity);
1836 ScaleVector(fVelocity, g_fRocketSpeed[iIndex]);
1837 SetEntPropVector(iEntity, Prop_Data, "m_vecAbsVelocity", fVelocity);
1838 SetEntPropVector(iEntity, Prop_Send, "m_angRotation", fAngles);
1839}
1840
1841/* UpdateRocketSkin()
1842**
1843** Changes the skin of the rocket based on it's team.
1844** -------------------------------------------------------------------------- */
1845void UpdateRocketSkin(int iEntity, int iTeam, bool bNeutral)
1846{
1847 if (bNeutral == true)SetEntProp(iEntity, Prop_Send, "m_nSkin", 2);
1848 else SetEntProp(iEntity, Prop_Send, "m_nSkin", (iTeam == view_as<int>(TFTeam_Blue)) ? 0 : 1);
1849}
1850
1851/* GetRandomRocketClass()
1852**
1853** Generates a random value and retrieves a rocket class based upon a chances table.
1854** -------------------------------------------------------------------------- */
1855int GetRandomRocketClass(int iSpawnerClass)
1856{
1857 int iRandom = GetURandomIntRange(0, 101);
1858 Handle hTable = g_hSpawnersChancesTable[iSpawnerClass];
1859 int iTableSize = GetArraySize(hTable);
1860 int iChancesLower = 0;
1861 int iChancesUpper = 0;
1862
1863 for (int iEntry = 0; iEntry < iTableSize; iEntry++)
1864 {
1865 iChancesLower += iChancesUpper;
1866 iChancesUpper = iChancesLower + GetArrayCell(hTable, iEntry);
1867
1868 if ((iRandom >= iChancesLower) && (iRandom < iChancesUpper))
1869 {
1870 return iEntry;
1871 }
1872 }
1873
1874 return 0;
1875}
1876
1877/* EmitRocketSound()
1878**
1879** Emits one of the rocket sounds
1880** -------------------------------------------------------------------------- */
1881void EmitRocketSound(RocketSound iSound, int iClass, int iEntity, int iTarget, RocketFlags iFlags)
1882{
1883 switch (iSound)
1884 {
1885 case RocketSound_Spawn:
1886 {
1887 if (TestFlags(iFlags, RocketFlag_PlaySpawnSound))
1888 {
1889 if (TestFlags(iFlags, RocketFlag_CustomSpawnSound))EmitSoundToAll(g_strRocketClassSpawnSound[iClass], iEntity);
1890 else EmitSoundToAll(SOUND_DEFAULT_SPAWN, iEntity);
1891 }
1892 }
1893 case RocketSound_Beep:
1894 {
1895 if (TestFlags(iFlags, RocketFlag_PlayBeepSound))
1896 {
1897 if (TestFlags(iFlags, RocketFlag_CustomBeepSound))EmitSoundToAll(g_strRocketClassBeepSound[iClass], iEntity);
1898 else EmitSoundToAll(SOUND_DEFAULT_BEEP, iEntity);
1899 }
1900 }
1901 case RocketSound_Alert:
1902 {
1903 if (TestFlags(iFlags, RocketFlag_PlayAlertSound))
1904 {
1905 if (TestFlags(iFlags, RocketFlag_CustomAlertSound))EmitSoundToClient(iTarget, g_strRocketClassAlertSound[iClass]);
1906 else EmitSoundToClient(iTarget, SOUND_DEFAULT_ALERT, _, _, _, _, 0.5);
1907 }
1908 }
1909 }
1910}
1911
1912// ___ _ _ ___ _
1913// | _ \___ __| |_____| |_ / __| |__ _ ______ ___ ___
1914// | / _ \/ _| / / -_) _| | (__| / _` (_-<_-</ -_|_-<
1915// |_|_\___/\__|_\_\___|\__| \___|_\__,_/__/__/\___/__/
1916//
1917
1918/* DestroyRocketClasses()
1919**
1920** Frees up all the rocket classes defined now.
1921** -------------------------------------------------------------------------- */
1922void DestroyRocketClasses()
1923{
1924 for (int iIndex = 0; iIndex < g_iRocketClassCount; iIndex++)
1925 {
1926 Handle hCmdOnSpawn = g_hRocketClassCmdsOnSpawn[iIndex];
1927 Handle hCmdOnKill = g_hRocketClassCmdsOnKill[iIndex];
1928 Handle hCmdOnExplode = g_hRocketClassCmdsOnExplode[iIndex];
1929 Handle hCmdOnDeflect = g_hRocketClassCmdsOnDeflect[iIndex];
1930 if (hCmdOnSpawn != INVALID_HANDLE)CloseHandle(hCmdOnSpawn);
1931 if (hCmdOnKill != INVALID_HANDLE)CloseHandle(hCmdOnKill);
1932 if (hCmdOnExplode != INVALID_HANDLE)CloseHandle(hCmdOnExplode);
1933 if (hCmdOnDeflect != INVALID_HANDLE)CloseHandle(hCmdOnDeflect);
1934 g_hRocketClassCmdsOnSpawn[iIndex] = INVALID_HANDLE;
1935 g_hRocketClassCmdsOnKill[iIndex] = INVALID_HANDLE;
1936 g_hRocketClassCmdsOnExplode[iIndex] = INVALID_HANDLE;
1937 g_hRocketClassCmdsOnDeflect[iIndex] = INVALID_HANDLE;
1938 }
1939 g_iRocketClassCount = 0;
1940 ClearTrie(g_hRocketClassTrie);
1941}
1942
1943// ___ ___ _ _ _ ___ _
1944// / __|_ __ __ ___ __ ___ _ | _ \___(_)_ _| |_ ___ __ _ _ _ __| | / __| |__ _ ______ ___ ___
1945// \__ \ '_ \/ _` \ V V / ' \ | _/ _ \ | ' \ _(_-< / _` | ' \/ _` | | (__| / _` (_-<_-</ -_|_-<
1946// |___/ .__/\__,_|\_/\_/|_||_| |_| \___/_|_||_\__/__/ \__,_|_||_\__,_| \___|_\__,_/__/__/\___/__/
1947// |_|
1948
1949/* DestroySpawners()
1950**
1951** Frees up all the spawner points defined up to now.
1952** -------------------------------------------------------------------------- */
1953void DestroySpawners()
1954{
1955 for (int iIndex = 0; iIndex < g_iSpawnersCount; iIndex++)
1956 {
1957 CloseHandle(g_hSpawnersChancesTable[iIndex]);
1958 }
1959 g_iSpawnersCount = 0;
1960 g_iSpawnPointsRedCount = 0;
1961 g_iSpawnPointsBluCount = 0;
1962 g_iDefaultRedSpawner = -1;
1963 g_iDefaultBluSpawner = -1;
1964 ClearTrie(g_hSpawnersTrie);
1965}
1966
1967/* PopulateSpawnPoints()
1968**
1969** Iterates through all the possible spawn points and assigns them an spawner.
1970** -------------------------------------------------------------------------- */
1971void PopulateSpawnPoints()
1972{
1973 // Clear the current settings
1974 g_iSpawnPointsRedCount = 0;
1975 g_iSpawnPointsBluCount = 0;
1976
1977 // Iterate through all the info target points and check 'em out.
1978 int iEntity = -1;
1979 while ((iEntity = FindEntityByClassname(iEntity, "info_target")) != -1)
1980 {
1981 char strName[32]; GetEntPropString(iEntity, Prop_Data, "m_iName", strName, sizeof(strName));
1982 if ((StrContains(strName, "rocket_spawn_red") != -1) || (StrContains(strName, "tf_dodgeball_red") != -1))
1983 {
1984 // Find most appropiate spawner class for this entity.
1985 int iIndex = FindSpawnerByName(strName);
1986 if (!IsValidRocket(iIndex)) iIndex = g_iDefaultRedSpawner;
1987
1988 // Upload to point list
1989 g_iSpawnPointsRedClass[g_iSpawnPointsRedCount] = iIndex;
1990 g_iSpawnPointsRedEntity[g_iSpawnPointsRedCount] = iEntity;
1991 g_iSpawnPointsRedCount++;
1992 }
1993 if ((StrContains(strName, "rocket_spawn_blue") != -1) || (StrContains(strName, "tf_dodgeball_blu") != -1))
1994 {
1995 // Find most appropiate spawner class for this entity.
1996 int iIndex = FindSpawnerByName(strName);
1997 if (!IsValidRocket(iIndex))iIndex = g_iDefaultBluSpawner;
1998
1999 // Upload to point list
2000 g_iSpawnPointsBluClass[g_iSpawnPointsBluCount] = iIndex;
2001 g_iSpawnPointsBluEntity[g_iSpawnPointsBluCount] = iEntity;
2002 g_iSpawnPointsBluCount++;
2003 }
2004 }
2005
2006 // Check if there exists spawn points
2007 if (g_iSpawnPointsRedCount == 0)
2008 SetFailState("No RED spawn points found on this map.");
2009
2010 if (g_iSpawnPointsBluCount == 0)
2011 SetFailState("No BLU spawn points found on this map.");
2012
2013
2014 //ObserverPoint
2015 float opPos[3];
2016 float opAng[3];
2017
2018 int spawner = GetRandomInt(0, 1);
2019 if (spawner == 0)
2020 spawner = g_iSpawnPointsRedEntity[0];
2021 else
2022 spawner = g_iSpawnPointsBluEntity[0];
2023
2024 if (IsValidEntity(spawner) && spawner > MAXPLAYERS)
2025 {
2026 GetEntPropVector(spawner, Prop_Data, "m_vecOrigin", opPos);
2027 GetEntPropVector(spawner, Prop_Data, "m_angAbsRotation", opAng);
2028 g_observer = CreateEntityByName("info_observer_point");
2029 DispatchKeyValue(g_observer, "Angles", "90 0 0");
2030 DispatchKeyValue(g_observer, "TeamNum", "0");
2031 DispatchKeyValue(g_observer, "StartDisabled", "0");
2032 DispatchSpawn(g_observer);
2033 AcceptEntityInput(g_observer, "Enable");
2034 TeleportEntity(g_observer, opPos, opAng, NULL_VECTOR);
2035 }
2036 else
2037 {
2038 g_observer = -1;
2039 }
2040
2041}
2042
2043/* FindSpawnerByName()
2044**
2045** Finds the first spawner wich contains the given name.
2046** -------------------------------------------------------------------------- */
2047int FindSpawnerByName(char strName[32])
2048{
2049 int iIndex = -1;
2050 GetTrieValue(g_hSpawnersTrie, strName, iIndex);
2051 return iIndex;
2052}
2053
2054
2055/*
2056**����������������������������������������������������������������������������������
2057** ______ __
2058** / ____/___ ____ ___ ____ ___ ____ _____ ____/ /____
2059** / / / __ \/ __ `__ \/ __ `__ \/ __ `/ __ \/ __ / ___/
2060** / /___/ /_/ / / / / / / / / / / / /_/ / / / / /_/ (__ )
2061** \____/\____/_/ /_/ /_/_/ /_/ /_/\__,_/_/ /_/\__,_/____/
2062**
2063**����������������������������������������������������������������������������������
2064*/
2065
2066/* RegisterCommands()
2067**
2068** Creates helper server commands to use with the plugin's events system.
2069** -------------------------------------------------------------------------- */
2070void RegisterCommands()
2071{
2072 RegServerCmd("tf_dodgeball_explosion", CmdExplosion);
2073 RegServerCmd("tf_dodgeball_shockwave", CmdShockwave);
2074 RegServerCmd("tf_dodgeball_resize", CmdResize);
2075}
2076
2077public Action CmdResize(int iIndex)
2078{
2079 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
2080 if (iEntity && IsValidEntity(iEntity) && g_bRocketIsNuke[iEntity])
2081 {
2082 SetEntPropFloat(iEntity, Prop_Send, "m_flModelScale", (4.0));
2083 }
2084}
2085
2086/* CmdExplosion()
2087**
2088** Creates a huge explosion at the location of the client.
2089** -------------------------------------------------------------------------- */
2090public Action CmdExplosion(int iArgs)
2091{
2092 if (iArgs == 1)
2093 {
2094 char strBuffer[8], iClient;
2095 GetCmdArg(1, strBuffer, sizeof(strBuffer));
2096 iClient = StringToInt(strBuffer);
2097 if (IsValidEntity(iClient))
2098 {
2099 float fPosition[3];
2100 GetClientAbsOrigin(iClient, fPosition);
2101 switch (GetURandomIntRange(0, 4))
2102 {
2103 case 0:
2104 {
2105 PlayParticle(fPosition, PARTICLE_NUKE_1_ANGLES, PARTICLE_NUKE_1);
2106 }
2107 case 1:
2108 {
2109 PlayParticle(fPosition, PARTICLE_NUKE_2_ANGLES, PARTICLE_NUKE_2);
2110 }
2111 case 2:
2112 {
2113 PlayParticle(fPosition, PARTICLE_NUKE_3_ANGLES, PARTICLE_NUKE_3);
2114 }
2115 case 3:
2116 {
2117 PlayParticle(fPosition, PARTICLE_NUKE_4_ANGLES, PARTICLE_NUKE_4);
2118 }
2119 case 4:
2120 {
2121 PlayParticle(fPosition, PARTICLE_NUKE_5_ANGLES, PARTICLE_NUKE_5);
2122 }
2123 }
2124 PlayParticle(fPosition, PARTICLE_NUKE_COLLUMN_ANGLES, PARTICLE_NUKE_COLLUMN);
2125 }
2126 }
2127 else
2128 {
2129 PrintToServer("Usage: tf_dodgeball_explosion <client index>");
2130 }
2131
2132 return Plugin_Handled;
2133}
2134
2135/* CmdShockwave()
2136**
2137** Creates a huge shockwave at the location of the client, with the given
2138** parameters.
2139** -------------------------------------------------------------------------- */
2140public Action CmdShockwave(int iArgs)
2141{
2142 if (iArgs == 5)
2143 {
2144 char strBuffer[8];
2145 int iClient;
2146 int iTeam;
2147 float fPosition[3];
2148 int iDamage;
2149 float fPushStrength;
2150 float fRadius;
2151 float fFalloffRadius;
2152 GetCmdArg(1, strBuffer, sizeof(strBuffer)); iClient = StringToInt(strBuffer);
2153 GetCmdArg(2, strBuffer, sizeof(strBuffer)); iDamage = StringToInt(strBuffer);
2154 GetCmdArg(3, strBuffer, sizeof(strBuffer)); fPushStrength = StringToFloat(strBuffer);
2155 GetCmdArg(4, strBuffer, sizeof(strBuffer)); fRadius = StringToFloat(strBuffer);
2156 GetCmdArg(5, strBuffer, sizeof(strBuffer)); fFalloffRadius = StringToFloat(strBuffer);
2157
2158 if (IsValidClient(iClient))
2159 {
2160 iTeam = GetClientTeam(iClient);
2161 GetClientAbsOrigin(iClient, fPosition);
2162
2163 for (iClient = 1; iClient <= MaxClients; iClient++)
2164 {
2165 if ((IsValidClient(iClient, true) == true) && (GetClientTeam(iClient) == iTeam))
2166 {
2167 float fPlayerPosition[3]; GetClientEyePosition(iClient, fPlayerPosition);
2168 float fDistanceToShockwave = GetVectorDistance(fPosition, fPlayerPosition);
2169
2170 if (fDistanceToShockwave < fRadius)
2171 {
2172 float fImpulse[3];
2173 float fFinalPush;
2174 int iFinalDamage;
2175 fImpulse[0] = fPlayerPosition[0] - fPosition[0];
2176 fImpulse[1] = fPlayerPosition[1] - fPosition[1];
2177 fImpulse[2] = fPlayerPosition[2] - fPosition[2];
2178 NormalizeVector(fImpulse, fImpulse);
2179 if (fImpulse[2] < 0.4) { fImpulse[2] = 0.4; NormalizeVector(fImpulse, fImpulse); }
2180
2181 if (fDistanceToShockwave < fFalloffRadius)
2182 {
2183 fFinalPush = fPushStrength;
2184 iFinalDamage = iDamage;
2185 }
2186 else
2187 {
2188 float fImpact = (1.0 - ((fDistanceToShockwave - fFalloffRadius) / (fRadius - fFalloffRadius)));
2189 fFinalPush = fImpact * fPushStrength;
2190 iFinalDamage = RoundToFloor(fImpact * iDamage);
2191 }
2192 ScaleVector(fImpulse, fFinalPush);
2193 SetEntPropVector(iClient, Prop_Data, "m_vecAbsVelocity", fImpulse);
2194
2195 Handle hDamage = CreateDataPack();
2196 WritePackCell(hDamage, iClient);
2197 WritePackCell(hDamage, iFinalDamage);
2198 CreateTimer(0.1, ApplyDamage, hDamage, TIMER_FLAG_NO_MAPCHANGE);
2199 }
2200 }
2201 }
2202 }
2203 }
2204 else
2205 {
2206 PrintToServer("Usage: tf_dodgeball_shockwave <client index> <damage> <push strength> <radius> <falloff>");
2207 }
2208
2209 return Plugin_Handled;
2210}
2211
2212/* ExecuteCommands()
2213**
2214** The core of the plugin's event system, unpacks and correctly formats the
2215** given command strings to be executed.
2216** -------------------------------------------------------------------------- */
2217void ExecuteCommands(Handle hDataPack, int iClass, int iRocket, int iOwner, int iTarget, int iLastDead, float fSpeed, int iNumDeflections)
2218{
2219 ResetPack(hDataPack, false);
2220 int iNumCommands = ReadPackCell(hDataPack);
2221 while (iNumCommands-- > 0)
2222 {
2223 char strCmd[256];
2224 char strBuffer[8];
2225 ReadPackString(hDataPack, strCmd, sizeof(strCmd));
2226 ReplaceString(strCmd, sizeof(strCmd), "@name", g_strRocketClassLongName[iClass]);
2227 Format(strBuffer, sizeof(strBuffer), "%i", iRocket); ReplaceString(strCmd, sizeof(strCmd), "@rocket", strBuffer);
2228 Format(strBuffer, sizeof(strBuffer), "%i", iOwner); ReplaceString(strCmd, sizeof(strCmd), "@owner", strBuffer);
2229 Format(strBuffer, sizeof(strBuffer), "%i", iTarget); ReplaceString(strCmd, sizeof(strCmd), "@target", strBuffer);
2230 Format(strBuffer, sizeof(strBuffer), "%i", iLastDead); ReplaceString(strCmd, sizeof(strCmd), "@dead", strBuffer);
2231 Format(strBuffer, sizeof(strBuffer), "%f", fSpeed); ReplaceString(strCmd, sizeof(strCmd), "@speed", strBuffer);
2232 Format(strBuffer, sizeof(strBuffer), "%i", iNumDeflections); ReplaceString(strCmd, sizeof(strCmd), "@deflections", strBuffer);
2233 ServerCommand(strCmd);
2234 }
2235}
2236
2237/*
2238**����������������������������������������������������������������������������������
2239** ______ _____
2240** / ____/___ ____ / __(_)___ _
2241** / / / __ \/ __ \/ /_/ / __ `/
2242** / /___/ /_/ / / / / __/ / /_/ /
2243** \____/\____/_/ /_/_/ /_/\__, /
2244** /____/
2245**����������������������������������������������������������������������������������
2246*/
2247
2248/* ParseConfiguration()
2249**
2250** Parses a Dodgeball configuration file. It doesn't clear any of the previous
2251** data, so multiple files can be parsed.
2252** -------------------------------------------------------------------------- */
2253bool ParseConfigurations(char strConfigFile[] = "general.cfg")
2254{
2255 // Parse configuration
2256 char strPath[PLATFORM_MAX_PATH];
2257 char strFileName[PLATFORM_MAX_PATH];
2258 Format(strFileName, sizeof(strFileName), "configs/dodgeball/%s", strConfigFile);
2259 BuildPath(Path_SM, strPath, sizeof(strPath), strFileName);
2260
2261 // Try to parse if it exists
2262 LogMessage("Executing configuration file %s", strPath);
2263 if (FileExists(strPath, true))
2264 {
2265 KeyValues kvConfig = CreateKeyValues("TF2_Dodgeball");
2266
2267 if (FileToKeyValues(kvConfig, strPath) == false)
2268 SetFailState("Error while parsing the configuration file.");
2269
2270 kvConfig.GotoFirstSubKey();
2271
2272 // Parse the subsections
2273 do
2274 {
2275 char strSection[64];
2276 KvGetSectionName(kvConfig, strSection, sizeof(strSection));
2277
2278 if (StrEqual(strSection, "general"))
2279 ParseGeneral(kvConfig);
2280 else if (StrEqual(strSection, "classes"))
2281 ParseClasses(kvConfig);
2282 else if (StrEqual(strSection, "spawners"))
2283 ParseSpawners(kvConfig);
2284 }
2285 while (KvGotoNextKey(kvConfig));
2286
2287 CloseHandle(kvConfig);
2288 }
2289}
2290
2291/* ParseGeneral()
2292**
2293** Parses general settings, such as the music, urls, etc.
2294** -------------------------------------------------------------------------- */
2295void ParseGeneral(Handle kvConfig)
2296{
2297 g_bMusicEnabled = view_as<bool>(KvGetNum(kvConfig, "music", 0));
2298 if (g_bMusicEnabled == true)
2299 {
2300 g_bUseWebPlayer = view_as<bool>(KvGetNum(kvConfig, "use web player", 0));
2301 KvGetString(kvConfig, "web player url", g_strWebPlayerUrl, sizeof(g_strWebPlayerUrl));
2302
2303 g_bMusic[Music_RoundStart] = KvGetString(kvConfig, "round start", g_strMusic[Music_RoundStart], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundStart]);
2304 g_bMusic[Music_RoundWin] = KvGetString(kvConfig, "round end (win)", g_strMusic[Music_RoundWin], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundWin]);
2305 g_bMusic[Music_RoundLose] = KvGetString(kvConfig, "round end (lose)", g_strMusic[Music_RoundLose], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundLose]);
2306 g_bMusic[Music_Gameplay] = KvGetString(kvConfig, "gameplay", g_strMusic[Music_Gameplay], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_Gameplay]);
2307 }
2308}
2309
2310/* ParseClasses()
2311**
2312** Parses the rocket classes data from the given configuration file.
2313** -------------------------------------------------------------------------- */
2314void ParseClasses(Handle kvConfig)
2315{
2316 char strName[64];
2317 char strBuffer[256];
2318
2319 KvGotoFirstSubKey(kvConfig);
2320 do
2321 {
2322 int iIndex = g_iRocketClassCount;
2323 RocketFlags iFlags;
2324
2325 // Basic parameters
2326 KvGetSectionName(kvConfig, strName, sizeof(strName)); strcopy(g_strRocketClassName[iIndex], 16, strName);
2327 KvGetString(kvConfig, "name", strBuffer, sizeof(strBuffer)); strcopy(g_strRocketClassLongName[iIndex], 32, strBuffer);
2328 if (KvGetString(kvConfig, "model", strBuffer, sizeof(strBuffer)))
2329 {
2330 strcopy(g_strRocketClassModel[iIndex], PLATFORM_MAX_PATH, strBuffer);
2331 if (strlen(g_strRocketClassModel[iIndex]) != 0)
2332 {
2333 iFlags |= RocketFlag_CustomModel;
2334 if (KvGetNum(kvConfig, "is animated", 0))iFlags |= RocketFlag_IsAnimated;
2335 }
2336 }
2337
2338 KvGetString(kvConfig, "behaviour", strBuffer, sizeof(strBuffer), "homing");
2339 if (StrEqual(strBuffer, "homing"))g_iRocketClassBehaviour[iIndex] = Behaviour_Homing;
2340 else g_iRocketClassBehaviour[iIndex] = Behaviour_Unknown;
2341
2342 if (KvGetNum(kvConfig, "play spawn sound", 0) == 1)
2343 {
2344 iFlags |= RocketFlag_PlaySpawnSound;
2345 if (KvGetString(kvConfig, "spawn sound", g_strRocketClassSpawnSound[iIndex], PLATFORM_MAX_PATH) && (strlen(g_strRocketClassSpawnSound[iIndex]) != 0))
2346 {
2347 iFlags |= RocketFlag_CustomSpawnSound;
2348 }
2349 }
2350
2351 if (KvGetNum(kvConfig, "play beep sound", 0) == 1)
2352 {
2353 iFlags |= RocketFlag_PlayBeepSound;
2354 g_fRocketClassBeepInterval[iIndex] = KvGetFloat(kvConfig, "beep interval", 0.5);
2355 if (KvGetString(kvConfig, "beep sound", g_strRocketClassBeepSound[iIndex], PLATFORM_MAX_PATH) && (strlen(g_strRocketClassBeepSound[iIndex]) != 0))
2356 {
2357 iFlags |= RocketFlag_CustomBeepSound;
2358 }
2359 }
2360
2361 if (KvGetNum(kvConfig, "play alert sound", 0) == 1)
2362 {
2363 iFlags |= RocketFlag_PlayAlertSound;
2364 if (KvGetString(kvConfig, "alert sound", g_strRocketClassAlertSound[iIndex], PLATFORM_MAX_PATH) && strlen(g_strRocketClassAlertSound[iIndex]) != 0)
2365 {
2366 iFlags |= RocketFlag_CustomAlertSound;
2367 }
2368 }
2369
2370 // Behaviour modifiers
2371 if (KvGetNum(kvConfig, "elevate on deflect", 1) == 1)iFlags |= RocketFlag_ElevateOnDeflect;
2372 if (KvGetNum(kvConfig, "neutral rocket", 0) == 1)iFlags |= RocketFlag_IsNeutral;
2373
2374 // Movement parameters
2375 g_fRocketClassDamage[iIndex] = KvGetFloat(kvConfig, "damage");
2376 g_fRocketClassDamageIncrement[iIndex] = KvGetFloat(kvConfig, "damage increment");
2377 g_fRocketClassCritChance[iIndex] = KvGetFloat(kvConfig, "critical chance");
2378 g_fRocketClassSpeed[iIndex] = KvGetFloat(kvConfig, "speed");
2379 g_fSavedSpeed = g_fRocketClassSpeed[iIndex];
2380 g_fRocketClassSpeedIncrement[iIndex] = KvGetFloat(kvConfig, "speed increment");
2381 g_fSavedSpeedIncrement = g_fRocketClassSpeedIncrement[iIndex];
2382 g_fRocketClassTurnRate[iIndex] = KvGetFloat(kvConfig, "turn rate");
2383 g_fRocketClassTurnRateIncrement[iIndex] = KvGetFloat(kvConfig, "turn rate increment");
2384 g_fRocketClassElevationRate[iIndex] = KvGetFloat(kvConfig, "elevation rate");
2385 g_fRocketClassElevationLimit[iIndex] = KvGetFloat(kvConfig, "elevation limit");
2386 g_fRocketClassControlDelay[iIndex] = KvGetFloat(kvConfig, "control delay");
2387 g_fRocketClassPlayerModifier[iIndex] = KvGetFloat(kvConfig, "no. players modifier");
2388 g_fRocketClassRocketsModifier[iIndex] = KvGetFloat(kvConfig, "no. rockets modifier");
2389 g_fRocketClassTargetWeight[iIndex] = KvGetFloat(kvConfig, "direction to target weight");
2390
2391 // Events
2392 Handle hCmds = INVALID_HANDLE;
2393 KvGetString(kvConfig, "on spawn", strBuffer, sizeof(strBuffer));
2394 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnSpawnCmd; g_hRocketClassCmdsOnSpawn[iIndex] = hCmds; }
2395 KvGetString(kvConfig, "on deflect", strBuffer, sizeof(strBuffer));
2396 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnDeflectCmd; g_hRocketClassCmdsOnDeflect[iIndex] = hCmds; }
2397 KvGetString(kvConfig, "on kill", strBuffer, sizeof(strBuffer));
2398 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnKillCmd; g_hRocketClassCmdsOnKill[iIndex] = hCmds; }
2399 KvGetString(kvConfig, "on explode", strBuffer, sizeof(strBuffer));
2400 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnExplodeCmd; g_hRocketClassCmdsOnExplode[iIndex] = hCmds; }
2401
2402 // Done
2403 SetTrieValue(g_hRocketClassTrie, strName, iIndex);
2404 g_iRocketClassFlags[iIndex] = iFlags;
2405 g_iRocketClassCount++;
2406 }
2407 while (KvGotoNextKey(kvConfig));
2408 KvGoBack(kvConfig);
2409}
2410
2411/* ParseSpawners()
2412**
2413** Parses the spawn points classes data from the given configuration file.
2414** -------------------------------------------------------------------------- */
2415void ParseSpawners(KeyValues kvConfig)
2416{
2417 kvConfig.JumpToKey("spawners"); //jump to spawners section
2418 char strBuffer[256];
2419 kvConfig.GotoFirstSubKey(); //goto to first subkey of "spawners" section
2420
2421 do
2422 {
2423 int iIndex = g_iSpawnersCount;
2424
2425 // Basic parameters
2426 kvConfig.GetSectionName(strBuffer, sizeof(strBuffer)); //okay, here we got section name, as example, red
2427 strcopy(g_strSpawnersName[iIndex], 32, strBuffer); //here we copied it to the g_strSpawnersName array
2428 g_iSpawnersMaxRockets[iIndex] = kvConfig.GetNum("max rockets", 1); //get some values...
2429 g_fSpawnersInterval[iIndex] = kvConfig.GetFloat("interval", 1.0);
2430
2431 // Chances table
2432 g_hSpawnersChancesTable[iIndex] = CreateArray(); //not interested in this
2433 for (int iClassIndex = 0; iClassIndex < g_iRocketClassCount; iClassIndex++)
2434 {
2435 Format(strBuffer, sizeof(strBuffer), "%s%%", g_strRocketClassName[iClassIndex]);
2436 PushArrayCell(g_hSpawnersChancesTable[iIndex], KvGetNum(kvConfig, strBuffer, 0));
2437 if (KvGetNum(kvConfig, strBuffer, 0) == 100) strcopy(g_strSavedClassName, sizeof(g_strSavedClassName), g_strRocketClassLongName[iClassIndex]);
2438 }
2439
2440 // Done.
2441 SetTrieValue(g_hSpawnersTrie, g_strSpawnersName[iIndex], iIndex); //okay, push section name to g_hSpawnersTrie
2442 g_iSpawnersCount++;
2443 } while (kvConfig.GotoNextKey());
2444
2445 kvConfig.Rewind(); //rewind
2446
2447 GetTrieValue(g_hSpawnersTrie, "Red", g_iDefaultRedSpawner); //get value by section name, section name exists in the g_hSpawnersTrie, everything should work
2448 GetTrieValue(g_hSpawnersTrie, "Blue", g_iDefaultBluSpawner);
2449}
2450
2451/* ParseCommands()
2452**
2453** Part of the event system, parses the given command strings and packs them
2454** to a Datapack.
2455** -------------------------------------------------------------------------- */
2456Handle ParseCommands(char[] strLine)
2457{
2458 TrimString(strLine);
2459 if (strlen(strLine) == 0)
2460 {
2461 return INVALID_HANDLE;
2462 }
2463 else
2464 {
2465 char strStrings[8][255];
2466 int iNumStrings = ExplodeString(strLine, ";", strStrings, 8, 255);
2467
2468 Handle hDataPack = CreateDataPack();
2469 WritePackCell(hDataPack, iNumStrings);
2470 for (int i = 0; i < iNumStrings; i++)
2471 {
2472 WritePackString(hDataPack, strStrings[i]);
2473 }
2474
2475 return hDataPack;
2476 }
2477}
2478
2479/*
2480**����������������������������������������������������������������������������������
2481** ______ __
2482** /_ __/___ ____ / /____
2483** / / / __ \/ __ \/ / ___/
2484** / / / /_/ / /_/ / (__ )
2485** /_/ \____/\____/_/____/
2486**
2487**����������������������������������������������������������������������������������
2488*/
2489
2490/* ApplyDamage()
2491**
2492** Applies a damage to a player.
2493** -------------------------------------------------------------------------- */
2494public Action ApplyDamage(Handle hTimer, any hDataPack)
2495{
2496 ResetPack(hDataPack, false);
2497 int iClient = ReadPackCell(hDataPack);
2498 int iDamage = ReadPackCell(hDataPack);
2499 CloseHandle(hDataPack);
2500 SlapPlayer(iClient, iDamage, true);
2501}
2502
2503/* CopyVectors()
2504**
2505** Copies the contents from a vector to another.
2506** -------------------------------------------------------------------------- */
2507stock void CopyVectors(float fFrom[3], float fTo[3])
2508{
2509 fTo[0] = fFrom[0];
2510 fTo[1] = fFrom[1];
2511 fTo[2] = fFrom[2];
2512}
2513
2514/* LerpVectors()
2515**
2516** Calculates the linear interpolation of the two given vectors and stores
2517** it on the third one.
2518** -------------------------------------------------------------------------- */
2519stock void LerpVectors(float fA[3], float fB[3], float fC[3], float t)
2520{
2521 if (t < 0.0)t = 0.0;
2522 if (t > 1.0)t = 1.0;
2523
2524 fC[0] = fA[0] + (fB[0] - fA[0]) * t;
2525 fC[1] = fA[1] + (fB[1] - fA[1]) * t;
2526 fC[2] = fA[2] + (fB[2] - fA[2]) * t;
2527}
2528
2529/* IsValidClient()
2530**
2531** Checks if the given client index is valid, and if it's alive or not.
2532** -------------------------------------------------------------------------- */
2533stock bool IsValidClient(int iClient, bool bAlive = false)
2534{
2535 if (iClient >= 1 &&
2536 iClient <= MaxClients &&
2537 IsClientConnected(iClient) &&
2538 IsClientInGame(iClient) &&
2539 (bAlive == false || IsPlayerAlive(iClient)))
2540 {
2541 return true;
2542 }
2543
2544 return false;
2545}
2546
2547/* BothTeamsPlaying()
2548**
2549** Checks if there are players on both teams.
2550** -------------------------------------------------------------------------- */
2551stock bool BothTeamsPlaying()
2552{
2553 bool bRedFound;
2554 bool bBluFound;
2555 for (int iClient = 1; iClient <= MaxClients; iClient++)
2556 {
2557 if (IsValidClient(iClient, true) == false)continue;
2558 int iTeam = GetClientTeam(iClient);
2559 if (iTeam == view_as<int>(TFTeam_Red))bRedFound = true;
2560 if (iTeam == view_as<int>(TFTeam_Blue))bBluFound = true;
2561 }
2562 return bRedFound && bBluFound;
2563}
2564
2565/* CountAlivePlayers()
2566**
2567** Retrieves the number of players alive.
2568** -------------------------------------------------------------------------- */
2569stock int CountAlivePlayers()
2570{
2571 int iCount = 0;
2572 for (int iClient = 1; iClient <= MaxClients; iClient++)
2573 {
2574 if (IsValidClient(iClient, true))iCount++;
2575 }
2576 return iCount;
2577}
2578
2579/* SelectTarget()
2580**
2581** Determines a random target of the given team for the homing rocket.
2582** -------------------------------------------------------------------------- */
2583stock int SelectTarget(int iTeam, int iRocket = -1)
2584{
2585 int iTarget = -1;
2586 float fTargetWeight = 0.0;
2587 float fRocketPosition[3];
2588 float fRocketDirection[3];
2589 float fWeight;
2590 bool bUseRocket;
2591
2592 if (iRocket != -1)
2593 {
2594 int iClass = g_iRocketClass[iRocket];
2595 int iEntity = EntRefToEntIndex(g_iRocketEntity[iRocket]);
2596
2597 GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fRocketPosition);
2598 CopyVectors(g_fRocketDirection[iRocket], fRocketDirection);
2599 fWeight = g_fRocketClassTargetWeight[iClass];
2600
2601 bUseRocket = true;
2602 }
2603
2604 for (int iClient = 1; iClient <= MaxClients; iClient++)
2605 {
2606 // If the client isn't connected, skip.
2607 if (!IsValidClient(iClient, true))continue;
2608 if (iTeam && GetClientTeam(iClient) != iTeam)continue;
2609
2610 // Determine if this client should be the target.
2611 float fNewWeight = GetURandomFloatRange(0.0, 100.0);
2612
2613 if (bUseRocket == true)
2614 {
2615 float fClientPosition[3]; GetClientEyePosition(iClient, fClientPosition);
2616 float fDirectionToClient[3]; MakeVectorFromPoints(fRocketPosition, fClientPosition, fDirectionToClient);
2617 fNewWeight += GetVectorDotProduct(fRocketDirection, fDirectionToClient) * fWeight;
2618 }
2619
2620 if ((iTarget == -1) || fNewWeight >= fTargetWeight)
2621 {
2622 iTarget = iClient;
2623 fTargetWeight = fNewWeight;
2624 }
2625 }
2626
2627 return iTarget;
2628}
2629
2630/* StopSoundToAll()
2631**
2632** Stops a sound for all the clients on the given channel.
2633** -------------------------------------------------------------------------- */
2634stock void StopSoundToAll(int iChannel, const char[] strSound)
2635{
2636 for (int iClient = 1; iClient <= MaxClients; iClient++)
2637 {
2638 if (IsValidClient(iClient))StopSound(iClient, iChannel, strSound);
2639 }
2640}
2641
2642/* PlayParticle()
2643**
2644** Plays a particle system at the given location & angles.
2645** -------------------------------------------------------------------------- */
2646stock void PlayParticle(float fPosition[3], float fAngles[3], char[] strParticleName, float fEffectTime = 5.0, float fLifeTime = 9.0)
2647{
2648 int iEntity = CreateEntityByName("info_particle_system");
2649 if (iEntity && IsValidEdict(iEntity))
2650 {
2651 TeleportEntity(iEntity, fPosition, fAngles, NULL_VECTOR);
2652 DispatchKeyValue(iEntity, "effect_name", strParticleName);
2653 ActivateEntity(iEntity);
2654 AcceptEntityInput(iEntity, "Start");
2655 CreateTimer(fEffectTime, StopParticle, EntIndexToEntRef(iEntity));
2656 CreateTimer(fLifeTime, KillParticle, EntIndexToEntRef(iEntity));
2657 }
2658 else
2659 {
2660 LogError("ShowParticle: could not create info_particle_system");
2661 }
2662}
2663
2664/* StopParticle()
2665**
2666** Turns of the particle system. Automatically called by PlayParticle
2667** -------------------------------------------------------------------------- */
2668public Action StopParticle(Handle hTimer, any iEntityRef)
2669{
2670 if (iEntityRef != INVALID_ENT_REFERENCE)
2671 {
2672 int iEntity = EntRefToEntIndex(iEntityRef);
2673 if (iEntity && IsValidEntity(iEntity))
2674 {
2675 AcceptEntityInput(iEntity, "Stop");
2676 }
2677 }
2678}
2679
2680/* KillParticle()
2681**
2682** Destroys the particle system. Automatically called by PlayParticle
2683** -------------------------------------------------------------------------- */
2684public Action KillParticle(Handle hTimer, any iEntityRef)
2685{
2686 if (iEntityRef != INVALID_ENT_REFERENCE)
2687 {
2688 int iEntity = EntRefToEntIndex(iEntityRef);
2689 if (iEntity && IsValidEntity(iEntity))
2690 {
2691 RemoveEdict(iEntity);
2692 }
2693 }
2694}
2695
2696/* PrecacheParticle()
2697**
2698** Forces the client to precache a particle system.
2699** -------------------------------------------------------------------------- */
2700stock void PrecacheParticle(char[] strParticleName)
2701{
2702 PlayParticle(view_as<float>( { 0.0, 0.0, 0.0 } ), view_as<float>( { 0.0, 0.0, 0.0 } ), strParticleName, 0.1, 0.1);
2703}
2704
2705/* FindEntityByClassnameSafe()
2706**
2707** Used to iterate through entity types, avoiding problems in cases where
2708** the entity may not exist anymore.
2709** -------------------------------------------------------------------------- */
2710stock void FindEntityByClassnameSafe(int iStart, const char[] strClassname)
2711{
2712 while (iStart > -1 && !IsValidEntity(iStart))
2713 {
2714 iStart--;
2715 }
2716 return FindEntityByClassname(iStart, strClassname);
2717}
2718
2719/* GetAnalogueTeam()
2720**
2721** Gets the analogue team for this. In case of Red, it's Blue, and viceversa.
2722** -------------------------------------------------------------------------- */
2723stock int GetAnalogueTeam(int iTeam)
2724{
2725 if (iTeam == view_as<int>(TFTeam_Red))return view_as<int>(TFTeam_Blue);
2726 return view_as<int>(TFTeam_Red);
2727}
2728
2729/* ShowHiddenMOTDPanel()
2730**
2731** Shows a hidden MOTD panel, useful for streaming music.
2732** -------------------------------------------------------------------------- */
2733stock void ShowHiddenMOTDPanel(int iClient, char[] strTitle, char[] strMsg, char[] strType = "2")
2734{
2735 Handle hPanel = CreateKeyValues("data");
2736 KvSetString(hPanel, "title", strTitle);
2737 KvSetString(hPanel, "type", strType);
2738 KvSetString(hPanel, "msg", strMsg);
2739 ShowVGUIPanel(iClient, "info", hPanel, false);
2740 CloseHandle(hPanel);
2741}
2742
2743/* PrecacheSoundEx()
2744**
2745** Precaches a sound and adds it to the download table.
2746** -------------------------------------------------------------------------- */
2747stock void PrecacheSoundEx(char[] strFileName, bool bPreload = false, bool bAddToDownloadTable = false)
2748{
2749 char strFinalPath[PLATFORM_MAX_PATH];
2750 Format(strFinalPath, sizeof(strFinalPath), "sound/%s", strFileName);
2751 PrecacheSound(strFileName, bPreload);
2752 if (bAddToDownloadTable == true)AddFileToDownloadsTable(strFinalPath);
2753}
2754
2755/* PrecacheModelEx()
2756**
2757** Precaches a models and adds it to the download table.
2758** -------------------------------------------------------------------------- */
2759stock void PrecacheModelEx(char[] strFileName, bool bPreload = false, bool bAddToDownloadTable = false)
2760{
2761 PrecacheModel(strFileName, bPreload);
2762 if (bAddToDownloadTable)
2763 {
2764 char strDepFileName[PLATFORM_MAX_PATH];
2765 Format(strDepFileName, sizeof(strDepFileName), "%s.res", strFileName);
2766
2767 if (FileExists(strDepFileName))
2768 {
2769 // Open stream, if possible
2770 Handle hStream = OpenFile(strDepFileName, "r");
2771 if (hStream == INVALID_HANDLE) { LogMessage("Error, can't read file containing model dependencies."); return; }
2772
2773 while (!IsEndOfFile(hStream))
2774 {
2775 char strBuffer[PLATFORM_MAX_PATH];
2776 ReadFileLine(hStream, strBuffer, sizeof(strBuffer));
2777 CleanString(strBuffer);
2778
2779 // If file exists...
2780 if (FileExists(strBuffer, true))
2781 {
2782 // Precache depending on type, and add to download table
2783 if (StrContains(strBuffer, ".vmt", false) != -1)PrecacheDecal(strBuffer, true);
2784 else if (StrContains(strBuffer, ".mdl", false) != -1)PrecacheModel(strBuffer, true);
2785 else if (StrContains(strBuffer, ".pcf", false) != -1)PrecacheGeneric(strBuffer, true);
2786 AddFileToDownloadsTable(strBuffer);
2787 }
2788 }
2789
2790 // Close file
2791 CloseHandle(hStream);
2792 }
2793 }
2794}
2795
2796/* CleanString()
2797**
2798** Cleans the given string from any illegal character.
2799** -------------------------------------------------------------------------- */
2800stock void CleanString(char[] strBuffer)
2801{
2802 // Cleanup any illegal characters
2803 int Length = strlen(strBuffer);
2804 for (int iPos = 0; iPos < Length; iPos++)
2805 {
2806 switch (strBuffer[iPos])
2807 {
2808 case '\r':strBuffer[iPos] = ' ';
2809 case '\n':strBuffer[iPos] = ' ';
2810 case '\t':strBuffer[iPos] = ' ';
2811 }
2812 }
2813
2814 // Trim string
2815 TrimString(strBuffer);
2816}
2817
2818/* FMax()
2819**
2820** Returns the maximum of the two values given.
2821** -------------------------------------------------------------------------- */
2822stock float FMax(float a, float b)
2823{
2824 return (a > b) ? a:b;
2825}
2826
2827/* FMin()
2828**
2829** Returns the minimum of the two values given.
2830** -------------------------------------------------------------------------- */
2831stock float FMin(float a, float b)
2832{
2833 return (a < b) ? a:b;
2834}
2835
2836/* GetURandomIntRange()
2837**
2838**
2839** -------------------------------------------------------------------------- */
2840stock int GetURandomIntRange(const int iMin, const int iMax)
2841{
2842 return iMin + (GetURandomInt() % (iMax - iMin + 1));
2843}
2844
2845/* GetURandomFloatRange()
2846**
2847**
2848** -------------------------------------------------------------------------- */
2849stock float GetURandomFloatRange(float fMin, float fMax)
2850{
2851 return fMin + (GetURandomFloat() * (fMax - fMin));
2852}
2853
2854// Pyro vision
2855public void tf2dodgeball_hooks(Handle convar, const char[] oldValue, const char[] newValue)
2856{
2857 if (GetConVarBool(g_hCvarPyroVisionEnabled))
2858 {
2859 for (int i = 1; i <= MaxClients; ++i)
2860 {
2861 if (IsClientInGame(i))
2862 {
2863 TF2Attrib_SetByName(i, PYROVISION_ATTRIBUTE, 1.0);
2864 }
2865 }
2866 }
2867 else
2868 {
2869 for (int i = 1; i <= MaxClients; ++i)
2870 {
2871 if (IsClientInGame(i))
2872 {
2873 TF2Attrib_RemoveByName(i, PYROVISION_ATTRIBUTE);
2874 }
2875 }
2876 }
2877 g_config_iMaxBounces = StringToInt(newValue);
2878}
2879
2880// Asherkins RocketBounce
2881
2882public void OnEntityCreated(int entity, const char[] classname)
2883{
2884 if (!StrEqual(classname, "tf_projectile_rocket", false))
2885 return;
2886
2887 if (StrEqual(classname, "tf_projectile_rocket") || StrEqual(classname, "tf_projectile_sentryrocket"))
2888 {
2889 if (IsValidEntity(entity))
2890 {
2891 SetEntPropEnt(entity, Prop_Send, "m_hOriginalLauncher", entity);
2892 SetEntPropEnt(entity, Prop_Send, "m_hLauncher", entity);
2893 }
2894 }
2895
2896 g_nBounces[entity] = 0;
2897 SDKHook(entity, SDKHook_StartTouch, OnStartTouch);
2898}
2899
2900public Action OnStartTouch(int entity, int other)
2901{
2902 if (other > 0 && other <= MaxClients)
2903 return Plugin_Continue;
2904
2905 // Only allow a rocket to bounce x times.
2906 if (g_nBounces[entity] >= g_config_iMaxBounces)
2907 return Plugin_Continue;
2908
2909 SDKHook(entity, SDKHook_Touch, OnTouch);
2910 return Plugin_Handled;
2911}
2912
2913public Action OnTouch(int entity, int other)
2914{
2915 float vOrigin[3];
2916 GetEntPropVector(entity, Prop_Data, "m_vecOrigin", vOrigin);
2917
2918 float vAngles[3];
2919 GetEntPropVector(entity, Prop_Data, "m_angRotation", vAngles);
2920
2921 float vVelocity[3];
2922 GetEntPropVector(entity, Prop_Data, "m_vecAbsVelocity", vVelocity);
2923
2924 Handle trace = TR_TraceRayFilterEx(vOrigin, vAngles, MASK_SHOT, RayType_Infinite, TEF_ExcludeEntity, entity);
2925
2926 if (!TR_DidHit(trace))
2927 {
2928 CloseHandle(trace);
2929 return Plugin_Continue;
2930 }
2931
2932 float vNormal[3];
2933 TR_GetPlaneNormal(trace, vNormal);
2934
2935 //PrintToServer("Surface Normal: [%.2f, %.2f, %.2f]", vNormal[0], vNormal[1], vNormal[2]);
2936
2937 CloseHandle(trace);
2938
2939 float dotProduct = GetVectorDotProduct(vNormal, vVelocity);
2940
2941 ScaleVector(vNormal, dotProduct);
2942 ScaleVector(vNormal, 2.0);
2943
2944 float vBounceVec[3];
2945 SubtractVectors(vVelocity, vNormal, vBounceVec);
2946
2947 float vNewAngles[3];
2948 GetVectorAngles(vBounceVec, vNewAngles);
2949
2950 //PrintToServer("Angles: [%.2f, %.2f, %.2f] -> [%.2f, %.2f, %.2f]", vAngles[0], vAngles[1], vAngles[2], vNewAngles[0], vNewAngles[1], vNewAngles[2]);
2951 //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));
2952
2953 TeleportEntity(entity, NULL_VECTOR, vNewAngles, vBounceVec);
2954
2955 g_nBounces[entity]++;
2956
2957 SDKUnhook(entity, SDKHook_Touch, OnTouch);
2958 return Plugin_Handled;
2959}
2960
2961public bool TEF_ExcludeEntity(int entity, int contentsMask, any data)
2962{
2963 return (entity != data);
2964}
2965
2966void preventAirblast(int clientId, bool prevent)
2967{
2968 int flags;
2969
2970 if (prevent == true)
2971 {
2972 abPrevention[clientId] = true;
2973 flags = GetEntityFlags(clientId) | FL_NOTARGET;
2974 }
2975 else
2976 {
2977 abPrevention[clientId] = false;
2978 flags = GetEntityFlags(clientId) & ~FL_NOTARGET;
2979 }
2980
2981 SetEntityFlags(clientId, flags);
2982}
2983
2984public Action TauntCheck(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom)
2985{
2986 switch (damagecustom)
2987 {
2988 case TF_CUSTOM_TAUNT_ARMAGEDDON:
2989 {
2990 damage = 0.0;
2991 return Plugin_Changed;
2992 }
2993
2994 }
2995 return Plugin_Continue;
2996}
2997
2998void checkStolenRocket(int clientId, int entId)
2999{
3000 if (EntRefToEntIndex(g_iRocketTarget[entId]) != clientId && !bStealArray[clientId][stoleRocket])
3001 {
3002 bStealArray[clientId][stoleRocket] = true;
3003 if (bStealArray[clientId][rocketsStolen] < GetConVarInt(g_hCvarStealPreventionNumber))
3004 {
3005 bStealArray[clientId][rocketsStolen]++;
3006 CreateTimer(0.1, tStealTimer, GetClientUserId(clientId), TIMER_FLAG_NO_MAPCHANGE);
3007 SlapPlayer(clientId, 0, true);
3008 PrintToChat(clientId, "\x03Do not steal rockets. [Warning %i/%i]", bStealArray[clientId][rocketsStolen], GetConVarInt(g_hCvarStealPreventionNumber));
3009 }
3010 else
3011 {
3012 ForcePlayerSuicide(clientId);
3013 PrintToChat(clientId, "\x03You have been slain for stealing rockets.");
3014 PrintToChatAll("\x03%N was slain for stealing rockets.", clientId);
3015 }
3016 }
3017}
3018
3019public Action tStealTimer(Handle hTimer, int iClientUid)
3020{
3021 int iClient = GetClientOfUserId(iClientUid);
3022 bStealArray[iClient][stoleRocket] = false;
3023}
3024
3025/*void AttachParticle(int iEntity, char[] strParticleType)
3026{
3027 int iParticle = CreateEntityByName("info_particle_system");
3028
3029 char strName[128];
3030 if (IsValidEdict(iParticle))
3031 {
3032 float fPos[3];
3033 GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fPos);
3034 fPos[2] += 10;
3035 TeleportEntity(iParticle, fPos, NULL_VECTOR, NULL_VECTOR);
3036
3037 Format(strName, sizeof(strName), "target%i", iEntity);
3038 DispatchKeyValue(iEntity, "targetname", strName);
3039
3040 DispatchKeyValue(iParticle, "targetname", "tf2particle");
3041 DispatchKeyValue(iParticle, "parentname", strName);
3042 DispatchKeyValue(iParticle, "effect_name", strParticleType);
3043 DispatchSpawn(iParticle);
3044 SetVariantString(strName);
3045 AcceptEntityInput(iParticle, "SetParent", iParticle, iParticle, 0);
3046 SetVariantString("");
3047 AcceptEntityInput(iParticle, "SetParentAttachment", iParticle, iParticle, 0);
3048 ActivateEntity(iParticle);
3049 AcceptEntityInput(iParticle, "start");
3050
3051 g_RocketParticle[iEntity] = iParticle;
3052 }
3053}*/
3054
3055stock void CreateTempParticle(char[] particle, int entity = -1, float origin[3] = NULL_VECTOR, float angles[3] = { 0.0, 0.0, 0.0 }, bool resetparticles = false)
3056{
3057 int tblidx = FindStringTable("ParticleEffectNames");
3058
3059 char tmp[256];
3060 int stridx = INVALID_STRING_INDEX;
3061
3062 for (int i = 0; i < GetStringTableNumStrings(tblidx); i++)
3063 {
3064 ReadStringTable(tblidx, i, tmp, sizeof(tmp));
3065 if (StrEqual(tmp, particle, false))
3066 {
3067 stridx = i;
3068 break;
3069 }
3070 }
3071
3072 TE_Start("TFParticleEffect");
3073 TE_WriteFloat("m_vecOrigin[0]", origin[0]);
3074 TE_WriteFloat("m_vecOrigin[1]", origin[1]);
3075 TE_WriteFloat("m_vecOrigin[2]", origin[2]);
3076 TE_WriteVector("m_vecAngles", angles);
3077 TE_WriteNum("m_iParticleSystemIndex", stridx);
3078 TE_WriteNum("entindex", entity);
3079 TE_WriteNum("m_iAttachType", 1);
3080 TE_WriteNum("m_bResetParticles", resetparticles);
3081 TE_SendToAll();
3082}
3083
3084stock void ClearTempParticles(int client)
3085{
3086 float empty[3];
3087 CreateTempParticle("sandwich_fx", client, empty, empty, true);
3088}
3089
3090/*void StolenRocket(int iClient, int iTarget)
3091{
3092 if (iTarget != iClient && GetClientTeam(iClient) == GetClientTeam(iTarget))
3093 {
3094 PrintToChatAll("\x03%N\x01 stole \x03%N\x01's rocket!", iClient, iTarget);
3095 g_stolen[iClient]++;
3096 if (g_stolen[iClient] >= GetConVarInt(g_hCvarStealPreventionNumber))
3097 {
3098 g_stolen[iClient] = 0;
3099 ForcePlayerSuicide(iClient);
3100 PrintToChat(iClient, "\x03You stole %d rockets and was slayed.", g_hCvarStealPreventionNumber);
3101 }
3102 }
3103}*/
3104
3105
3106// EOF