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