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