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