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