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