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