1 var void remove(entity e);
2 void objerror(string s);
4 .vector dropped_origin;
6 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
8 string ColoredTeamName(float t);
10 float RandomSelection_totalweight;
11 float RandomSelection_best_priority;
12 entity RandomSelection_chosen_ent;
13 float RandomSelection_chosen_float;
14 void RandomSelection_Init()
16 RandomSelection_totalweight = 0;
17 RandomSelection_chosen_ent = world;
18 RandomSelection_chosen_float = 0;
19 RandomSelection_best_priority = -1;
21 void RandomSelection_Add(entity e, float f, float weight, float priority)
23 if(priority > RandomSelection_best_priority)
25 RandomSelection_best_priority = priority;
26 RandomSelection_chosen_ent = e;
27 RandomSelection_chosen_float = f;
28 RandomSelection_totalweight = weight;
30 else if(priority == RandomSelection_best_priority)
32 RandomSelection_totalweight += weight;
33 if(random() * RandomSelection_totalweight <= weight)
35 RandomSelection_chosen_ent = e;
36 RandomSelection_chosen_float = f;
41 float DistributeEvenly_amount;
42 float DistributeEvenly_totalweight;
43 void DistributeEvenly_Init(float amount, float totalweight)
45 if(DistributeEvenly_amount)
47 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
48 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
51 DistributeEvenly_amount = 0;
53 DistributeEvenly_amount = amount;
54 DistributeEvenly_totalweight = totalweight;
56 float DistributeEvenly_Get(float weight)
61 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
62 DistributeEvenly_totalweight -= weight;
63 DistributeEvenly_amount -= f;
67 void move_out_of_solid_expand(entity e, vector by)
70 tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
73 if(trace_fraction < 1)
76 // adjust origin in the other direction...
77 e.origin = e.origin - by * (1 - trace_fraction);
81 float move_out_of_solid(entity e)
86 traceline(o, o, MOVE_WORLDONLY, e);
90 tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
98 move_out_of_solid_expand(e, '1 0 0' * m0_x); e.mins_x = m0_x;
99 move_out_of_solid_expand(e, '1 0 0' * m1_x); e.maxs_x = m1_x;
100 move_out_of_solid_expand(e, '0 1 0' * m0_y); e.mins_y = m0_y;
101 move_out_of_solid_expand(e, '0 1 0' * m1_y); e.maxs_y = m1_y;
102 move_out_of_solid_expand(e, '0 0 1' * m0_z); e.mins_z = m0_z;
103 move_out_of_solid_expand(e, '0 0 1' * m1_z); e.maxs_z = m1_z;
104 setorigin(e, e.origin);
106 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
116 string STR_PLAYER = "player";
117 string STR_SPECTATOR = "spectator";
118 string STR_OBSERVER = "observer";
121 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
122 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
123 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
124 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
126 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
127 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
128 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
129 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
130 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
133 // copies a string to a tempstring (so one can strunzone it)
134 string strcat1(string s) = #115; // FRIK_FILE
139 void bcenterprint(string s)
141 // TODO replace by MSG_ALL (would show it to spectators too, though)?
143 FOR_EACH_PLAYER(head)
144 if(clienttype(head) == CLIENTTYPE_REAL)
145 centerprint(head, s);
148 void GameLogEcho(string s)
153 if(cvar("sv_eventlog_files"))
158 matches = cvar("sv_eventlog_files_counter") + 1;
159 cvar_set("sv_eventlog_files_counter", ftos(matches));
162 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
163 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
164 logfile = fopen(fn, FILE_APPEND);
165 fputs(logfile, ":logversion:3\n");
169 if(cvar("sv_eventlog_files_timestamps"))
170 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
172 fputs(logfile, strcat(s, "\n"));
175 if(cvar("sv_eventlog_console"))
184 // will be opened later
189 if(logfile_open && logfile >= 0)
196 float spawnpoint_nag;
197 void relocate_spawnpoint()
199 // nudge off the floor
200 setorigin(self, self.origin + '0 0 1');
202 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
203 if (trace_startsolid)
209 if(!move_out_of_solid(self))
210 objerror("could not get out of solid at all!");
211 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
212 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
213 print(" ", ftos(self.origin_y - o_y));
214 print(" ", ftos(self.origin_z - o_z), "'\n");
215 if(cvar("g_spawnpoints_auto_move_out_of_solid"))
218 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
224 self.mins = self.maxs = '0 0 0';
225 objerror("player spawn point in solid, mapper sucks!\n");
230 if(cvar("g_spawnpoints_autodrop"))
232 setsize(self, PL_MIN, PL_MAX);
236 self.use = spawnpoint_use;
237 self.team_saved = self.team;
241 if(g_ctf || g_assault || g_onslaught || g_domination || g_nexball)
243 have_team_spawns = 1;
245 if(cvar("r_showbboxes"))
247 // show where spawnpoints point at too
248 makevectors(self.angles);
251 e.classname = "info_player_foo";
252 setorigin(e, self.origin + v_forward * 24);
253 setsize(e, '-8 -8 -8', '8 8 8');
254 e.solid = SOLID_TRIGGER;
258 #define strstr strstrofs
260 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
261 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
262 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
263 // BE CONSTANT OR strzoneD!
264 float strstr(string haystack, string needle, float offset)
268 len = strlen(needle);
269 endpos = strlen(haystack) - len;
270 while(offset <= endpos)
272 found = substring(haystack, offset, len);
281 float NUM_NEAREST_ENTITIES = 4;
282 entity nearest_entity[NUM_NEAREST_ENTITIES];
283 float nearest_length[NUM_NEAREST_ENTITIES];
284 entity findnearest(vector point, .string field, string value, vector axismod)
295 localhead = find(world, field, value);
298 if((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
299 dist = localhead.oldorigin;
301 dist = localhead.origin;
303 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
306 for(i = 0; i < num_nearest; ++i)
308 if(len < nearest_length[i])
312 // now i tells us where to insert at
313 // INSERTION SORT! YOU'VE SEEN IT! RUN!
314 if(i < NUM_NEAREST_ENTITIES)
316 for(j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
318 nearest_length[j + 1] = nearest_length[j];
319 nearest_entity[j + 1] = nearest_entity[j];
321 nearest_length[i] = len;
322 nearest_entity[i] = localhead;
323 if(num_nearest < NUM_NEAREST_ENTITIES)
324 num_nearest = num_nearest + 1;
327 localhead = find(localhead, field, value);
330 // now use the first one from our list that we can see
331 for(i = 0; i < num_nearest; ++i)
333 traceline(point, nearest_entity[i].origin, TRUE, world);
334 if(trace_fraction == 1)
338 dprint("Nearest point (");
339 dprint(nearest_entity[0].netname);
340 dprint(") is not visible, using a visible one.\n");
342 return nearest_entity[i];
349 dprint("Not seeing any location point, using nearest as fallback.\n");
351 dprint("Candidates were: ");
352 for(j = 0; j < num_nearest; ++j)
356 dprint(nearest_entity[j].netname);
361 return nearest_entity[0];
364 void spawnfunc_target_location()
366 self.classname = "target_location";
367 // location name in netname
368 // eventually support: count, teamgame selectors, line of sight?
371 void spawnfunc_info_location()
373 self.classname = "target_location";
374 self.message = self.netname;
377 string NearestLocation(vector p)
382 loc = findnearest(p, classname, "target_location", '1 1 1');
389 loc = findnearest(p, target, "###item###", '1 1 4');
396 string formatmessage(string msg)
407 break; // too many replacements
409 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
410 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
420 replacement = substring(msg, p, 2);
421 escape = substring(msg, p + 1, 1);
424 else if(escape == "\\")
426 else if(escape == "n")
428 else if(escape == "a")
429 replacement = ftos(floor(self.armorvalue));
430 else if(escape == "h")
431 replacement = ftos(floor(self.health));
432 else if(escape == "l")
433 replacement = NearestLocation(self.origin);
434 else if(escape == "y")
435 replacement = NearestLocation(self.cursor_trace_endpos);
436 else if(escape == "d")
437 replacement = NearestLocation(self.death_origin);
438 else if(escape == "w")
443 wep = self.switchweapon;
446 replacement = W_Name(wep);
448 else if(escape == "W")
450 if(self.items & IT_SHELLS) replacement = "shells";
451 else if(self.items & IT_NAILS) replacement = "bullets";
452 else if(self.items & IT_ROCKETS) replacement = "rockets";
453 else if(self.items & IT_CELLS) replacement = "cells";
454 else replacement = "batteries"; // ;)
456 else if(escape == "x")
458 replacement = self.cursor_trace_ent.netname;
459 if(!replacement || !self.cursor_trace_ent)
460 replacement = "nothing";
462 else if(escape == "p")
464 if(self.last_selected_player)
465 replacement = self.last_selected_player.netname;
467 replacement = "(nobody)";
469 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
470 p = p + strlen(replacement);
481 >0: receives a cvar from name=argv(f) value=argv(f+1)
483 void GetCvars_handleString(string thisname, float f, .string field, string name)
488 strunzone(self.field);
489 self.field = string_null;
496 strunzone(self.field);
497 self.field = strzone(argv(f + 1));
501 stuffcmd(self, strcat("sendcvar ", name, "\n"));
503 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
505 GetCvars_handleString(thisname, f, field, name);
506 if(f >= 0) // also initialize to the fitting value for "" when sending cvars out
510 s = func(strcat1(self.field));
513 strunzone(self.field);
514 self.field = strzone(s);
518 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
526 self.field = stof(argv(f + 1));
529 stuffcmd(self, strcat("sendcvar ", name, "\n"));
531 string W_FixWeaponOrder_ForceComplete(string s);
532 string W_FixWeaponOrder_AllowIncomplete(string s);
533 float w_getbestweapon(entity e);
534 void GetCvars(float f)
538 s = strcat1(argv(f));
539 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
540 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
541 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
542 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
543 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
544 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
545 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
546 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
547 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
548 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
549 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
550 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
551 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
552 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
553 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
554 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
555 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
556 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
557 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
558 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
559 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
561 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
564 if(s == "cl_weaponpriority")
565 self.switchweapon = w_getbestweapon(self);
569 float fexists(string f)
572 fh = fopen(f, FILE_READ);
579 void backtrace(string msg)
582 dev = cvar("developer");
583 cvar_set("developer", "1");
585 dprint("--- CUT HERE ---\nWARNING: ");
588 remove(world); // isn't there any better way to cause a backtrace?
589 dprint("\n--- CUT UNTIL HERE ---\n");
590 cvar_set("developer", ftos(dev));
593 string Team_ColorCode(float teamid)
595 if(teamid == COLOR_TEAM1)
597 else if(teamid == COLOR_TEAM2)
599 else if(teamid == COLOR_TEAM3)
601 else if(teamid == COLOR_TEAM4)
606 string Team_ColorName(float t)
608 // fixme: Search for team entities and get their .netname's!
619 string Team_ColorNameLowerCase(float t)
621 // fixme: Search for team entities and get their .netname's!
633 #define CENTERPRIO_POINT 1
634 #define CENTERPRIO_SPAM 2
635 #define CENTERPRIO_VOTE 4
636 #define CENTERPRIO_NORMAL 5
637 #define CENTERPRIO_SHIELDING 7
638 #define CENTERPRIO_MAPVOTE 9
639 #define CENTERPRIO_IDLEKICK 50
640 #define CENTERPRIO_ADMIN 99
641 .float centerprint_priority;
642 .float centerprint_expires;
643 void centerprint_atprio(entity e, float prio, string s)
645 if(intermission_running)
646 if(prio < CENTERPRIO_MAPVOTE)
648 if(time > e.centerprint_expires)
649 e.centerprint_priority = 0;
650 if(prio >= e.centerprint_priority)
652 e.centerprint_priority = prio;
653 if(timeoutStatus == 2)
654 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
656 e.centerprint_expires = time + e.cvar_scr_centertime;
657 centerprint_builtin(e, s);
660 void centerprint_expire(entity e, float prio)
662 if(prio == e.centerprint_priority)
664 e.centerprint_priority = 0;
665 centerprint_builtin(e, "");
668 void centerprint(entity e, string s)
670 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
673 // decolorizes and team colors the player name when needed
674 string playername(entity p)
677 if(teams_matter && !intermission_running && p.classname == "player")
679 t = Team_ColorCode(p.team);
680 return strcat(t, strdecolorize(p.netname));
686 vector randompos(vector m1, vector m2)
690 v_x = m2_x * random() + m1_x;
691 v_y = m2_y * random() + m1_y;
692 v_z = m2_z * random() + m1_z;
696 float g_pickup_shells;
697 float g_pickup_shells_max;
698 float g_pickup_nails;
699 float g_pickup_nails_max;
700 float g_pickup_rockets;
701 float g_pickup_rockets_max;
702 float g_pickup_cells;
703 float g_pickup_cells_max;
705 float g_pickup_fuel_jetpack;
706 float g_pickup_fuel_max;
707 float g_pickup_armorsmall;
708 float g_pickup_armorsmall_max;
709 float g_pickup_armormedium;
710 float g_pickup_armormedium_max;
711 float g_pickup_armorbig;
712 float g_pickup_armorbig_max;
713 float g_pickup_armorlarge;
714 float g_pickup_armorlarge_max;
715 float g_pickup_healthsmall;
716 float g_pickup_healthsmall_max;
717 float g_pickup_healthmedium;
718 float g_pickup_healthmedium_max;
719 float g_pickup_healthlarge;
720 float g_pickup_healthlarge_max;
721 float g_pickup_healthmega;
722 float g_pickup_healthmega_max;
724 string g_weaponarena_list;
728 float start_ammo_shells;
729 float start_ammo_nails;
730 float start_ammo_rockets;
731 float start_ammo_cells;
732 float start_ammo_fuel;
734 float start_armorvalue;
735 float warmup_start_weapons;
736 float warmup_start_ammo_shells;
737 float warmup_start_ammo_nails;
738 float warmup_start_ammo_rockets;
739 float warmup_start_ammo_cells;
740 float warmup_start_ammo_fuel;
741 float warmup_start_health;
742 float warmup_start_armorvalue;
745 entity get_weaponinfo(float w);
747 float NixNex_CanChooseWeapon(float wpn);
748 void readplayerstartcvars()
754 // initialize starting values for players
757 start_ammo_shells = 0;
758 start_ammo_nails = 0;
759 start_ammo_rockets = 0;
760 start_ammo_cells = 0;
761 start_health = cvar("g_balance_health_start");
762 start_armorvalue = cvar("g_balance_armor_start");
765 s = cvar_string("g_weaponarena");
771 g_weaponarena_list = "All Weapons";
772 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
774 e = get_weaponinfo(j);
775 g_weaponarena |= e.weapons;
776 weapon_action(e.weapon, WR_PRECACHE);
781 g_weaponarena_list = "Most Weapons";
782 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
784 e = get_weaponinfo(j);
785 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
787 g_weaponarena |= e.weapons;
788 weapon_action(e.weapon, WR_PRECACHE);
794 t = tokenize_console(s);
795 g_weaponarena_list = "";
796 for(i = 0; i < t; ++i)
799 for(j = WEP_FIRST; j <= WEP_LAST; ++j)
801 e = get_weaponinfo(j);
804 g_weaponarena |= e.weapons;
805 weapon_action(e.weapon, WR_PRECACHE);
806 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
812 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
815 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
821 // will be done later
822 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
823 if(NixNex_CanChooseWeapon(i))
824 weapon_action(i, WR_PRECACHE);
826 else if(g_weaponarena)
828 start_weapons = g_weaponarena;
829 start_ammo_rockets = 999;
830 start_ammo_shells = 999;
831 start_ammo_cells = 999;
832 start_ammo_nails = 999;
833 start_ammo_fuel = 999;
834 start_items |= IT_UNLIMITED_AMMO;
839 start_armorvalue = 0;
840 start_weapons = WEPBIT_MINSTANEX;
841 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
842 start_ammo_cells = cvar("g_minstagib_ammo_start");
843 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
844 start_ammo_fuel = cvar("g_start_ammo_fuel");
846 if(g_minstagib_invis_alpha <= 0)
847 g_minstagib_invis_alpha = -1;
853 start_ammo_shells = cvar("g_lms_start_ammo_shells");
854 start_ammo_nails = cvar("g_lms_start_ammo_nails");
855 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
856 start_ammo_cells = cvar("g_lms_start_ammo_cells");
857 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
858 start_health = cvar("g_lms_start_health");
859 start_armorvalue = cvar("g_lms_start_armor");
860 } else if (cvar("g_use_ammunition")) {
861 start_ammo_shells = cvar("g_start_ammo_shells");
862 start_ammo_nails = cvar("g_start_ammo_nails");
863 start_ammo_rockets = cvar("g_start_ammo_rockets");
864 start_ammo_cells = cvar("g_start_ammo_cells");
865 start_ammo_fuel = cvar("g_start_ammo_fuel");
867 start_ammo_shells = cvar("g_pickup_shells_max");
868 start_ammo_nails = cvar("g_pickup_nails_max");
869 start_ammo_rockets = cvar("g_pickup_rockets_max");
870 start_ammo_cells = cvar("g_pickup_cells_max");
871 start_ammo_fuel = cvar("g_pickup_fuel_max");
872 start_items |= IT_UNLIMITED_AMMO;
875 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
877 e = get_weaponinfo(i);
881 t = cvar(strcat("g_start_weapon_", e.netname));
883 if(t < 0) // "default" weapon selection
886 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
888 t = (i == WEP_LASER);
890 t = 0; //GL is set a few lines later
892 t = (i == WEP_LASER || i == WEP_SHOTGUN);
893 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
894 t += (i == WEP_HOOK);
897 if(g_nexball && i == WEP_GRENADE_LAUNCHER)
902 start_weapons |= e.weapons;
903 weapon_action(e.weapon, WR_PRECACHE);
910 warmup_start_ammo_shells = start_ammo_shells;
911 warmup_start_ammo_nails = start_ammo_nails;
912 warmup_start_ammo_rockets = start_ammo_rockets;
913 warmup_start_ammo_cells = start_ammo_cells;
914 warmup_start_health = start_health;
915 warmup_start_armorvalue = start_armorvalue;
916 warmup_start_weapons = start_weapons;
918 if(!g_weaponarena && !g_nixnex && !g_minstagib)
920 if(cvar("g_use_ammunition"))
922 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
923 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
924 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
925 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
927 warmup_start_health = cvar("g_warmup_start_health");
928 warmup_start_armorvalue = cvar("g_warmup_start_armor");
929 if(cvar("g_warmup_allguns"))
931 for(i = WEP_FIRST; i <= WEP_LAST; ++i)
933 e = get_weaponinfo(i);
936 if(e.spawnflags & WEPSPAWNFLAG_NORMAL)
938 warmup_start_weapons |= e.weapons;
939 weapon_action(e.weapon, WR_PRECACHE);
946 if(start_weapons & WEPBIT_HOOK)
948 // can't have off-hand hook, if hook weapon is enabled
950 // note: if g_grappling_hook is 1, also give some initial cells
953 if(!start_ammo_cells)
954 start_ammo_cells = g_pickup_cells;
955 if(!warmup_start_ammo_cells)
956 warmup_start_ammo_cells = g_pickup_cells;
959 g_grappling_hook = 0;
964 g_grappling_hook = 0; // these two can't coexist, as they use the same button
965 start_items |= IT_JETPACK;
966 start_items |= IT_FUEL_REGEN;
967 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_stable"));
970 if(g_weapon_stay == 2)
972 if(!start_ammo_shells) start_ammo_shells = g_pickup_shells;
973 if(!start_ammo_nails) start_ammo_nails = g_pickup_nails;
974 if(!start_ammo_cells) start_ammo_cells = g_pickup_cells;
975 if(!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
976 if(!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
977 if(!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
978 if(!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
979 if(!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
980 if(!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
981 if(!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
984 start_ammo_shells = max(0, start_ammo_shells);
985 start_ammo_nails = max(0, start_ammo_nails);
986 start_ammo_cells = max(0, start_ammo_cells);
987 start_ammo_rockets = max(0, start_ammo_rockets);
988 start_ammo_fuel = max(0, start_ammo_fuel);
990 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
991 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
992 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
993 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
994 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
998 float g_bugrigs_planar_movement;
999 float g_bugrigs_planar_movement_car_jumping;
1000 float g_bugrigs_reverse_spinning;
1001 float g_bugrigs_reverse_speeding;
1002 float g_bugrigs_reverse_stopping;
1003 float g_bugrigs_air_steering;
1004 float g_bugrigs_angle_smoothing;
1005 float g_bugrigs_friction_floor;
1006 float g_bugrigs_friction_brake;
1007 float g_bugrigs_friction_air;
1008 float g_bugrigs_accel;
1009 float g_bugrigs_speed_ref;
1010 float g_bugrigs_speed_pow;
1011 float g_bugrigs_steer;
1013 float g_touchexplode;
1014 float g_touchexplode_radius;
1015 float g_touchexplode_damage;
1016 float g_touchexplode_edgedamage;
1017 float g_touchexplode_force;
1019 void readlevelcvars(void)
1021 g_bugrigs = cvar("g_bugrigs");
1022 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1023 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1024 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1025 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1026 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1027 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1028 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1029 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1030 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1031 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1032 g_bugrigs_accel = cvar("g_bugrigs_accel");
1033 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1034 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1035 g_bugrigs_steer = cvar("g_bugrigs_steer");
1037 g_touchexplode = cvar("g_touchexplode");
1038 g_touchexplode_radius = cvar("g_touchexplode_radius");
1039 g_touchexplode_damage = cvar("g_touchexplode_damage");
1040 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1041 g_touchexplode_force = cvar("g_touchexplode_force");
1043 sv_clones = cvar("sv_clones");
1044 sv_cheats = cvar("sv_cheats");
1045 sv_gentle = cvar("sv_gentle");
1046 sv_foginterval = cvar("sv_foginterval");
1047 g_cloaked = cvar("g_cloaked");
1048 g_jump_grunt = cvar("g_jump_grunt");
1049 g_footsteps = cvar("g_footsteps");
1050 g_grappling_hook = cvar("g_grappling_hook");
1051 g_jetpack = cvar("g_jetpack");
1052 g_laserguided_missile = cvar("g_laserguided_missile");
1053 g_midair = cvar("g_midair");
1054 g_minstagib = cvar("g_minstagib");
1055 g_nixnex = cvar("g_nixnex");
1056 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1057 g_norecoil = cvar("g_norecoil");
1058 g_vampire = cvar("g_vampire");
1059 g_bloodloss = cvar("g_bloodloss");
1060 sv_maxidle = cvar("sv_maxidle");
1061 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1062 sv_pogostick = cvar("sv_pogostick");
1063 sv_doublejump = cvar("sv_doublejump");
1064 g_maplist_allow_hidden = cvar("g_maplist_allow_hidden");
1065 g_ctf_reverse = cvar("g_ctf_reverse");
1067 inWarmupStage = cvar("g_warmup");
1068 g_warmup_limit = cvar("g_warmup_limit");
1069 g_warmup_allguns = cvar("g_warmup_allguns");
1070 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1072 if(g_race && g_race_qualifying == 2 || g_arena || g_assault || cvar("g_campaign"))
1073 inWarmupStage = 0; // these modes cannot work together, sorry
1075 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1076 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1077 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1078 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1079 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1080 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1082 if(g_minstagib) g_nixnex = g_weaponarena = 0;
1083 if(g_nixnex) g_weaponarena = 0;
1086 g_pickup_shells = cvar("g_pickup_shells");
1087 g_pickup_shells_max = cvar("g_pickup_shells_max");
1088 g_pickup_nails = cvar("g_pickup_nails");
1089 g_pickup_nails_max = cvar("g_pickup_nails_max");
1090 g_pickup_rockets = cvar("g_pickup_rockets");
1091 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1092 g_pickup_cells = cvar("g_pickup_cells");
1093 g_pickup_cells_max = cvar("g_pickup_cells_max");
1094 g_pickup_fuel = cvar("g_pickup_fuel");
1095 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1096 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1097 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1098 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1099 g_pickup_armormedium = cvar("g_pickup_armormedium");
1100 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1101 g_pickup_armorbig = cvar("g_pickup_armorbig");
1102 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1103 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1104 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1105 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1106 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1107 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1108 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1109 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1110 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1111 g_pickup_healthmega = cvar("g_pickup_healthmega");
1112 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1114 g_pinata = cvar("g_pinata");
1116 g_weapon_stay = cvar("g_weapon_stay");
1117 if(!g_weapon_stay && (cvar("deathmatch") == 2))
1120 if not(inWarmupStage)
1121 game_starttime = cvar("g_start_delay");
1123 readplayerstartcvars();
1127 // TODO sound pack system
1130 string precache_sound_builtin (string s) = #19;
1131 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1132 string precache_sound(string s)
1134 return precache_sound_builtin(strcat(soundpack, s));
1136 void play2(entity e, string filename)
1138 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1140 void sound(entity e, float chan, string samp, float vol, float atten)
1142 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1147 string precache_sound (string s) = #19;
1148 void(entity e, float chan, string samp, float vol, float atten) sound = #8;
1149 float precache_sound_index (string s) = #19;
1151 #define SND_VOLUME 1
1152 #define SND_ATTENUATION 2
1153 #define SND_LARGEENTITY 8
1154 #define SND_LARGESOUND 16
1156 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1159 entno = num_for_edict(e);
1160 idx = precache_sound_index(samp);
1165 atten = floor(atten * 64);
1166 vol = floor(vol * 255);
1169 sflags |= SND_VOLUME;
1171 sflags |= SND_ATTENUATION;
1173 sflags |= SND_LARGEENTITY;
1175 sflags |= SND_LARGESOUND;
1177 WriteByte(dest, SVC_SOUND);
1178 WriteByte(dest, sflags);
1179 if(sflags & SND_VOLUME)
1180 WriteByte(dest, vol);
1181 if(sflags & SND_ATTENUATION)
1182 WriteByte(dest, atten);
1183 if(sflags & SND_LARGEENTITY)
1185 WriteShort(dest, entno);
1186 WriteByte(dest, chan);
1190 WriteShort(dest, entno * 8 + chan);
1192 if(sflags & SND_LARGESOUND)
1193 WriteShort(dest, idx);
1195 WriteByte(dest, idx);
1197 WriteCoord(dest, o_x);
1198 WriteCoord(dest, o_y);
1199 WriteCoord(dest, o_z);
1201 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1204 o = e.origin + 0.5 * (e.mins + e.maxs);
1205 soundtoat(dest, e, o, chan, samp, vol, atten);
1207 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1209 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1211 void stopsoundto(float dest, entity e, float chan)
1214 entno = num_for_edict(e);
1219 idx = precache_sound_index("misc/null.wav");
1220 sflags = SND_LARGEENTITY;
1222 sflags |= SND_LARGESOUND;
1223 WriteByte(dest, SVC_SOUND);
1224 WriteByte(dest, sflags);
1225 WriteShort(dest, entno);
1226 WriteByte(dest, chan);
1227 if(sflags & SND_LARGESOUND)
1228 WriteShort(dest, idx);
1230 WriteByte(dest, idx);
1231 WriteCoord(dest, e.origin_x);
1232 WriteCoord(dest, e.origin_y);
1233 WriteCoord(dest, e.origin_z);
1237 WriteByte(dest, SVC_STOPSOUND);
1238 WriteShort(dest, entno * 8 + chan);
1241 void stopsound(entity e, float chan)
1243 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1244 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1247 void play2(entity e, string filename)
1249 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1251 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1254 .float announcetime;
1255 float announce(entity player, string msg)
1257 if(time > player.announcetime)
1258 if(clienttype(player) == CLIENTTYPE_REAL)
1260 player.announcetime = time + 0.8;
1266 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1267 float spamsound(entity e, float chan, string samp, float vol, float atten)
1269 if(time > e.announcetime)
1271 e.announcetime = time;
1272 sound(e, chan, samp, vol, atten);
1278 void play2team(float t, string filename)
1281 FOR_EACH_REALPLAYER(head)
1284 play2(head, filename);
1288 void play2all(string samp)
1290 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1293 void PrecachePlayerSounds(string f);
1294 void precache_all_models(string pattern)
1296 float globhandle, i, n;
1299 globhandle = search_begin(pattern, TRUE, FALSE);
1302 n = search_getsize(globhandle);
1303 for(i = 0; i < n; ++i)
1305 //print(search_getfilename(globhandle, i), "\n");
1306 f = search_getfilename(globhandle, i);
1308 PrecachePlayerSounds(strcat(f, ".sounds"));
1310 search_end(globhandle);
1315 // gamemode related things
1316 precache_model ("models/misc/chatbubble.spr");
1317 precache_model ("models/misc/teambubble.spr");
1320 precache_model ("models/runematch/curse.mdl");
1321 precache_model ("models/runematch/rune.mdl");
1324 #ifdef TTURRETS_ENABLED
1325 if(cvar("g_turrets"))
1329 // Precache all player models if desired
1330 if (cvar("sv_precacheplayermodels"))
1332 PrecachePlayerSounds("sound/player/default.sounds");
1333 precache_all_models("models/player/*.zym");
1334 precache_all_models("models/player/*.dpm");
1335 precache_all_models("models/player/*.md3");
1336 precache_all_models("models/player/*.psk");
1337 //precache_model("models/player/carni.zym");
1338 //precache_model("models/player/crash.zym");
1339 //precache_model("models/player/grunt.zym");
1340 //precache_model("models/player/headhunter.zym");
1341 //precache_model("models/player/insurrectionist.zym");
1342 //precache_model("models/player/jeandarc.zym");
1343 //precache_model("models/player/lurk.zym");
1344 //precache_model("models/player/lycanthrope.zym");
1345 //precache_model("models/player/marine.zym");
1346 //precache_model("models/player/nexus.zym");
1347 //precache_model("models/player/pyria.zym");
1348 //precache_model("models/player/shock.zym");
1349 //precache_model("models/player/skadi.zym");
1350 //precache_model("models/player/specop.zym");
1351 //precache_model("models/player/visitant.zym");
1354 if(cvar("sv_defaultcharacter"))
1357 s = cvar_string("sv_defaultplayermodel_red");
1361 PrecachePlayerSounds(strcat(s, ".sounds"));
1363 s = cvar_string("sv_defaultplayermodel_blue");
1367 PrecachePlayerSounds(strcat(s, ".sounds"));
1369 s = cvar_string("sv_defaultplayermodel_yellow");
1373 PrecachePlayerSounds(strcat(s, ".sounds"));
1375 s = cvar_string("sv_defaultplayermodel_pink");
1379 PrecachePlayerSounds(strcat(s, ".sounds"));
1381 s = cvar_string("sv_defaultplayermodel");
1385 PrecachePlayerSounds(strcat(s, ".sounds"));
1391 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1392 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1395 // gore and miscellaneous sounds
1396 //precache_sound ("misc/h2ohit.wav");
1397 precache_model ("models/hook.md3");
1398 precache_sound ("misc/armorimpact.wav");
1399 precache_sound ("misc/bodyimpact1.wav");
1400 precache_sound ("misc/bodyimpact2.wav");
1401 precache_sound ("misc/gib.wav");
1402 precache_sound ("misc/gib_splat01.wav");
1403 precache_sound ("misc/gib_splat02.wav");
1404 precache_sound ("misc/gib_splat03.wav");
1405 precache_sound ("misc/gib_splat04.wav");
1406 precache_sound ("misc/hit.wav");
1407 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1408 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1409 precache_sound ("misc/null.wav");
1410 precache_sound ("misc/spawn.wav");
1411 precache_sound ("misc/talk.wav");
1412 precache_sound ("misc/teleport.wav");
1413 precache_sound ("misc/poweroff.wav");
1414 precache_sound ("player/lava.wav");
1415 precache_sound ("player/slime.wav");
1418 precache_sound ("misc/jetpack_fly.wav");
1420 // announcer sounds - male
1421 precache_sound ("announcer/male/electrobitch.wav");
1422 precache_sound ("announcer/male/airshot.wav");
1423 precache_sound ("announcer/male/03kills.wav");
1424 precache_sound ("announcer/male/05kills.wav");
1425 precache_sound ("announcer/male/10kills.wav");
1426 precache_sound ("announcer/male/15kills.wav");
1427 precache_sound ("announcer/male/20kills.wav");
1428 precache_sound ("announcer/male/25kills.wav");
1429 precache_sound ("announcer/male/30kills.wav");
1430 precache_sound ("announcer/male/botlike.wav");
1431 precache_sound ("announcer/male/yoda.wav");
1432 precache_sound ("announcer/male/headshot.wav");
1433 precache_sound ("announcer/male/impressive.wav");
1435 // announcer sounds - robotic
1436 precache_sound ("announcer/robotic/prepareforbattle.wav");
1437 precache_sound ("announcer/robotic/begin.wav");
1438 precache_sound ("announcer/robotic/timeoutcalled.wav");
1439 precache_sound ("announcer/robotic/1fragleft.wav");
1440 precache_sound ("announcer/robotic/1minuteremains.wav");
1441 precache_sound ("announcer/robotic/2fragsleft.wav");
1442 precache_sound ("announcer/robotic/3fragsleft.wav");
1445 precache_sound ("announcer/robotic/lastsecond.wav");
1446 precache_sound ("announcer/robotic/narrowly.wav");
1449 precache_model ("models/sprites/1.spr32");
1450 precache_model ("models/sprites/2.spr32");
1451 precache_model ("models/sprites/3.spr32");
1452 precache_model ("models/sprites/4.spr32");
1453 precache_model ("models/sprites/5.spr32");
1454 precache_model ("models/sprites/6.spr32");
1455 precache_model ("models/sprites/7.spr32");
1456 precache_model ("models/sprites/8.spr32");
1457 precache_model ("models/sprites/9.spr32");
1458 precache_model ("models/sprites/10.spr32");
1459 precache_sound ("announcer/robotic/1.wav");
1460 precache_sound ("announcer/robotic/2.wav");
1461 precache_sound ("announcer/robotic/3.wav");
1462 precache_sound ("announcer/robotic/4.wav");
1463 precache_sound ("announcer/robotic/5.wav");
1464 precache_sound ("announcer/robotic/6.wav");
1465 precache_sound ("announcer/robotic/7.wav");
1466 precache_sound ("announcer/robotic/8.wav");
1467 precache_sound ("announcer/robotic/9.wav");
1468 precache_sound ("announcer/robotic/10.wav");
1470 // common weapon precaches
1471 precache_sound ("weapons/weapon_switch.wav");
1472 precache_sound ("weapons/weaponpickup.wav");
1473 if(g_grappling_hook)
1475 precache_sound ("weapons/hook_fire.wav"); // hook
1476 precache_sound ("weapons/hook_impact.wav"); // hook
1479 if (cvar("sv_precacheweapons") || g_nixnex)
1481 //precache weapon models/sounds
1484 while (wep <= WEP_LAST)
1486 weapon_action(wep, WR_PRECACHE);
1491 precache_model("models/elaser.mdl");
1492 precache_model("models/laser.mdl");
1493 precache_model("models/ebomb.mdl");
1496 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1498 if (!self.noise && self.music) // quake 3 uses the music field
1499 self.noise = self.music;
1501 // plays music for the level if there is any
1504 precache_sound (self.noise);
1505 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1510 // sorry, but using \ in macros breaks line numbers
1511 #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
1512 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1513 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1515 vector ExactTriggerHit_mins;
1516 vector ExactTriggerHit_maxs;
1517 float ExactTriggerHit_Recurse()
1523 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1526 if(trace_ent == self)
1531 se.solid = SOLID_NOT;
1532 f = ExactTriggerHit_Recurse();
1538 float ExactTriggerHit()
1542 if not(self.modelindex)
1546 self.solid = SOLID_BSP;
1547 ExactTriggerHit_mins = other.absmin;
1548 ExactTriggerHit_maxs = other.absmax;
1549 f = ExactTriggerHit_Recurse();
1555 // WARNING: this kills the trace globals
1556 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1557 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1559 #define INITPRIO_FIRST 0
1560 #define INITPRIO_GAMETYPE 0
1561 #define INITPRIO_GAMETYPE_FALLBACK 1
1562 #define INITPRIO_CVARS 5
1563 #define INITPRIO_FINDTARGET 10
1564 #define INITPRIO_DROPTOFLOOR 20
1565 #define INITPRIO_SETLOCATION 90
1566 #define INITPRIO_LINKDOORS 91
1567 #define INITPRIO_LAST 99
1569 .void(void) initialize_entity;
1570 .float initialize_entity_order;
1571 .entity initialize_entity_next;
1572 entity initialize_entity_first;
1574 void make_safe_for_remove(entity e)
1576 if(e.initialize_entity)
1579 for(ent = initialize_entity_first; ent; )
1581 if((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1583 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1584 // skip it in linked list
1587 prev.initialize_entity_next = ent.initialize_entity_next;
1588 ent = prev.initialize_entity_next;
1592 initialize_entity_first = ent.initialize_entity_next;
1593 ent = initialize_entity_first;
1599 ent = ent.initialize_entity_next;
1605 void objerror(string s)
1607 make_safe_for_remove(self);
1608 objerror_builtin(s);
1611 void remove_unsafely(entity e)
1616 void remove_safely(entity e)
1618 make_safe_for_remove(e);
1622 void InitializeEntity(entity e, void(void) func, float order)
1626 if(!e || e.initialize_entity)
1628 // make a proxy initializer entity
1632 e.classname = "initialize_entity";
1636 e.initialize_entity = func;
1637 e.initialize_entity_order = order;
1639 cur = initialize_entity_first;
1642 if(!cur || cur.initialize_entity_order > order)
1644 // insert between prev and cur
1646 prev.initialize_entity_next = e;
1648 initialize_entity_first = e;
1649 e.initialize_entity_next = cur;
1653 cur = cur.initialize_entity_next;
1656 void InitializeEntitiesRun()
1659 startoflist = initialize_entity_first;
1660 initialize_entity_first = world;
1661 for(self = startoflist; self; )
1664 var void(void) func;
1665 e = self.initialize_entity_next;
1666 func = self.initialize_entity;
1667 self.initialize_entity_order = 0;
1668 self.initialize_entity = func_null;
1669 self.initialize_entity_next = world;
1670 if(self.classname == "initialize_entity")
1674 remove_builtin(self);
1677 //dprint("Delayed initialization: ", self.classname, "\n");
1683 .float uncustomizeentityforclient_set;
1684 .void(void) uncustomizeentityforclient;
1685 void(void) SUB_Nullpointer = #0;
1686 void UncustomizeEntitiesRun()
1690 for(self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1691 self.uncustomizeentityforclient();
1694 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1696 e.customizeentityforclient = customizer;
1697 e.uncustomizeentityforclient = uncustomizer;
1698 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1702 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1705 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1709 if(e.classname == "")
1710 e.classname = "net_linked";
1712 if(e.model == "" || self.modelindex == 0)
1716 setmodel(e, "null");
1720 e.SendEntity = sendfunc;
1721 e.SendFlags = 0xFFFFFF;
1724 e.effects |= EF_NODEPTHTEST;
1728 e.nextthink = time + dt;
1729 e.think = SUB_Remove;
1733 void adaptor_think2touch()
1742 void adaptor_think2use()
1754 // deferred dropping
1755 void DropToFloor_Handler()
1757 droptofloor_builtin();
1758 self.dropped_origin = self.origin;
1763 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1768 float trace_hits_box_a0, trace_hits_box_a1;
1770 float trace_hits_box_1d(float end, float thmi, float thma)
1774 // just check if x is in range
1782 // do the trace with respect to x
1783 // 0 -> end has to stay in thmi -> thma
1784 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1785 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1786 if(trace_hits_box_a0 > trace_hits_box_a1)
1792 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1797 // now it is a trace from 0 to end
1799 trace_hits_box_a0 = 0;
1800 trace_hits_box_a1 = 1;
1802 if(!trace_hits_box_1d(end_x, thmi_x, thma_x))
1804 if(!trace_hits_box_1d(end_y, thmi_y, thma_y))
1806 if(!trace_hits_box_1d(end_z, thmi_z, thma_z))
1812 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1814 return trace_hits_box(start, end, thmi - ma, thma - mi);
1817 float SUB_NoImpactCheck()
1819 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1821 if(other == world && self.size != '0 0 0')
1824 tic = self.velocity * sys_ticrate;
1825 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1826 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1827 if(trace_fraction >= 1)
1829 dprint("Odd... did not hit...?\n");
1831 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1833 dprint("Detected and prevented the sky-grapple bug.\n");
1841 #define SUB_OwnerCheck() (other && (other == self.owner))
1843 #define PROJECTILE_TOUCH do { if(SUB_OwnerCheck()) return; if(SUB_NoImpactCheck()) { remove(self); return; } if(trace_ent && trace_ent.solid > SOLID_TRIGGER) UpdateCSQCProjectileNextFrame(self); } while(0)
1845 float MAX_IPBAN_URIS = 16;
1847 float URI_GET_DISCARD = 0;
1848 float URI_GET_IPBAN = 1;
1849 float URI_GET_IPBAN_END = 16;
1851 void URI_Get_Callback(float id, float status, string data)
1853 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1855 dprint("\nEnd of data.\n");
1857 if(id == URI_GET_DISCARD)
1861 else if(id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1864 OnlineBanList_URI_Get_Callback(id, status, data);
1868 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1872 void print_to(entity e, string s)
1875 sprint(e, strcat(s, "\n"));
1894 for(i = 0; i < MapInfo_count; ++i)
1896 if(MapInfo_Get_ByID(i))
1898 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1901 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1902 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1910 for(i = 0; i < MapInfo_count; ++i)
1912 if(MapInfo_Get_ByID(i))
1914 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1917 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1918 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1923 MapInfo_ClearTemps();
1926 return "No records are available on this server.\n";
1928 return strcat("Records on this server:\n", s);
1931 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1934 vector start, org, delta, end, enddown, mstart;
1936 m = e.dphitcontentsmask;
1937 e.dphitcontentsmask = goodcontents | badcontents;
1940 delta = world.maxs - world.mins;
1942 for(i = 0; i < attempts; ++i)
1944 start_x = org_x + random() * delta_x;
1945 start_y = org_y + random() * delta_y;
1946 start_z = org_z + random() * delta_z;
1948 // rule 1: start inside world bounds, and outside
1949 // solid, and don't start from somewhere where you can
1950 // fall down to evil
1951 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1952 if(trace_fraction >= 1)
1954 if(trace_startsolid)
1956 if(trace_dphitcontents & badcontents)
1958 if(trace_dphitq3surfaceflags & badsurfaceflags)
1961 // rule 2: if we are too high, lower the point
1962 if(trace_fraction * delta_z > maxaboveground)
1963 start = trace_endpos + '0 0 1' * maxaboveground;
1964 enddown = trace_endpos;
1966 // rule 3: make sure we aren't outside the map. This only works
1967 // for somewhat well formed maps. A good rule of thumb is that
1968 // the map should have a convex outside hull.
1969 // these can be traceLINES as we already verified the starting box
1970 mstart = start + 0.5 * (e.mins + e.maxs);
1971 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
1972 if(trace_fraction >= 1)
1974 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
1975 if(trace_fraction >= 1)
1977 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
1978 if(trace_fraction >= 1)
1980 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
1981 if(trace_fraction >= 1)
1983 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
1984 if(trace_fraction >= 1)
1987 // find a random vector to "look at"
1988 end_x = org_x + random() * delta_x;
1989 end_y = org_y + random() * delta_y;
1990 end_z = org_z + random() * delta_z;
1991 end = start + normalize(end - start) * vlen(delta);
1993 // rule 4: start TO end must not be too short
1994 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1995 if(trace_startsolid)
1997 if(trace_fraction < minviewdistance / vlen(delta))
2000 // rule 5: don't want to look at sky
2001 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2004 // rule 6: we must not end up in trigger_hurt
2005 if(tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2007 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2014 e.dphitcontentsmask = m;
2018 setorigin(e, start);
2019 e.angles = vectoangles(end - start);
2020 dprint("Needed ", ftos(i + 1), " attempts\n");
2027 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2029 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2030 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2031 WriteShort(MSG_BROADCAST, effectno);
2032 WriteCoord(MSG_BROADCAST, start_x);
2033 WriteCoord(MSG_BROADCAST, start_y);
2034 WriteCoord(MSG_BROADCAST, start_z);
2035 WriteCoord(MSG_BROADCAST, end_x);
2036 WriteCoord(MSG_BROADCAST, end_y);
2037 WriteCoord(MSG_BROADCAST, end_z);
2038 WriteCoord(MSG_BROADCAST, end_dz);
2039 WriteShort(MSG_BROADCAST, spd / 16);
2042 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2045 vector vecxy, velxy;
2047 vecxy = end - start; vecxy_z = 0;
2048 velxy = vel; velxy_z = 0;
2050 if(vlen(velxy) < 0.000001 * fabs(vel_z))
2052 trailparticles(world, effectno, start, end);
2056 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2057 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2060 string GetGametype(); // g_world.qc
2061 void write_recordmarker(entity pl, float tstart, float dt)
2063 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2065 // also write a marker into demo files for demotc-race-record-extractor to find
2068 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2069 " ", ftos(tstart), " ", ftos(dt), "\n"));
2072 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2076 if (cvar("g_shootfromeye"))
2089 else if (cvar("g_shootfromcenter"))
2094 else if((s = cvar_string("g_shootfromfixedorigin")) != "")
2109 void attach_sameorigin(entity e, entity to, string tag)
2111 vector org, t_forward, t_left, t_up, e_forward, e_up;
2118 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2119 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2120 t_forward = v_forward * tagscale;
2121 t_left = v_right * -tagscale;
2122 t_up = v_up * tagscale;
2124 e.origin_x = org * t_forward;
2125 e.origin_y = org * t_left;
2126 e.origin_z = org * t_up;
2128 // current forward and up directions
2129 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2130 e.angles_x = -e.angles_x;
2131 fixedmakevectors(e.angles);
2133 // untransform forward, up!
2134 e_forward_x = v_forward * t_forward;
2135 e_forward_y = v_forward * t_left;
2136 e_forward_z = v_forward * t_up;
2137 e_up_x = v_up * t_forward;
2138 e_up_y = v_up * t_left;
2139 e_up_z = v_up * t_up;
2141 e.angles = fixedvectoangles2(e_forward, e_up);
2142 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2143 e.angles_x = -e.angles_x;
2145 setattachment(e, to, tag);
2146 setorigin(e, e.origin);
2149 void detach_sameorigin(entity e)
2152 org = gettaginfo(e, 0);
2153 e.angles = fixedvectoangles2(v_forward, v_up);
2154 if(substring(e.model, 0, 1) == "*") // bmodels have their own rules
2155 e.angles_x = -e.angles_x;
2157 setattachment(e, world, "");
2158 setorigin(e, e.origin);
2161 void follow_sameorigin(entity e, entity to)
2163 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2164 e.aiment = to; // make the hole follow bmodel
2165 e.punchangle = to.angles; // the original angles of bmodel
2166 e.view_ofs = e.origin - to.origin; // relative origin
2167 e.v_angle = e.angles - to.angles; // relative angles
2170 void unfollow_sameorigin(entity e)
2172 e.movetype = MOVETYPE_NONE;
2175 entity gettaginfo_relative_ent;
2176 vector gettaginfo_relative(entity e, float tag)
2178 if(!gettaginfo_relative_ent)
2180 gettaginfo_relative_ent = spawn();
2181 gettaginfo_relative_ent.effects = EF_NODRAW;
2183 gettaginfo_relative_ent.model = e.model;
2184 gettaginfo_relative_ent.modelindex = e.modelindex;
2185 gettaginfo_relative_ent.frame = e.frame;
2186 return gettaginfo(gettaginfo_relative_ent, tag);
2189 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2193 if(pl.soundentity.cnt & p)
2195 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2196 pl.soundentity.cnt |= p;
2199 void SoundEntity_StopSound(entity pl, float chan)
2203 if(pl.soundentity.cnt & p)
2205 stopsoundto(MSG_ALL, pl.soundentity, chan);
2206 pl.soundentity.cnt &~= p;
2210 void SoundEntity_Attach(entity pl)
2212 pl.soundentity = spawn();
2213 pl.soundentity.classname = "soundentity";
2214 setattachment(pl.soundentity, pl, "");
2215 setmodel(pl.soundentity, "null");
2218 void SoundEntity_Detach(entity pl)
2221 for(i = 0; i <= 7; ++i)
2222 SoundEntity_StopSound(pl, i);