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