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