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