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