· 6 years ago · Jul 26, 2019, 12:12 AM
1// *********************************************************************************
2// PREPROCESSOR
3// *********************************************************************************
4#pragma semicolon 1 // Force strict semicolon mode.
5
6// *********************************************************************************
7// INCLUDES
8// *********************************************************************************
9#include <sourcemod>
10#include <sdktools>
11#include <sdkhooks>
12#include <tf2>
13#include <tf2_stocks>
14#include <tf2attributes>
15#include <morecolors>
16
17#pragma newdecls required
18
19// *********************************************************************************
20// CONSTANTS
21// *********************************************************************************
22// ---- Plugin-related constants ---------------------------------------------------
23#define PLUGIN_NAME "[TF2] Yet Another Dodgeball Plugin"
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
1752public void OnEntityDestroyed(int entity)
1753{
1754 if (entity == -1)
1755 {
1756 return;
1757 }
1758
1759 if (entity == g_op_rocket && g_bEnabled == true && IsValidEntity(g_observer) && IsValidEntity(g_op_rocket))
1760 {
1761 int rocketIndex = FindRocketByEntity(entity);
1762 int iClass = g_iRocketClass[rocketIndex];
1763 float fSpeed = g_fRocketSpeed[rocketIndex];
1764 int iDeflections = g_iRocketDeflections[rocketIndex];
1765 CPrintToChatAll("%s %sThe rocket exploded %swhile travelling %s%i %smph!", g_strServerChatTag, g_strKeywordChatColor, g_strMainChatColor, g_strKeywordChatColor, g_iRocketSpeed, g_strMainChatColor);
1766 if ((g_iRocketFlags[rocketIndex] & RocketFlag_OnExplodeCmd) && !(g_iRocketFlags[rocketIndex] & RocketFlag_Exploded))
1767 {
1768 ExecuteCommands(g_hRocketClassCmdsOnExplode[iClass], iClass, entity, entity, entity, g_iLastDeadClient, fSpeed, iDeflections);
1769 g_iRocketFlags[rocketIndex] |= RocketFlag_Exploded;
1770 }
1771 SetVariantString("");
1772 AcceptEntityInput(g_observer, "ClearParent");
1773 g_op_rocket = -1;
1774
1775 float opPos[3];
1776 float opAng[3];
1777
1778 int spawner = GetRandomInt(0, 1);
1779 if (spawner == 0)
1780 spawner = g_iSpawnPointsRedEntity[0];
1781 else
1782 spawner = g_iSpawnPointsBluEntity[0];
1783
1784 if (IsValidEntity(spawner) && spawner > MAXPLAYERS)
1785 {
1786 GetEntPropVector(spawner, Prop_Data, "m_vecOrigin", opPos);
1787 GetEntPropVector(spawner, Prop_Data, "m_angAbsRotation", opAng);
1788 TeleportEntity(g_observer, opPos, opAng, NULL_VECTOR);
1789 }
1790 }
1791}
1792
1793/* DestroyRocket()
1794**
1795** Destroys the rocket at the given index.
1796** -------------------------------------------------------------------------- */
1797void DestroyRocket(int iIndex)
1798{
1799 if (IsValidRocket(iIndex) == true)
1800 {
1801 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
1802 if (iEntity && IsValidEntity(iEntity))RemoveEdict(iEntity);
1803 g_bRocketIsValid[iIndex] = false;
1804 g_iRocketCount--;
1805 }
1806}
1807
1808/* DestroyRockets()
1809**
1810** Destroys all the rockets that are currently active.
1811** -------------------------------------------------------------------------- */
1812void DestroyRockets()
1813{
1814 for (int iIndex = 0; iIndex < MAX_ROCKETS; iIndex++)
1815 {
1816 DestroyRocket(iIndex);
1817 }
1818 g_iRocketCount = 0;
1819}
1820
1821/* IsValidRocket()
1822**
1823** Checks if a rocket structure is valid.
1824** -------------------------------------------------------------------------- */
1825bool IsValidRocket(int iIndex)
1826{
1827 if ((iIndex >= 0) && (g_bRocketIsValid[iIndex] == true))
1828 {
1829 if (EntRefToEntIndex(g_iRocketEntity[iIndex]) == -1)
1830 {
1831 g_bRocketIsValid[iIndex] = false;
1832 g_iRocketCount--;
1833 return false;
1834 }
1835 return true;
1836 }
1837 return false;
1838}
1839
1840/* FindNextValidRocket()
1841**
1842** Retrieves the index of the next valid rocket from the current offset.
1843** -------------------------------------------------------------------------- */
1844int FindNextValidRocket(int iIndex, bool bWrap = false)
1845{
1846 for (int iCurrent = iIndex + 1; iCurrent < MAX_ROCKETS; iCurrent++)
1847 if (IsValidRocket(iCurrent))
1848 return iCurrent;
1849
1850 return (bWrap == true) ? FindNextValidRocket(-1, false) : -1;
1851}
1852
1853/* FindFreeRocketSlot()
1854**
1855** Retrieves the next free rocket slot since the current one. If all of them
1856** are full, returns -1.
1857** -------------------------------------------------------------------------- */
1858int FindFreeRocketSlot()
1859{
1860 int iIndex = g_iLastCreatedRocket;
1861 int iCurrent = iIndex;
1862
1863 do
1864 {
1865 if (!IsValidRocket(iCurrent))return iCurrent;
1866 if ((++iCurrent) == MAX_ROCKETS)iCurrent = 0;
1867 } while (iCurrent != iIndex);
1868
1869 return -1;
1870}
1871
1872/* FindRocketByEntity()
1873**
1874** Finds a rocket index from it's entity.
1875** -------------------------------------------------------------------------- */
1876int FindRocketByEntity(int iEntity)
1877{
1878 int iIndex = -1;
1879 while ((iIndex = FindNextValidRocket(iIndex)) != -1)
1880 if (EntRefToEntIndex(g_iRocketEntity[iIndex]) == iEntity)
1881 return iIndex;
1882
1883 return -1;
1884}
1885
1886/* HomingRocketThinkg()
1887**
1888** Logic process for the Behaviour_Homing type rockets, wich is simply a
1889** follower rocket, picking a random target.
1890** -------------------------------------------------------------------------- */
1891void HomingRocketThink(int iIndex)
1892{
1893 // Retrieve the rocket's attributes.
1894 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
1895 int iClass = g_iRocketClass[iIndex];
1896 RocketFlags iFlags = g_iRocketFlags[iIndex];
1897 int iTarget = EntRefToEntIndex(g_iRocketTarget[iIndex]);
1898 int iTeam = GetEntProp(iEntity, Prop_Send, "m_iTeamNum", 1);
1899 int iTargetTeam = (TestFlags(iFlags, RocketFlag_IsNeutral)) ? 0 : GetAnalogueTeam(iTeam);
1900 int iDeflectionCount = GetEntProp(iEntity, Prop_Send, "m_iDeflected") - 1;
1901 float fModifier = CalculateModifier(iClass, iDeflectionCount);
1902
1903 // Check if the target is available
1904 if (!IsValidClient(iTarget, true))
1905 {
1906 iTarget = SelectTarget(iTargetTeam);
1907 if (!IsValidClient(iTarget, true))return;
1908 g_iRocketTarget[iIndex] = EntIndexToEntRef(iTarget);
1909
1910 if (GetConVarBool(g_hCvarRedirectBeep))
1911 {
1912 EmitRocketSound(RocketSound_Alert, iClass, iEntity, iTarget, iFlags);
1913 }
1914 }
1915 // Has the rocket been deflected recently? If so, set new target.
1916 else if ((iDeflectionCount > g_iRocketDeflections[iIndex]))
1917 {
1918 // Calculate new direction from the player's forward
1919 int iClient = GetEntPropEnt(iEntity, Prop_Send, "m_hOwnerEntity");
1920 if (IsValidClient(iClient))
1921 {
1922 float fViewAngles[3];
1923 float fDirection[3];
1924 GetClientEyeAngles(iClient, fViewAngles);
1925 GetAngleVectors(fViewAngles, fDirection, NULL_VECTOR, NULL_VECTOR);
1926 CopyVectors(fDirection, g_fRocketDirection[iIndex]);
1927 UpdateRocketSkin(iEntity, iTeam, TestFlags(iFlags, RocketFlag_IsNeutral));
1928 if (GetConVarBool(g_hCvarStealPrevention))
1929 {
1930 checkStolenRocket(iClient, iIndex);
1931 }
1932 }
1933
1934 // Set new target & deflection count
1935 iTarget = SelectTarget(iTargetTeam, iIndex);
1936 g_iRocketTarget[iIndex] = EntIndexToEntRef(iTarget);
1937 g_iRocketDeflections[iIndex] = iDeflectionCount;
1938 g_fRocketLastDeflectionTime[iIndex] = GetGameTime();
1939 g_fRocketSpeed[iIndex] = CalculateRocketSpeed(iClass, fModifier);
1940 g_iRocketSpeed = RoundFloat(g_fRocketSpeed[iIndex] * 0.042614);
1941 g_bPreventingDelay = false;
1942
1943 SetEntDataFloat(iEntity, FindSendPropInfo("CTFProjectile_Rocket", "m_iDeflected") + 4, CalculateRocketDamage(iClass, fModifier), true);
1944 if (TestFlags(iFlags, RocketFlag_ElevateOnDeflect))g_iRocketFlags[iIndex] |= RocketFlag_Elevating;
1945 EmitRocketSound(RocketSound_Alert, iClass, iEntity, iTarget, iFlags);
1946 //Send out temp entity to target
1947 //SendTempEnt(iTarget, "superrare_greenenergy", iEntity, _, _, true);
1948
1949 // Execute appropiate command
1950 if (TestFlags(iFlags, RocketFlag_OnDeflectCmd))
1951 {
1952 ExecuteCommands(g_hRocketClassCmdsOnDeflect[iClass], iClass, iEntity, iClient, iTarget, g_iLastDeadClient, g_fRocketSpeed[iIndex], iDeflectionCount);
1953 }
1954 }
1955 else
1956 {
1957 // If the delay time since the last reflection has been elapsed, rotate towards the client.
1958 if ((GetGameTime() - g_fRocketLastDeflectionTime[iIndex]) >= g_fRocketClassControlDelay[iClass])
1959 {
1960 // Calculate turn rate and retrieve directions.
1961 float fTurnRate = CalculateRocketTurnRate(iClass, fModifier);
1962 float fDirectionToTarget[3]; CalculateDirectionToClient(iEntity, iTarget, fDirectionToTarget);
1963
1964 // Elevate the rocket after a deflection (if it's enabled on the class definition, of course.)
1965 if (g_iRocketFlags[iIndex] & RocketFlag_Elevating)
1966 {
1967 if (g_fRocketDirection[iIndex][2] < g_fRocketClassElevationLimit[iClass])
1968 {
1969 g_fRocketDirection[iIndex][2] = FMin(g_fRocketDirection[iIndex][2] + g_fRocketClassElevationRate[iClass], g_fRocketClassElevationLimit[iClass]);
1970 fDirectionToTarget[2] = g_fRocketDirection[iIndex][2];
1971 }
1972 else
1973 {
1974 g_iRocketFlags[iIndex] &= ~RocketFlag_Elevating;
1975 }
1976 }
1977
1978 // Smoothly change the orientation to the new one.
1979 LerpVectors(g_fRocketDirection[iIndex], fDirectionToTarget, g_fRocketDirection[iIndex], fTurnRate);
1980 }
1981
1982 // If it's a nuke, beep every some time
1983 if ((GetGameTime() - g_fRocketLastBeepTime[iIndex]) >= g_fRocketClassBeepInterval[iClass])
1984 {
1985 g_bRocketIsNuke[iIndex] = true;
1986 EmitRocketSound(RocketSound_Beep, iClass, iEntity, iTarget, iFlags);
1987 g_fRocketLastBeepTime[iIndex] = GetGameTime();
1988 }
1989
1990 if (GetConVarBool(g_hCvarDelayPrevention))
1991 {
1992 checkRoundDelays(iIndex);
1993 }
1994 }
1995
1996 // Done
1997 ApplyRocketParameters(iIndex);
1998}
1999
2000/* CalculateModifier()
2001**
2002** Gets the modifier for the damage/speed/rotation calculations.
2003** -------------------------------------------------------------------------- */
2004float CalculateModifier(int iClass, int iDeflections)
2005{
2006 return iDeflections +
2007 (g_iRocketsFired * g_fRocketClassRocketsModifier[iClass]) +
2008 (g_iPlayerCount * g_fRocketClassPlayerModifier[iClass]);
2009}
2010
2011/* CalculateRocketDamage()
2012**
2013** Calculates the damage of the rocket based on it's type and deflection count.
2014** -------------------------------------------------------------------------- */
2015float CalculateRocketDamage(int iClass, float fModifier)
2016{
2017 return g_fRocketClassDamage[iClass] + g_fRocketClassDamageIncrement[iClass] * fModifier;
2018}
2019
2020/* CalculateRocketSpeed()
2021**
2022** Calculates the speed of the rocket based on it's type and deflection count.
2023** -------------------------------------------------------------------------- */
2024float CalculateRocketSpeed(int iClass, float fModifier)
2025{
2026 return g_fRocketClassSpeed[iClass] + g_fRocketClassSpeedIncrement[iClass] * fModifier;
2027}
2028
2029/* CalculateRocketTurnRate()
2030**
2031** Calculates the rocket's turn rate based upon it's type and deflection count.
2032** -------------------------------------------------------------------------- */
2033float CalculateRocketTurnRate(int iClass, float fModifier)
2034{
2035 return g_fRocketClassTurnRate[iClass] + g_fRocketClassTurnRateIncrement[iClass] * fModifier;
2036}
2037
2038/* CalculateDirectionToClient()
2039**
2040** As the name indicates, calculates the orientation for the rocket to move
2041** towards the specified client.
2042** -------------------------------------------------------------------------- */
2043void CalculateDirectionToClient(int iEntity, int iClient, float fOut[3])
2044{
2045 float fRocketPosition[3]; GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fRocketPosition);
2046 GetClientEyePosition(iClient, fOut);
2047 MakeVectorFromPoints(fRocketPosition, fOut, fOut);
2048 NormalizeVector(fOut, fOut);
2049}
2050
2051/* ApplyRocketParameters()
2052**
2053** Transforms and applies the speed, direction and angles for the rocket
2054** entity.
2055** -------------------------------------------------------------------------- */
2056void ApplyRocketParameters(int iIndex)
2057{
2058 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
2059 float fAngles[3]; GetVectorAngles(g_fRocketDirection[iIndex], fAngles);
2060 float fVelocity[3]; CopyVectors(g_fRocketDirection[iIndex], fVelocity);
2061 ScaleVector(fVelocity, g_fRocketSpeed[iIndex]);
2062 SetEntPropVector(iEntity, Prop_Data, "m_vecAbsVelocity", fVelocity);
2063 SetEntPropVector(iEntity, Prop_Send, "m_angRotation", fAngles);
2064}
2065
2066/* UpdateRocketSkin()
2067**
2068** Changes the skin of the rocket based on it's team.
2069** -------------------------------------------------------------------------- */
2070void UpdateRocketSkin(int iEntity, int iTeam, bool bNeutral)
2071{
2072 if (bNeutral == true)SetEntProp(iEntity, Prop_Send, "m_nSkin", 2);
2073 else SetEntProp(iEntity, Prop_Send, "m_nSkin", (iTeam == view_as<int>(TFTeam_Blue)) ? 0 : 1);
2074}
2075
2076/* GetRandomRocketClass()
2077**
2078** Generates a random value and retrieves a rocket class based upon a chances table.
2079** -------------------------------------------------------------------------- */
2080int GetRandomRocketClass(int iSpawnerClass)
2081{
2082 int iRandom = GetURandomIntRange(0, 101);
2083 Handle hTable = g_hSpawnersChancesTable[iSpawnerClass];
2084 int iTableSize = GetArraySize(hTable);
2085 int iChancesLower = 0;
2086 int iChancesUpper = 0;
2087
2088 for (int iEntry = 0; iEntry < iTableSize; iEntry++)
2089 {
2090 iChancesLower += iChancesUpper;
2091 iChancesUpper = iChancesLower + GetArrayCell(hTable, iEntry);
2092
2093 if ((iRandom >= iChancesLower) && (iRandom < iChancesUpper))
2094 {
2095 return iEntry;
2096 }
2097 }
2098
2099 return 0;
2100}
2101
2102/* EmitRocketSound()
2103**
2104** Emits one of the rocket sounds
2105** -------------------------------------------------------------------------- */
2106void EmitRocketSound(RocketSound iSound, int iClass, int iEntity, int iTarget, RocketFlags iFlags)
2107{
2108 switch (iSound)
2109 {
2110 case RocketSound_Spawn:
2111 {
2112 if (TestFlags(iFlags, RocketFlag_PlaySpawnSound))
2113 {
2114 if (TestFlags(iFlags, RocketFlag_CustomSpawnSound))EmitSoundToAll(g_strRocketClassSpawnSound[iClass], iEntity);
2115 else EmitSoundToAll(SOUND_DEFAULT_SPAWN, iEntity);
2116 }
2117 }
2118 case RocketSound_Beep:
2119 {
2120 if (TestFlags(iFlags, RocketFlag_PlayBeepSound))
2121 {
2122 if (TestFlags(iFlags, RocketFlag_CustomBeepSound))EmitSoundToAll(g_strRocketClassBeepSound[iClass], iEntity);
2123 else EmitSoundToAll(SOUND_DEFAULT_BEEP, iEntity);
2124 }
2125 }
2126 case RocketSound_Alert:
2127 {
2128 if (TestFlags(iFlags, RocketFlag_PlayAlertSound))
2129 {
2130 if (TestFlags(iFlags, RocketFlag_CustomAlertSound))EmitSoundToClient(iTarget, g_strRocketClassAlertSound[iClass]);
2131 else EmitSoundToClient(iTarget, SOUND_DEFAULT_ALERT, _, _, _, _, 0.5);
2132 }
2133 }
2134 }
2135}
2136
2137// ___ _ _ ___ _
2138// | _ \___ __| |_____| |_ / __| |__ _ ______ ___ ___
2139// | / _ \/ _| / / -_) _| | (__| / _` (_-<_-</ -_|_-<
2140// |_|_\___/\__|_\_\___|\__| \___|_\__,_/__/__/\___/__/
2141//
2142
2143/* DestroyRocketClasses()
2144**
2145** Frees up all the rocket classes defined now.
2146** -------------------------------------------------------------------------- */
2147void DestroyRocketClasses()
2148{
2149 for (int iIndex = 0; iIndex < g_iRocketClassCount; iIndex++)
2150 {
2151 Handle hCmdOnSpawn = g_hRocketClassCmdsOnSpawn[iIndex];
2152 Handle hCmdOnKill = g_hRocketClassCmdsOnKill[iIndex];
2153 Handle hCmdOnExplode = g_hRocketClassCmdsOnExplode[iIndex];
2154 Handle hCmdOnDeflect = g_hRocketClassCmdsOnDeflect[iIndex];
2155 if (hCmdOnSpawn != INVALID_HANDLE)CloseHandle(hCmdOnSpawn);
2156 if (hCmdOnKill != INVALID_HANDLE)CloseHandle(hCmdOnKill);
2157 if (hCmdOnExplode != INVALID_HANDLE)CloseHandle(hCmdOnExplode);
2158 if (hCmdOnDeflect != INVALID_HANDLE)CloseHandle(hCmdOnDeflect);
2159 g_hRocketClassCmdsOnSpawn[iIndex] = INVALID_HANDLE;
2160 g_hRocketClassCmdsOnKill[iIndex] = INVALID_HANDLE;
2161 g_hRocketClassCmdsOnExplode[iIndex] = INVALID_HANDLE;
2162 g_hRocketClassCmdsOnDeflect[iIndex] = INVALID_HANDLE;
2163 }
2164 g_iRocketClassCount = 0;
2165 ClearTrie(g_hRocketClassTrie);
2166}
2167
2168// ___ ___ _ _ _ ___ _
2169// / __|_ __ __ ___ __ ___ _ | _ \___(_)_ _| |_ ___ __ _ _ _ __| | / __| |__ _ ______ ___ ___
2170// \__ \ '_ \/ _` \ V V / ' \ | _/ _ \ | ' \ _(_-< / _` | ' \/ _` | | (__| / _` (_-<_-</ -_|_-<
2171// |___/ .__/\__,_|\_/\_/|_||_| |_| \___/_|_||_\__/__/ \__,_|_||_\__,_| \___|_\__,_/__/__/\___/__/
2172// |_|
2173
2174/* DestroySpawners()
2175**
2176** Frees up all the spawner points defined up to now.
2177** -------------------------------------------------------------------------- */
2178void DestroySpawners()
2179{
2180 for (int iIndex = 0; iIndex < g_iSpawnersCount; iIndex++)
2181 {
2182 CloseHandle(g_hSpawnersChancesTable[iIndex]);
2183 }
2184 g_iSpawnersCount = 0;
2185 g_iSpawnPointsRedCount = 0;
2186 g_iSpawnPointsBluCount = 0;
2187 g_iDefaultRedSpawner = -1;
2188 g_iDefaultBluSpawner = -1;
2189 g_strSavedClassName[0] = '\0';
2190 ClearTrie(g_hSpawnersTrie);
2191}
2192
2193/* PopulateSpawnPoints()
2194**
2195** Iterates through all the possible spawn points and assigns them an spawner.
2196** -------------------------------------------------------------------------- */
2197void PopulateSpawnPoints()
2198{
2199 // Clear the current settings
2200 g_iSpawnPointsRedCount = 0;
2201 g_iSpawnPointsBluCount = 0;
2202
2203 // Iterate through all the info target points and check 'em out.
2204 int iEntity = -1;
2205 while ((iEntity = FindEntityByClassname(iEntity, "info_target")) != -1)
2206 {
2207 char strName[32]; GetEntPropString(iEntity, Prop_Data, "m_iName", strName, sizeof(strName));
2208 if ((StrContains(strName, "rocket_spawn_red") != -1) || (StrContains(strName, "tf_dodgeball_red") != -1))
2209 {
2210 // Find most appropiate spawner class for this entity.
2211 int iIndex = FindSpawnerByName(strName);
2212 if (!IsValidRocket(iIndex)) iIndex = g_iDefaultRedSpawner;
2213
2214 // Upload to point list
2215 g_iSpawnPointsRedClass[g_iSpawnPointsRedCount] = iIndex;
2216 g_iSpawnPointsRedEntity[g_iSpawnPointsRedCount] = iEntity;
2217 g_iSpawnPointsRedCount++;
2218 }
2219 if ((StrContains(strName, "rocket_spawn_blue") != -1) || (StrContains(strName, "tf_dodgeball_blu") != -1))
2220 {
2221 // Find most appropiate spawner class for this entity.
2222 int iIndex = FindSpawnerByName(strName);
2223 if (!IsValidRocket(iIndex))iIndex = g_iDefaultBluSpawner;
2224
2225 // Upload to point list
2226 g_iSpawnPointsBluClass[g_iSpawnPointsBluCount] = iIndex;
2227 g_iSpawnPointsBluEntity[g_iSpawnPointsBluCount] = iEntity;
2228 g_iSpawnPointsBluCount++;
2229 }
2230 }
2231
2232 // Check if there exists spawn points
2233 if (g_iSpawnPointsRedCount == 0)
2234 SetFailState("No RED spawn points found on this map.");
2235
2236 if (g_iSpawnPointsBluCount == 0)
2237 SetFailState("No BLU spawn points found on this map.");
2238
2239
2240 //ObserverPoint
2241 float opPos[3];
2242 float opAng[3];
2243
2244 int spawner = GetRandomInt(0, 1);
2245 if (spawner == 0)
2246 spawner = g_iSpawnPointsRedEntity[0];
2247 else
2248 spawner = g_iSpawnPointsBluEntity[0];
2249
2250 if (IsValidEntity(spawner) && spawner > MAXPLAYERS)
2251 {
2252 GetEntPropVector(spawner, Prop_Data, "m_vecOrigin", opPos);
2253 GetEntPropVector(spawner, Prop_Data, "m_angAbsRotation", opAng);
2254 g_observer = CreateEntityByName("info_observer_point");
2255 DispatchKeyValue(g_observer, "Angles", "90 0 0");
2256 DispatchKeyValue(g_observer, "TeamNum", "0");
2257 DispatchKeyValue(g_observer, "StartDisabled", "0");
2258 DispatchSpawn(g_observer);
2259 AcceptEntityInput(g_observer, "Enable");
2260 TeleportEntity(g_observer, opPos, opAng, NULL_VECTOR);
2261 }
2262 else
2263 {
2264 g_observer = -1;
2265 }
2266
2267}
2268
2269/* FindSpawnerByName()
2270**
2271** Finds the first spawner wich contains the given name.
2272** -------------------------------------------------------------------------- */
2273int FindSpawnerByName(char strName[32])
2274{
2275 int iIndex = -1;
2276 GetTrieValue(g_hSpawnersTrie, strName, iIndex);
2277 return iIndex;
2278}
2279
2280
2281/*
2282**����������������������������������������������������������������������������������
2283** ______ __
2284** / ____/___ ____ ___ ____ ___ ____ _____ ____/ /____
2285** / / / __ \/ __ `__ \/ __ `__ \/ __ `/ __ \/ __ / ___/
2286** / /___/ /_/ / / / / / / / / / / / /_/ / / / / /_/ (__ )
2287** \____/\____/_/ /_/ /_/_/ /_/ /_/\__,_/_/ /_/\__,_/____/
2288**
2289**����������������������������������������������������������������������������������
2290*/
2291
2292/* RegisterCommands()
2293**
2294** Creates helper server commands to use with the plugin's events system.
2295** -------------------------------------------------------------------------- */
2296void RegisterCommands()
2297{
2298 RegServerCmd("tf_dodgeball_explosion", CmdExplosion);
2299 RegServerCmd("tf_dodgeball_shockwave", CmdShockwave);
2300 RegServerCmd("tf_dodgeball_resize", CmdResize);
2301}
2302
2303public Action CmdResize(int iIndex)
2304{
2305 int iEntity = EntRefToEntIndex(g_iRocketEntity[iIndex]);
2306 if (iEntity && IsValidEntity(iEntity) && g_bRocketIsNuke[iEntity])
2307 {
2308 SetEntPropFloat(iEntity, Prop_Send, "m_flModelScale", (4.0));
2309 }
2310}
2311
2312/* CmdExplosion()
2313**
2314** Creates a huge explosion at the location of the client.
2315** -------------------------------------------------------------------------- */
2316public Action CmdExplosion(int iArgs)
2317{
2318 if (iArgs == 1)
2319 {
2320 char strBuffer[8], iClient;
2321 GetCmdArg(1, strBuffer, sizeof(strBuffer));
2322 iClient = StringToInt(strBuffer);
2323 if (IsValidEntity(iClient))
2324 {
2325 float fPosition[3];
2326 GetClientAbsOrigin(iClient, fPosition);
2327 switch (GetURandomIntRange(0, 4))
2328 {
2329 case 0:
2330 {
2331 PlayParticle(fPosition, PARTICLE_NUKE_1_ANGLES, PARTICLE_NUKE_1);
2332 }
2333 case 1:
2334 {
2335 PlayParticle(fPosition, PARTICLE_NUKE_2_ANGLES, PARTICLE_NUKE_2);
2336 }
2337 case 2:
2338 {
2339 PlayParticle(fPosition, PARTICLE_NUKE_3_ANGLES, PARTICLE_NUKE_3);
2340 }
2341 case 3:
2342 {
2343 PlayParticle(fPosition, PARTICLE_NUKE_4_ANGLES, PARTICLE_NUKE_4);
2344 }
2345 case 4:
2346 {
2347 PlayParticle(fPosition, PARTICLE_NUKE_5_ANGLES, PARTICLE_NUKE_5);
2348 }
2349 }
2350 PlayParticle(fPosition, PARTICLE_NUKE_COLLUMN_ANGLES, PARTICLE_NUKE_COLLUMN);
2351 }
2352 }
2353 else
2354 {
2355 PrintToServer("Usage: tf_dodgeball_explosion <client index>");
2356 }
2357
2358 return Plugin_Handled;
2359}
2360
2361/* CmdShockwave()
2362**
2363** Creates a huge shockwave at the location of the client, with the given
2364** parameters.
2365** -------------------------------------------------------------------------- */
2366public Action CmdShockwave(int iArgs)
2367{
2368 if (iArgs == 5)
2369 {
2370 char strBuffer[8];
2371 int iClient;
2372 int iTeam;
2373 float fPosition[3];
2374 int iDamage;
2375 float fPushStrength;
2376 float fRadius;
2377 float fFalloffRadius;
2378 GetCmdArg(1, strBuffer, sizeof(strBuffer)); iClient = StringToInt(strBuffer);
2379 GetCmdArg(2, strBuffer, sizeof(strBuffer)); iDamage = StringToInt(strBuffer);
2380 GetCmdArg(3, strBuffer, sizeof(strBuffer)); fPushStrength = StringToFloat(strBuffer);
2381 GetCmdArg(4, strBuffer, sizeof(strBuffer)); fRadius = StringToFloat(strBuffer);
2382 GetCmdArg(5, strBuffer, sizeof(strBuffer)); fFalloffRadius = StringToFloat(strBuffer);
2383
2384 if (IsValidClient(iClient))
2385 {
2386 iTeam = GetClientTeam(iClient);
2387 GetClientAbsOrigin(iClient, fPosition);
2388
2389 for (iClient = 1; iClient <= MaxClients; iClient++)
2390 {
2391 if ((IsValidClient(iClient, true) == true) && (GetClientTeam(iClient) == iTeam))
2392 {
2393 float fPlayerPosition[3]; GetClientEyePosition(iClient, fPlayerPosition);
2394 float fDistanceToShockwave = GetVectorDistance(fPosition, fPlayerPosition);
2395
2396 if (fDistanceToShockwave < fRadius)
2397 {
2398 float fImpulse[3];
2399 float fFinalPush;
2400 int iFinalDamage;
2401 fImpulse[0] = fPlayerPosition[0] - fPosition[0];
2402 fImpulse[1] = fPlayerPosition[1] - fPosition[1];
2403 fImpulse[2] = fPlayerPosition[2] - fPosition[2];
2404 NormalizeVector(fImpulse, fImpulse);
2405 if (fImpulse[2] < 0.4) { fImpulse[2] = 0.4; NormalizeVector(fImpulse, fImpulse); }
2406
2407 if (fDistanceToShockwave < fFalloffRadius)
2408 {
2409 fFinalPush = fPushStrength;
2410 iFinalDamage = iDamage;
2411 }
2412 else
2413 {
2414 float fImpact = (1.0 - ((fDistanceToShockwave - fFalloffRadius) / (fRadius - fFalloffRadius)));
2415 fFinalPush = fImpact * fPushStrength;
2416 iFinalDamage = RoundToFloor(fImpact * iDamage);
2417 }
2418 ScaleVector(fImpulse, fFinalPush);
2419 SetEntPropVector(iClient, Prop_Data, "m_vecAbsVelocity", fImpulse);
2420
2421 Handle hDamage = CreateDataPack();
2422 WritePackCell(hDamage, iClient);
2423 WritePackCell(hDamage, iFinalDamage);
2424 CreateTimer(0.1, ApplyDamage, hDamage, TIMER_FLAG_NO_MAPCHANGE);
2425 }
2426 }
2427 }
2428 }
2429 }
2430 else
2431 {
2432 PrintToServer("Usage: tf_dodgeball_shockwave <client index> <damage> <push strength> <radius> <falloff>");
2433 }
2434
2435 return Plugin_Handled;
2436}
2437
2438/* ExecuteCommands()
2439**
2440** The core of the plugin's event system, unpacks and correctly formats the
2441** given command strings to be executed.
2442** -------------------------------------------------------------------------- */
2443void ExecuteCommands(Handle hDataPack, int iClass, int iRocket, int iOwner, int iTarget, int iLastDead, float fSpeed, int iNumDeflections)
2444{
2445 ResetPack(hDataPack, false);
2446 int iNumCommands = ReadPackCell(hDataPack);
2447 while (iNumCommands-- > 0)
2448 {
2449 char strCmd[256];
2450 char strBuffer[8];
2451 ReadPackString(hDataPack, strCmd, sizeof(strCmd));
2452 ReplaceString(strCmd, sizeof(strCmd), "@name", g_strRocketClassLongName[iClass]);
2453 Format(strBuffer, sizeof(strBuffer), "%i", iRocket); ReplaceString(strCmd, sizeof(strCmd), "@rocket", strBuffer);
2454 Format(strBuffer, sizeof(strBuffer), "%i", iOwner); ReplaceString(strCmd, sizeof(strCmd), "@owner", strBuffer);
2455 Format(strBuffer, sizeof(strBuffer), "%i", iTarget); ReplaceString(strCmd, sizeof(strCmd), "@target", strBuffer);
2456 Format(strBuffer, sizeof(strBuffer), "%i", iLastDead); ReplaceString(strCmd, sizeof(strCmd), "@dead", strBuffer);
2457 Format(strBuffer, sizeof(strBuffer), "%f", fSpeed); ReplaceString(strCmd, sizeof(strCmd), "@speed", strBuffer);
2458 Format(strBuffer, sizeof(strBuffer), "%i", iNumDeflections); ReplaceString(strCmd, sizeof(strCmd), "@deflections", strBuffer);
2459 ServerCommand(strCmd);
2460 }
2461}
2462
2463/*
2464**����������������������������������������������������������������������������������
2465** ______ _____
2466** / ____/___ ____ / __(_)___ _
2467** / / / __ \/ __ \/ /_/ / __ `/
2468** / /___/ /_/ / / / / __/ / /_/ /
2469** \____/\____/_/ /_/_/ /_/\__, /
2470** /____/
2471**����������������������������������������������������������������������������������
2472*/
2473
2474/* ParseConfiguration()
2475**
2476** Parses a Dodgeball configuration file. It doesn't clear any of the previous
2477** data, so multiple files can be parsed.
2478** -------------------------------------------------------------------------- */
2479bool ParseConfigurations(char strConfigFile[] = "general.cfg")
2480{
2481 // Parse configuration
2482 char strPath[PLATFORM_MAX_PATH];
2483 char strFileName[PLATFORM_MAX_PATH];
2484 Format(strFileName, sizeof(strFileName), "configs/dodgeball/%s", strConfigFile);
2485 BuildPath(Path_SM, strPath, sizeof(strPath), strFileName);
2486
2487 // Try to parse if it exists
2488 LogMessage("Executing configuration file %s", strPath);
2489 if (FileExists(strPath, true))
2490 {
2491 KeyValues kvConfig = CreateKeyValues("TF2_Dodgeball");
2492
2493 if (FileToKeyValues(kvConfig, strPath) == false)
2494 SetFailState("Error while parsing the configuration file.");
2495
2496 kvConfig.GotoFirstSubKey();
2497
2498 // Parse the subsections
2499 do
2500 {
2501 char strSection[64];
2502 KvGetSectionName(kvConfig, strSection, sizeof(strSection));
2503
2504 if (StrEqual(strSection, "general"))
2505 ParseGeneral(kvConfig);
2506 else if (StrEqual(strSection, "classes"))
2507 ParseClasses(kvConfig);
2508 else if (StrEqual(strSection, "spawners"))
2509 ParseSpawners(kvConfig);
2510 }
2511 while (KvGotoNextKey(kvConfig));
2512
2513 CloseHandle(kvConfig);
2514 }
2515}
2516
2517/* ParseGeneral()
2518**
2519** Parses general settings, such as the music, urls, etc.
2520** -------------------------------------------------------------------------- */
2521void ParseGeneral(Handle kvConfig)
2522{
2523 g_bMusicEnabled = view_as<bool>(KvGetNum(kvConfig, "music", 0));
2524 if (g_bMusicEnabled == true)
2525 {
2526 g_bUseWebPlayer = view_as<bool>(KvGetNum(kvConfig, "use web player", 0));
2527 KvGetString(kvConfig, "web player url", g_strWebPlayerUrl, sizeof(g_strWebPlayerUrl));
2528
2529 g_bMusic[Music_RoundStart] = KvGetString(kvConfig, "round start", g_strMusic[Music_RoundStart], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundStart]);
2530 g_bMusic[Music_RoundWin] = KvGetString(kvConfig, "round end (win)", g_strMusic[Music_RoundWin], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundWin]);
2531 g_bMusic[Music_RoundLose] = KvGetString(kvConfig, "round end (lose)", g_strMusic[Music_RoundLose], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_RoundLose]);
2532 g_bMusic[Music_Gameplay] = KvGetString(kvConfig, "gameplay", g_strMusic[Music_Gameplay], PLATFORM_MAX_PATH) && strlen(g_strMusic[Music_Gameplay]);
2533 }
2534}
2535
2536/* ParseClasses()
2537**
2538** Parses the rocket classes data from the given configuration file.
2539** -------------------------------------------------------------------------- */
2540void ParseClasses(Handle kvConfig)
2541{
2542 char strName[64];
2543 char strBuffer[256];
2544
2545 KvGotoFirstSubKey(kvConfig);
2546 do
2547 {
2548 int iIndex = g_iRocketClassCount;
2549 RocketFlags iFlags;
2550
2551 // Basic parameters
2552 KvGetSectionName(kvConfig, strName, sizeof(strName)); strcopy(g_strRocketClassName[iIndex], 16, strName);
2553 KvGetString(kvConfig, "name", strBuffer, sizeof(strBuffer)); strcopy(g_strRocketClassLongName[iIndex], 32, strBuffer);
2554 if (KvGetString(kvConfig, "model", strBuffer, sizeof(strBuffer)))
2555 {
2556 strcopy(g_strRocketClassModel[iIndex], PLATFORM_MAX_PATH, strBuffer);
2557 if (strlen(g_strRocketClassModel[iIndex]) != 0)
2558 {
2559 iFlags |= RocketFlag_CustomModel;
2560 if (KvGetNum(kvConfig, "is animated", 0))iFlags |= RocketFlag_IsAnimated;
2561 }
2562 }
2563
2564 KvGetString(kvConfig, "behaviour", strBuffer, sizeof(strBuffer), "homing");
2565 if (StrEqual(strBuffer, "homing"))g_iRocketClassBehaviour[iIndex] = Behaviour_Homing;
2566 else g_iRocketClassBehaviour[iIndex] = Behaviour_Unknown;
2567
2568 if (KvGetNum(kvConfig, "play spawn sound", 0) == 1)
2569 {
2570 iFlags |= RocketFlag_PlaySpawnSound;
2571 if (KvGetString(kvConfig, "spawn sound", g_strRocketClassSpawnSound[iIndex], PLATFORM_MAX_PATH) && (strlen(g_strRocketClassSpawnSound[iIndex]) != 0))
2572 {
2573 iFlags |= RocketFlag_CustomSpawnSound;
2574 }
2575 }
2576
2577 if (KvGetNum(kvConfig, "play beep sound", 0) == 1)
2578 {
2579 iFlags |= RocketFlag_PlayBeepSound;
2580 g_fRocketClassBeepInterval[iIndex] = KvGetFloat(kvConfig, "beep interval", 0.5);
2581 if (KvGetString(kvConfig, "beep sound", g_strRocketClassBeepSound[iIndex], PLATFORM_MAX_PATH) && (strlen(g_strRocketClassBeepSound[iIndex]) != 0))
2582 {
2583 iFlags |= RocketFlag_CustomBeepSound;
2584 }
2585 }
2586
2587 if (KvGetNum(kvConfig, "play alert sound", 0) == 1)
2588 {
2589 iFlags |= RocketFlag_PlayAlertSound;
2590 if (KvGetString(kvConfig, "alert sound", g_strRocketClassAlertSound[iIndex], PLATFORM_MAX_PATH) && strlen(g_strRocketClassAlertSound[iIndex]) != 0)
2591 {
2592 iFlags |= RocketFlag_CustomAlertSound;
2593 }
2594 }
2595
2596 // Behaviour modifiers
2597 if (KvGetNum(kvConfig, "elevate on deflect", 1) == 1)iFlags |= RocketFlag_ElevateOnDeflect;
2598 if (KvGetNum(kvConfig, "neutral rocket", 0) == 1)iFlags |= RocketFlag_IsNeutral;
2599
2600 // Movement parameters
2601 g_fRocketClassDamage[iIndex] = KvGetFloat(kvConfig, "damage");
2602 g_fRocketClassDamageIncrement[iIndex] = KvGetFloat(kvConfig, "damage increment");
2603 g_fRocketClassCritChance[iIndex] = KvGetFloat(kvConfig, "critical chance");
2604 g_fRocketClassSpeed[iIndex] = KvGetFloat(kvConfig, "speed");
2605 g_fSavedSpeed = g_fRocketClassSpeed[iIndex];
2606 g_fRocketClassSpeedIncrement[iIndex] = KvGetFloat(kvConfig, "speed increment");
2607 g_fSavedSpeedIncrement = g_fRocketClassSpeedIncrement[iIndex];
2608 g_fRocketClassTurnRate[iIndex] = KvGetFloat(kvConfig, "turn rate");
2609 g_fRocketClassTurnRateIncrement[iIndex] = KvGetFloat(kvConfig, "turn rate increment");
2610 g_fRocketClassElevationRate[iIndex] = KvGetFloat(kvConfig, "elevation rate");
2611 g_fRocketClassElevationLimit[iIndex] = KvGetFloat(kvConfig, "elevation limit");
2612 g_fRocketClassControlDelay[iIndex] = KvGetFloat(kvConfig, "control delay");
2613 g_fRocketClassPlayerModifier[iIndex] = KvGetFloat(kvConfig, "no. players modifier");
2614 g_fRocketClassRocketsModifier[iIndex] = KvGetFloat(kvConfig, "no. rockets modifier");
2615 g_fRocketClassTargetWeight[iIndex] = KvGetFloat(kvConfig, "direction to target weight");
2616
2617 // Events
2618 Handle hCmds = INVALID_HANDLE;
2619 KvGetString(kvConfig, "on spawn", strBuffer, sizeof(strBuffer));
2620 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnSpawnCmd; g_hRocketClassCmdsOnSpawn[iIndex] = hCmds; }
2621 KvGetString(kvConfig, "on deflect", strBuffer, sizeof(strBuffer));
2622 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnDeflectCmd; g_hRocketClassCmdsOnDeflect[iIndex] = hCmds; }
2623 KvGetString(kvConfig, "on kill", strBuffer, sizeof(strBuffer));
2624 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnKillCmd; g_hRocketClassCmdsOnKill[iIndex] = hCmds; }
2625 KvGetString(kvConfig, "on explode", strBuffer, sizeof(strBuffer));
2626 if ((hCmds = ParseCommands(strBuffer)) != INVALID_HANDLE) { iFlags |= RocketFlag_OnExplodeCmd; g_hRocketClassCmdsOnExplode[iIndex] = hCmds; }
2627
2628 // Done
2629 SetTrieValue(g_hRocketClassTrie, strName, iIndex);
2630 g_iRocketClassFlags[iIndex] = iFlags;
2631 g_iRocketClassCount++;
2632 }
2633 while (KvGotoNextKey(kvConfig));
2634 KvGoBack(kvConfig);
2635}
2636
2637/* ParseSpawners()
2638**
2639** Parses the spawn points classes data from the given configuration file.
2640** -------------------------------------------------------------------------- */
2641void ParseSpawners(KeyValues kvConfig)
2642{
2643 kvConfig.JumpToKey("spawners"); //jump to spawners section
2644 char strBuffer[256];
2645 kvConfig.GotoFirstSubKey(); //goto to first subkey of "spawners" section
2646
2647 do
2648 {
2649 int iIndex = g_iSpawnersCount;
2650
2651 // Basic parameters
2652 kvConfig.GetSectionName(strBuffer, sizeof(strBuffer)); //okay, here we got section name, as example, red
2653 strcopy(g_strSpawnersName[iIndex], 32, strBuffer); //here we copied it to the g_strSpawnersName array
2654 g_iSpawnersMaxRockets[iIndex] = kvConfig.GetNum("max rockets", 1); //get some values...
2655 g_fSpawnersInterval[iIndex] = kvConfig.GetFloat("interval", 1.0);
2656
2657 // Chances table
2658 g_hSpawnersChancesTable[iIndex] = CreateArray(); //not interested in this
2659 for (int iClassIndex = 0; iClassIndex < g_iRocketClassCount; iClassIndex++)
2660 {
2661 Format(strBuffer, sizeof(strBuffer), "%s%%", g_strRocketClassName[iClassIndex]);
2662 PushArrayCell(g_hSpawnersChancesTable[iIndex], KvGetNum(kvConfig, strBuffer, 0));
2663 if (KvGetNum(kvConfig, strBuffer, 0) == 100) strcopy(g_strSavedClassName, sizeof(g_strSavedClassName), g_strRocketClassLongName[iClassIndex]);
2664 }
2665
2666 // Done.
2667 SetTrieValue(g_hSpawnersTrie, g_strSpawnersName[iIndex], iIndex); //okay, push section name to g_hSpawnersTrie
2668 g_iSpawnersCount++;
2669 } while (kvConfig.GotoNextKey());
2670
2671 kvConfig.Rewind(); //rewind
2672
2673 GetTrieValue(g_hSpawnersTrie, "Red", g_iDefaultRedSpawner); //get value by section name, section name exists in the g_hSpawnersTrie, everything should work
2674 GetTrieValue(g_hSpawnersTrie, "Blue", g_iDefaultBluSpawner);
2675}
2676
2677/* ParseCommands()
2678**
2679** Part of the event system, parses the given command strings and packs them
2680** to a Datapack.
2681** -------------------------------------------------------------------------- */
2682Handle ParseCommands(char[] strLine)
2683{
2684 TrimString(strLine);
2685 if (strlen(strLine) == 0)
2686 {
2687 return INVALID_HANDLE;
2688 }
2689 else
2690 {
2691 char strStrings[8][255];
2692 int iNumStrings = ExplodeString(strLine, ";", strStrings, 8, 255);
2693
2694 Handle hDataPack = CreateDataPack();
2695 WritePackCell(hDataPack, iNumStrings);
2696 for (int i = 0; i < iNumStrings; i++)
2697 {
2698 WritePackString(hDataPack, strStrings[i]);
2699 }
2700
2701 return hDataPack;
2702 }
2703}
2704
2705/*
2706**����������������������������������������������������������������������������������
2707** ______ __
2708** /_ __/___ ____ / /____
2709** / / / __ \/ __ \/ / ___/
2710** / / / /_/ / /_/ / (__ )
2711** /_/ \____/\____/_/____/
2712**
2713**����������������������������������������������������������������������������������
2714*/
2715
2716/* ApplyDamage()
2717**
2718** Applies a damage to a player.
2719** -------------------------------------------------------------------------- */
2720public Action ApplyDamage(Handle hTimer, any hDataPack)
2721{
2722 ResetPack(hDataPack, false);
2723 int iClient = ReadPackCell(hDataPack);
2724 int iDamage = ReadPackCell(hDataPack);
2725 CloseHandle(hDataPack);
2726 SlapPlayer(iClient, iDamage, true);
2727}
2728
2729/* CopyVectors()
2730**
2731** Copies the contents from a vector to another.
2732** -------------------------------------------------------------------------- */
2733stock void CopyVectors(float fFrom[3], float fTo[3])
2734{
2735 fTo[0] = fFrom[0];
2736 fTo[1] = fFrom[1];
2737 fTo[2] = fFrom[2];
2738}
2739
2740/* LerpVectors()
2741**
2742** Calculates the linear interpolation of the two given vectors and stores
2743** it on the third one.
2744** -------------------------------------------------------------------------- */
2745stock void LerpVectors(float fA[3], float fB[3], float fC[3], float t)
2746{
2747 if (t < 0.0)t = 0.0;
2748 if (t > 1.0)t = 1.0;
2749
2750 fC[0] = fA[0] + (fB[0] - fA[0]) * t;
2751 fC[1] = fA[1] + (fB[1] - fA[1]) * t;
2752 fC[2] = fA[2] + (fB[2] - fA[2]) * t;
2753}
2754
2755/* IsValidClient()
2756**
2757** Checks if the given client index is valid, and if it's alive or not.
2758** -------------------------------------------------------------------------- */
2759stock bool IsValidClient(int iClient, bool bAlive = false)
2760{
2761 if (iClient >= 1 &&
2762 iClient <= MaxClients &&
2763 IsClientConnected(iClient) &&
2764 IsClientInGame(iClient) &&
2765 (bAlive == false || IsPlayerAlive(iClient)))
2766 {
2767 return true;
2768 }
2769
2770 return false;
2771}
2772
2773/* BothTeamsPlaying()
2774**
2775** Checks if there are players on both teams.
2776** -------------------------------------------------------------------------- */
2777stock bool BothTeamsPlaying()
2778{
2779 bool bRedFound;
2780 bool bBluFound;
2781 for (int iClient = 1; iClient <= MaxClients; iClient++)
2782 {
2783 if (IsValidClient(iClient, true) == false)continue;
2784 int iTeam = GetClientTeam(iClient);
2785 if (iTeam == view_as<int>(TFTeam_Red))bRedFound = true;
2786 if (iTeam == view_as<int>(TFTeam_Blue))bBluFound = true;
2787 }
2788 return bRedFound && bBluFound;
2789}
2790
2791/* CountAlivePlayers()
2792**
2793** Retrieves the number of players alive.
2794** -------------------------------------------------------------------------- */
2795stock int CountAlivePlayers()
2796{
2797 int iCount = 0;
2798 for (int iClient = 1; iClient <= MaxClients; iClient++)
2799 {
2800 if (IsValidClient(iClient, true))iCount++;
2801 }
2802 return iCount;
2803}
2804
2805/* GetTotalClientCount()
2806**
2807** Retrieves the number of real players connected.
2808** -------------------------------------------------------------------------- */
2809stock int GetTotalClientCount() {
2810 int count = 0;
2811 for (int i = 1; i <= MaxClients; i++) {
2812 if (IsClientInGame(i) && !IsFakeClient(i) && GetClientTeam(i) > 1) {
2813 count += 1;
2814 }
2815 }
2816 return count;
2817}
2818
2819/* SelectTarget()
2820**
2821** Determines a random target of the given team for the homing rocket.
2822** -------------------------------------------------------------------------- */
2823stock int SelectTarget(int iTeam, int iRocket = -1)
2824{
2825 int iTarget = -1;
2826 float fTargetWeight = 0.0;
2827 float fRocketPosition[3];
2828 float fRocketDirection[3];
2829 float fWeight;
2830 bool bUseRocket;
2831
2832 if (iRocket != -1)
2833 {
2834 int iClass = g_iRocketClass[iRocket];
2835 int iEntity = EntRefToEntIndex(g_iRocketEntity[iRocket]);
2836
2837 GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fRocketPosition);
2838 CopyVectors(g_fRocketDirection[iRocket], fRocketDirection);
2839 fWeight = g_fRocketClassTargetWeight[iClass];
2840
2841 bUseRocket = true;
2842 }
2843
2844 for (int iClient = 1; iClient <= MaxClients; iClient++)
2845 {
2846 // If the client isn't connected, skip.
2847 if (!IsValidClient(iClient, true))continue;
2848 if (iTeam && GetClientTeam(iClient) != iTeam)continue;
2849
2850 // Determine if this client should be the target.
2851 float fNewWeight = GetURandomFloatRange(0.0, 100.0);
2852
2853 if (bUseRocket == true)
2854 {
2855 float fClientPosition[3]; GetClientEyePosition(iClient, fClientPosition);
2856 float fDirectionToClient[3]; MakeVectorFromPoints(fRocketPosition, fClientPosition, fDirectionToClient);
2857 fNewWeight += GetVectorDotProduct(fRocketDirection, fDirectionToClient) * fWeight;
2858 }
2859
2860 if ((iTarget == -1) || fNewWeight >= fTargetWeight)
2861 {
2862 iTarget = iClient;
2863 fTargetWeight = fNewWeight;
2864 }
2865 }
2866
2867 return iTarget;
2868}
2869
2870/* StopSoundToAll()
2871**
2872** Stops a sound for all the clients on the given channel.
2873** -------------------------------------------------------------------------- */
2874stock void StopSoundToAll(int iChannel, const char[] strSound)
2875{
2876 for (int iClient = 1; iClient <= MaxClients; iClient++)
2877 {
2878 if (IsValidClient(iClient))StopSound(iClient, iChannel, strSound);
2879 }
2880}
2881
2882/* PlayParticle()
2883**
2884** Plays a particle system at the given location & angles.
2885** -------------------------------------------------------------------------- */
2886stock void PlayParticle(float fPosition[3], float fAngles[3], char[] strParticleName, float fEffectTime = 5.0, float fLifeTime = 9.0)
2887{
2888 int iEntity = CreateEntityByName("info_particle_system");
2889 if (iEntity && IsValidEdict(iEntity))
2890 {
2891 TeleportEntity(iEntity, fPosition, fAngles, NULL_VECTOR);
2892 DispatchKeyValue(iEntity, "effect_name", strParticleName);
2893 ActivateEntity(iEntity);
2894 AcceptEntityInput(iEntity, "Start");
2895 CreateTimer(fEffectTime, StopParticle, EntIndexToEntRef(iEntity));
2896 CreateTimer(fLifeTime, KillParticle, EntIndexToEntRef(iEntity));
2897 }
2898 else
2899 {
2900 LogError("ShowParticle: could not create info_particle_system");
2901 }
2902}
2903
2904/* StopParticle()
2905**
2906** Turns of the particle system. Automatically called by PlayParticle
2907** -------------------------------------------------------------------------- */
2908public Action StopParticle(Handle hTimer, any iEntityRef)
2909{
2910 if (iEntityRef != INVALID_ENT_REFERENCE)
2911 {
2912 int iEntity = EntRefToEntIndex(iEntityRef);
2913 if (iEntity && IsValidEntity(iEntity))
2914 {
2915 AcceptEntityInput(iEntity, "Stop");
2916 }
2917 }
2918}
2919
2920/* KillParticle()
2921**
2922** Destroys the particle system. Automatically called by PlayParticle
2923** -------------------------------------------------------------------------- */
2924public Action KillParticle(Handle hTimer, any iEntityRef)
2925{
2926 if (iEntityRef != INVALID_ENT_REFERENCE)
2927 {
2928 int iEntity = EntRefToEntIndex(iEntityRef);
2929 if (iEntity && IsValidEntity(iEntity))
2930 {
2931 RemoveEdict(iEntity);
2932 }
2933 }
2934}
2935
2936/* PrecacheParticle()
2937**
2938** Forces the client to precache a particle system.
2939** -------------------------------------------------------------------------- */
2940stock void PrecacheParticle(char[] strParticleName)
2941{
2942 PlayParticle(view_as<float>( { 0.0, 0.0, 0.0 } ), view_as<float>( { 0.0, 0.0, 0.0 } ), strParticleName, 0.1, 0.1);
2943}
2944
2945/* FindEntityByClassnameSafe()
2946**
2947** Used to iterate through entity types, avoiding problems in cases where
2948** the entity may not exist anymore.
2949** -------------------------------------------------------------------------- */
2950stock void FindEntityByClassnameSafe(int iStart, const char[] strClassname)
2951{
2952 while (iStart > -1 && !IsValidEntity(iStart))
2953 {
2954 iStart--;
2955 }
2956 return FindEntityByClassname(iStart, strClassname);
2957}
2958
2959/* GetAnalogueTeam()
2960**
2961** Gets the analogue team for this. In case of Red, it's Blue, and viceversa.
2962** -------------------------------------------------------------------------- */
2963stock int GetAnalogueTeam(int iTeam)
2964{
2965 if (iTeam == view_as<int>(TFTeam_Red))return view_as<int>(TFTeam_Blue);
2966 return view_as<int>(TFTeam_Red);
2967}
2968
2969/* ShowHiddenMOTDPanel()
2970**
2971** Shows a hidden MOTD panel, useful for streaming music.
2972** -------------------------------------------------------------------------- */
2973stock void ShowHiddenMOTDPanel(int iClient, char[] strTitle, char[] strMsg, char[] strType = "2")
2974{
2975 Handle hPanel = CreateKeyValues("data");
2976 KvSetString(hPanel, "title", strTitle);
2977 KvSetString(hPanel, "type", strType);
2978 KvSetString(hPanel, "msg", strMsg);
2979 ShowVGUIPanel(iClient, "info", hPanel, false);
2980 CloseHandle(hPanel);
2981}
2982
2983/* PrecacheSoundEx()
2984**
2985** Precaches a sound and adds it to the download table.
2986** -------------------------------------------------------------------------- */
2987stock void PrecacheSoundEx(char[] strFileName, bool bPreload = false, bool bAddToDownloadTable = false)
2988{
2989 char strFinalPath[PLATFORM_MAX_PATH];
2990 Format(strFinalPath, sizeof(strFinalPath), "sound/%s", strFileName);
2991 PrecacheSound(strFileName, bPreload);
2992 if (bAddToDownloadTable == true)AddFileToDownloadsTable(strFinalPath);
2993}
2994
2995/* PrecacheModelEx()
2996**
2997** Precaches a models and adds it to the download table.
2998** -------------------------------------------------------------------------- */
2999stock void PrecacheModelEx(char[] strFileName, bool bPreload = false, bool bAddToDownloadTable = false)
3000{
3001 PrecacheModel(strFileName, bPreload);
3002 if (bAddToDownloadTable)
3003 {
3004 char strDepFileName[PLATFORM_MAX_PATH];
3005 Format(strDepFileName, sizeof(strDepFileName), "%s.res", strFileName);
3006
3007 if (FileExists(strDepFileName))
3008 {
3009 // Open stream, if possible
3010 Handle hStream = OpenFile(strDepFileName, "r");
3011 if (hStream == INVALID_HANDLE) { LogMessage("Error, can't read file containing model dependencies."); return; }
3012
3013 while (!IsEndOfFile(hStream))
3014 {
3015 char strBuffer[PLATFORM_MAX_PATH];
3016 ReadFileLine(hStream, strBuffer, sizeof(strBuffer));
3017 CleanString(strBuffer);
3018
3019 // If file exists...
3020 if (FileExists(strBuffer, true))
3021 {
3022 // Precache depending on type, and add to download table
3023 if (StrContains(strBuffer, ".vmt", false) != -1)PrecacheDecal(strBuffer, true);
3024 else if (StrContains(strBuffer, ".mdl", false) != -1)PrecacheModel(strBuffer, true);
3025 else if (StrContains(strBuffer, ".pcf", false) != -1)PrecacheGeneric(strBuffer, true);
3026 AddFileToDownloadsTable(strBuffer);
3027 }
3028 }
3029
3030 // Close file
3031 CloseHandle(hStream);
3032 }
3033 }
3034}
3035
3036/* CleanString()
3037**
3038** Cleans the given string from any illegal character.
3039** -------------------------------------------------------------------------- */
3040stock void CleanString(char[] strBuffer)
3041{
3042 // Cleanup any illegal characters
3043 int Length = strlen(strBuffer);
3044 for (int iPos = 0; iPos < Length; iPos++)
3045 {
3046 switch (strBuffer[iPos])
3047 {
3048 case '\r':strBuffer[iPos] = ' ';
3049 case '\n':strBuffer[iPos] = ' ';
3050 case '\t':strBuffer[iPos] = ' ';
3051 }
3052 }
3053
3054 // Trim string
3055 TrimString(strBuffer);
3056}
3057
3058/* FMax()
3059**
3060** Returns the maximum of the two values given.
3061** -------------------------------------------------------------------------- */
3062stock float FMax(float a, float b)
3063{
3064 return (a > b) ? a:b;
3065}
3066
3067/* FMin()
3068**
3069** Returns the minimum of the two values given.
3070** -------------------------------------------------------------------------- */
3071stock float FMin(float a, float b)
3072{
3073 return (a < b) ? a:b;
3074}
3075
3076/* GetURandomIntRange()
3077**
3078**
3079** -------------------------------------------------------------------------- */
3080stock int GetURandomIntRange(const int iMin, const int iMax)
3081{
3082 return iMin + (GetURandomInt() % (iMax - iMin + 1));
3083}
3084
3085/* GetURandomFloatRange()
3086**
3087**
3088** -------------------------------------------------------------------------- */
3089stock float GetURandomFloatRange(float fMin, float fMax)
3090{
3091 return fMin + (GetURandomFloat() * (fMax - fMin));
3092}
3093
3094// Pyro vision
3095public void tf2dodgeball_hooks(Handle convar, const char[] oldValue, const char[] newValue)
3096{
3097 if (GetConVarBool(g_hCvarPyroVisionEnabled))
3098 {
3099 for (int i = 1; i <= MaxClients; ++i)
3100 {
3101 if (IsClientInGame(i))
3102 {
3103 TF2Attrib_SetByName(i, PYROVISION_ATTRIBUTE, 1.0);
3104 }
3105 }
3106 }
3107 else
3108 {
3109 for (int i = 1; i <= MaxClients; ++i)
3110 {
3111 if (IsClientInGame(i))
3112 {
3113 TF2Attrib_RemoveByName(i, PYROVISION_ATTRIBUTE);
3114 }
3115 }
3116 }
3117 if(convar == g_hMaxBouncesConVar)
3118 g_config_iMaxBounces = StringToInt(newValue);
3119 if(convar == g_hCvarVoteRocketClassCommandEnabled)
3120 g_bVoteClassEnabled = view_as<bool>(StringToInt(newValue));
3121 if(convar == g_hCvarServerChatTag)
3122 strcopy(g_strServerChatTag, sizeof(g_strServerChatTag), newValue);
3123 if(convar == g_hCvarMainChatColor)
3124 strcopy(g_strMainChatColor, sizeof(g_strMainChatColor), newValue);
3125 if(convar == g_hCvarKeywordChatColor)
3126 strcopy(g_strKeywordChatColor, sizeof(g_strKeywordChatColor), newValue);
3127}
3128
3129// Asherkins RocketBounce
3130
3131public void OnEntityCreated(int entity, const char[] classname)
3132{
3133 if (!StrEqual(classname, "tf_projectile_rocket", false))
3134 return;
3135
3136 if (StrEqual(classname, "tf_projectile_rocket") || StrEqual(classname, "tf_projectile_sentryrocket"))
3137 {
3138 if (IsValidEntity(entity))
3139 {
3140 SetEntPropEnt(entity, Prop_Send, "m_hOriginalLauncher", entity);
3141 SetEntPropEnt(entity, Prop_Send, "m_hLauncher", entity);
3142 }
3143 }
3144
3145 g_nBounces[entity] = 0;
3146 SDKHook(entity, SDKHook_StartTouch, OnStartTouch);
3147}
3148
3149public Action OnStartTouch(int entity, int other)
3150{
3151 if (other > 0 && other <= MaxClients)
3152 return Plugin_Continue;
3153
3154 // Only allow a rocket to bounce x times.
3155 if (g_nBounces[entity] >= g_config_iMaxBounces)
3156 return Plugin_Continue;
3157
3158 SDKHook(entity, SDKHook_Touch, OnTouch);
3159 return Plugin_Handled;
3160}
3161
3162public Action OnTouch(int entity, int other)
3163{
3164 float vOrigin[3];
3165 GetEntPropVector(entity, Prop_Data, "m_vecOrigin", vOrigin);
3166
3167 float vAngles[3];
3168 GetEntPropVector(entity, Prop_Data, "m_angRotation", vAngles);
3169
3170 float vVelocity[3];
3171 GetEntPropVector(entity, Prop_Data, "m_vecAbsVelocity", vVelocity);
3172
3173 Handle trace = TR_TraceRayFilterEx(vOrigin, vAngles, MASK_SHOT, RayType_Infinite, TEF_ExcludeEntity, entity);
3174
3175 if (!TR_DidHit(trace))
3176 {
3177 CloseHandle(trace);
3178 return Plugin_Continue;
3179 }
3180
3181 float vNormal[3];
3182 TR_GetPlaneNormal(trace, vNormal);
3183
3184 //PrintToServer("Surface Normal: [%.2f, %.2f, %.2f]", vNormal[0], vNormal[1], vNormal[2]);
3185
3186 CloseHandle(trace);
3187
3188 float dotProduct = GetVectorDotProduct(vNormal, vVelocity);
3189
3190 ScaleVector(vNormal, dotProduct);
3191 ScaleVector(vNormal, 2.0);
3192
3193 float vBounceVec[3];
3194 SubtractVectors(vVelocity, vNormal, vBounceVec);
3195
3196 float vNewAngles[3];
3197 GetVectorAngles(vBounceVec, vNewAngles);
3198
3199 //PrintToServer("Angles: [%.2f, %.2f, %.2f] -> [%.2f, %.2f, %.2f]", vAngles[0], vAngles[1], vAngles[2], vNewAngles[0], vNewAngles[1], vNewAngles[2]);
3200 //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));
3201
3202 TeleportEntity(entity, NULL_VECTOR, vNewAngles, vBounceVec);
3203
3204 g_nBounces[entity]++;
3205
3206 SDKUnhook(entity, SDKHook_Touch, OnTouch);
3207 return Plugin_Handled;
3208}
3209
3210public bool TEF_ExcludeEntity(int entity, int contentsMask, any data)
3211{
3212 return (entity != data);
3213}
3214
3215void preventAirblast(int clientId, bool prevent)
3216{
3217 int flags;
3218
3219 if (prevent == true)
3220 {
3221 abPrevention[clientId] = true;
3222 flags = GetEntityFlags(clientId) | FL_NOTARGET;
3223 }
3224 else
3225 {
3226 abPrevention[clientId] = false;
3227 flags = GetEntityFlags(clientId) & ~FL_NOTARGET;
3228 }
3229
3230 SetEntityFlags(clientId, flags);
3231}
3232
3233public Action TauntCheck(int victim, int &attacker, int &inflictor, float &damage, int &damagetype, int &weapon, float damageForce[3], float damagePosition[3], int damagecustom)
3234{
3235 switch (damagecustom)
3236 {
3237 case TF_CUSTOM_TAUNT_ARMAGEDDON:
3238 {
3239 damage = 0.0;
3240 return Plugin_Changed;
3241 }
3242
3243 }
3244 return Plugin_Continue;
3245}
3246
3247void checkStolenRocket(int clientId, int entId)
3248{
3249 if (EntRefToEntIndex(g_iRocketTarget[entId]) != clientId && !bStealArray[clientId][stoleRocket])
3250 {
3251 bStealArray[clientId][stoleRocket] = true;
3252 if (bStealArray[clientId][rocketsStolen] < GetConVarInt(g_hCvarStealPreventionNumber))
3253 {
3254 bStealArray[clientId][rocketsStolen]++;
3255 CreateTimer(0.1, tStealTimer, GetClientUserId(clientId), TIMER_FLAG_NO_MAPCHANGE);
3256 SlapPlayer(clientId, 0, true);
3257 PrintToChat(clientId, "\x03Do not steal rockets. [Warning %i/%i]", bStealArray[clientId][rocketsStolen], GetConVarInt(g_hCvarStealPreventionNumber));
3258 }
3259 else
3260 {
3261 ForcePlayerSuicide(clientId);
3262 PrintToChat(clientId, "\x03You have been slain for stealing rockets.");
3263 PrintToChatAll("\x03%N was slain for stealing rockets.", clientId);
3264 }
3265 }
3266}
3267
3268void checkRoundDelays(int entId)
3269{
3270 int iEntity = EntRefToEntIndex(g_iRocketEntity[entId]);
3271 int iTarget = EntRefToEntIndex(g_iRocketTarget[entId]);
3272 float timeToCheck;
3273 if (g_iRocketDeflections[entId] == 0)
3274 timeToCheck = g_fLastSpawnTime;
3275 else
3276 timeToCheck = g_fRocketLastDeflectionTime[entId];
3277
3278 if (iTarget != INVALID_ENT_REFERENCE && (GetGameTime() - timeToCheck) >= GetConVarFloat(g_hCvarDelayPreventionTime))
3279 {
3280 g_fRocketSpeed[entId] += GetConVarFloat(g_hCvarDelayPreventionSpeedup);
3281 if (!g_bPreventingDelay)
3282 {
3283 PrintToChatAll("\x03%N is delaying, the rocket will now speed up.", iTarget);
3284 EmitSoundToAll(SOUND_DEFAULT_SPEEDUPALERT, iEntity, SNDCHAN_AUTO, SNDLEVEL_GUNFIRE);
3285 }
3286 g_bPreventingDelay = true;
3287 }
3288}
3289
3290/* SetMainRocketClass()
3291**
3292** Takes a specified rocket class index and sets it as the only rocket class able to spawn.
3293** -------------------------------------------------------------------------- */
3294stock void SetMainRocketClass(int Index, bool isVote, int client = 0)
3295{
3296 int iSpawnerClassRed = g_iSpawnPointsRedClass[g_iCurrentRedSpawn];
3297 char strBufferRed[256];
3298 strcopy(strBufferRed, sizeof(strBufferRed), "Red");
3299
3300 Format(strBufferRed, sizeof(strBufferRed), "%s%%", g_strRocketClassName[Index]);
3301 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassRed], Index, 100);
3302
3303 for (int iClassIndex = 0; iClassIndex < g_iRocketClassCount; iClassIndex++)
3304 {
3305 if (!(iClassIndex == Index))
3306 {
3307 Format(strBufferRed, sizeof(strBufferRed), "%s%%", g_strRocketClassName[iClassIndex]);
3308 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassRed], iClassIndex, 0);
3309 }
3310 }
3311
3312 int iSpawnerClassBlu = g_iSpawnPointsBluClass[g_iCurrentBluSpawn];
3313 char strBufferBlue[256];
3314 strcopy(strBufferBlue, sizeof(strBufferBlue), "Blue");
3315
3316 Format(strBufferBlue, sizeof(strBufferBlue), "%s%%", g_strRocketClassName[Index]);
3317 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassBlu], Index, 100);
3318
3319 char strSelectionBlue[256];
3320 strcopy(strSelectionBlue, sizeof(strBufferBlue), strBufferBlue);
3321
3322 for (int iClassIndex = 0; iClassIndex < g_iRocketClassCount; iClassIndex++)
3323 {
3324 if (!(iClassIndex == Index))
3325 {
3326 Format(strBufferBlue, sizeof(strBufferBlue), "%s%%", g_strRocketClassName[iClassIndex]);
3327 SetArrayCell(g_hSpawnersChancesTable[iSpawnerClassBlu], iClassIndex, 0);
3328 }
3329 }
3330
3331 int iClass = GetRandomRocketClass(iSpawnerClassRed);
3332 strcopy(g_strSavedClassName, sizeof(g_strSavedClassName), g_strRocketClassLongName[iClass]);
3333
3334 if (isVote)
3335 CPrintToChatAll("%s %sVote %sfinished! %sRocket class changed to %s%s", g_strServerChatTag, g_strMainChatColor, g_strKeywordChatColor, g_strMainChatColor, g_strKeywordChatColor, g_strSavedClassName);
3336 else CPrintToChatAll("%s %s%N %schanged the rocket class to %s%s", g_strServerChatTag, g_strKeywordChatColor, client, g_strMainChatColor, g_strKeywordChatColor, g_strRocketClassLongName[iClass]);
3337}
3338
3339public Action tStealTimer(Handle hTimer, int iClientUid)
3340{
3341 int iClient = GetClientOfUserId(iClientUid);
3342 bStealArray[iClient][stoleRocket] = false;
3343}
3344
3345/*void AttachParticle(int iEntity, char[] strParticleType)
3346{
3347 int iParticle = CreateEntityByName("info_particle_system");
3348
3349 char strName[128];
3350 if (IsValidEdict(iParticle))
3351 {
3352 float fPos[3];
3353 GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fPos);
3354 fPos[2] += 10;
3355 TeleportEntity(iParticle, fPos, NULL_VECTOR, NULL_VECTOR);
3356
3357 Format(strName, sizeof(strName), "target%i", iEntity);
3358 DispatchKeyValue(iEntity, "targetname", strName);
3359
3360 DispatchKeyValue(iParticle, "targetname", "tf2particle");
3361 DispatchKeyValue(iParticle, "parentname", strName);
3362 DispatchKeyValue(iParticle, "effect_name", strParticleType);
3363 DispatchSpawn(iParticle);
3364 SetVariantString(strName);
3365 AcceptEntityInput(iParticle, "SetParent", iParticle, iParticle, 0);
3366 SetVariantString("");
3367 AcceptEntityInput(iParticle, "SetParentAttachment", iParticle, iParticle, 0);
3368 ActivateEntity(iParticle);
3369 AcceptEntityInput(iParticle, "start");
3370
3371 g_RocketParticle[iEntity] = iParticle;
3372 }
3373}*/
3374
3375stock void CreateTempParticle(char[] particle, int entity = -1, float origin[3] = NULL_VECTOR, float angles[3] = { 0.0, 0.0, 0.0 }, bool resetparticles = false)
3376{
3377 int tblidx = FindStringTable("ParticleEffectNames");
3378
3379 char tmp[256];
3380 int stridx = INVALID_STRING_INDEX;
3381
3382 for (int i = 0; i < GetStringTableNumStrings(tblidx); i++)
3383 {
3384 ReadStringTable(tblidx, i, tmp, sizeof(tmp));
3385 if (StrEqual(tmp, particle, false))
3386 {
3387 stridx = i;
3388 break;
3389 }
3390 }
3391
3392 TE_Start("TFParticleEffect");
3393 TE_WriteFloat("m_vecOrigin[0]", origin[0]);
3394 TE_WriteFloat("m_vecOrigin[1]", origin[1]);
3395 TE_WriteFloat("m_vecOrigin[2]", origin[2]);
3396 TE_WriteVector("m_vecAngles", angles);
3397 TE_WriteNum("m_iParticleSystemIndex", stridx);
3398 TE_WriteNum("entindex", entity);
3399 TE_WriteNum("m_iAttachType", 1);
3400 TE_WriteNum("m_bResetParticles", resetparticles);
3401 TE_SendToAll();
3402}
3403
3404stock void ClearTempParticles(int client)
3405{
3406 float empty[3];
3407 CreateTempParticle("sandwich_fx", client, empty, empty, true);
3408}
3409
3410/*void StolenRocket(int iClient, int iTarget)
3411{
3412 if (iTarget != iClient && GetClientTeam(iClient) == GetClientTeam(iTarget))
3413 {
3414 PrintToChatAll("\x03%N\x01 stole \x03%N\x01's rocket!", iClient, iTarget);
3415 g_stolen[iClient]++;
3416 if (g_stolen[iClient] >= GetConVarInt(g_hCvarStealPreventionNumber))
3417 {
3418 g_stolen[iClient] = 0;
3419 ForcePlayerSuicide(iClient);
3420 PrintToChat(iClient, "\x03You stole %d rockets and was slayed.", g_hCvarStealPreventionNumber);
3421 }
3422 }
3423}*/
3424
3425
3426// EOF