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