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