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