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