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