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