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