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