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