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