· 8 years ago · Jan 01, 2018, 01:04 AM
1// ---- Preprocessor -----------------------------------------------------------
2#pragma semicolon 1
3
4// ---- Includes ---------------------------------------------------------------
5#include <sourcemod>
6#include <sdkhooks>
7#include <tf2_stocks>
8#include <tf2>
9#include <tf2items>
10#include <tf2attributes>
11#include <steamtools>
12#include <morecolors>
13
14//I use this so the compiler will warn about the old syntax
15//#pragma newdecls required
16
17#include <dodgeball>
18
19// ---- Defines ----------------------------------------------------------------
20#define DB_VERSION "0.2.4"
21#define PLAYERCOND_SPYCLOAK (1<<4)
22#define MAXGENERIC 25
23#define MAXMULTICOLORHUD 5
24#define MAXHUDNUMBER 6
25#define TEAM_RED 2
26#define TEAM_BLUE 3
27#define CLASS_PYRO 7
28#define CLASS_SPY 8
29
30#define SOUND_ALERT_VOL 0.8
31#define HUD_LINE_SEPARATION 0.04
32
33//Nuke explosion
34#define PARTICLE_NUKE_1 "fireSmokeExplosion"
35#define PARTICLE_NUKE_2 "fireSmokeExplosion1"
36#define PARTICLE_NUKE_3 "fireSmokeExplosion2"
37#define PARTICLE_NUKE_4 "fireSmokeExplosion3"
38#define PARTICLE_NUKE_5 "fireSmokeExplosion4"
39#define PARTICLE_NUKE_COLLUMN "fireSmoke_collumnP"
40#define PARTICLE_NUKE_1_ANGLES Float:{270.0, 0.0, 0.0}
41#define PARTICLE_NUKE_2_ANGLES PARTICLE_NUKE_1_ANGLES
42#define PARTICLE_NUKE_3_ANGLES PARTICLE_NUKE_1_ANGLES
43#define PARTICLE_NUKE_4_ANGLES PARTICLE_NUKE_1_ANGLES
44#define PARTICLE_NUKE_5_ANGLES PARTICLE_NUKE_1_ANGLES
45#define PARTICLE_NUKE_COLLUMN_ANGLES PARTICLE_NUKE_1_ANGLES
46
47enum
48{
49 rsnd_spawn,
50 rsnd_alert,
51 rsnd_bludeflect,
52 rsnd_reddeflect,
53 rsnd_beep,
54 rsnd_bounce,
55 rsnd_aimed,
56 rsnd_exp
57}
58
59// ---- Variables --------------------------------------------------------------
60bool g_isDBmap = false;
61bool g_onPreparation = false;
62bool g_roundActive = false;
63bool g_reloadConfig = false;
64bool g_canSpawn = false;
65bool g_canEmitKillSound = true;
66int g_lastSpawned;
67int g_max_rockets_dynamic;
68
69int g_BlueSpawn;
70int g_RedSpawn;
71int g_observer;
72int g_observer_slot;
73
74bool g_1v1_started = false;
75int g_1v1_music_played;
76int g_1v1_red_life;
77Handle g_1v1_red_beep;
78int g_1v1_blue_life;
79Handle g_1v1_blue_beep;
80
81Handle g_HudSyncs[MAXHUDNUMBER];
82char g_mainfile[PLATFORM_MAX_PATH];
83char g_rocketclasses[PLATFORM_MAX_PATH];
84
85
86// ---- Plugin's Configuration -------------------------------------------------
87float g_player_speed;
88bool g_pyro_only;
89bool g_annotation_show;
90float g_annotation_distance;
91bool g_hud_show;
92float g_hud_x;
93float g_hud_y;
94char g_hud_color[32];
95char g_hud_aimed_text[PLATFORM_MAX_PATH];
96char g_hud_aimed_color[32];
97
98//Multi rocket color
99bool g_allow_multirocketcolor;
100char g_mrc_name[MAXMULTICOLORHUD][MAX_NAME_LENGTH];
101char g_mrc_color[MAXMULTICOLORHUD][32];
102char g_mrc_trail[MAXMULTICOLORHUD][PLATFORM_MAX_PATH];
103bool g_mrc_applycolor_model[MAXMULTICOLORHUD];
104bool g_mrc_applycolor_trail[MAXMULTICOLORHUD];
105bool g_mrc_use_light[MAXMULTICOLORHUD];
106
107//1v1 Mode
108bool g_1v1_allow;
109int g_1v1_lives;
110char g_1v1_beep_snd[PLATFORM_MAX_PATH];
111float g_1v1_beep_delay;
112StringMap g_1v1_Music;
113
114//Sound-config
115StringMap g_SndRoundStart;
116StringMap g_SndOnDeath;
117float g_OnKillDelay;
118StringMap g_SndOnKill;
119StringMap g_SndLastAlive;
120
121//Flamethrower restriction
122StringMap g_RestrictedWeps;
123
124//Command-config
125StringMap g_CommandToBlock;
126StringMap g_BlockOnlyOnPreparation;
127
128//Spawner
129int g_max_rockets;
130bool g_limit_rockets;
131float g_spawn_delay;
132StringMap g_class_chance;
133
134//Rocket Classes and entities (will be an struct in the future)
135RocketClass g_RocketClass[MAXROCKETCLASS];
136int g_RocketClass_count;
137RocketEnt g_RocketEnt[MAXROCKETS];
138
139// ---- Server's CVars Management ----------------------------------------------
140Handle db_airdash;
141Handle db_push;
142Handle db_burstammo;
143
144int db_airdash_def = 1;
145int db_push_def = 1;
146int db_burstammo_def = 1;
147
148// ---- Plugin's Information ---------------------------------------------------
149public Plugin myinfo =
150{
151 name = "[TF2] Dodgeball Speed",
152 author = "Classic",
153 description = "Dodgeball plugin for TF2",
154 version = DB_VERSION,
155 url = "http://www.clangs.com.ar"
156};
157
158/* OnPluginStart()
159**
160** When the plugin is loaded.
161** -------------------------------------------------------------------------- */
162public void OnPluginStart()
163{
164 //Cvars
165 CreateConVar("sm_db_version", DB_VERSION, "Dogdeball Redux Version.", FCVAR_REPLICATED | FCVAR_PLUGIN | FCVAR_SPONLY | FCVAR_DONTRECORD | FCVAR_NOTIFY);
166
167 //Commands
168 RegAdminCmd("sm_reloaddb", Command_ReloadConfig, ADMFLAG_ROOT ,"Reload the dodgeball's configs on round end.");
169
170 //Creation of Tries
171 g_1v1_Music = CreateTrie();
172 g_SndRoundStart = CreateTrie();
173 g_SndOnDeath = CreateTrie();
174 g_SndOnKill = CreateTrie();
175 g_SndLastAlive = CreateTrie();
176 g_RestrictedWeps = CreateTrie();
177 g_CommandToBlock = CreateTrie();
178 g_BlockOnlyOnPreparation = CreateTrie();
179 g_class_chance = CreateTrie();
180
181 //Server's Cvars
182 db_airdash = FindConVar("tf_scout_air_dash_count");
183 db_push = FindConVar("tf_avoidteammates_pushaway");
184 db_burstammo = FindConVar("tf_flamethrower_burstammo");
185
186 //HUD
187 for(int i = 0; i < MAXHUDNUMBER; i++)
188 {
189 g_HudSyncs[i]= CreateHudSynchronizer();
190 }
191
192 //Rocket classes
193 for(int i = 0; i < MAXROCKETCLASS; ++i)
194 {
195 g_RocketClass[i] = RocketClass(i);
196 }
197
198 //Rocket entities
199 for(int i = 0; i < MAXROCKETS; ++i)
200 {
201 g_RocketEnt[i] = RocketEnt(i);
202 }
203
204 //Hooks
205 HookEvent("teamplay_round_start", OnPrepartionStart);
206 HookEvent("arena_round_start", OnRoundStart);
207 HookEvent("post_inventory_application", OnPlayerInventory);
208 HookEvent("player_spawn", OnPlayerSpawn);
209 HookEvent("player_death", OnPlayerDeath, EventHookMode_Post);
210 HookEvent("teamplay_round_win", OnRoundEnd);
211 HookEvent("teamplay_round_stalemate", OnRoundEnd);
212
213 //Constant file paths
214 BuildPath(Path_SM, g_mainfile, PLATFORM_MAX_PATH, "configs/dodgeball/dodgeball.cfg");
215 BuildPath(Path_SM, g_rocketclasses, PLATFORM_MAX_PATH, "configs/dodgeball/speed/rocketclasses.cfg");
216}
217
218/* OnMapStart()
219**
220** Here we reset every global variable, and we check if the current map is a dodgeball map.
221** If it is a db map, we get the cvars def. values and the we set up our own values.
222** -------------------------------------------------------------------------- */
223public void OnMapStart()
224{
225 char mapname[128];
226 GetCurrentMap(mapname, sizeof(mapname));
227 if (strncmp(mapname, "db_", 3, false) == 0 || (strncmp(mapname, "tfdb_", 5, false) == 0) )
228 {
229 LogMessage("[DB] Dodgeball map detected. Enabling Dodgeball Gamemode.");
230 g_isDBmap = true;
231 g_reloadConfig = false;
232 Steam_SetGameDescription("Dodgeball");
233 AddServerTag("dodgeball");
234
235 LoadRocketClasses();
236 LoadConfigs();
237
238 PrecacheFiles();
239 ProcessListeners(false);
240
241 }
242 else
243 {
244 LogMessage("[DB] Current map is not a Dodgeball map. Disabling Dodgeball Gamemode.");
245 RemoveServerTag("dodgeball");
246 g_isDBmap = false;
247 Steam_SetGameDescription("Team Fortress");
248 }
249}
250
251/* OnMapEnd()
252**
253** Here we reset the server's cvars to their default values.
254** -------------------------------------------------------------------------- */
255public void OnMapEnd()
256{
257 g_roundActive = false;
258 g_isDBmap = false;
259 for(int i = 0; i < MAXROCKETS; i++)
260 {
261 g_RocketEnt[i].entity = INVALID_ENT_REFERENCE;
262 }
263 ResetCvars();
264}
265
266
267
268public Action Command_ReloadConfig(client,args)
269{
270 g_reloadConfig = true;
271 ReplyToCommand(client,"[DB] Dodgeball's configuration will be reloaded on the next round.");
272 return Plugin_Continue;
273}
274
275
276/* LoadRocketClasses()
277**
278** Here we parse data/dodgeball/rocketclasses.cfg
279** -------------------------------------------------------------------------- */
280void LoadRocketClasses()
281{
282 if(!FileExists(g_rocketclasses))
283 {
284 SetFailState("Configuration file %s not found!", g_rocketclasses);
285 return;
286 }
287 KeyValues kv = CreateKeyValues("rocketclasses");
288 if(!kv.ImportFromFile(g_rocketclasses))
289 {
290 delete kv;
291 SetFailState("Improper structure for configuration file %s!", g_rocketclasses);
292 return;
293 }
294 if(!kv.JumpToKey("default"))
295 {
296 delete kv;
297 SetFailState("Missing default section on configuration file %s!", g_rocketclasses);
298 return;
299 }
300 RocketClass defClass = RocketClass(DEF_C);
301 char name[MAX_NAME_LENGTH], auxPath[PLATFORM_MAX_PATH];
302 //Rocket Name (section name)
303 kv.GetSectionName(name,MAX_NAME_LENGTH);
304 defClass.SetName(name);
305 //Trail
306 kv.GetString("Trail",auxPath,PLATFORM_MAX_PATH,"");
307 defClass.SetTrail(auxPath);
308 //Model
309 kv.GetString("Model",auxPath,PLATFORM_MAX_PATH,"");
310 defClass.SetModel(auxPath);
311 defClass.size = kv.GetFloat("ModelSize",1.0);
312 defClass.sizeinc = kv.GetFloat("DeflectSizeInc",0.0);
313 //Damage
314 defClass.damage = kv.GetFloat("BaseDamage",200.0);
315 defClass.damageinc = kv.GetFloat("DeflectDamageInc",0.0);
316 //Speed
317 defClass.speed = kv.GetFloat("BaseSpeed",1100.0);
318 defClass.speedinc = kv.GetFloat("DeflectSpeedInc",50.0);
319 //Turnrate
320 defClass.turnrate = kv.GetFloat("TurnRate",0.05);
321 defClass.turnrateinc = kv.GetFloat("DeflectTurnRateInc",0.005);
322 //Elevation
323 defClass.elevaterate = kv.GetFloat("ElevationRate",0.1075);
324 defClass.elevatemax = kv.GetFloat("ElevationLimitMax",0.125);
325 defClass.elevatemin = kv.GetFloat("ElevationLimitMin",-0.125);
326 //On deflect
327 defClass.deflectdelay = kv.GetFloat("DeflectDelay",0.1);
328 defClass.targetclosest = !!kv.GetNum("TargetClosest",0);
329 defClass.allowaimed = !!kv.GetNum("AllowAimed",0);
330 defClass.aimedspeed = kv.GetFloat("AimedSpeed",2.0);
331 //Bounce
332 defClass.maxbounce = kv.GetNum("MaxBounce",10);
333 defClass.bouncedelay = kv.GetFloat("BouceDelay",0.1);
334 defClass.bouncekeepdir = !!kv.GetNum("BounceKeepDirection",1);
335
336 //Sounds
337
338 //Spawn
339 defClass.snd_spawn_use = !!kv.GetNum("PlaySpawnSound",1);
340 kv.GetString("SpawnSound",auxPath,PLATFORM_MAX_PATH,"");
341 defClass.SetSndSpawn(auxPath);
342 //Alert
343 defClass.snd_alert_use = !!kv.GetNum("PlayAlertSound",1);
344 kv.GetString("AlertSound",auxPath,PLATFORM_MAX_PATH,"");
345 defClass.SetSndAlert(auxPath);
346 //Deflect
347 defClass.snd_deflect_use = !!kv.GetNum("PlayDeflectSound",1);
348 kv.GetString("RedDeflectSound",auxPath,PLATFORM_MAX_PATH,"");
349 defClass.SetSndDeflectRed(auxPath);
350 kv.GetString("BlueDeflectSound",auxPath,PLATFORM_MAX_PATH,"");
351 defClass.SetSndDeflectBlue(auxPath);
352 //Beep
353 defClass.snd_beep_use = !!kv.GetNum("PlayBeepSound",1);
354 kv.GetString("BeepSound",auxPath,PLATFORM_MAX_PATH,"");
355 defClass.SetSndBeep(auxPath);
356 defClass.snd_beep_delay = kv.GetFloat("BeepInterval",1.0);
357 //Bounce
358 defClass.snd_bounce_use = !!kv.GetNum("PlayBounceSound",1);
359 kv.GetString("BounceSound",auxPath,PLATFORM_MAX_PATH,"");
360 defClass.SetSndBounce(auxPath);
361 //Aimed
362 defClass.snd_aimed_use = !!kv.GetNum("PlayAimedSound",1);
363 kv.GetString("AimedSound",auxPath,PLATFORM_MAX_PATH,"");
364 defClass.SetSndAimed(auxPath);
365
366 //Explosion
367 if(kv.JumpToKey("explosion"))
368 {
369 defClass.exp_use = !!kv.GetNum("CreateBigExplosion",0);
370 defClass.exp_damage = kv.GetFloat("Damage",200.0);
371 defClass.exp_push = kv.GetFloat("PushStrength",1000.0);
372 defClass.exp_radius = kv.GetFloat("Radius",1000.0);
373 defClass.exp_fallof = kv.GetFloat("FallOfRadius",600.0);
374 kv.GetString("Sound",auxPath,PLATFORM_MAX_PATH,"");
375 defClass.SetExpSound(auxPath);
376 kv.GoBack();
377 }
378 else
379 {
380 defClass.exp_use = false;
381 }
382
383 kv.GoBack();
384
385 //Here we read all the classes
386 if(!kv.JumpToKey("Classes"))
387 {
388 delete kv;
389 SetFailState("Missing Classes section on configuration file %s!", g_rocketclasses);
390 return;
391 }
392 int count = 0;
393 kv.GotoFirstSubKey();
394 do
395 {
396 //Rocket Name (section name)
397 kv.GetSectionName(name,MAX_NAME_LENGTH);
398 g_RocketClass[count].SetName(name);
399 //Trail
400 defClass.GetTrail(auxPath,PLATFORM_MAX_PATH);
401 kv.GetString("Trail",auxPath,PLATFORM_MAX_PATH,auxPath);
402 g_RocketClass[count].SetTrail(auxPath);
403 //Model
404 defClass.GetModel(auxPath,PLATFORM_MAX_PATH);
405 kv.GetString("Model",auxPath,PLATFORM_MAX_PATH,auxPath);
406 g_RocketClass[count].SetModel(auxPath);
407 g_RocketClass[count].size = kv.GetFloat("ModelSize",defClass.size);
408 g_RocketClass[count].sizeinc = kv.GetFloat("DeflectSizeInc",defClass.sizeinc);
409 //Damage
410 g_RocketClass[count].damage = kv.GetFloat("BaseDamage",defClass.damage);
411 g_RocketClass[count].damageinc = kv.GetFloat("DeflectDamageInc",defClass.damageinc);
412 //Speed
413 g_RocketClass[count].speed = kv.GetFloat("BaseSpeed",defClass.speed);
414 g_RocketClass[count].speedinc = kv.GetFloat("DeflectSpeedInc",defClass.speedinc);
415 //Turnrate
416 g_RocketClass[count].turnrate = kv.GetFloat("TurnRate",defClass.turnrate);
417 g_RocketClass[count].turnrateinc = kv.GetFloat("DeflectTurnRateInc",defClass.turnrateinc);
418 //Elevation
419 g_RocketClass[count].elevaterate = kv.GetFloat("ElevationRate",defClass.elevaterate);
420 g_RocketClass[count].elevatemax = kv.GetFloat("ElevationLimitMax",defClass.elevatemax);
421 g_RocketClass[count].elevatemin = kv.GetFloat("ElevationLimitMin",defClass.elevatemin);
422 //On deflect
423 g_RocketClass[count].deflectdelay = kv.GetFloat("DeflectDelay",defClass.deflectdelay);
424 g_RocketClass[count].targetclosest = !!kv.GetNum("TargetClosest",defClass.targetclosest);
425 g_RocketClass[count].allowaimed = !!kv.GetNum("AllowAimed",defClass.allowaimed);
426 g_RocketClass[count].aimedspeed = kv.GetFloat("AimedSpeed",defClass.aimedspeed);
427 //Bounce
428 g_RocketClass[count].maxbounce = kv.GetNum("MaxBounce",defClass.maxbounce);
429 g_RocketClass[count].bouncedelay = kv.GetFloat("BouceDelay",defClass.bouncedelay);
430 g_RocketClass[count].bouncekeepdir = !!kv.GetNum("BounceKeepDirection",defClass.bouncekeepdir);
431
432 //Sounds
433
434 //Spawn
435 g_RocketClass[count].snd_spawn_use = !!kv.GetNum("PlaySpawnSound",defClass.snd_spawn_use);
436 defClass.GetSndSpawn(auxPath,PLATFORM_MAX_PATH);
437 kv.GetString("SpawnSound",auxPath,PLATFORM_MAX_PATH,auxPath);
438 g_RocketClass[count].SetSndSpawn(auxPath);
439 //Alert
440 g_RocketClass[count].snd_alert_use = !!kv.GetNum("PlayAlertSound",defClass.snd_alert_use);
441 defClass.GetSndAlert(auxPath,PLATFORM_MAX_PATH);
442 kv.GetString("AlertSound",auxPath,PLATFORM_MAX_PATH,auxPath);
443 g_RocketClass[count].SetSndAlert(auxPath);
444 //Deflect
445 g_RocketClass[count].snd_deflect_use = !!kv.GetNum("PlayDeflectSound",defClass.snd_deflect_use);
446 defClass.GetSndDeflectRed(auxPath,PLATFORM_MAX_PATH);
447 kv.GetString("RedDeflectSound",auxPath,PLATFORM_MAX_PATH,auxPath);
448 g_RocketClass[count].SetSndDeflectRed(auxPath);
449 defClass.GetSndDeflectBlue(auxPath,PLATFORM_MAX_PATH);
450 kv.GetString("BlueDeflectSound",auxPath,PLATFORM_MAX_PATH,auxPath);
451 g_RocketClass[count].SetSndDeflectBlue(auxPath);
452 //Beep
453 g_RocketClass[count].snd_beep_use = !!kv.GetNum("PlayBeepSound",defClass.snd_beep_use);
454 defClass.GetSndBeep(auxPath,PLATFORM_MAX_PATH);
455 kv.GetString("BeepSound",auxPath,PLATFORM_MAX_PATH,auxPath);
456 g_RocketClass[count].SetSndBeep(auxPath);
457 g_RocketClass[count].snd_beep_delay = kv.GetFloat("BeepInterval",defClass.snd_beep_delay);
458 //Bounce
459 g_RocketClass[count].snd_bounce_use = !!kv.GetNum("PlayBounceSound",defClass.snd_aimed_use);
460 defClass.GetSndBounce(auxPath,PLATFORM_MAX_PATH);
461 kv.GetString("BounceSound",auxPath,PLATFORM_MAX_PATH,auxPath);
462 g_RocketClass[count].SetSndBounce(auxPath);
463 //Aimed
464 g_RocketClass[count].snd_aimed_use = !!kv.GetNum("PlayAimedSound",defClass.snd_aimed_use);
465 defClass.GetSndAimed(auxPath,PLATFORM_MAX_PATH);
466 kv.GetString("AimedSound",auxPath,PLATFORM_MAX_PATH,auxPath);
467 g_RocketClass[count].SetSndAimed(auxPath);
468
469 //Explosion
470 if(kv.JumpToKey("explosion"))
471 {
472 g_RocketClass[count].exp_use = !!kv.GetNum("CreateBigExplosion",defClass.exp_use);
473 g_RocketClass[count].exp_damage = kv.GetFloat("Damage",defClass.exp_damage);
474 g_RocketClass[count].exp_push = kv.GetFloat("PushStrength",defClass.exp_push);
475 g_RocketClass[count].exp_radius = kv.GetFloat("Radius",defClass.exp_radius);
476 g_RocketClass[count].exp_fallof = kv.GetFloat("FallOfRadius",defClass.exp_fallof);
477 defClass.GetExpSound(auxPath,PLATFORM_MAX_PATH);
478 kv.GetString("Sound",auxPath,PLATFORM_MAX_PATH,auxPath);
479 g_RocketClass[count].SetExpSound(auxPath);
480
481 kv.GoBack();
482 }
483 else
484 {
485 g_RocketClass[count].exp_use = false;
486 }
487 count++;
488 }
489 while (kv.GotoNextKey() && count < MAXROCKETCLASS);
490
491 delete kv;
492 g_RocketClass_count = count;
493
494}
495
496/* LoadConfigs()
497**
498** Here we parse data/dodgeball/dodgeball.cfg
499** -------------------------------------------------------------------------- */
500void LoadConfigs()
501{
502 if(!FileExists(g_mainfile))
503 {
504 SetFailState("Configuration file %s not found!", g_mainfile);
505 return;
506 }
507 KeyValues kv = CreateKeyValues("dodgeball");
508 if(!kv.ImportFromFile(g_mainfile))
509 {
510 delete kv;
511 SetFailState("Improper structure for configuration file %s!", g_mainfile);
512 return;
513 }
514
515 //Here we clean the Tries
516 g_1v1_Music.Clear();
517 g_SndRoundStart.Clear();
518 g_SndOnDeath.Clear();
519 g_SndOnKill.Clear();
520 g_SndLastAlive.Clear();
521 g_RestrictedWeps.Clear();
522 g_CommandToBlock.Clear();
523 g_BlockOnlyOnPreparation.Clear();
524 g_class_chance.Clear();
525
526 //Main configuration
527 g_player_speed = kv.GetFloat("PlayerSpeed", 300.0);
528 g_pyro_only = !!kv.GetNum("OnlyPyro",0);
529 g_annotation_show = !!kv.GetNum("ShowAnnotation",1);
530 g_annotation_distance = kv.GetFloat("HideAnnotationDistance", 1000.0);
531 g_hud_show = !!kv.GetNum("ShowHud",1);
532 g_hud_x = kv.GetFloat("Xpos", 0.03);
533 g_hud_y = kv.GetFloat("Ypos", 0.21);
534 kv.GetString("color",g_hud_color,32,"63 255 127");
535 kv.GetString("supershottext",g_hud_aimed_text,PLATFORM_MAX_PATH,"Super Shot!");
536 kv.GetString("supershotcolor",g_hud_aimed_color,32,"63 255 127");
537
538 //Spawner limits and chances
539 if(kv.JumpToKey("spawner"))
540 {
541 g_max_rockets = kv.GetNum("MaxRockets", 2);
542 g_limit_rockets = !!kv.GetNum("LimitRockets",1);
543 g_spawn_delay = kv.GetFloat("SpawnDelay",2.0);
544 if(kv.JumpToKey("chances"))
545 {
546 char rocketname[MAX_NAME_LENGTH];
547 for(int i = 0; i < g_RocketClass_count; i++)
548 {
549 g_RocketClass[i].GetName(rocketname,MAX_NAME_LENGTH);
550 g_class_chance.SetValue(rocketname, kv.GetNum(rocketname,0));
551 }
552 kv.GoBack();
553 }
554 kv.GoBack();
555 }
556
557 //Multicolores Rockets
558 if(kv.JumpToKey("multirocketcolor"))
559 {
560 g_allow_multirocketcolor = !!kv.GetNum("AllowMultiRocketColor", 1);
561
562 int count = 0;
563 kv.GotoFirstSubKey();
564 do
565 {
566 kv.GetString("colorname",g_mrc_name[count],PLATFORM_MAX_PATH,"");
567 kv.GetString("color",g_mrc_color[count],32,"255 255 255");
568 kv.GetString("trail",g_mrc_trail[count],PLATFORM_MAX_PATH,"");
569 g_mrc_applycolor_model[count] = !!kv.GetNum("applycolormodel", 1);
570 g_mrc_applycolor_trail[count] = !!kv.GetNum("applycolortrail", 1);
571 g_mrc_use_light[count] = !!kv.GetNum("uselight", 1);
572 count++;
573 }
574 while (kv.GotoNextKey() && count < MAXMULTICOLORHUD);
575
576 kv.GoBack();
577 }
578
579 kv.Rewind();
580
581 //1v1 Mode
582 if(kv.JumpToKey("1v1mode"))
583 {
584 g_1v1_allow = !!kv.GetNum("Allow1v1",1);
585 if(kv.JumpToKey("Lives"))
586 {
587 g_1v1_lives = kv.GetNum("Lives",3);
588 kv.GetString("BeepSound",g_1v1_beep_snd,PLATFORM_MAX_PATH,"");
589 g_1v1_beep_delay = kv.GetFloat("BeepDelay",1.5);
590 kv.GoBack();
591 }
592
593 if(kv.JumpToKey("Music"))
594 {
595 char key[4], sndFile[PLATFORM_MAX_PATH];
596 for(int i=1; i<MAXGENERIC; i++)
597 {
598 IntToString(i, key, sizeof(key));
599 kv.GetString(key, sndFile, sizeof(sndFile),"");
600 if(StrEqual(sndFile, ""))
601 {
602 break;
603 }
604 g_1v1_Music.SetString(key,sndFile);
605 }
606 kv.GoBack();
607 }
608 }
609
610 kv.Rewind();
611 //Mod Sounds
612 if(kv.JumpToKey("sounds"))
613 {
614 char key[4], sndFile[PLATFORM_MAX_PATH];
615 if(kv.JumpToKey("RoundStart"))
616 {
617 for(int i=1; i<MAXGENERIC; i++)
618 {
619 IntToString(i, key, sizeof(key));
620 kv.GetString(key, sndFile, sizeof(sndFile),"");
621 if(StrEqual(sndFile, ""))
622 {
623 break;
624 }
625 g_SndRoundStart.SetString(key,sndFile);
626 }
627 kv.GoBack();
628 }
629 if(kv.JumpToKey("OnDeath"))
630 {
631 for(int i=1; i<MAXGENERIC; i++)
632 {
633 IntToString(i, key, sizeof(key));
634 kv.GetString(key, sndFile, sizeof(sndFile),"");
635 if(StrEqual(sndFile, ""))
636 {
637 break;
638 }
639 g_SndOnDeath.SetString(key,sndFile);
640 }
641 kv.GoBack();
642 }
643 if(kv.JumpToKey("OnKill"))
644 {
645 g_OnKillDelay = kv.GetFloat("delay",5.0);
646 for(int i=1; i<MAXGENERIC; i++)
647 {
648 IntToString(i, key, sizeof(key));
649 kv.GetString(key, sndFile, sizeof(sndFile),"");
650 if(StrEqual(sndFile, ""))
651 {
652 break;
653 }
654 g_SndOnKill.SetString(key,sndFile);
655 }
656 kv.GoBack();
657 }
658 if(kv.JumpToKey("LastAlive"))
659 {
660 for(int i=1; i<MAXGENERIC; i++)
661 {
662 IntToString(i, key, sizeof(key));
663 kv.GetString(key, sndFile, sizeof(sndFile),"");
664 if(StrEqual(sndFile, ""))
665 {
666 break;
667 }
668 g_SndLastAlive.SetString(key,sndFile);
669 }
670 kv.GoBack();
671 }
672 kv.GoBack();
673 }
674 kv.Rewind();
675
676 //Bloked Weapons
677 if(kv.JumpToKey("blockedflamethrowers"))
678 {
679 char key[4];
680 int auxInt;
681 for(int i=1; i<MAXGENERIC; i++)
682 {
683 IntToString(i, key, sizeof(key));
684 auxInt = kv.GetNum(key, -1);
685 if(auxInt == -1)
686 {
687 break;
688 }
689 g_RestrictedWeps.SetValue(key,auxInt);
690 }
691
692 kv.GoBack();
693 }
694 kv.Rewind();
695
696 //Bloked Commands
697 if(kv.JumpToKey("blockcommands"))
698 {
699 do
700 {
701 char SectionName[128], CommandName[128];
702 int onprep;
703 kv.GotoFirstSubKey();
704 kv.GetSectionName( SectionName, sizeof(SectionName));
705
706 kv.GetString("command", CommandName, sizeof(CommandName));
707 onprep = kv.GetNum("OnlyOnPreparation", 1);
708
709 if(!StrEqual(CommandName, ""))
710 {
711 g_CommandToBlock.SetString(SectionName,CommandName);
712 g_BlockOnlyOnPreparation.SetValue(SectionName,onprep);
713 }
714 }
715 while(kv.GotoNextKey());
716 kv.GoBack();
717 }
718
719 delete kv;
720
721 //Map's configuration
722 char mapfile[PLATFORM_MAX_PATH], mapname[128];
723 GetCurrentMap(mapname, sizeof(mapname));
724 BuildPath(Path_SM, mapfile, sizeof(mapfile), "configs/dodgeball/maps/%s.cfg",mapname);
725
726 if(FileExists(mapfile))
727 {
728 kv = CreateKeyValues("dodgeball");
729 if(!kv.ImportFromFile(g_mainfile))
730 {
731 LogMessage("Improper structure for configuration file %s! Since it's a map file it'll be ignored.", g_mainfile);
732 delete kv;
733 return;
734 }
735 //Spawner limits and chances. Will override the default's one.
736 if(kv.JumpToKey("spawner"))
737 {
738 g_max_rockets = kv.GetNum("MaxRockets", g_max_rockets);
739 g_limit_rockets = !!kv.GetNum("LimitRockets",g_limit_rockets);
740 g_spawn_delay = kv.GetFloat("SpawnDelay", g_spawn_delay);
741 if(kv.JumpToKey("chances"))
742 {
743 g_class_chance.Clear();
744 char rocketname[MAX_NAME_LENGTH];
745 for(int i = 0; i < g_RocketClass_count; i++)
746 {
747 g_RocketClass[i].GetName(rocketname,MAX_NAME_LENGTH);
748 g_class_chance.SetValue(rocketname, kv.GetNum(rocketname,0));
749 }
750 kv.GoBack();
751 }
752 kv.GoBack();
753 }
754 delete kv;
755 }
756}
757
758/* PrecacheFiles()
759**
760** We precache and add to the download table every sound/model/material file found on the config file.
761** -------------------------------------------------------------------------- */
762public void PrecacheFiles()
763{
764 //Mod's sounds
765 PrecacheSoundFromTrie(g_SndRoundStart);
766 PrecacheSoundFromTrie(g_SndOnDeath);
767 PrecacheSoundFromTrie(g_SndOnKill);
768 PrecacheSoundFromTrie(g_SndLastAlive);
769 //1v1 mode
770 if(!StrEqual(g_1v1_beep_snd,""))
771 {
772 PrecacheSoundFile(g_1v1_beep_snd);
773 }
774 PrecacheSoundFromTrie(g_1v1_Music);
775
776 //Multi-colored trails
777 for( int i = 0; i < MAXMULTICOLORHUD; i++)
778 {
779 if(!StrEqual(g_mrc_trail[i],""))
780 {
781 PrecacheTrail(g_mrc_trail[i]);
782 }
783 }
784
785 //Esplosion sounds
786 PrecacheParticle(PARTICLE_NUKE_1);
787 PrecacheParticle(PARTICLE_NUKE_2);
788 PrecacheParticle(PARTICLE_NUKE_3);
789 PrecacheParticle(PARTICLE_NUKE_4);
790 PrecacheParticle(PARTICLE_NUKE_5);
791 PrecacheParticle(PARTICLE_NUKE_COLLUMN);
792
793 //Class' model,trail and sounds.
794 char auxPath[PLATFORM_MAX_PATH];
795 for( int i = 0; i < g_RocketClass_count; i++)
796 {
797 g_RocketClass[i].GetTrail(auxPath,PLATFORM_MAX_PATH);
798 if(!StrEqual(auxPath,""))
799 {
800 PrecacheTrail(auxPath);
801 }
802 g_RocketClass[i].GetModel(auxPath,PLATFORM_MAX_PATH);
803 if(!StrEqual(auxPath,""))
804 {
805 PrecacheModelEx(auxPath,true,true);
806 }
807 g_RocketClass[i].GetSndSpawn(auxPath,PLATFORM_MAX_PATH);
808 if(!StrEqual(auxPath,""))
809 {
810 PrecacheSoundFile(auxPath);
811 }
812 g_RocketClass[i].GetSndAlert(auxPath,PLATFORM_MAX_PATH);
813 if(!StrEqual(auxPath,""))
814 {
815 PrecacheSoundFile(auxPath);
816 }
817 g_RocketClass[i].GetSndDeflectBlue(auxPath,PLATFORM_MAX_PATH);
818 if(!StrEqual(auxPath,""))
819 {
820 PrecacheSoundFile(auxPath);
821 }
822 g_RocketClass[i].GetSndDeflectRed(auxPath,PLATFORM_MAX_PATH);
823 if(!StrEqual(auxPath,""))
824 {
825 PrecacheSoundFile(auxPath);
826 }
827 g_RocketClass[i].GetSndBeep(auxPath,PLATFORM_MAX_PATH);
828 if(!StrEqual(auxPath,""))
829 {
830 PrecacheSoundFile(auxPath);
831 }
832 g_RocketClass[i].GetSndAimed(auxPath,PLATFORM_MAX_PATH);
833 if(!StrEqual(auxPath,""))
834 {
835 PrecacheSoundFile(auxPath);
836 }
837 g_RocketClass[i].GetSndBounce(auxPath,PLATFORM_MAX_PATH);
838 if(!StrEqual(auxPath,""))
839 {
840 PrecacheSoundFile(auxPath);
841 }
842 g_RocketClass[i].GetExpSound(auxPath,PLATFORM_MAX_PATH);
843 if(!StrEqual(auxPath,""))
844 {
845 PrecacheSoundFile(auxPath);
846 }
847 }
848
849
850
851}
852
853/* PrecacheSoundFromTrie()
854**
855** We precache every sound from a trie.
856** -------------------------------------------------------------------------- */
857PrecacheSoundFromTrie(StringMap sndTrie)
858{
859 char soundString[PLATFORM_MAX_PATH], downloadString[PLATFORM_MAX_PATH], key[4];
860 for(int i = 1; i <= sndTrie.Size; i++)
861 {
862 IntToString(i,key,sizeof(key));
863 if(GetTrieString(sndTrie,key,soundString, sizeof(soundString)))
864 {
865 if(PrecacheSound(soundString))
866 {
867 Format(downloadString, sizeof(downloadString), "sound/%s", soundString);
868 AddFileToDownloadsTable(downloadString);
869 }
870 }
871 }
872}
873
874/* PrecacheSoundFile()
875**
876** We precache a sound file.
877** -------------------------------------------------------------------------- */
878PrecacheSoundFile(char[] strFileName)
879{
880 if(PrecacheSound(strFileName))
881 {
882 char downloadString[PLATFORM_MAX_PATH];
883 Format(downloadString, sizeof(downloadString), "sound/%s", strFileName);
884 AddFileToDownloadsTable(downloadString);
885 }
886}
887
888/* PrecacheSoundFile()
889**
890** We precache trail file.
891** -------------------------------------------------------------------------- */
892PrecacheTrail(char[] strFileName)
893{
894 char downloadString[PLATFORM_MAX_PATH];
895 FormatEx(downloadString, sizeof(downloadString), "%s.vmt", strFileName);
896 PrecacheGeneric(downloadString, true);
897 AddFileToDownloadsTable(downloadString);
898 FormatEx(downloadString, sizeof(downloadString), "%s.vtf", strFileName);
899 PrecacheGeneric(downloadString, true);
900 AddFileToDownloadsTable(downloadString);
901}
902
903/* PrecacheModelEx()
904**
905** Precaches a models and adds it to the download table.
906** -------------------------------------------------------------------------- */
907stock PrecacheModelEx(String:strFileName[], bool:bPreload=false, bool:bAddToDownloadTable=false)
908{
909 PrecacheModel(strFileName, bPreload);
910 if (bAddToDownloadTable)
911 {
912 char strDepFileName[PLATFORM_MAX_PATH];
913 Format(strDepFileName, sizeof(strDepFileName), "%s.res", strFileName);
914
915 if (FileExists(strDepFileName))
916 {
917 // Open stream, if possible
918 Handle hStream = OpenFile(strDepFileName, "r");
919 if (hStream == INVALID_HANDLE) {LogMessage("Error, can't read file containing model dependencies."); return; }
920
921 while(!IsEndOfFile(hStream))
922 {
923 char strBuffer[PLATFORM_MAX_PATH];
924 ReadFileLine(hStream, strBuffer, sizeof(strBuffer));
925 CleanString(strBuffer);
926
927 // If file exists...
928 if (FileExists(strBuffer, true))
929 {
930 // Precache depending on type, and add to download table
931 if (StrContains(strBuffer, ".vmt", false) != -1) PrecacheDecal(strBuffer, true);
932 else if (StrContains(strBuffer, ".mdl", false) != -1) PrecacheModel(strBuffer, true);
933 else if (StrContains(strBuffer, ".pcf", false) != -1) PrecacheGeneric(strBuffer, true);
934 AddFileToDownloadsTable(strBuffer);
935 }
936 }
937
938 // Close file
939 CloseHandle(hStream);
940 }
941 }
942}
943
944/* ProcessListeners()
945**
946** Here we add the listeners to block the commands defined on the config file.
947** -------------------------------------------------------------------------- */
948public void ProcessListeners(bool removeListerners)
949{
950
951 char command[PLATFORM_MAX_PATH], key[4];
952 int PreparationOnly;
953 for(int i = 1; i <= g_CommandToBlock.Size; i++)
954 {
955 IntToString(i,key,sizeof(key));
956 if(GetTrieString(g_CommandToBlock,key,command, sizeof(command)))
957 {
958 if(StrEqual(command, ""))
959 {
960 break;
961 }
962
963 GetTrieValue(g_BlockOnlyOnPreparation,key,PreparationOnly);
964 if(removeListerners)
965 {
966 if(PreparationOnly == 1)
967 {
968 RemoveCommandListener(Command_Block_PreparationOnly,command);
969 }
970 else
971 {
972 RemoveCommandListener(Command_Block,command);
973 }
974 }
975 else
976 {
977 if(PreparationOnly == 1)
978 {
979 AddCommandListener(Command_Block_PreparationOnly,command);
980 }
981 else
982 {
983 AddCommandListener(Command_Block,command);
984 }
985 }
986
987
988 }
989 }
990}
991
992/* OnPrepartionStart()
993**
994** We setup the cvars again and we freeze the players.
995** -------------------------------------------------------------------------- */
996public Action OnPrepartionStart(Handle event, const char[] name, bool dontBroadcast)
997{
998 if(!g_isDBmap)
999 {
1000 return;
1001 }
1002
1003 g_onPreparation = true;
1004
1005 //We force the cvars values needed every round (to override if any cvar was changed).
1006 SetupCvars();
1007
1008 //Players shouldn't move until the round starts
1009 for(int i = 1; i <= MaxClients; i++)
1010 {
1011 if(IsValidAliveClient(i))
1012 {
1013 SetEntityMoveType(i, MOVETYPE_NONE);
1014 }
1015 }
1016
1017 EmitRandomSound(g_SndRoundStart);
1018}
1019
1020/* OnRoundStart()
1021**
1022** We unfreeze every player and we start the rocket timer
1023** -------------------------------------------------------------------------- */
1024public Action OnRoundStart(Handle event, const char[] name, bool dontBroadcast)
1025{
1026 if(!g_isDBmap)
1027 {
1028 return;
1029 }
1030 SearchSpawns();
1031 RenderHud();
1032 for(int i = 1; i <= MaxClients; i++)
1033 {
1034 if(IsValidAliveClient(i))
1035 {
1036 SetEntityMoveType(i, MOVETYPE_WALK);
1037 }
1038 }
1039 g_onPreparation = false;
1040 g_roundActive = true;
1041 g_canSpawn = true;
1042
1043 if(g_1v1_allow)
1044 {
1045 if( GetAlivePlayersCount(TEAM_BLUE,-1) == 1 && GetAlivePlayersCount(TEAM_RED,-1) == 1)
1046 {
1047
1048 Start1V1Mode();
1049 }
1050 }
1051
1052 //Rocket's limit
1053 g_max_rockets_dynamic = g_max_rockets;
1054 if(g_limit_rockets)
1055 {
1056 //Here we get the alive player count for each team and we set the maxium rockets to the lower number.
1057 int AliveCount = GetAlivePlayersCount(TEAM_RED,-1);
1058 if(g_max_rockets_dynamic > AliveCount)
1059 {
1060 g_max_rockets_dynamic = AliveCount;
1061 }
1062
1063 AliveCount = GetAlivePlayersCount(TEAM_BLUE,-1);
1064 if(g_max_rockets_dynamic > AliveCount)
1065 {
1066 g_max_rockets_dynamic = AliveCount;
1067 }
1068 }
1069
1070 g_lastSpawned = GetRandomInt(TEAM_RED,TEAM_BLUE);
1071 FireRocket();
1072
1073}
1074
1075/* OnRoundEnd()
1076**
1077** Here we destroy the rocket.
1078** -------------------------------------------------------------------------- */
1079public Action OnRoundEnd(Handle event, const char[] name, bool dontBroadcast)
1080{
1081 if(!g_isDBmap)
1082 {
1083 return;
1084 }
1085 g_roundActive=false;
1086 for(int i = 0; i < g_max_rockets; i++)
1087 {
1088 int index = EntRefToEntIndex(g_RocketEnt[i].entity);
1089 if (index != INVALID_ENT_REFERENCE)
1090 {
1091 int dissolver = CreateEntityByName("env_entity_dissolver");
1092
1093 if (dissolver == -1) return;
1094
1095 DispatchKeyValue(dissolver, "dissolvetype", "3");
1096 DispatchKeyValue(dissolver, "magnitude", "250");
1097 DispatchKeyValue(dissolver, "target", "!activator");
1098
1099 AcceptEntityInput(dissolver, "Dissolve", index);
1100 AcceptEntityInput(dissolver, "Kill");
1101 }
1102 }
1103 if(g_1v1_allow)
1104 {
1105 g_1v1_started = false;
1106 if(g_1v1_red_beep != null)
1107 {
1108 CloseHandle(g_1v1_red_beep);
1109 g_1v1_red_beep = null;
1110 }
1111 if(g_1v1_blue_beep != null)
1112 {
1113 CloseHandle(g_1v1_blue_beep);
1114 g_1v1_blue_beep = null;
1115 }
1116 if(g_1v1_music_played > -1)
1117 {
1118 char key[4],sndFile[PLATFORM_MAX_PATH];
1119 IntToString(g_1v1_music_played,key,sizeof(key));
1120
1121 if(GetTrieString(g_1v1_Music,key,sndFile,sizeof(sndFile)))
1122 {
1123 if(!StrEqual(sndFile, ""))
1124 {
1125 for(int i=1; i<= MaxClients; i++)
1126 {
1127 if(IsValidClient(i))
1128 {
1129 StopSound(i, SNDCHAN_AUTO, sndFile);
1130 }
1131 }
1132 }
1133 }
1134 }
1135 g_1v1_music_played = -1;
1136 }
1137 ClearHud();
1138
1139 if(g_reloadConfig)
1140 {
1141 LoadRocketClasses();
1142 LoadConfigs();
1143 ProcessListeners(false);
1144 g_reloadConfig = false;
1145 }
1146
1147}
1148
1149/* TF2Items_OnGiveNamedItem_Post()
1150**
1151** Here we check for the demoshield and the sapper.
1152** -------------------------------------------------------------------------- */
1153public TF2Items_OnGiveNamedItem_Post(client, String:classname[], index, level, quality, ent)
1154{
1155 if(!g_isDBmap)
1156 {
1157 return;
1158 }
1159
1160 if(StrEqual(classname,"tf_weapon_builder", false) || StrEqual(classname,"tf_wearable_demoshield", false))
1161 {
1162 CreateTimer(0.1, Timer_RemoveWep, EntIndexToEntRef(ent));
1163 }
1164}
1165
1166/* Timer_RemoveWep()
1167**
1168** We kill the demoshield/sapper
1169** -------------------------------------------------------------------------- */
1170public Action Timer_RemoveWep(Handle timer, int ref)
1171{
1172 int ent = EntRefToEntIndex(ref);
1173 if( IsValidEntity(ent) && ent > MaxClients)
1174 {
1175 AcceptEntityInput(ent, "Kill");
1176 }
1177}
1178
1179/* OnPlayerInventory()
1180**
1181** Here we strip players weapons (if we have to).
1182** Also we give special melee weapons (again, if we have to).
1183** -------------------------------------------------------------------------- */
1184public Action OnPlayerInventory(Handle event, const char[] name, bool dontBroadcast)
1185{
1186 if(!g_isDBmap)
1187 {
1188 return;
1189 }
1190
1191 bool replace_primary = false;
1192 int client = GetClientOfUserId(GetEventInt(event, "userid"));
1193
1194 TF2_RemoveWeaponSlot(client, TFWeaponSlot_Secondary);
1195 TF2_RemoveWeaponSlot(client, TFWeaponSlot_Melee);
1196 TF2_RemoveWeaponSlot(client, TFWeaponSlot_Grenade);
1197 TF2_RemoveWeaponSlot(client, TFWeaponSlot_Building);
1198 TF2_RemoveWeaponSlot(client, TFWeaponSlot_PDA);
1199
1200 char classname[64];
1201 int wep_ent = GetPlayerWeaponSlot(client, TFWeaponSlot_Primary);
1202 if(wep_ent > MaxClients && IsValidEntity(wep_ent))
1203 {
1204 int wep_index = GetEntProp(wep_ent, Prop_Send, "m_iItemDefinitionIndex");
1205 if (wep_ent > MaxClients && IsValidEdict(wep_ent) && GetEdictClassname(wep_ent, classname, sizeof(classname)))
1206 {
1207 if (StrEqual(classname, "tf_weapon_flamethrower", false) )
1208 {
1209 char key[4];
1210 int auxIndex;
1211 for(int i = 1; i <= g_RestrictedWeps.Size; i++)
1212 {
1213 IntToString(i,key,sizeof(key));
1214 if(g_RestrictedWeps.GetValue(key,auxIndex))
1215 {
1216 if(wep_index == auxIndex)
1217 {
1218 replace_primary=true;
1219 }
1220 }
1221 }
1222 if(!replace_primary)
1223 {
1224 TF2Attrib_SetByDefIndex(wep_ent, 823, 1.0);
1225 }
1226 }
1227 else
1228 {
1229 replace_primary = true;
1230 }
1231 }
1232 }
1233 if(replace_primary)
1234 {
1235 TF2_RemoveWeaponSlot(client, TFWeaponSlot_Primary);
1236 Handle hItem = TF2Items_CreateItem(FORCE_GENERATION | OVERRIDE_CLASSNAME | OVERRIDE_ITEM_DEF | OVERRIDE_ITEM_LEVEL | OVERRIDE_ITEM_QUALITY | OVERRIDE_ATTRIBUTES);
1237 TF2Items_SetClassname(hItem, "tf_weapon_flamethrower");
1238 TF2Items_SetItemIndex(hItem, 21);
1239 TF2Items_SetLevel(hItem, 69);
1240 TF2Items_SetQuality(hItem, 6);
1241 TF2Items_SetAttribute(hItem, 0, 823, 1.0); //Can't push other players
1242 TF2Items_SetNumAttributes(hItem, 1);
1243 int iWeapon = TF2Items_GiveNamedItem(client, hItem);
1244 CloseHandle(hItem);
1245 EquipPlayerWeapon(client, iWeapon);
1246 }
1247
1248 TF2_SwitchtoSlot(client, TFWeaponSlot_Primary);
1249
1250}
1251
1252/* OnPlayerSpawn()
1253**
1254** Here we set the spy cloak and we move the death player.
1255** -------------------------------------------------------------------------- */
1256public Action OnPlayerSpawn(Handle event, const char[] name, bool dontBroadcast)
1257{
1258 if(!g_isDBmap)
1259 {
1260 return;
1261 }
1262
1263 int client = GetClientOfUserId(GetEventInt(event, "userid"));
1264 int class = GetEntProp(client, Prop_Send, "m_iClass");
1265 if(g_pyro_only)
1266 {
1267 if(!(class == CLASS_PYRO || class == 0 ))
1268 {
1269 SetEntProp(client, Prop_Send, "m_iDesiredPlayerClass", CLASS_PYRO);
1270 SetEntProp(client, Prop_Send, "m_iClass", CLASS_PYRO);
1271 TF2_RespawnPlayer(client);
1272 }
1273 }
1274 else if(class == CLASS_SPY)
1275 {
1276 int cond = GetEntProp(client, Prop_Send, "m_nPlayerCond");
1277 if (cond & PLAYERCOND_SPYCLOAK)
1278 {
1279 SetEntProp(client, Prop_Send, "m_nPlayerCond", cond | ~PLAYERCOND_SPYCLOAK);
1280 }
1281 }
1282 if(g_onPreparation)
1283 {
1284 SetEntityMoveType(client, MOVETYPE_NONE);
1285 }
1286}
1287
1288
1289/* OnPlayerDeath()
1290**
1291** Here we reproduce sounds if needed and activate the glow effect if needed
1292** -------------------------------------------------------------------------- */
1293public Action:OnPlayerDeath(Handle:event, const String:name[], bool:dontBroadcast)
1294{
1295 if(!g_isDBmap)
1296 {
1297 return;
1298 }
1299 if(g_onPreparation)
1300 {
1301 return;
1302 }
1303
1304 //Victim
1305 int client = GetClientOfUserId(GetEventInt(event, "userid"));
1306 EmitRandomSound(g_SndOnDeath,client);
1307
1308 //Killer
1309 int killer = GetClientOfUserId(GetEventInt(event, "attacker"));
1310 if(g_canEmitKillSound && client != killer && killer > 0)
1311 {
1312 EmitRandomSound(g_SndOnKill,killer);
1313 g_canEmitKillSound = false;
1314 CreateTimer(g_OnKillDelay, ReenableKillSound);
1315 }
1316
1317 //Check for last one alive
1318 int victimTeam = GetClientTeam(client);
1319 int enemyteam;
1320 if(victimTeam == TEAM_RED)
1321 {
1322 enemyteam = TEAM_BLUE;
1323 }
1324 else
1325 {
1326 enemyteam = TEAM_RED;
1327 }
1328 int aliveTeammates = GetAlivePlayersCount(victimTeam,client);
1329
1330 if(aliveTeammates == 0 && g_1v1_started)
1331 {
1332 LivesAnnotation(client, 0);
1333 LivesAnnotation(GetLastPlayer(enemyteam,-1), 0);
1334 }
1335 if(aliveTeammates == 1)
1336 {
1337 EmitRandomSound(g_SndLastAlive,GetLastPlayer(victimTeam,client));
1338 if(g_1v1_allow)
1339 {
1340 int aliveEnemies;
1341 if(victimTeam == TEAM_RED)
1342 {
1343 aliveEnemies = GetAlivePlayersCount(TEAM_BLUE,-1);
1344 }
1345 else
1346 {
1347 aliveEnemies = GetAlivePlayersCount(TEAM_RED,-1);
1348 }
1349 if(aliveEnemies == 1)
1350 {
1351 CreateTimer(0.1,Timer_Start1V1Mode);
1352 }
1353 }
1354 }
1355
1356
1357 //Check if we need to crear a big boom
1358 int iInflictor = GetEventInt(event, "inflictor_entindex");
1359 int rIndex = GetRocketIndex(EntIndexToEntRef(iInflictor));
1360 CPrintToChatAll("{DEFAULT}[{RED}HarDzOneâ„¢{DEFAULT}] {GREEN}%N {DEFAULT}matou {REDTEAM}%N {DEFAULT}com rocket em uma {GREEN}Velocidade: %.1f km/h {DEFAULT}({GREEN}%d Refletidas{DEFAULT})",killer,client,g_RocketEnt[rIndex].speed/10,g_RocketEnt[rIndex].deflects);
1361 if(rIndex >= 0)
1362 {
1363 int class = g_RocketEnt[rIndex].class;
1364 if(g_RocketClass[class].exp_use)
1365 {
1366 CreateExplosion(rIndex);
1367 }
1368 }
1369 //Check if the max num of rockets has to be limited.
1370 if(g_limit_rockets)
1371 {
1372 if(aliveTeammates < g_max_rockets_dynamic)
1373 {
1374 g_max_rockets_dynamic--;
1375 if(g_RocketEnt[g_max_rockets_dynamic].entity != INVALID_ENT_REFERENCE)
1376 {
1377 CreateTimer(0.1,Timer_MoveRocketSlot,g_max_rockets_dynamic);
1378 }
1379 ClearHud();
1380 }
1381 }
1382
1383}
1384
1385public Action ReenableKillSound(Handle timer, int data)
1386{
1387 g_canEmitKillSound = true;
1388}
1389
1390public Action Timer_Start1V1Mode(Handle timer, int data)
1391{
1392 Start1V1Mode();
1393}
1394
1395public void Start1V1Mode()
1396{
1397 if(!g_isDBmap)
1398 {
1399 return;
1400 }
1401 if(g_onPreparation)
1402 {
1403 return;
1404 }
1405 g_1v1_started = true;
1406 g_1v1_music_played = EmitRandomSound(g_1v1_Music,-1);
1407
1408 g_1v1_red_life = g_1v1_lives;
1409 g_1v1_blue_life = g_1v1_lives;
1410 g_1v1_red_beep = null;
1411 g_1v1_blue_beep = null;
1412
1413 SetHudTextParams(-1.0, 0.40,7.0,0,255,255,255, 1, 3.0, 1.5, 1.5);
1414 for(int i = 1; i <= MaxClients; i++)
1415 {
1416 if(IsValidClient(i))
1417 {
1418 ShowSyncHudText(i, g_HudSyncs[MAXMULTICOLORHUD], "Battle between %N vs %N starts in 10 seconds!",GetLastPlayer(TEAM_RED),GetLastPlayer(TEAM_BLUE));
1419 }
1420 }
1421 LivesAnnotation(GetLastPlayer(TEAM_RED),g_1v1_red_life);
1422 LivesAnnotation(GetLastPlayer(TEAM_BLUE),g_1v1_blue_life);
1423 for(int i = 0; i < g_max_rockets; i++)
1424 {
1425 int index = EntRefToEntIndex(g_RocketEnt[i].entity);
1426 if (index != INVALID_ENT_REFERENCE)
1427 {
1428 int dissolver = CreateEntityByName("env_entity_dissolver");
1429
1430 if (dissolver == -1) return;
1431
1432 DispatchKeyValue(dissolver, "dissolvetype", "3");
1433 DispatchKeyValue(dissolver, "magnitude", "250");
1434 DispatchKeyValue(dissolver, "target", "!activator");
1435
1436 AcceptEntityInput(dissolver, "Dissolve", index);
1437 AcceptEntityInput(dissolver, "Kill");
1438 }
1439 }
1440 g_canSpawn = false;
1441 CreateTimer(10.0,AllowSpawn);
1442}
1443
1444public OnClientPutInServer(client)
1445{
1446 SDKHook(client, SDKHook_OnTakeDamage, OnClientTakeDamage);
1447}
1448
1449
1450public Action OnClientTakeDamage(client, &attacker, &inflictor, &Float:damage, &damagetype, &weapon, Float:damageForce[3], Float:damagePosition[3])
1451{
1452 if(!g_isDBmap)
1453 {
1454 return Plugin_Continue;
1455 }
1456 if(g_onPreparation)
1457 {
1458 return Plugin_Continue;
1459 }
1460
1461 if(!g_1v1_started)
1462 {
1463 return Plugin_Continue;
1464 }
1465
1466 int rIndex = GetRocketIndex(EntIndexToEntRef(inflictor));
1467 if(rIndex >= 0)
1468 {
1469 if(GetClientTeam(client) == TEAM_RED)
1470 {
1471 if(g_1v1_red_life == 0)
1472 {
1473 return Plugin_Continue;
1474 }
1475 g_1v1_red_life--;
1476 //PrintToChatAll("%N lost a life, now he has %d",client,g_1v1_red_life);
1477 LivesAnnotation(client, g_1v1_red_life);
1478 if(g_1v1_red_life == 1 && !StrEqual(g_1v1_beep_snd,""))
1479 {
1480 g_1v1_red_beep = CreateTimer(g_1v1_beep_delay,LifeBeep,GetClientUserId(client),TIMER_REPEAT);
1481 }
1482 if(g_1v1_red_life > 0)
1483 {
1484 damage = 0.0;
1485 return Plugin_Changed;
1486 }
1487 }
1488 else
1489 {
1490 if(g_1v1_blue_life == 0)
1491 {
1492 return Plugin_Continue;
1493 }
1494 g_1v1_blue_life--;
1495 //PrintToChatAll("%N lost a life, now he has %d",client,g_1v1_blue_life);
1496 LivesAnnotation(client, g_1v1_blue_life);
1497 if(g_1v1_blue_life == 1 && !StrEqual(g_1v1_beep_snd,""))
1498 {
1499 g_1v1_blue_beep = CreateTimer(g_1v1_beep_delay,LifeBeep,GetClientUserId(client),TIMER_REPEAT);
1500 }
1501 if(g_1v1_blue_life > 0)
1502 {
1503 damage = 0.0;
1504 return Plugin_Changed;
1505 }
1506 }
1507 }
1508
1509 return Plugin_Continue;
1510}
1511
1512void LivesAnnotation(int client, int lives)
1513{
1514
1515 if(!IsValidAliveClient(client))
1516 {
1517 return;
1518 }
1519 if(lives == 0)
1520 {
1521 for(int i = 1; i <= MaxClients; i++)
1522 {
1523 if(IsValidClient(i) && i != client)
1524 {
1525 Handle event = CreateEvent("hide_annotation");
1526 if(event == INVALID_HANDLE)
1527 {
1528 return;
1529 }
1530 SetEventInt(event, "id", MAXROCKETS + client * MAXPLAYERS + i);
1531 FireEvent(event);
1532 }
1533 }
1534 ClearHud();
1535 }
1536 else
1537 {
1538
1539 char livesString[MAX_NAME_LENGTH];
1540 Format(livesString,MAX_NAME_LENGTH,"♥");
1541 for(int i=2; i <= g_1v1_lives; i++)
1542 {
1543 if(i <= lives)
1544 {
1545 Format(livesString,MAX_NAME_LENGTH,"%s ♥",livesString);
1546 }
1547 else
1548 {
1549 Format(livesString,MAX_NAME_LENGTH,"%s -",livesString);
1550 }
1551 }
1552
1553 for(int i = 1; i <= MaxClients; i++)
1554 {
1555 if(IsValidClient(i) && i != client)
1556 {
1557 Handle event = CreateEvent("show_annotation");
1558 if(event == INVALID_HANDLE)
1559 {
1560 continue;
1561 }
1562 SetEventInt(event, "follow_entindex", client);
1563 SetEventFloat(event, "lifetime", 9999.0);
1564 SetEventInt(event, "id", MAXROCKETS + client * MAXPLAYERS + i);
1565 SetEventString(event, "text", livesString);
1566 SetEventString(event, "play_sound", "vo/null.mp3");
1567
1568 SetEventInt(event, "visibilityBitfield", 1 << i);
1569 SetEventBool(event,"show_effect", true);
1570 FireEvent(event);
1571
1572 }
1573 }
1574
1575
1576 SetHudTextParams(-1.0, 0.70,9999.0,255,0,0,255, 0, 0.0, 0.0, 0.0);
1577 ShowSyncHudText(client, g_HudSyncs[MAXMULTICOLORHUD-1], "%s",livesString);
1578 }
1579}
1580
1581/* LifeBeep()
1582**
1583** If the client is valid, it beeps.
1584** -------------------------------------------------------------------------- */
1585public Action LifeBeep(Handle timer, int data)
1586{
1587 if(!g_isDBmap)
1588 {
1589 return Plugin_Stop;
1590 }
1591 if(g_onPreparation)
1592 {
1593 return Plugin_Stop;
1594 }
1595
1596 if(!g_1v1_started)
1597 {
1598 return Plugin_Stop;
1599 }
1600
1601 int client = GetClientOfUserId(data);
1602 if(!IsValidClient(client))
1603 {
1604 return Plugin_Stop;
1605 }
1606
1607 EmitSoundToClient(client,g_1v1_beep_snd,_,_, SNDLEVEL_TRAIN);
1608 return Plugin_Continue;
1609}
1610
1611
1612
1613/* SearchSpawns()
1614**
1615** Searchs for blue and red rocket spawns
1616** -------------------------------------------------------------------------- */
1617public void SearchSpawns()
1618{
1619 if(!g_isDBmap)
1620 {
1621 return;
1622 }
1623 int iEntity = -1;
1624 g_RedSpawn = -1;
1625 g_BlueSpawn = -1;
1626 while ((iEntity = FindEntityByClassname(iEntity, "info_target")) != -1)
1627 {
1628 char strName[32];
1629 GetEntPropString(iEntity, Prop_Data, "m_iName", strName, sizeof(strName));
1630 if ((StrContains(strName, "rocket_spawn_red") != -1) || (StrContains(strName, "tf_dodgeball_red") != -1))
1631 {
1632 g_RedSpawn = EntIndexToEntRef(iEntity);
1633 }
1634 if ((StrContains(strName, "rocket_spawn_blu") != -1) || (StrContains(strName, "tf_dodgeball_blu") != -1))
1635 {
1636 g_BlueSpawn = EntIndexToEntRef(iEntity);
1637 }
1638 }
1639
1640 if (g_RedSpawn == INVALID_ENT_REFERENCE)
1641 {
1642 SetFailState("No RED spawn points found on this map.");
1643 }
1644
1645 if (g_BlueSpawn == INVALID_ENT_REFERENCE)
1646 {
1647 SetFailState("No BLU spawn points found on this map.");
1648 }
1649
1650
1651 //ObserverPoint
1652
1653 float opPos[3];
1654 float opAng[3];
1655
1656 int spawner = GetRandomInt(0,1);
1657 if(spawner == 0)
1658 spawner = EntRefToEntIndex(g_RedSpawn);
1659 else
1660 spawner = EntRefToEntIndex(g_BlueSpawn);
1661 if(IsValidEntity(spawner)&& spawner > MaxClients)
1662 {
1663 GetEntPropVector(spawner,Prop_Data,"m_vecOrigin",opPos);
1664 GetEntPropVector(spawner,Prop_Data, "m_angAbsRotation", opAng);
1665 g_observer = CreateEntityByName("info_observer_point");
1666 DispatchKeyValue(g_observer, "Angles", "90 0 0");
1667 DispatchKeyValue(g_observer, "TeamNum", "0");
1668 DispatchKeyValue(g_observer, "StartDisabled", "0");
1669 DispatchSpawn(g_observer);
1670 AcceptEntityInput(g_observer, "Enable");
1671 TeleportEntity(g_observer, opPos, opAng, NULL_VECTOR);
1672 g_observer = EntIndexToEntRef(g_observer);
1673 g_observer_slot = -1;
1674 }
1675 else
1676 {
1677 g_observer = INVALID_ENT_REFERENCE;
1678 }
1679 return;
1680}
1681
1682/* GetRandomRocketClass()
1683**
1684** Returns a random rocket based on config's chance
1685** -------------------------------------------------------------------------- */
1686public int GetRandomRocketClass()
1687{
1688 int classChance[MAXROCKETS];
1689 char className[MAX_NAME_LENGTH];
1690
1691 //Here we get the probability of each rocket class
1692 int maxNum = 0;
1693 for(int i = 0; i < g_RocketClass_count; i++)
1694 {
1695 g_RocketClass[i].GetName(className,MAX_NAME_LENGTH);
1696 if(!g_class_chance.GetValue(className,classChance[i]))
1697 {
1698 classChance[i] = 0;
1699 }
1700 else
1701 {
1702 maxNum+=classChance[i];
1703 }
1704 }
1705
1706 int random = GetRandomInt(1, maxNum);
1707
1708 int upChance = 0, downChance = 1;
1709 for(int i = 0; i < g_RocketClass_count; i++)
1710 {
1711 downChance = upChance + 1;
1712 upChance = downChance + classChance[i] -1;
1713 if(random >= downChance && upChance >= random)
1714 {
1715 return i;
1716 }
1717
1718 }
1719 return 0;
1720}
1721
1722/* GetRocketSlot()
1723**
1724** Checks if every "slot" of rockets is used
1725** -------------------------------------------------------------------------- */
1726public int GetRocketSlot()
1727{
1728 int index;
1729 //for(int i = 0; i < g_max_rockets; i++)
1730 for(int i = 0; i < g_max_rockets_dynamic; i++)
1731 {
1732 index = EntRefToEntIndex(g_RocketEnt[i].entity);
1733 if (index == INVALID_ENT_REFERENCE)
1734 {
1735 return i;
1736 }
1737 }
1738 return -1;
1739}
1740
1741/* GetRocketIndex()
1742**
1743** Gets the rocket index from a entity reference
1744** -------------------------------------------------------------------------- */
1745public int GetRocketIndex(int entity)
1746{
1747 for(int i = 0; i < g_max_rockets; i++)
1748 {
1749 if(g_RocketEnt[i].entity == entity)
1750 {
1751 return i;
1752 }
1753 }
1754 return -1;
1755}
1756
1757/* Timer_MoveRocketSlot()
1758**
1759** Copye the info on a slot, to another with a lower index, if it can't find one, it destroys the rocket
1760** -------------------------------------------------------------------------- */
1761public Action Timer_MoveRocketSlot(Handle timer, int oldSlot)
1762{
1763 int newSlot = GetRocketSlot();
1764 int index = g_RocketEnt[oldSlot].entity;
1765 if(index == INVALID_ENT_REFERENCE)
1766 {
1767 return;
1768 }
1769
1770 //If there isn't any slot available we just remove the rocket.
1771 if(newSlot == -1)
1772 {
1773 int dissolver = CreateEntityByName("env_entity_dissolver");
1774
1775 if (dissolver == -1)
1776 {
1777 AcceptEntityInput(index, "Kill");
1778 }
1779 else
1780 {
1781 DispatchKeyValue(dissolver, "dissolvetype", "3");
1782 DispatchKeyValue(dissolver, "magnitude", "250");
1783 DispatchKeyValue(dissolver, "target", "!activator");
1784
1785 AcceptEntityInput(dissolver, "Dissolve", index);
1786 AcceptEntityInput(dissolver, "Kill");
1787 }
1788 }
1789 else
1790 {
1791 //If there was avaible slot, we just copy the info form the old slot to the new one
1792 g_RocketEnt[newSlot].entity = g_RocketEnt[oldSlot].entity;
1793 g_RocketEnt[newSlot].class = g_RocketEnt[oldSlot].class;
1794 g_RocketEnt[newSlot].bounces = g_RocketEnt[oldSlot].bounces;
1795 g_RocketEnt[newSlot].target = g_RocketEnt[oldSlot].target;
1796 g_RocketEnt[newSlot].owner = g_RocketEnt[oldSlot].owner ;
1797 g_RocketEnt[newSlot].aimed = g_RocketEnt[oldSlot].aimed;
1798 g_RocketEnt[newSlot].deflects = g_RocketEnt[oldSlot].deflects;
1799 g_RocketEnt[newSlot].speed = g_RocketEnt[oldSlot].speed;
1800 g_RocketEnt[newSlot].homing = g_RocketEnt[oldSlot].homing;
1801 g_RocketEnt[newSlot].keepdir = g_RocketEnt[oldSlot].keepdir;
1802 g_RocketEnt[newSlot].annotation = g_RocketEnt[oldSlot].annotation;
1803 float auxVec[3];
1804 g_RocketEnt[oldSlot].GetDirection(auxVec);
1805 g_RocketEnt[newSlot].SetDirection(auxVec);
1806
1807 int class = g_RocketEnt[newSlot].class;
1808 if(g_RocketClass[class].snd_beep_use)
1809 {
1810 g_RocketEnt[newSlot].beeptimer = CreateTimer(g_RocketClass[class].snd_beep_delay,RocketBeep,newSlot,TIMER_REPEAT);
1811 }
1812
1813 if(useMultiColor())
1814 {
1815 //model
1816 int ent;
1817 int parent;
1818 if(g_mrc_applycolor_model[newSlot])
1819 {
1820 DispatchKeyValue(g_RocketEnt[newSlot].entity, "rendercolor", g_mrc_color[newSlot]);
1821 }
1822 //trail
1823 if(!StrEqual(g_mrc_trail[newSlot],""))
1824 {
1825 ent = -1;
1826 while((ent = FindEntityByClassname(ent,"env_spritetrail")) != -1)
1827 {
1828 parent = GetEntPropEnt(ent, Prop_Data, "m_pParent");
1829 if(IsValidEntity(parent) && g_RocketEnt[newSlot].entity == EntIndexToEntRef(parent))
1830 {
1831 DispatchKeyValue(ent, "rendercolor", g_mrc_color[newSlot]);
1832 }
1833 }
1834 }
1835 //light
1836 if(g_mrc_use_light[newSlot])
1837 {
1838 ent = -1;
1839 while((ent = FindEntityByClassname(ent,"light_dynamic")) != -1)
1840 {
1841
1842 parent = GetEntPropEnt(ent, Prop_Data, "m_pParent");
1843 if(IsValidEntity(parent) && g_RocketEnt[newSlot].entity == EntIndexToEntRef(parent))
1844 {
1845 DispatchKeyValue(ent, "_light", g_mrc_color[newSlot]);
1846 }
1847 }
1848 }
1849 }
1850
1851 g_RocketEnt[oldSlot].entity = INVALID_ENT_REFERENCE;
1852 g_RocketEnt[oldSlot].class = -1;
1853 g_RocketEnt[oldSlot].bounces = 0;
1854 g_RocketEnt[oldSlot].target = -1;
1855 g_RocketEnt[oldSlot].owner = -1;
1856 g_RocketEnt[oldSlot].aimed = false;
1857 g_RocketEnt[oldSlot].deflects = 0;
1858 g_RocketEnt[oldSlot].speed = 0.0;
1859 g_RocketEnt[oldSlot].homing = true;
1860 g_RocketEnt[oldSlot].keepdir = false;
1861 g_RocketEnt[oldSlot].annotation = false;
1862 if(g_RocketEnt[oldSlot].beeptimer != null)
1863 {
1864 CloseHandle(g_RocketEnt[oldSlot].beeptimer);
1865 }
1866 g_RocketEnt[oldSlot].beeptimer = null;
1867
1868 }
1869
1870}
1871
1872/* SearchTarget()
1873**
1874** Searchs for a new Target
1875** -------------------------------------------------------------------------- */
1876public int SearchTarget(int rIndex)
1877{
1878 if(!g_isDBmap)
1879 {
1880 return -1;
1881 }
1882 if(!g_roundActive)
1883 {
1884 return -1;
1885 }
1886
1887 int index = g_RocketEnt[rIndex].entity;
1888 if (index == INVALID_ENT_REFERENCE)
1889 {
1890 return -1;
1891 }
1892
1893 int rTeam = GetEntProp(index, Prop_Send, "m_iTeamNum", 1);
1894 int class = g_RocketEnt[rIndex].class;
1895
1896 //Check by aim
1897 if(g_RocketClass[class].allowaimed)
1898 {
1899 int rOwner = GetEntPropEnt(index, Prop_Send, "m_hOwnerEntity");
1900 if(rOwner != 0)
1901 {
1902 int cAimed = GetClientAimTarget(rOwner, true);
1903 if( IsValidAliveClient(cAimed) && GetClientTeam(cAimed) != rTeam )
1904 {
1905 g_RocketEnt[rIndex].aimed = true;
1906 return cAimed;
1907 }
1908 }
1909 }
1910 g_RocketEnt[rIndex].aimed = false;
1911 int auxClient, clientTargeted[MAXPLAYERS+1];
1912 for(int i = 0; i < g_max_rockets_dynamic; i++)
1913 {
1914 auxClient = g_RocketEnt[i].target;
1915 if(IsValidAliveClient(auxClient))
1916 {
1917 clientTargeted[auxClient]++;
1918 }
1919 }
1920
1921 //We make a list of possibles players
1922 int possiblePlayers[MAXPLAYERS+1];
1923 int possibleNumber = 0;
1924 for(int i = 1; i <= MaxClients ; i++)
1925 {
1926 if(!IsValidAliveClient(i) || GetClientTeam(i) == rTeam || clientTargeted[i] > 0)
1927 {
1928 continue;
1929 }
1930 possiblePlayers[possibleNumber] = i;
1931 possibleNumber++;
1932 }
1933
1934 //If there weren't any player the could be targeted the we try even with already aimed clients.
1935 if(possibleNumber == 0)
1936 {
1937 for(int i = 1; i <= MaxClients ; i++)
1938 {
1939 if(!IsValidAliveClient(i) || GetClientTeam(i) == rTeam)
1940 {
1941 continue;
1942 }
1943 possiblePlayers[possibleNumber] = i;
1944 possibleNumber++;
1945 }
1946 if(possibleNumber == 0)
1947 {
1948 return -1;
1949 }
1950 }
1951
1952 //Random player
1953 if(!g_RocketClass[class].targetclosest)
1954 {
1955 return possiblePlayers[ GetRandomInt(0,possibleNumber-1)];
1956 }
1957
1958 //We find the closest player in the valid players vector
1959 else
1960 {
1961 //Some aux variables
1962 float aux_dist;
1963 float aux_pos[3];
1964 //Rocket's position
1965 float rPos[3];
1966 GetEntPropVector(index, Prop_Send, "m_vecOrigin", rPos);
1967
1968 //First player in the list will be the current closest player
1969 int closest = possiblePlayers[0];
1970 GetClientAbsOrigin(closest,aux_pos);
1971 float closest_dist = GetVectorDistance(rPos,aux_pos, true);
1972
1973
1974 for(int i = 1; i < possibleNumber; i++)
1975 {
1976 //We use the squared option for optimization since we don't need the absolute distance.
1977 GetClientAbsOrigin(possiblePlayers[i],aux_pos);
1978 aux_dist = GetVectorDistance(rPos, aux_pos, true);
1979 if(closest_dist > aux_dist)
1980 {
1981 closest = possiblePlayers[i];
1982 closest_dist = aux_dist;
1983 }
1984 }
1985 return closest;
1986 }
1987
1988
1989}
1990
1991/* TryFireRocket()
1992**
1993** Timer used to try fire a new rocket.
1994** -------------------------------------------------------------------------- */
1995public Action TryFireRocket(Handle timer, int data)
1996{
1997 FireRocket();
1998
1999}
2000/* AllowSpawn()
2001**
2002** Timer used to allow the new rocket and fire it.
2003** -------------------------------------------------------------------------- */
2004public Action AllowSpawn(Handle timer, int data)
2005{
2006 g_canSpawn = true;
2007 FireRocket();
2008}
2009/* FireRocket()
2010**
2011** Function used to spawn the actual rocket.
2012** This will check if there are available slots and won't fire if a rocket was fired recently.
2013** -------------------------------------------------------------------------- */
2014public void FireRocket()
2015{
2016 if(!g_isDBmap)
2017 {
2018 return;
2019 }
2020 if(!g_roundActive)
2021 {
2022 return;
2023 }
2024 if(!g_canSpawn)
2025 {
2026 return;
2027 }
2028 if(!g_roundActive)
2029 {
2030 return;
2031 }
2032
2033 int rIndex = GetRocketSlot();
2034 if(rIndex == -1) return;
2035
2036 int spawner, rocketTeam;
2037 if(g_lastSpawned == TEAM_RED)
2038 {
2039 rocketTeam = TEAM_BLUE;
2040 spawner = g_BlueSpawn;
2041 }
2042 else
2043 {
2044 rocketTeam = TEAM_RED;
2045 spawner = g_RedSpawn;
2046 }
2047
2048 int iEntity = CreateEntityByName( "tf_projectile_rocket");
2049 if(iEntity && IsValidEntity(iEntity))
2050 {
2051 int class = GetRandomRocketClass();
2052 g_RocketEnt[rIndex].entity = EntIndexToEntRef(iEntity);
2053 g_RocketEnt[rIndex].class = class;
2054 g_RocketEnt[rIndex].bounces = 0;
2055 g_RocketEnt[rIndex].aimed = false;
2056 g_RocketEnt[rIndex].deflects = 0;
2057 g_RocketEnt[rIndex].speed = 0.0;
2058 g_RocketEnt[rIndex].homing = true;
2059 g_RocketEnt[rIndex].keepdir = false;
2060 g_RocketEnt[rIndex].annotation = false;
2061 g_RocketEnt[rIndex].beeptimer = null;
2062
2063
2064 // Fetch spawn point's location and angles.
2065 float fPosition[3], fAngles[3], fDirection[3], fVelocity[3];
2066 GetEntPropVector(spawner, Prop_Send, "m_vecOrigin", fPosition);
2067 GetEntPropVector(spawner, Prop_Send, "m_angRotation", fAngles);
2068 GetAngleVectors(fAngles, fDirection, NULL_VECTOR, NULL_VECTOR);
2069 g_RocketEnt[rIndex].SetDirection(fDirection);
2070
2071 // Setup rocket entity.
2072 SetEntPropEnt(iEntity, Prop_Send, "m_hOwnerEntity", 0);
2073 SetEntProp(iEntity, Prop_Send, "m_bCritical", 1);
2074 SetEntProp(iEntity, Prop_Send, "m_iTeamNum", rocketTeam, 1);
2075 SetEntProp(iEntity, Prop_Send, "m_iDeflected", 1);
2076
2077 float aux_mul = g_RocketClass[class].speed;
2078 g_RocketEnt[rIndex].speed = aux_mul;
2079 fVelocity[0] = fDirection[0]*aux_mul;
2080 fVelocity[1] = fDirection[1]*aux_mul;
2081 fVelocity[2] = fDirection[2]*aux_mul;
2082 TeleportEntity(iEntity, fPosition, fAngles, fVelocity);
2083
2084 SetEntDataFloat(iEntity, FindSendPropOffs("CTFProjectile_Rocket", "m_iDeflected") + 4, g_RocketClass[class].damage, true);
2085 DispatchSpawn(iEntity);
2086
2087 char auxPath[PLATFORM_MAX_PATH];
2088 g_RocketClass[class].GetModel(auxPath,PLATFORM_MAX_PATH);
2089 if(!StrEqual(auxPath,""))
2090 {
2091 SetEntityModel(iEntity, auxPath);
2092 }
2093
2094 if(g_RocketClass[class].size > 0.0)
2095 {
2096 SetEntPropFloat(iEntity, Prop_Send, "m_flModelScale", g_RocketClass[class].size);
2097 }
2098
2099 if(useMultiColor())
2100 {
2101 if(!StrEqual(g_mrc_trail[rIndex],""))
2102 {
2103 AttachTrail(rIndex);
2104 }
2105 if(g_mrc_applycolor_model[rIndex])
2106 {
2107 DispatchKeyValue(iEntity, "rendercolor", g_mrc_color[rIndex]);
2108 }
2109 if(g_mrc_use_light[rIndex])
2110 {
2111 AttachLight(rIndex);
2112 }
2113 }
2114 else
2115 {
2116 g_RocketClass[class].GetTrail(auxPath,PLATFORM_MAX_PATH);
2117 if(!StrEqual(auxPath,""))
2118 {
2119 AttachTrail(rIndex);
2120 }
2121 }
2122
2123
2124 SDKHook(iEntity, SDKHook_StartTouch, OnStartTouch);
2125
2126 g_RocketEnt[rIndex].owner = 0;
2127 g_RocketEnt[rIndex].target = SearchTarget(rIndex);
2128
2129 if( !IsValidAliveClient(g_RocketEnt[rIndex].target ))
2130 {
2131 AcceptEntityInput(iEntity, "Kill");
2132 g_RocketEnt[rIndex].entity = -1;
2133 return;
2134 }
2135
2136 if(g_annotation_show)
2137 {
2138 CreateTimer(0.1,Timer_ShowAnnotation,rIndex);
2139 }
2140 EmitSoundClientDB(g_RocketEnt[rIndex].target, rsnd_alert ,rIndex,false);
2141 EmitSoundAllDB( rsnd_spawn,rIndex,true);
2142 if(g_RocketClass[class].snd_beep_use)
2143 {
2144 g_RocketEnt[rIndex].beeptimer = CreateTimer(g_RocketClass[class].snd_beep_delay,RocketBeep,rIndex,TIMER_REPEAT);
2145 }
2146
2147 //Observer point
2148 if(g_observer != INVALID_ENT_REFERENCE && g_observer_slot == -1)
2149 {
2150 TeleportEntity(g_observer, fPosition, fAngles, Float:{0.0, 0.0, 0.0});
2151 SetVariantString("!activator");
2152 AcceptEntityInput(g_observer, "SetParent", g_RocketEnt[rIndex].entity);
2153 g_observer_slot = rIndex;
2154 }
2155
2156 g_lastSpawned = rocketTeam;
2157 g_canSpawn = false;
2158 RenderHud();
2159 CreateTimer(g_spawn_delay,AllowSpawn);
2160
2161 }
2162}
2163
2164public void AttachTrail(int rIndex)
2165{
2166 int index = EntRefToEntIndex(g_RocketEnt[rIndex].entity);
2167 if (index == INVALID_ENT_REFERENCE)
2168 {
2169 return;
2170 }
2171
2172 int trail = CreateEntityByName("env_spritetrail");
2173 int colornum = -1;
2174 if(useMultiColor())
2175 {
2176 colornum = rIndex;
2177 }
2178 if (!IsValidEntity(trail))
2179 {
2180 return;
2181 }
2182
2183 char strTargetName[MAX_NAME_LENGTH];
2184 Format(strTargetName,sizeof(strTargetName),"projectile%d",index);
2185 DispatchKeyValue(index, "targetname", strTargetName);
2186 DispatchKeyValue(trail, "parentname", strTargetName);
2187 DispatchKeyValueFloat(trail, "lifetime", 1.0);
2188 DispatchKeyValueFloat(trail, "endwidth", 15.0);
2189 DispatchKeyValueFloat(trail, "startwidth", 6.0);
2190
2191 char trailMaterial[PLATFORM_MAX_PATH];
2192
2193 //We check if we should use the multicolored trail or the class one.
2194 if(colornum >= 0 && colornum < MAXMULTICOLORHUD)
2195 {
2196 Format(trailMaterial,PLATFORM_MAX_PATH,"%s.vmt",g_mrc_trail[colornum]);
2197 if(g_mrc_applycolor_trail[colornum])
2198 {
2199 DispatchKeyValue(trail, "rendercolor", g_mrc_color[colornum]);
2200 }
2201 }
2202 else
2203 {
2204 g_RocketClass[g_RocketEnt[rIndex].class].GetTrail(trailMaterial,PLATFORM_MAX_PATH);
2205 Format(trailMaterial,PLATFORM_MAX_PATH,"%s.vmt",trailMaterial);
2206 DispatchKeyValue(trail, "rendercolor", "255 255 255 255");
2207 }
2208
2209 DispatchKeyValue(trail, "spritename", trailMaterial);
2210 DispatchKeyValue(trail, "renderamt", "255");
2211
2212
2213 DispatchKeyValue(trail, "rendermode", "3");
2214 DispatchSpawn(trail);
2215
2216 float vec[3];
2217 GetEntPropVector(index, Prop_Data, "m_vecOrigin", vec);
2218
2219 TeleportEntity(trail, vec, NULL_VECTOR, NULL_VECTOR);
2220
2221 SetVariantString(strTargetName);
2222 AcceptEntityInput(trail, "SetParent");
2223 SetEntPropFloat(trail, Prop_Send, "m_flTextureRes", 0.05);
2224 return;
2225}
2226
2227public void AttachLight(int rIndex)
2228{
2229 int index = EntRefToEntIndex(g_RocketEnt[rIndex].entity);
2230 if (index == INVALID_ENT_REFERENCE)
2231 {
2232 return;
2233 }
2234
2235 int colornum = -1;
2236 if(!useMultiColor())
2237 {
2238 return;
2239 }
2240 colornum = rIndex;
2241 int iLightEntity = CreateEntityByName("light_dynamic");
2242 if (IsValidEntity(iLightEntity))
2243 {
2244 DispatchKeyValue(iLightEntity, "inner_cone", "0");
2245 DispatchKeyValue(iLightEntity, "cone", "80");
2246 DispatchKeyValue(iLightEntity, "brightness", "10");
2247 DispatchKeyValueFloat(iLightEntity, "spotlight_radius", 100.0);
2248 DispatchKeyValueFloat(iLightEntity, "distance", 150.0);
2249 DispatchKeyValue(iLightEntity, "_light", g_mrc_color[colornum]);
2250 DispatchKeyValue(iLightEntity, "pitch", "-90");
2251 DispatchKeyValue(iLightEntity, "style", "5");
2252 DispatchSpawn(iLightEntity);
2253
2254 float fOrigin[3];
2255 GetEntPropVector(index, Prop_Data, "m_vecOrigin", fOrigin);
2256
2257 fOrigin[2] += 40.0;
2258 TeleportEntity(iLightEntity, fOrigin, NULL_VECTOR, NULL_VECTOR);
2259
2260 char strName[32];
2261 Format(strName, sizeof(strName), "target%i", index);
2262 DispatchKeyValue(index, "targetname", strName);
2263
2264 DispatchKeyValue(iLightEntity, "parentname", strName);
2265 SetVariantString("!activator");
2266 AcceptEntityInput(iLightEntity, "SetParent", index, iLightEntity, 0);
2267 AcceptEntityInput(iLightEntity, "TurnOn");
2268 }
2269
2270}
2271
2272/* RocketBeep()
2273**
2274** If the rocket is valid, beeps
2275** -------------------------------------------------------------------------- */
2276public Action RocketBeep(Handle timer, int rIndex)
2277{
2278 EmitSoundAllDB(rsnd_beep,rIndex,true);
2279 return Plugin_Continue;
2280}
2281
2282public Action OnStartTouch(int entity, int other)
2283{
2284 if (other > 0 && other <= MaxClients)
2285 {
2286 return Plugin_Continue;
2287 }
2288
2289 int ref = EntIndexToEntRef(entity);
2290 int rIndex = GetRocketIndex(ref);
2291 if(rIndex == -1)
2292 return Plugin_Continue;
2293
2294 int class = g_RocketEnt[rIndex].class;
2295
2296 //We check the bounce counter
2297 if (g_RocketEnt[rIndex].bounces >= g_RocketClass[class].maxbounce)
2298 {
2299 if(g_RocketClass[class].exp_use)
2300 {
2301 CreateExplosion(rIndex);
2302 }
2303 return Plugin_Continue;
2304 }
2305
2306 SDKHook(entity, SDKHook_Touch, OnTouch);
2307 return Plugin_Handled;
2308}
2309
2310public Action OnTouch(int entity, int other)
2311{
2312
2313 float vOrigin[3];
2314 GetEntPropVector(entity, Prop_Data, "m_vecOrigin", vOrigin);
2315
2316 float vAngles[3];
2317 GetEntPropVector(entity, Prop_Data, "m_angRotation", vAngles);
2318
2319 float vVelocity[3];
2320 GetEntPropVector(entity, Prop_Data, "m_vecAbsVelocity", vVelocity);
2321
2322 Handle trace = TR_TraceRayFilterEx(vOrigin, vAngles, MASK_SHOT, RayType_Infinite, TEF_ExcludeEntity, entity);
2323
2324 if(!TR_DidHit(trace))
2325 {
2326 CloseHandle(trace);
2327 return Plugin_Continue;
2328 }
2329
2330 float vNormal[3];
2331 TR_GetPlaneNormal(trace, vNormal);
2332
2333 CloseHandle(trace);
2334
2335 float dotProduct = GetVectorDotProduct(vNormal, vVelocity);
2336
2337 ScaleVector(vNormal, dotProduct);
2338 ScaleVector(vNormal, 2.0);
2339
2340 float vBounceVec[3];
2341 SubtractVectors(vVelocity, vNormal, vBounceVec);
2342
2343 float vNewAngles[3];
2344 GetVectorAngles(vBounceVec, vNewAngles);
2345
2346 TeleportEntity(entity, NULL_VECTOR, vNewAngles, vBounceVec);
2347
2348
2349 int ref = EntIndexToEntRef(entity);
2350 int rIndex = GetRocketIndex(ref);
2351 if(rIndex == -1)
2352 {
2353 return Plugin_Continue;
2354 }
2355 int class = g_RocketEnt[rIndex].class;
2356 g_RocketEnt[rIndex].bounces++;
2357 g_RocketEnt[rIndex].homing = false;
2358 CreateTimer(g_RocketClass[class].bouncedelay,EnableHoming,rIndex);
2359 if(g_RocketClass[class].bouncekeepdir)
2360 {
2361 g_RocketEnt[rIndex].keepdir = true;
2362 CreateTimer(g_RocketClass[class].bouncedelay,DisableKeepDirection,rIndex);
2363 }
2364 else
2365 {
2366 float fDirection[3];
2367 GetAngleVectors(vNewAngles,fDirection,NULL_VECTOR,NULL_VECTOR);
2368 g_RocketEnt[rIndex].SetDirection(fDirection);
2369 }
2370
2371 EmitSoundAllDB(rsnd_bounce,rIndex,true);
2372
2373 SDKUnhook(entity, SDKHook_Touch, OnTouch);
2374 return Plugin_Handled;
2375}
2376
2377public bool TEF_ExcludeEntity(int entity, int contentsMask, int data)
2378{
2379 return (entity != data);
2380}
2381
2382/* OnGameFrame()
2383**
2384** We set the player max speed on every frame, and also we set the spy's cloak on empty.
2385** Here we also check what to do with the rocket. We checks for deflects and modify the rocket's speed.
2386** -------------------------------------------------------------------------- */
2387public void OnGameFrame()
2388{
2389 if(!g_isDBmap)
2390 {
2391 return;
2392 }
2393 if(!g_roundActive)
2394 {
2395 return;
2396 }
2397 //We control everybody's speed
2398 for(int i = 1; i <= MaxClients; i++)
2399 {
2400 if(IsValidAliveClient(i))
2401 {
2402 SetEntPropFloat(i, Prop_Send, "m_flMaxspeed", g_player_speed);
2403 if(TF2_GetPlayerClass(i) == TFClass_Spy)
2404 {
2405 SetCloak(i, 1.0);
2406 }
2407 }
2408 }
2409
2410 //Rocket Management
2411 for(int i = 0; i < MAXROCKETS; i++)
2412 {
2413 int index = EntRefToEntIndex(g_RocketEnt[i].entity);
2414 if (index == INVALID_ENT_REFERENCE)
2415 {
2416 continue;
2417 }
2418 int class = g_RocketEnt[i].class;
2419 int rDeflects = GetEntProp(index, Prop_Send, "m_iDeflected") - 1;
2420 float aux_mul = 0.0;
2421 //Check if the target is available
2422 if(!IsValidAliveClient(g_RocketEnt[i].target))
2423 {
2424 g_RocketEnt[i].target = SearchTarget(i);
2425 g_RocketEnt[i].homing = false;
2426 if(g_annotation_show)
2427 {
2428 CreateTimer(0.1,Timer_ShowAnnotation,i);
2429 }
2430 CreateTimer(g_RocketClass[class].deflectdelay,EnableHoming,i);
2431 }
2432 //Check deflects
2433 else if(rDeflects > g_RocketEnt[i].deflects)
2434 {
2435 //We hide the old annotation (if there is still active)
2436 if(g_annotation_show)
2437 {
2438 if(g_RocketEnt[i].annotation)
2439 {
2440 HideAnnotation(i);
2441 }
2442 }
2443 //We get the rocket's angle and we aplicate de elevation deflect
2444 float fAngles[3], fDirection[3];
2445 GetClientEyeAngles(g_RocketEnt[i].target, fAngles);
2446 GetAngleVectors(fAngles, fDirection, NULL_VECTOR, NULL_VECTOR);
2447
2448 fDirection[2]+= g_RocketClass[class].elevaterate;
2449 if(fDirection[2] > g_RocketClass[class].elevatemax)
2450 {
2451 fDirection[2] = g_RocketClass[class].elevatemax;
2452 }
2453 else if(fDirection[2] < g_RocketClass[class].elevatemin)
2454 {
2455 fDirection[2] = g_RocketClass[class].elevatemin;
2456 }
2457 GetVectorAngles(fDirection,fAngles);
2458 SetEntPropVector(index, Prop_Send, "m_angRotation", fAngles);
2459 g_RocketEnt[i].SetDirection(fDirection);
2460
2461 g_RocketEnt[i].target = SearchTarget(i);
2462 g_RocketEnt[i].deflects++;
2463 g_RocketEnt[i].bounces = 0;
2464 if(g_annotation_show)
2465 {
2466 CreateTimer(0.1,Timer_ShowAnnotation,i);
2467 }
2468 EmitSoundClientDB(g_RocketEnt[i].target, rsnd_alert ,i,false);
2469 if(g_RocketEnt[i].aimed && g_RocketClass[class].snd_aimed_use)
2470 {
2471 EmitSoundAllDB(rsnd_aimed,i,false);
2472 }
2473 else
2474 {
2475 int rTeam = GetEntProp(index, Prop_Send, "m_iTeamNum", 1);
2476 if(rTeam == TEAM_RED)
2477 {
2478 EmitSoundAllDB(rsnd_bludeflect,i,true);
2479 }
2480 else
2481 {
2482 EmitSoundAllDB(rsnd_reddeflect,i,true);
2483 }
2484 }
2485 g_RocketEnt[i].homing = false;
2486 g_RocketEnt[i].owner = GetEntPropEnt(index, Prop_Send, "m_hOwnerEntity");
2487 if(g_RocketEnt[i].aimed)
2488 {
2489 int ncolor[3];
2490 GetIntColor(g_hud_aimed_color,ncolor);
2491 SetHudTextParams(-1.0,-1.0,2.5,ncolor[0],ncolor[1],ncolor[2],255, 1, 0.0, 0.5, 0.5);
2492 ShowSyncHudText(g_RocketEnt[i].owner, g_HudSyncs[MAXHUDNUMBER-1], "%s",g_hud_aimed_text);
2493 }
2494 CreateTimer(g_RocketClass[class].deflectdelay,EnableHoming,i);
2495 RenderHud();
2496 }
2497 //If isn't a deflect then we have to modify the rocket's direction and velocity
2498 else
2499 {
2500 if(g_RocketEnt[i].homing)
2501 {
2502 if(IsValidAliveClient(g_RocketEnt[i].target))
2503 {
2504 float fDirectionToTarget[3], rocketDirection[3];
2505 g_RocketEnt[i].GetDirection(rocketDirection);
2506
2507 CalculateDirectionToClient(index, g_RocketEnt[i].target, fDirectionToTarget);
2508 float turnrate = g_RocketClass[class].turnrate + g_RocketClass[class].turnrateinc * g_RocketEnt[i].deflects;
2509 LerpVectors(rocketDirection, fDirectionToTarget, rocketDirection, turnrate);
2510
2511 g_RocketEnt[i].SetDirection(rocketDirection);
2512 }
2513 }
2514 if(g_RocketEnt[i].annotation)
2515 {
2516 float clientPos[3],rocketPos[3];
2517 GetClientAbsOrigin(g_RocketEnt[i].target, clientPos);
2518 GetEntPropVector(index, Prop_Send, "m_vecOrigin", rocketPos);
2519 if(GetVectorDistance(clientPos,rocketPos) < g_annotation_distance)
2520 {
2521 HideAnnotation(i);
2522 }
2523 }
2524 }
2525
2526 if(!g_RocketEnt[i].keepdir)
2527 {
2528 float rocketDirection[3];
2529 g_RocketEnt[i].GetDirection(rocketDirection);
2530 float fAngles[3]; GetVectorAngles(rocketDirection, fAngles);
2531 float fVelocity[3]; CopyVectors(rocketDirection, fVelocity);
2532
2533 if(aux_mul == 0.0)
2534 {
2535 aux_mul = g_RocketClass[class].speed + g_RocketClass[class].speedinc * g_RocketEnt[i].deflects;
2536 }
2537 if(g_RocketClass[class].allowaimed && g_RocketEnt[i].aimed)
2538 {
2539 aux_mul *= g_RocketClass[class].aimedspeed;
2540 }
2541
2542 float damage = g_RocketClass[class].damage + g_RocketClass[class].damageinc * g_RocketEnt[i].deflects;
2543 SetEntDataFloat(index, FindSendPropOffs("CTFProjectile_Rocket", "m_iDeflected") + 4, damage, true);
2544
2545 if(g_RocketClass[class].size > 0.0 && g_RocketClass[class].sizeinc > 0.0)
2546 {
2547 float size = g_RocketClass[class].size + g_RocketClass[class].sizeinc * g_RocketEnt[i].deflects;
2548 SetEntPropFloat(index, Prop_Send, "m_flModelScale", size);
2549 }
2550 g_RocketEnt[i].speed = aux_mul;
2551 fVelocity[0] = rocketDirection[0]*aux_mul;
2552 fVelocity[1] = rocketDirection[1]*aux_mul;
2553 fVelocity[2] = rocketDirection[2]*aux_mul;
2554 SetEntPropVector(index, Prop_Data, "m_vecAbsVelocity", fVelocity);
2555 SetEntPropVector(index, Prop_Send, "m_angRotation", fAngles);
2556 }
2557
2558 }
2559}
2560
2561/* EnableHoming()
2562**
2563** Timer used re-enable the rocket's movement.
2564** -------------------------------------------------------------------------- */
2565public Action EnableHoming(Handle timer, int rIndex)
2566{
2567 g_RocketEnt[rIndex].homing = true;
2568}
2569
2570/* DisableKeepDirection()
2571**
2572** Timer used to disable the keep direction state.
2573** -------------------------------------------------------------------------- */
2574public Action DisableKeepDirection(Handle timer, int rIndex)
2575{
2576 g_RocketEnt[rIndex].keepdir = false;
2577}
2578
2579public OnEntityCreated(entity, const String:classname[])
2580{
2581 if(!g_isDBmap)
2582 {
2583 return;
2584 }
2585 if(!StrEqual("tf_ammo_pack",classname))
2586 {
2587 return;
2588 }
2589
2590 int dissolver = CreateEntityByName("env_entity_dissolver");
2591
2592 if (dissolver == -1)
2593 {
2594 AcceptEntityInput(entity, "Kill");
2595 }
2596 else
2597 {
2598 DispatchKeyValue(dissolver, "dissolvetype", "1");
2599 DispatchKeyValue(dissolver, "magnitude", "1000");
2600 DispatchKeyValue(dissolver, "target", "!activator");
2601 AcceptEntityInput(dissolver, "Dissolve", entity);
2602 AcceptEntityInput(dissolver, "Kill");
2603 }
2604}
2605
2606/* OnEntityDestroyed()
2607**
2608** We check if the rocket got destroyed, and then fire a timer for a new rocket.
2609** -------------------------------------------------------------------------- */
2610public OnEntityDestroyed(entity)
2611{
2612 if(!g_isDBmap)
2613 {
2614 return;
2615 }
2616 if(entity == INVALID_ENT_REFERENCE || !IsValidEntity(entity))
2617 {
2618 return;
2619 }
2620 char classname[64];
2621 GetEntityClassname(entity, classname, sizeof(classname));
2622 if (!StrEqual(classname, "tf_projectile_rocket", false))
2623 {
2624 return;
2625 }
2626 int rIndex = GetRocketIndex(EntIndexToEntRef(entity));
2627 if(rIndex == -1)
2628 {
2629 return;
2630 }
2631
2632 g_RocketEnt[rIndex].entity = INVALID_ENT_REFERENCE;
2633 g_RocketEnt[rIndex].target = -1;
2634 g_RocketEnt[rIndex].owner = -1;
2635 g_RocketEnt[rIndex].class = -1;
2636 g_RocketEnt[rIndex].bounces = 0;
2637 g_RocketEnt[rIndex].deflects = -1;
2638 g_RocketEnt[rIndex].speed = -1.0;
2639 g_RocketEnt[rIndex].aimed = false;
2640 g_RocketEnt[rIndex].homing = true;
2641 g_RocketEnt[rIndex].annotation = false;
2642 if(g_RocketEnt[rIndex].beeptimer != null)
2643 {
2644 CloseHandle (g_RocketEnt[rIndex].beeptimer);
2645 }
2646 g_RocketEnt[rIndex].beeptimer = null;
2647
2648 if(g_annotation_show)
2649 {
2650 HideAnnotation(rIndex);
2651 }
2652 RenderHud();
2653 if(g_roundActive)
2654 {
2655 CreateTimer(g_spawn_delay, TryFireRocket);
2656
2657 }
2658
2659 if(g_observer != -1 && g_observer_slot == rIndex)
2660 {
2661 int entObs = EntRefToEntIndex(g_observer);
2662 SetVariantString("");
2663 AcceptEntityInput(entObs, "ClearParent");
2664
2665 float opPos[3];
2666 float opAng[3];
2667
2668 int spawner = GetRandomInt(0,1);
2669 if(spawner == 0)
2670 spawner = g_RedSpawn;
2671 else
2672 spawner = g_BlueSpawn;
2673
2674 if(spawner != INVALID_ENT_REFERENCE)
2675 {
2676 GetEntPropVector(spawner,Prop_Data,"m_vecOrigin",opPos);
2677 GetEntPropVector(spawner,Prop_Data, "m_angAbsRotation", opAng);
2678 TeleportEntity(entObs, opPos, opAng, NULL_VECTOR);
2679 }
2680 g_observer_slot = -1;
2681 }
2682
2683}
2684
2685/* CmdShockwave()
2686**
2687** Creates a huge shockwave at the location of the client, with the given
2688** parameters.
2689** -------------------------------------------------------------------------- */
2690public CreateExplosion(rIndex)
2691{
2692 if(g_1v1_started)
2693 {
2694 return;
2695 }
2696 int class = g_RocketEnt[rIndex].class;
2697 float fPosition[3];
2698 int index = EntRefToEntIndex(g_RocketEnt[rIndex].entity);
2699 if (index == INVALID_ENT_REFERENCE)
2700 {
2701 return;
2702 }
2703 int iTeam = GetEntProp(index, Prop_Send, "m_iTeamNum", 1);
2704 GetEntPropVector(index, Prop_Data, "m_vecOrigin", fPosition);
2705
2706 switch (GetRandomInt(0, 4))
2707 {
2708 case 0: { PlayParticle(fPosition, PARTICLE_NUKE_1_ANGLES, PARTICLE_NUKE_1); }
2709 case 1: { PlayParticle(fPosition, PARTICLE_NUKE_2_ANGLES, PARTICLE_NUKE_2); }
2710 case 2: { PlayParticle(fPosition, PARTICLE_NUKE_3_ANGLES, PARTICLE_NUKE_3); }
2711 case 3: { PlayParticle(fPosition, PARTICLE_NUKE_4_ANGLES, PARTICLE_NUKE_4); }
2712 case 4: { PlayParticle(fPosition, PARTICLE_NUKE_5_ANGLES, PARTICLE_NUKE_5); }
2713 }
2714 PlayParticle(fPosition, PARTICLE_NUKE_COLLUMN_ANGLES, PARTICLE_NUKE_COLLUMN);
2715
2716 for (int iClient = 1; iClient <= MaxClients; iClient++)
2717 {
2718 if(IsValidAliveClient(iClient) && GetClientTeam(iClient) != iTeam)
2719 {
2720 float fPlayerPosition[3];
2721 GetClientEyePosition(iClient, fPlayerPosition);
2722 float fDistanceToShockwave = GetVectorDistance(fPosition, fPlayerPosition);
2723
2724 if (fDistanceToShockwave < g_RocketClass[class].exp_radius)
2725 {
2726 float fImpulse[3], fFinalPush;
2727 int iFinalDamage;
2728 fImpulse[0] = fPlayerPosition[0] - fPosition[0];
2729 fImpulse[1] = fPlayerPosition[1] - fPosition[1];
2730 fImpulse[2] = fPlayerPosition[2] - fPosition[2];
2731 NormalizeVector(fImpulse, fImpulse);
2732 if (fImpulse[2] < 0.4)
2733 {
2734 fImpulse[2] = 0.4;
2735 NormalizeVector(fImpulse, fImpulse);
2736 }
2737
2738 if (fDistanceToShockwave < g_RocketClass[class].exp_fallof)
2739 {
2740 fFinalPush = g_RocketClass[class].exp_push;
2741 iFinalDamage = RoundFloat(g_RocketClass[class].exp_damage);
2742 }
2743 else
2744 {
2745 float fImpact = (1.0 - ((fDistanceToShockwave - g_RocketClass[class].exp_fallof) / ( g_RocketClass[class].exp_radius - g_RocketClass[class].exp_fallof)));
2746 fFinalPush = fImpact * g_RocketClass[class].exp_push;
2747 iFinalDamage = RoundToFloor(fImpact * g_RocketClass[class].exp_damage);
2748 }
2749 ScaleVector(fImpulse, fFinalPush);
2750 SetEntPropVector(iClient, Prop_Data, "m_vecAbsVelocity", fImpulse);
2751
2752 Handle hDamage = CreateDataPack();
2753 WritePackCell(hDamage, iClient);
2754 WritePackCell(hDamage, iFinalDamage);
2755 CreateTimer(0.1, ApplyDamage, hDamage, TIMER_FLAG_NO_MAPCHANGE);
2756 }
2757 }
2758 }
2759
2760 EmitSoundAllDB(rsnd_exp,rIndex,false);
2761}
2762/* ApplyDamage()
2763**
2764** Applies a damage to a player.
2765** -------------------------------------------------------------------------- */
2766public Action:ApplyDamage(Handle:hTimer, any:hDataPack)
2767{
2768 ResetPack(hDataPack, false);
2769 int iClient = ReadPackCell(hDataPack);
2770 int iDamage = ReadPackCell(hDataPack);
2771 CloseHandle(hDataPack);
2772 if(IsValidAliveClient(iClient))
2773 {
2774 SlapPlayer(iClient, iDamage, true);
2775 }
2776}
2777/* PlayParticle()
2778**
2779** Plays a particle system at the given location & angles.
2780** -------------------------------------------------------------------------- */
2781stock PlayParticle(Float:fPosition[3], Float:fAngles[3], String:strParticleName[], Float:fEffectTime = 5.0, Float:fLifeTime = 9.0)
2782{
2783 int iEntity = CreateEntityByName("info_particle_system");
2784 if (iEntity && IsValidEdict(iEntity))
2785 {
2786 TeleportEntity(iEntity, fPosition, fAngles, NULL_VECTOR);
2787 DispatchKeyValue(iEntity, "effect_name", strParticleName);
2788 ActivateEntity(iEntity);
2789 AcceptEntityInput(iEntity, "Start");
2790 CreateTimer(fEffectTime, StopParticle, EntIndexToEntRef(iEntity));
2791 CreateTimer(fLifeTime, KillParticle, EntIndexToEntRef(iEntity));
2792 }
2793 else
2794 {
2795 LogError("ShowParticle: could not create info_particle_system");
2796 }
2797}
2798/* StopParticle()
2799**
2800** Turns of the particle system. Automatically called by PlayParticle
2801** -------------------------------------------------------------------------- */
2802public Action:StopParticle(Handle:hTimer, any:iEntityRef)
2803{
2804 if (iEntityRef != INVALID_ENT_REFERENCE)
2805 {
2806 int iEntity = EntRefToEntIndex(iEntityRef);
2807 if (iEntity && IsValidEntity(iEntity))
2808 {
2809 AcceptEntityInput(iEntity, "Stop");
2810 }
2811 }
2812}
2813
2814/* KillParticle()
2815**
2816** Destroys the particle system. Automatically called by PlayParticle
2817** -------------------------------------------------------------------------- */
2818public Action:KillParticle(Handle:hTimer, any:iEntityRef)
2819{
2820 if (iEntityRef != INVALID_ENT_REFERENCE)
2821 {
2822 int iEntity = EntRefToEntIndex(iEntityRef);
2823 if (iEntity && IsValidEntity(iEntity))
2824 {
2825 RemoveEdict(iEntity);
2826 }
2827 }
2828}
2829
2830/* PrecacheParticle()
2831**
2832** Forces the client to precache a particle system.
2833** -------------------------------------------------------------------------- */
2834stock PrecacheParticle(String:strParticleName[])
2835{
2836 PlayParticle(Float:{0.0, 0.0, 0.0}, Float:{0.0, 0.0, 0.0}, strParticleName, 0.1, 0.1);
2837}
2838
2839/* Timer_ShowAnnotation()
2840**
2841** Shows an annotation over the rocket only to the rocket's target.
2842** -------------------------------------------------------------------------- */
2843public Action Timer_ShowAnnotation(Handle timer, int rIndex)
2844{
2845 if(g_1v1_started)
2846 {
2847 return;
2848 }
2849 int index = EntRefToEntIndex(g_RocketEnt[rIndex].entity);
2850 if (index == INVALID_ENT_REFERENCE)
2851 {
2852 return;
2853 }
2854 int client = g_RocketEnt[rIndex].target;
2855 if(!IsValidAliveClient(client))
2856 {
2857 return;
2858 }
2859 float clientPos[3], rocketPos[3];
2860
2861 GetClientAbsOrigin(client, clientPos);
2862 GetEntPropVector(index, Prop_Send, "m_vecOrigin", rocketPos);
2863 if(GetVectorDistance(clientPos,rocketPos) < g_annotation_distance)
2864 {
2865 return;
2866 }
2867 Handle event = CreateEvent("show_annotation");
2868 if(event == INVALID_HANDLE)
2869 {
2870 return;
2871 }
2872 SetEventInt(event, "follow_entindex", index);
2873 SetEventFloat(event, "lifetime", 9999.0);
2874 SetEventInt(event, "id", rIndex);
2875 char rocketName[MAX_NAME_LENGTH], auxString[MAX_NAME_LENGTH];
2876 int class = g_RocketEnt[rIndex].class;
2877 g_RocketClass[class].GetName(auxString,sizeof(auxString));
2878 if(useMultiColor())
2879 {
2880 Format(rocketName,sizeof(rocketName),"%s %s",g_mrc_name[rIndex],auxString);
2881 }
2882 else
2883 {
2884 Format(rocketName,sizeof(rocketName),"%s",auxString);
2885 }
2886 SetEventString(event, "text", rocketName);
2887 SetEventString(event, "play_sound", "vo/null.mp3");
2888 SetEventInt(event, "visibilityBitfield",1 << client);
2889 SetEventBool(event,"show_effect", true);
2890 FireEvent(event);
2891 g_RocketEnt[rIndex].annotation = true;
2892}
2893
2894/* HideAnnotation()
2895**
2896** Hides teh rocket's annotation
2897** -------------------------------------------------------------------------- */
2898public void HideAnnotation(int rIndex)
2899{
2900 Handle event = CreateEvent("hide_annotation");
2901 if(event == INVALID_HANDLE)
2902 {
2903 return;
2904 }
2905 SetEventInt(event, "id", rIndex);
2906 FireEvent(event);
2907 g_RocketEnt[rIndex].annotation = false;
2908}
2909/* ClearHud()
2910**
2911** Clears the hud's synchronizers on game end.
2912** -------------------------------------------------------------------------- */
2913public void ClearHud()
2914{
2915 for( int c = 0; c < MAXMULTICOLORHUD; c++)
2916 {
2917 for(int client = 1; client <= MaxClients; client++)
2918 {
2919 if(IsValidAliveClient(client))
2920 {
2921 ClearSyncHud(client,g_HudSyncs[c]);
2922 }
2923 }
2924 }
2925}
2926
2927/* RenderHud()
2928**
2929** This will render the hud for 30 secs (called on start/rocket fired/ rocket deflected and rocket destroyed).
2930** -------------------------------------------------------------------------- */
2931public void RenderHud()
2932{
2933 if(!g_hud_show)
2934 {
2935 return;
2936 }
2937 //Multi Color hud
2938 if(useMultiColor())
2939 {
2940 int ncolor[3];
2941 char strHud[PLATFORM_MAX_PATH];
2942 for( int c = 0; c < g_max_rockets_dynamic; c++)
2943 {
2944 GetIntColor(g_mrc_color[c],ncolor);
2945 SetHudTextParams(g_hud_x,g_hud_y+ c*2*HUD_LINE_SEPARATION,30.0,ncolor[0],ncolor[1],ncolor[2],255, 0, 0.0, 0.0, 0.0);
2946
2947 GetHudString(strHud, PLATFORM_MAX_PATH, c, true);
2948
2949 for(int client = 1; client <= MaxClients; client++)
2950 {
2951 if(IsValidClient(client))
2952 {
2953 ShowSyncHudText(client, g_HudSyncs[c], "%s",strHud);
2954 }
2955 }
2956 }
2957
2958 }
2959 //Just one rocket
2960 else if (g_max_rockets == 1)
2961 {
2962 int ncolor[3];
2963 char strHud[PLATFORM_MAX_PATH];
2964
2965 GetIntColor(g_hud_color,ncolor);
2966 SetHudTextParams(-1.0,0.90,30.0,ncolor[0],ncolor[1],ncolor[2],255, 0, 0.0, 0.0, 0.0);
2967
2968 GetHudString(strHud, PLATFORM_MAX_PATH, 0, false);
2969
2970 for(int client = 1; client <= MaxClients; client++)
2971 {
2972 if(IsValidClient(client))
2973 {
2974 ShowSyncHudText(client, g_HudSyncs[0], "%s",strHud);
2975 }
2976 }
2977
2978 }
2979}
2980
2981void GetIntColor(char[] strColor, int buffer[3])
2982{
2983 char scolor[3][8];
2984 ExplodeString(strColor," ",scolor,3,8);
2985 for(int i = 0; i < 3; i++)
2986 {
2987 buffer[i] = StringToInt(scolor[i]);
2988 }
2989}
2990
2991void GetHudString(char[] strHud, int length, int rIndex, bool twoLines)
2992{
2993 char owner[MAX_NAME_LENGTH] = "", target[MAX_NAME_LENGTH] = "", speed[MAX_NAME_LENGTH] = "",deflects[MAX_NAME_LENGTH] = "";
2994
2995 int index = EntRefToEntIndex(g_RocketEnt[rIndex].entity);
2996 if (index != INVALID_ENT_REFERENCE)
2997 {
2998 if( g_RocketEnt[rIndex].owner >= 0 && g_RocketEnt[rIndex].owner <= MaxClients )
2999 {
3000 if( g_RocketEnt[rIndex].owner == 0)
3001 {
3002 Format(owner,MAX_NAME_LENGTH,"The server");
3003 }
3004 else
3005 {
3006 if(IsValidClient(g_RocketEnt[rIndex].owner))
3007 {
3008 Format(owner,MAX_NAME_LENGTH,"%N",g_RocketEnt[rIndex].owner);
3009 }
3010 }
3011 }
3012 if(IsValidClient(g_RocketEnt[rIndex].target))
3013 {
3014 Format(target,MAX_NAME_LENGTH,"%N",g_RocketEnt[rIndex].target);
3015 }
3016 if( g_RocketEnt[rIndex].speed >= 0)
3017 {
3018 Format(speed,MAX_NAME_LENGTH,"%.1f",g_RocketEnt[rIndex].speed/10);
3019 }
3020 if( g_RocketEnt[rIndex].deflects >= 0)
3021 {
3022 Format(deflects,MAX_NAME_LENGTH,"%d",g_RocketEnt[rIndex].deflects);
3023 }
3024
3025 }
3026 if(twoLines)
3027 {
3028 Format(strHud, length, "Velocidade: %s km/h (%s Refletidas)",speed,deflects);
3029 }
3030 else
3031 {
3032 Format(strHud, length, "Velocidade: %s km/h (%s Refletidas)",speed,deflects);
3033 }
3034}
3035
3036bool useMultiColor()
3037{
3038 if(g_max_rockets > 1 && g_max_rockets <= MAXMULTICOLORHUD && g_allow_multirocketcolor)
3039 {
3040 return true;
3041 }
3042 return false;
3043}
3044
3045
3046/* OnConfigsExecuted()
3047**
3048** Here we get the default values of the CVars that the plugin is going to modify.
3049** -------------------------------------------------------------------------- */
3050public void OnConfigsExecuted()
3051{
3052 if(!g_isDBmap)
3053 {
3054 return;
3055 }
3056 db_airdash_def = GetConVarInt(db_airdash);
3057 db_push_def = GetConVarInt(db_push);
3058 db_burstammo_def = GetConVarInt(db_burstammo);
3059 SetupCvars();
3060}
3061
3062/* SetupCvars()
3063**
3064** Modify several values of the CVars that the plugin needs to work properly.
3065** -------------------------------------------------------------------------- */
3066public void SetupCvars()
3067{
3068 SetConVarInt(db_airdash, 0);
3069 SetConVarInt(db_push, 0);
3070 SetConVarInt(db_burstammo,0);
3071}
3072
3073/* ResetCvars()
3074**
3075** Reset the values of the CVars that the plugin used to their default values.
3076** -------------------------------------------------------------------------- */
3077public void ResetCvars()
3078{
3079 SetConVarInt(db_airdash, db_airdash_def);
3080 SetConVarInt(db_push, db_push_def);
3081 SetConVarInt(db_burstammo, db_burstammo_def);
3082
3083 ProcessListeners(true);
3084 g_1v1_Music.Clear();
3085 g_SndRoundStart.Clear();
3086 g_SndOnDeath.Clear();
3087 g_SndOnKill.Clear();
3088 g_SndLastAlive.Clear();
3089 g_RestrictedWeps.Clear();
3090 g_CommandToBlock.Clear();
3091 g_BlockOnlyOnPreparation.Clear();
3092 g_class_chance.Clear();
3093}
3094
3095/* OnPlayerRunCmd()
3096**
3097** Block flamethrower's Mouse1 attack.
3098** -------------------------------------------------------------------------- */
3099public Action OnPlayerRunCmd(iClient, &iButtons, &iImpulse, Float:fVelocity[3], Float:fAngles[3], &iWeapon)
3100{
3101 if(!g_isDBmap)
3102 {
3103 return Plugin_Continue;
3104 }
3105 iButtons &= ~IN_ATTACK;
3106 return Plugin_Continue;
3107}
3108
3109
3110/* Command_Block()
3111**
3112** Blocks a command
3113** -------------------------------------------------------------------------- */
3114public Action Command_Block(int client, const char[] command, int argc)
3115{
3116 if(g_isDBmap)
3117 {
3118 CPrintToChat(client, "{DEFAULT}[{RED}HarDzOne™{DEFAULT}] {REDTEAM}Comando bloqueado! Você não pode se suicidar.");
3119 return Plugin_Stop;
3120 }
3121
3122 return Plugin_Continue;
3123}
3124
3125
3126/* Command_Block_PreparationOnly()
3127**
3128** Blocks a command, but only if we are on preparation
3129** -------------------------------------------------------------------------- */
3130public Action Command_Block_PreparationOnly(client, const char[] command, int argc)
3131{
3132 if(g_isDBmap && g_onPreparation)
3133 {
3134 return Plugin_Stop;
3135 }
3136 return Plugin_Continue;
3137}
3138
3139
3140/* EmitSoundClientDB()
3141**
3142** Emits a to a client checking if the sound is empty, using the rocket's sound enum.
3143** -------------------------------------------------------------------------- */
3144void EmitSoundClientDB(int client, int rocketsnd, int rIndex, bool fromEntity)
3145{
3146 int index = EntRefToEntIndex(g_RocketEnt[rIndex].entity);
3147 if (index == INVALID_ENT_REFERENCE)
3148 {
3149 return;
3150 }
3151 char strFile[PLATFORM_MAX_PATH]="";
3152 GetSndString(strFile,PLATFORM_MAX_PATH,rIndex,rocketsnd);
3153
3154 if(StrEqual(strFile, ""))
3155 {
3156 return;
3157 }
3158 if(fromEntity)
3159 {
3160 EmitSoundToClient(client,strFile, index, _, SNDLEVEL_TRAIN,_,SOUND_ALERT_VOL);
3161 }
3162 else
3163 {
3164 EmitSoundToClient(client,strFile, _, _, SNDLEVEL_TRAIN,_,SOUND_ALERT_VOL);
3165 }
3166
3167}
3168/* EmitSoundAllDB()
3169**
3170** Emits a to everyone checking if the sound is empty, using the rocket's sound enum.
3171** -------------------------------------------------------------------------- */
3172void EmitSoundAllDB(int rocketsnd, int rIndex, bool fromEntity)
3173{
3174 int index = EntRefToEntIndex(g_RocketEnt[rIndex].entity);
3175 if (index == INVALID_ENT_REFERENCE)
3176 {
3177 return;
3178 }
3179 char strFile[PLATFORM_MAX_PATH]="";
3180 GetSndString(strFile,PLATFORM_MAX_PATH,rIndex,rocketsnd);
3181 if(StrEqual(strFile, ""))
3182 {
3183 return;
3184 }
3185 if(fromEntity)
3186 {
3187 EmitSoundToAll(strFile, index, _, SNDLEVEL_TRAIN,_,SOUND_ALERT_VOL);
3188 }
3189 else
3190 {
3191 EmitSoundToAll(strFile, _, _, SNDLEVEL_TRAIN,_,SOUND_ALERT_VOL);
3192 }
3193}
3194
3195/* EmitRandomSound()
3196**
3197** Emits a random sound from a trie, it will be emitted for everyone is a client isn't passed.
3198** -------------------------------------------------------------------------- */
3199stock int EmitRandomSound(StringMap sndTrie,client = -1)
3200{
3201 int trieSize = sndTrie.Size;
3202 char key[4], sndFile[PLATFORM_MAX_PATH];
3203 int rndSound = GetRandomInt(1,trieSize);
3204 IntToString(rndSound,key,sizeof(key));
3205
3206 if(GetTrieString(sndTrie,key,sndFile,sizeof(sndFile)))
3207 {
3208 if(StrEqual(sndFile, ""))
3209 {
3210 return -1;
3211 }
3212 if(client != -1)
3213 {
3214 if(IsValidClient(client))
3215 {
3216 EmitSoundToClient(client,sndFile,_,_, SNDLEVEL_TRAIN);
3217 }
3218 else
3219 {
3220 return -1;
3221 }
3222 }
3223 else
3224 {
3225 EmitSoundToAll(sndFile, _, _, SNDLEVEL_TRAIN);
3226 }
3227 }
3228 return rndSound;
3229}
3230
3231/* GetSndString()
3232**
3233** Gets the sound string of the enum passed as argument.
3234** -------------------------------------------------------------------------- */
3235void GetSndString(char[] buffer, int length, int rIndex, int rocketsnd)
3236{
3237 Format(buffer, length, "");
3238 int class = g_RocketEnt[rIndex].class;
3239 if(rocketsnd == rsnd_spawn)
3240 {
3241 if(g_RocketClass[class].snd_spawn_use)
3242 {
3243 g_RocketClass[class].GetSndSpawn(buffer,length);
3244 }
3245 }
3246 else if(rocketsnd == rsnd_alert)
3247 {
3248 if(g_RocketClass[class].snd_alert_use)
3249 {
3250 g_RocketClass[class].GetSndAlert(buffer,length);
3251 }
3252 }
3253 else if(rocketsnd == rsnd_bludeflect)
3254 {
3255 if(g_RocketClass[class].snd_deflect_use)
3256 {
3257 g_RocketClass[class].GetSndDeflectBlue(buffer,length);
3258 }
3259 }
3260 else if(rocketsnd == rsnd_reddeflect)
3261 {
3262 if(g_RocketClass[class].snd_deflect_use)
3263 {
3264 g_RocketClass[class].GetSndDeflectRed(buffer,length);
3265 }
3266 }
3267 else if(rocketsnd == rsnd_beep)
3268 {
3269 if(g_RocketClass[class].snd_beep_use)
3270 {
3271 g_RocketClass[class].GetSndBeep(buffer,length);
3272 }
3273 }
3274 else if(rocketsnd == rsnd_aimed)
3275 {
3276 if(g_RocketClass[class].snd_aimed_use)
3277 {
3278 g_RocketClass[class].GetSndAimed(buffer,length);
3279 }
3280 }
3281 else if(rocketsnd == rsnd_bounce)
3282 {
3283 if(g_RocketClass[class].snd_bounce_use)
3284 {
3285 g_RocketClass[class].GetSndBounce(buffer,length);
3286 }
3287 }
3288 else if(rocketsnd == rsnd_exp)
3289 {
3290 g_RocketClass[class].GetExpSound(buffer,length);
3291 }
3292}
3293
3294/* TF2_SwitchtoSlot()
3295**
3296** Changes the client's slot to the desired one.
3297** -------------------------------------------------------------------------- */
3298stock void TF2_SwitchtoSlot(int client, int slot)
3299{
3300 if (slot >= 0 && slot <= 5 && IsValidAliveClient(client))
3301 {
3302 char classname[64];
3303 int wep = GetPlayerWeaponSlot(client, slot);
3304 if (wep > MaxClients && IsValidEdict(wep) && GetEdictClassname(wep, classname, sizeof(classname)))
3305 {
3306 FakeClientCommandEx(client, "use %s", classname);
3307 SetEntPropEnt(client, Prop_Send, "m_hActiveWeapon", wep);
3308 }
3309 }
3310}
3311
3312/* SetCloak()
3313**
3314** Function used to set the spy's cloak meter.
3315** -------------------------------------------------------------------------- */
3316stock void SetCloak(int client, float value)
3317{
3318 SetEntPropFloat(client, Prop_Send, "m_flCloakMeter", value);
3319}
3320
3321/* IsValidAliveClient()
3322**
3323** Check if the client is valid and alive/ingame
3324** -------------------------------------------------------------------------- */
3325stock bool IsValidAliveClient(int client)
3326{
3327 if(client <= 0 || client > MaxClients || !IsClientConnected(client) || !IsClientInGame(client) || !IsPlayerAlive(client))
3328 {
3329 return false;
3330 }
3331 return true;
3332}
3333
3334/* IsValidClient()
3335**
3336** Check if the client is valid and alive/ingame
3337** -------------------------------------------------------------------------- */
3338stock bool IsValidClient(int client)
3339{
3340 if(client <= 0 || client > MaxClients || !IsClientConnected(client) || !IsClientInGame(client) )
3341 {
3342 return false;
3343 }
3344 return true;
3345}
3346/* GetAlivePlayersCount()
3347**
3348** Get alive players of a team (ignoring one)
3349** -------------------------------------------------------------------------- */
3350stock GetAlivePlayersCount(team,ignore=-1)
3351{
3352 int count = 0, i;
3353
3354 for( i = 1; i <= MaxClients; i++ )
3355 {
3356 if(IsValidAliveClient(i) && GetClientTeam(i) == team && i != ignore)
3357 {
3358 count++;
3359 }
3360 }
3361 return count;
3362}
3363
3364/* GetAlivePlayersCount()
3365**
3366** Get last player of a team (ignoring one), asuming that GetAlivePlayersCountwas used before.
3367** -------------------------------------------------------------------------- */
3368stock GetLastPlayer(team,ignore=-1)
3369{
3370 for(int i = 1; i <= MaxClients; i++ )
3371 {
3372 if(IsValidAliveClient(i) && GetClientTeam(i) == team && i != ignore)
3373 {
3374 return i;
3375 }
3376 }
3377 return -1;
3378}
3379
3380/* CopyVectors()
3381**
3382** Copies the contents from a vector to another.
3383** -------------------------------------------------------------------------- */
3384stock void CopyVectors(float fFrom[3], float fTo[3])
3385{
3386 fTo[0] = fFrom[0];
3387 fTo[1] = fFrom[1];
3388 fTo[2] = fFrom[2];
3389}
3390
3391/* LerpVectors()
3392**
3393** Calculates the linear interpolation of the two given vectors and stores
3394** it on the third one.
3395** -------------------------------------------------------------------------- */
3396stock void LerpVectors(float fA[3], float fB[3], float fC[3], float t)
3397{
3398 if (t < 0.0)
3399 {
3400 t = 0.0;
3401 }
3402 if (t > 1.0)
3403 {
3404 t = 1.0;
3405 }
3406
3407 fC[0] = fA[0] + (fB[0] - fA[0]) * t;
3408 fC[1] = fA[1] + (fB[1] - fA[1]) * t;
3409 fC[2] = fA[2] + (fB[2] - fA[2]) * t;
3410}
3411
3412/* CalculateDirectionToClient()
3413**
3414** As the name indicates, calculates the orientation for the rocket to move
3415** towards the specified client.
3416** -------------------------------------------------------------------------- */
3417stock void CalculateDirectionToClient(int iEntity, int iClient, float fOut[3])
3418{
3419 if(iClient < 0 || iClient > MaxClients)
3420 {
3421 return;
3422 }
3423 float fRocketPosition[3];
3424 GetEntPropVector(iEntity, Prop_Send, "m_vecOrigin", fRocketPosition);
3425 GetClientEyePosition(iClient, fOut);
3426 MakeVectorFromPoints(fRocketPosition, fOut, fOut);
3427 NormalizeVector(fOut, fOut);
3428}
3429
3430/* CleanString()
3431**
3432** Cleans the given string from any illegal character.
3433** -------------------------------------------------------------------------- */
3434stock CleanString(String:strBuffer[])
3435{
3436 // Cleanup any illegal characters
3437 int Length = strlen(strBuffer);
3438 for (int iPos=0; iPos<Length; iPos++)
3439 {
3440 switch(strBuffer[iPos])
3441 {
3442 case '\r': strBuffer[iPos] = ' ';
3443 case '\n': strBuffer[iPos] = ' ';
3444 case '\t': strBuffer[iPos] = ' ';
3445 }
3446 }
3447
3448 // Trim string
3449 TrimString(strBuffer);
3450}