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