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 DistributeEvenly_amount;
11 float DistributeEvenly_totalweight;
12 void DistributeEvenly_Init(float amount, float totalweight)
14 if (DistributeEvenly_amount)
16 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
17 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
20 DistributeEvenly_amount = 0;
22 DistributeEvenly_amount = amount;
23 DistributeEvenly_totalweight = totalweight;
25 float DistributeEvenly_Get(float weight)
30 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
31 DistributeEvenly_totalweight -= weight;
32 DistributeEvenly_amount -= f;
36 void move_out_of_solid_expand(entity e, vector by)
39 tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
42 if (trace_fraction < 1)
45 // adjust origin in the other direction...
46 e.origin = e.origin - by * (1 - trace_fraction);
50 float move_out_of_solid(entity e)
55 traceline(o, o, MOVE_WORLDONLY, e);
59 tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
60 if (!trace_startsolid)
67 move_out_of_solid_expand(e, '1 0 0' * m0_x);
69 move_out_of_solid_expand(e, '1 0 0' * m1_x);
71 move_out_of_solid_expand(e, '0 1 0' * m0_y);
73 move_out_of_solid_expand(e, '0 1 0' * m1_y);
75 move_out_of_solid_expand(e, '0 0 1' * m0_z);
77 move_out_of_solid_expand(e, '0 0 1' * m1_z);
79 setorigin(e, e.origin);
81 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
91 string STR_PLAYER = "player";
92 string STR_SPECTATOR = "spectator";
93 string STR_OBSERVER = "observer";
96 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
97 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
98 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
99 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
101 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
102 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
103 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
104 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
105 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
108 // copies a string to a tempstring (so one can strunzone it)
109 string strcat1(string s) = #115; // FRIK_FILE
114 void bcenterprint(string s)
116 // TODO replace by MSG_ALL (would show it to spectators too, though)?
118 FOR_EACH_PLAYER(head)
119 if (clienttype(head) == CLIENTTYPE_REAL)
120 centerprint(head, s);
123 void GameLogEcho(string s)
128 if (cvar("sv_eventlog_files"))
133 matches = cvar("sv_eventlog_files_counter") + 1;
134 cvar_set("sv_eventlog_files_counter", ftos(matches));
137 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
138 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
139 logfile = fopen(fn, FILE_APPEND);
140 fputs(logfile, ":logversion:3\n");
144 if (cvar("sv_eventlog_files_timestamps"))
145 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
147 fputs(logfile, strcat(s, "\n"));
150 if (cvar("sv_eventlog_console"))
159 // will be opened later
164 if (logfile_open && logfile >= 0)
171 float spawnpoint_nag;
172 void relocate_spawnpoint()
174 // nudge off the floor
175 setorigin(self, self.origin + '0 0 1');
177 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
178 if (trace_startsolid)
184 if (!move_out_of_solid(self))
185 objerror("could not get out of solid at all!");
186 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
187 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
188 print(" ", ftos(self.origin_y - o_y));
189 print(" ", ftos(self.origin_z - o_z), "'\n");
190 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
193 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
199 self.mins = self.maxs = '0 0 0';
200 objerror("player spawn point in solid, mapper sucks!\n");
205 if (cvar("g_spawnpoints_autodrop"))
207 setsize(self, PL_MIN, PL_MAX);
211 self.use = spawnpoint_use;
212 self.team_saved = self.team;
216 if (g_ctf || g_assault || g_onslaught || g_domination || g_nexball)
218 have_team_spawns = 1;
220 if (cvar("r_showbboxes"))
222 // show where spawnpoints point at too
223 makevectors(self.angles);
226 e.classname = "info_player_foo";
227 setorigin(e, self.origin + v_forward * 24);
228 setsize(e, '-8 -8 -8', '8 8 8');
229 e.solid = SOLID_TRIGGER;
233 #define strstr strstrofs
235 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
236 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
237 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
238 // BE CONSTANT OR strzoneD!
239 float strstr(string haystack, string needle, float offset)
243 len = strlen(needle);
244 endpos = strlen(haystack) - len;
245 while(offset <= endpos)
247 found = substring(haystack, offset, len);
256 float NUM_NEAREST_ENTITIES = 4;
257 entity nearest_entity[NUM_NEAREST_ENTITIES];
258 float nearest_length[NUM_NEAREST_ENTITIES];
259 entity findnearest(vector point, .string field, string value, vector axismod)
270 localhead = find(world, field, value);
273 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
274 dist = localhead.oldorigin;
276 dist = localhead.origin;
278 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
281 for (i = 0; i < num_nearest; ++i)
283 if (len < nearest_length[i])
287 // now i tells us where to insert at
288 // INSERTION SORT! YOU'VE SEEN IT! RUN!
289 if (i < NUM_NEAREST_ENTITIES)
291 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
293 nearest_length[j + 1] = nearest_length[j];
294 nearest_entity[j + 1] = nearest_entity[j];
296 nearest_length[i] = len;
297 nearest_entity[i] = localhead;
298 if (num_nearest < NUM_NEAREST_ENTITIES)
299 num_nearest = num_nearest + 1;
302 localhead = find(localhead, field, value);
305 // now use the first one from our list that we can see
306 for (i = 0; i < num_nearest; ++i)
308 traceline(point, nearest_entity[i].origin, TRUE, world);
309 if (trace_fraction == 1)
313 dprint("Nearest point (");
314 dprint(nearest_entity[0].netname);
315 dprint(") is not visible, using a visible one.\n");
317 return nearest_entity[i];
321 if (num_nearest == 0)
324 dprint("Not seeing any location point, using nearest as fallback.\n");
326 dprint("Candidates were: ");
327 for(j = 0; j < num_nearest; ++j)
331 dprint(nearest_entity[j].netname);
336 return nearest_entity[0];
339 void spawnfunc_target_location()
341 self.classname = "target_location";
342 // location name in netname
343 // eventually support: count, teamgame selectors, line of sight?
346 void spawnfunc_info_location()
348 self.classname = "target_location";
349 self.message = self.netname;
352 string NearestLocation(vector p)
357 loc = findnearest(p, classname, "target_location", '1 1 1');
364 loc = findnearest(p, target, "###item###", '1 1 4');
371 string formatmessage(string msg)
382 break; // too many replacements
384 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
385 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
395 replacement = substring(msg, p, 2);
396 escape = substring(msg, p + 1, 1);
399 else if (escape == "\\")
401 else if (escape == "n")
403 else if (escape == "a")
404 replacement = ftos(floor(self.armorvalue));
405 else if (escape == "h")
406 replacement = ftos(floor(self.health));
407 else if (escape == "l")
408 replacement = NearestLocation(self.origin);
409 else if (escape == "y")
410 replacement = NearestLocation(self.cursor_trace_endpos);
411 else if (escape == "d")
412 replacement = NearestLocation(self.death_origin);
413 else if (escape == "w")
418 wep = self.switchweapon;
421 replacement = W_Name(wep);
423 else if (escape == "W")
425 if (self.items & IT_SHELLS) replacement = "shells";
426 else if (self.items & IT_NAILS) replacement = "bullets";
427 else if (self.items & IT_ROCKETS) replacement = "rockets";
428 else if (self.items & IT_CELLS) replacement = "cells";
429 else replacement = "batteries"; // ;)
431 else if (escape == "x")
433 replacement = self.cursor_trace_ent.netname;
434 if (!replacement || !self.cursor_trace_ent)
435 replacement = "nothing";
437 else if (escape == "p")
439 if (self.last_selected_player)
440 replacement = self.last_selected_player.netname;
442 replacement = "(nobody)";
444 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
445 p = p + strlen(replacement);
456 >0: receives a cvar from name=argv(f) value=argv(f+1)
458 void GetCvars_handleString(string thisname, float f, .string field, string name)
463 strunzone(self.field);
464 self.field = string_null;
468 if (thisname == name)
471 strunzone(self.field);
472 self.field = strzone(argv(f + 1));
476 stuffcmd(self, strcat("sendcvar ", name, "\n"));
478 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
480 GetCvars_handleString(thisname, f, field, name);
481 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
482 if (thisname == name)
485 s = func(strcat1(self.field));
488 strunzone(self.field);
489 self.field = strzone(s);
493 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
500 if (thisname == name)
501 self.field = stof(argv(f + 1));
504 stuffcmd(self, strcat("sendcvar ", name, "\n"));
506 string W_FixWeaponOrder_ForceComplete(string s);
507 string W_FixWeaponOrder_AllowIncomplete(string s);
508 float w_getbestweapon(entity e);
509 void GetCvars(float f)
513 s = strcat1(argv(f));
514 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
515 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
516 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
517 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
518 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
519 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
520 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
521 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
522 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
523 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
524 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
525 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
526 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
527 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
528 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
529 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
530 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
531 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
532 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
533 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
534 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
535 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
536 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
537 GetCvars_handleFloat(s, f, cvar_cl_gunalign, "cl_gunalign");
540 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
543 if (s == "cl_weaponpriority")
544 self.switchweapon = w_getbestweapon(self);
548 float fexists(string f)
551 fh = fopen(f, FILE_READ);
558 void backtrace(string msg)
561 dev = cvar("developer");
562 cvar_set("developer", "1");
564 dprint("--- CUT HERE ---\nWARNING: ");
567 remove(world); // isn't there any better way to cause a backtrace?
568 dprint("\n--- CUT UNTIL HERE ---\n");
569 cvar_set("developer", ftos(dev));
572 string Team_ColorCode(float teamid)
574 if (teamid == COLOR_TEAM1)
576 else if (teamid == COLOR_TEAM2)
578 else if (teamid == COLOR_TEAM3)
580 else if (teamid == COLOR_TEAM4)
585 string Team_ColorName(float t)
587 // fixme: Search for team entities and get their .netname's!
588 if (t == COLOR_TEAM1)
590 if (t == COLOR_TEAM2)
592 if (t == COLOR_TEAM3)
594 if (t == COLOR_TEAM4)
598 string Team_ColorNameLowerCase(float t)
600 // fixme: Search for team entities and get their .netname's!
601 if (t == COLOR_TEAM1)
603 if (t == COLOR_TEAM2)
605 if (t == COLOR_TEAM3)
607 if (t == COLOR_TEAM4)
612 #define CENTERPRIO_POINT 1
613 #define CENTERPRIO_SPAM 2
614 #define CENTERPRIO_VOTE 4
615 #define CENTERPRIO_NORMAL 5
616 #define CENTERPRIO_SHIELDING 7
617 #define CENTERPRIO_MAPVOTE 9
618 #define CENTERPRIO_IDLEKICK 50
619 #define CENTERPRIO_ADMIN 99
620 .float centerprint_priority;
621 .float centerprint_expires;
622 void centerprint_atprio(entity e, float prio, string s)
624 if (intermission_running)
625 if (prio < CENTERPRIO_MAPVOTE)
627 if (time > e.centerprint_expires)
628 e.centerprint_priority = 0;
629 if (prio >= e.centerprint_priority)
631 e.centerprint_priority = prio;
632 if (timeoutStatus == 2)
633 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
635 e.centerprint_expires = time + e.cvar_scr_centertime;
636 centerprint_builtin(e, s);
639 void centerprint_expire(entity e, float prio)
641 if (prio == e.centerprint_priority)
643 e.centerprint_priority = 0;
644 centerprint_builtin(e, "");
647 void centerprint(entity e, string s)
649 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
652 // decolorizes and team colors the player name when needed
653 string playername(entity p)
656 if (teams_matter && !intermission_running && p.classname == "player")
658 t = Team_ColorCode(p.team);
659 return strcat(t, strdecolorize(p.netname));
665 vector randompos(vector m1, vector m2)
669 v_x = m2_x * random() + m1_x;
670 v_y = m2_y * random() + m1_y;
671 v_z = m2_z * random() + m1_z;
675 float g_pickup_shells;
676 float g_pickup_shells_max;
677 float g_pickup_nails;
678 float g_pickup_nails_max;
679 float g_pickup_rockets;
680 float g_pickup_rockets_max;
681 float g_pickup_cells;
682 float g_pickup_cells_max;
684 float g_pickup_fuel_jetpack;
685 float g_pickup_fuel_max;
686 float g_pickup_armorsmall;
687 float g_pickup_armorsmall_max;
688 float g_pickup_armormedium;
689 float g_pickup_armormedium_max;
690 float g_pickup_armorbig;
691 float g_pickup_armorbig_max;
692 float g_pickup_armorlarge;
693 float g_pickup_armorlarge_max;
694 float g_pickup_healthsmall;
695 float g_pickup_healthsmall_max;
696 float g_pickup_healthmedium;
697 float g_pickup_healthmedium_max;
698 float g_pickup_healthlarge;
699 float g_pickup_healthlarge_max;
700 float g_pickup_healthmega;
701 float g_pickup_healthmega_max;
703 string g_weaponarena_list;
704 float g_weaponspeedfactor;
705 float g_weapondamagefactor;
709 float start_ammo_shells;
710 float start_ammo_nails;
711 float start_ammo_rockets;
712 float start_ammo_cells;
713 float start_ammo_fuel;
715 float start_armorvalue;
716 float warmup_start_weapons;
717 float warmup_start_ammo_shells;
718 float warmup_start_ammo_nails;
719 float warmup_start_ammo_rockets;
720 float warmup_start_ammo_cells;
721 float warmup_start_ammo_fuel;
722 float warmup_start_health;
723 float warmup_start_armorvalue;
726 entity get_weaponinfo(float w);
728 float NixNex_CanChooseWeapon(float wpn);
729 void readplayerstartcvars()
735 // initialize starting values for players
738 start_ammo_shells = 0;
739 start_ammo_nails = 0;
740 start_ammo_rockets = 0;
741 start_ammo_cells = 0;
742 start_health = cvar("g_balance_health_start");
743 start_armorvalue = cvar("g_balance_armor_start");
746 s = cvar_string("g_weaponarena");
752 g_weaponarena_list = "All Weapons";
753 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
755 e = get_weaponinfo(j);
756 g_weaponarena |= e.weapons;
757 weapon_action(e.weapon, WR_PRECACHE);
760 else if (s == "most")
762 g_weaponarena_list = "Most Weapons";
763 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
765 e = get_weaponinfo(j);
766 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
768 g_weaponarena |= e.weapons;
769 weapon_action(e.weapon, WR_PRECACHE);
773 else if (s == "none")
775 g_weaponarena_list = "No Weapons";
776 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
780 t = tokenize_console(s);
781 g_weaponarena_list = "";
782 for (i = 0; i < t; ++i)
785 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
787 e = get_weaponinfo(j);
790 g_weaponarena |= e.weapons;
791 weapon_action(e.weapon, WR_PRECACHE);
792 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
798 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
801 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
807 // will be done later
808 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
809 if (NixNex_CanChooseWeapon(i))
810 weapon_action(i, WR_PRECACHE);
812 else if (g_weaponarena)
814 start_weapons = g_weaponarena;
815 start_ammo_rockets = 999;
816 start_ammo_shells = 999;
817 start_ammo_cells = 999;
818 start_ammo_nails = 999;
819 start_ammo_fuel = 999;
820 start_items |= IT_UNLIMITED_AMMO;
822 else if (g_minstagib)
825 start_armorvalue = 0;
826 start_weapons = WEPBIT_MINSTANEX;
827 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
828 start_ammo_cells = cvar("g_minstagib_ammo_start");
829 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
830 start_ammo_fuel = cvar("g_start_ammo_fuel");
832 if (g_minstagib_invis_alpha <= 0)
833 g_minstagib_invis_alpha = -1;
839 start_ammo_shells = cvar("g_lms_start_ammo_shells");
840 start_ammo_nails = cvar("g_lms_start_ammo_nails");
841 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
842 start_ammo_cells = cvar("g_lms_start_ammo_cells");
843 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
844 start_health = cvar("g_lms_start_health");
845 start_armorvalue = cvar("g_lms_start_armor");
847 else if (cvar("g_use_ammunition"))
849 start_ammo_shells = cvar("g_start_ammo_shells");
850 start_ammo_nails = cvar("g_start_ammo_nails");
851 start_ammo_rockets = cvar("g_start_ammo_rockets");
852 start_ammo_cells = cvar("g_start_ammo_cells");
853 start_ammo_fuel = cvar("g_start_ammo_fuel");
857 start_ammo_shells = cvar("g_pickup_shells_max");
858 start_ammo_nails = cvar("g_pickup_nails_max");
859 start_ammo_rockets = cvar("g_pickup_rockets_max");
860 start_ammo_cells = cvar("g_pickup_cells_max");
861 start_ammo_fuel = cvar("g_pickup_fuel_max");
862 start_items |= IT_UNLIMITED_AMMO;
865 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
867 e = get_weaponinfo(i);
871 t = cvar(strcat("g_start_weapon_", e.netname));
873 if (t < 0) // "default" weapon selection
876 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
878 t = (i == WEP_LASER);
880 t = 0; // weapon is set a few lines later
882 t = (i == WEP_LASER || i == WEP_SHOTGUN);
883 if (g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
884 t += (i == WEP_HOOK);
887 if (g_nexball && i == WEP_PORTO)
892 start_weapons |= e.weapons;
893 weapon_action(e.weapon, WR_PRECACHE);
900 warmup_start_ammo_shells = start_ammo_shells;
901 warmup_start_ammo_nails = start_ammo_nails;
902 warmup_start_ammo_rockets = start_ammo_rockets;
903 warmup_start_ammo_cells = start_ammo_cells;
904 warmup_start_health = start_health;
905 warmup_start_armorvalue = start_armorvalue;
906 warmup_start_weapons = start_weapons;
908 if (!g_weaponarena && !g_nixnex && !g_minstagib)
910 if (cvar("g_use_ammunition"))
912 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
913 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
914 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
915 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
917 warmup_start_health = cvar("g_warmup_start_health");
918 warmup_start_armorvalue = cvar("g_warmup_start_armor");
919 if (cvar("g_warmup_allguns"))
921 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
923 e = get_weaponinfo(i);
926 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
928 warmup_start_weapons |= e.weapons;
929 weapon_action(e.weapon, WR_PRECACHE);
936 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
938 g_grappling_hook = 0; // these two can't coexist, as they use the same button
939 start_items |= IT_FUEL_REGEN;
940 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_stable"));
944 start_items |= IT_JETPACK;
946 if (g_weapon_stay == 2)
948 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
949 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
950 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
951 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
952 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
953 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
954 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
955 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
956 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
957 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
960 start_ammo_shells = max(0, start_ammo_shells);
961 start_ammo_nails = max(0, start_ammo_nails);
962 start_ammo_cells = max(0, start_ammo_cells);
963 start_ammo_rockets = max(0, start_ammo_rockets);
964 start_ammo_fuel = max(0, start_ammo_fuel);
966 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
967 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
968 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
969 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
970 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
974 float g_bugrigs_planar_movement;
975 float g_bugrigs_planar_movement_car_jumping;
976 float g_bugrigs_reverse_spinning;
977 float g_bugrigs_reverse_speeding;
978 float g_bugrigs_reverse_stopping;
979 float g_bugrigs_air_steering;
980 float g_bugrigs_angle_smoothing;
981 float g_bugrigs_friction_floor;
982 float g_bugrigs_friction_brake;
983 float g_bugrigs_friction_air;
984 float g_bugrigs_accel;
985 float g_bugrigs_speed_ref;
986 float g_bugrigs_speed_pow;
987 float g_bugrigs_steer;
989 float g_touchexplode;
990 float g_touchexplode_radius;
991 float g_touchexplode_damage;
992 float g_touchexplode_edgedamage;
993 float g_touchexplode_force;
995 void readlevelcvars(void)
997 g_bugrigs = cvar("g_bugrigs");
998 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
999 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1000 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1001 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1002 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1003 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1004 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1005 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1006 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1007 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1008 g_bugrigs_accel = cvar("g_bugrigs_accel");
1009 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1010 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1011 g_bugrigs_steer = cvar("g_bugrigs_steer");
1013 g_touchexplode = cvar("g_touchexplode");
1014 g_touchexplode_radius = cvar("g_touchexplode_radius");
1015 g_touchexplode_damage = cvar("g_touchexplode_damage");
1016 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1017 g_touchexplode_force = cvar("g_touchexplode_force");
1019 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1020 sv_clones = cvar("sv_clones");
1021 sv_cheats = cvar("sv_cheats");
1022 sv_gentle = cvar("sv_gentle");
1023 sv_foginterval = cvar("sv_foginterval");
1024 g_cloaked = cvar("g_cloaked");
1025 g_jump_grunt = cvar("g_jump_grunt");
1026 g_footsteps = cvar("g_footsteps");
1027 g_grappling_hook = cvar("g_grappling_hook");
1028 g_jetpack = cvar("g_jetpack");
1029 g_laserguided_missile = cvar("g_laserguided_missile");
1030 g_midair = cvar("g_midair");
1031 g_minstagib = cvar("g_minstagib");
1032 g_nixnex = cvar("g_nixnex");
1033 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1034 g_norecoil = cvar("g_norecoil");
1035 g_vampire = cvar("g_vampire");
1036 g_bloodloss = cvar("g_bloodloss");
1037 sv_maxidle = cvar("sv_maxidle");
1038 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1039 sv_pogostick = cvar("sv_pogostick");
1040 sv_doublejump = cvar("sv_doublejump");
1041 g_maplist_allow_hidden = cvar("g_maplist_allow_hidden");
1042 g_ctf_reverse = cvar("g_ctf_reverse");
1044 inWarmupStage = cvar("g_warmup");
1045 g_warmup_limit = cvar("g_warmup_limit");
1046 g_warmup_allguns = cvar("g_warmup_allguns");
1047 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1049 if (g_race && g_race_qualifying == 2 || g_arena || g_assault || cvar("g_campaign"))
1050 inWarmupStage = 0; // these modes cannot work together, sorry
1052 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1053 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1054 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1055 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1056 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1057 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1058 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1059 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1060 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1061 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1062 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1063 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1065 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1066 if (g_nixnex) g_weaponarena = 0;
1069 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1070 g_weapondamagefactor = cvar("g_weapondamagefactor");
1072 g_pickup_shells = cvar("g_pickup_shells");
1073 g_pickup_shells_max = cvar("g_pickup_shells_max");
1074 g_pickup_nails = cvar("g_pickup_nails");
1075 g_pickup_nails_max = cvar("g_pickup_nails_max");
1076 g_pickup_rockets = cvar("g_pickup_rockets");
1077 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1078 g_pickup_cells = cvar("g_pickup_cells");
1079 g_pickup_cells_max = cvar("g_pickup_cells_max");
1080 g_pickup_fuel = cvar("g_pickup_fuel");
1081 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1082 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1083 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1084 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1085 g_pickup_armormedium = cvar("g_pickup_armormedium");
1086 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1087 g_pickup_armorbig = cvar("g_pickup_armorbig");
1088 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1089 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1090 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1091 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1092 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1093 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1094 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1095 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1096 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1097 g_pickup_healthmega = cvar("g_pickup_healthmega");
1098 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1100 g_pinata = cvar("g_pinata");
1102 g_weapon_stay = cvar("g_weapon_stay");
1103 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1106 if not(inWarmupStage)
1107 game_starttime = cvar("g_start_delay");
1109 readplayerstartcvars();
1113 // TODO sound pack system
1116 string precache_sound_builtin (string s) = #19;
1117 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1118 string precache_sound(string s)
1120 return precache_sound_builtin(strcat(soundpack, s));
1122 void play2(entity e, string filename)
1124 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1126 void sound(entity e, float chan, string samp, float vol, float atten)
1128 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1133 string precache_sound (string s) = #19;
1134 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1135 float precache_sound_index (string s) = #19;
1137 #define SND_VOLUME 1
1138 #define SND_ATTENUATION 2
1139 #define SND_LARGEENTITY 8
1140 #define SND_LARGESOUND 16
1142 float sound_allowed(float dest, entity e)
1144 // sounds from world may always pass
1147 if (e.classname == "body")
1149 if (e.owner && e.owner != e)
1154 // sounds to self may always pass
1155 if (dest == MSG_ONE)
1156 if (e == msg_entity)
1158 // sounds by players can be removed
1159 if (cvar("bot_sound_monopoly"))
1160 if (clienttype(e) == CLIENTTYPE_REAL)
1162 // anything else may pass
1166 void sound(entity e, float chan, string samp, float vol, float atten)
1168 if (!sound_allowed(MSG_BROADCAST, e))
1170 sound_builtin(e, chan, samp, vol, atten);
1172 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1176 if (!sound_allowed(dest, e))
1179 entno = num_for_edict(e);
1180 idx = precache_sound_index(samp);
1185 atten = floor(atten * 64);
1186 vol = floor(vol * 255);
1189 sflags |= SND_VOLUME;
1191 sflags |= SND_ATTENUATION;
1193 sflags |= SND_LARGEENTITY;
1195 sflags |= SND_LARGESOUND;
1197 WriteByte(dest, SVC_SOUND);
1198 WriteByte(dest, sflags);
1199 if (sflags & SND_VOLUME)
1200 WriteByte(dest, vol);
1201 if (sflags & SND_ATTENUATION)
1202 WriteByte(dest, atten);
1203 if (sflags & SND_LARGEENTITY)
1205 WriteShort(dest, entno);
1206 WriteByte(dest, chan);
1210 WriteShort(dest, entno * 8 + chan);
1212 if (sflags & SND_LARGESOUND)
1213 WriteShort(dest, idx);
1215 WriteByte(dest, idx);
1217 WriteCoord(dest, o_x);
1218 WriteCoord(dest, o_y);
1219 WriteCoord(dest, o_z);
1221 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1225 if (!sound_allowed(dest, e))
1228 o = e.origin + 0.5 * (e.mins + e.maxs);
1229 soundtoat(dest, e, o, chan, samp, vol, atten);
1231 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1233 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1235 void stopsoundto(float dest, entity e, float chan)
1239 if (!sound_allowed(dest, e))
1242 entno = num_for_edict(e);
1247 idx = precache_sound_index("misc/null.wav");
1248 sflags = SND_LARGEENTITY;
1250 sflags |= SND_LARGESOUND;
1251 WriteByte(dest, SVC_SOUND);
1252 WriteByte(dest, sflags);
1253 WriteShort(dest, entno);
1254 WriteByte(dest, chan);
1255 if (sflags & SND_LARGESOUND)
1256 WriteShort(dest, idx);
1258 WriteByte(dest, idx);
1259 WriteCoord(dest, e.origin_x);
1260 WriteCoord(dest, e.origin_y);
1261 WriteCoord(dest, e.origin_z);
1265 WriteByte(dest, SVC_STOPSOUND);
1266 WriteShort(dest, entno * 8 + chan);
1269 void stopsound(entity e, float chan)
1271 if (!sound_allowed(MSG_BROADCAST, e))
1274 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1275 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1278 void play2(entity e, string filename)
1280 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1282 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1285 .float announcetime;
1286 float announce(entity player, string msg)
1288 if (time > player.announcetime)
1289 if (clienttype(player) == CLIENTTYPE_REAL)
1291 player.announcetime = time + 0.8;
1297 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1298 float spamsound(entity e, float chan, string samp, float vol, float atten)
1300 if (!sound_allowed(MSG_BROADCAST, e))
1303 if (time > e.announcetime)
1305 e.announcetime = time;
1306 sound(e, chan, samp, vol, atten);
1312 void play2team(float t, string filename)
1316 if (cvar("bot_sound_monopoly"))
1319 FOR_EACH_REALPLAYER(head)
1322 play2(head, filename);
1326 void play2all(string samp)
1328 if (cvar("bot_sound_monopoly"))
1331 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1334 void PrecachePlayerSounds(string f);
1335 void precache_all_models(string pattern)
1337 float globhandle, i, n;
1340 globhandle = search_begin(pattern, TRUE, FALSE);
1343 n = search_getsize(globhandle);
1344 for (i = 0; i < n; ++i)
1346 //print(search_getfilename(globhandle, i), "\n");
1347 f = search_getfilename(globhandle, i);
1349 PrecachePlayerSounds(strcat(f, ".sounds"));
1351 search_end(globhandle);
1356 // gamemode related things
1357 precache_model ("models/misc/chatbubble.spr");
1358 precache_model ("models/misc/teambubble.spr");
1361 precache_model ("models/runematch/curse.mdl");
1362 precache_model ("models/runematch/rune.mdl");
1365 #ifdef TTURRETS_ENABLED
1366 if (cvar("g_turrets"))
1370 // Precache all player models if desired
1371 if (cvar("sv_precacheplayermodels"))
1373 PrecachePlayerSounds("sound/player/default.sounds");
1374 precache_all_models("models/player/*.zym");
1375 precache_all_models("models/player/*.dpm");
1376 precache_all_models("models/player/*.md3");
1377 precache_all_models("models/player/*.psk");
1378 //precache_model("models/player/carni.zym");
1379 //precache_model("models/player/crash.zym");
1380 //precache_model("models/player/grunt.zym");
1381 //precache_model("models/player/headhunter.zym");
1382 //precache_model("models/player/insurrectionist.zym");
1383 //precache_model("models/player/jeandarc.zym");
1384 //precache_model("models/player/lurk.zym");
1385 //precache_model("models/player/lycanthrope.zym");
1386 //precache_model("models/player/marine.zym");
1387 //precache_model("models/player/nexus.zym");
1388 //precache_model("models/player/pyria.zym");
1389 //precache_model("models/player/shock.zym");
1390 //precache_model("models/player/skadi.zym");
1391 //precache_model("models/player/specop.zym");
1392 //precache_model("models/player/visitant.zym");
1395 if (cvar("sv_defaultcharacter"))
1398 s = cvar_string("sv_defaultplayermodel_red");
1402 PrecachePlayerSounds(strcat(s, ".sounds"));
1404 s = cvar_string("sv_defaultplayermodel_blue");
1408 PrecachePlayerSounds(strcat(s, ".sounds"));
1410 s = cvar_string("sv_defaultplayermodel_yellow");
1414 PrecachePlayerSounds(strcat(s, ".sounds"));
1416 s = cvar_string("sv_defaultplayermodel_pink");
1420 PrecachePlayerSounds(strcat(s, ".sounds"));
1422 s = cvar_string("sv_defaultplayermodel");
1426 PrecachePlayerSounds(strcat(s, ".sounds"));
1432 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1433 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1436 // gore and miscellaneous sounds
1437 //precache_sound ("misc/h2ohit.wav");
1438 precache_model ("models/hook.md3");
1439 precache_sound ("misc/armorimpact.wav");
1440 precache_sound ("misc/bodyimpact1.wav");
1441 precache_sound ("misc/bodyimpact2.wav");
1442 precache_sound ("misc/gib.wav");
1443 precache_sound ("misc/gib_splat01.wav");
1444 precache_sound ("misc/gib_splat02.wav");
1445 precache_sound ("misc/gib_splat03.wav");
1446 precache_sound ("misc/gib_splat04.wav");
1447 precache_sound ("misc/hit.wav");
1448 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1449 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1450 precache_sound ("misc/null.wav");
1451 precache_sound ("misc/spawn.wav");
1452 precache_sound ("misc/talk.wav");
1453 precache_sound ("misc/teleport.wav");
1454 precache_sound ("misc/poweroff.wav");
1455 precache_sound ("player/lava.wav");
1456 precache_sound ("player/slime.wav");
1459 precache_sound ("misc/jetpack_fly.wav");
1461 // announcer sounds - male
1462 precache_sound ("announcer/male/electrobitch.wav");
1463 precache_sound ("announcer/male/airshot.wav");
1464 precache_sound ("announcer/male/03kills.wav");
1465 precache_sound ("announcer/male/05kills.wav");
1466 precache_sound ("announcer/male/10kills.wav");
1467 precache_sound ("announcer/male/15kills.wav");
1468 precache_sound ("announcer/male/20kills.wav");
1469 precache_sound ("announcer/male/25kills.wav");
1470 precache_sound ("announcer/male/30kills.wav");
1471 precache_sound ("announcer/male/botlike.wav");
1472 precache_sound ("announcer/male/yoda.wav");
1473 precache_sound ("announcer/male/amazing.wav");
1474 precache_sound ("announcer/male/awesome.wav");
1475 precache_sound ("announcer/male/headshot.wav");
1476 precache_sound ("announcer/male/impressive.wav");
1478 // announcer sounds - robotic
1479 precache_sound ("announcer/robotic/prepareforbattle.wav");
1480 precache_sound ("announcer/robotic/begin.wav");
1481 precache_sound ("announcer/robotic/timeoutcalled.wav");
1482 precache_sound ("announcer/robotic/1fragleft.wav");
1483 precache_sound ("announcer/robotic/2fragsleft.wav");
1484 precache_sound ("announcer/robotic/3fragsleft.wav");
1487 precache_sound ("announcer/robotic/lastsecond.wav");
1488 precache_sound ("announcer/robotic/narrowly.wav");
1491 precache_model ("models/sprites/1.spr32");
1492 precache_model ("models/sprites/2.spr32");
1493 precache_model ("models/sprites/3.spr32");
1494 precache_model ("models/sprites/4.spr32");
1495 precache_model ("models/sprites/5.spr32");
1496 precache_model ("models/sprites/6.spr32");
1497 precache_model ("models/sprites/7.spr32");
1498 precache_model ("models/sprites/8.spr32");
1499 precache_model ("models/sprites/9.spr32");
1500 precache_model ("models/sprites/10.spr32");
1501 precache_sound ("announcer/robotic/1.wav");
1502 precache_sound ("announcer/robotic/2.wav");
1503 precache_sound ("announcer/robotic/3.wav");
1504 precache_sound ("announcer/robotic/4.wav");
1505 precache_sound ("announcer/robotic/5.wav");
1506 precache_sound ("announcer/robotic/6.wav");
1507 precache_sound ("announcer/robotic/7.wav");
1508 precache_sound ("announcer/robotic/8.wav");
1509 precache_sound ("announcer/robotic/9.wav");
1510 precache_sound ("announcer/robotic/10.wav");
1512 // common weapon precaches
1513 precache_sound ("weapons/weapon_switch.wav");
1514 precache_sound ("weapons/weaponpickup.wav");
1515 precache_sound ("weapons/unavailable.wav");
1516 if (g_grappling_hook)
1518 precache_sound ("weapons/hook_fire.wav"); // hook
1519 precache_sound ("weapons/hook_impact.wav"); // hook
1522 if (cvar("sv_precacheweapons") || g_nixnex)
1524 //precache weapon models/sounds
1527 while (wep <= WEP_LAST)
1529 weapon_action(wep, WR_PRECACHE);
1534 precache_model("models/elaser.mdl");
1535 precache_model("models/laser.mdl");
1536 precache_model("models/ebomb.mdl");
1539 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1541 if (!self.noise && self.music) // quake 3 uses the music field
1542 self.noise = self.music;
1544 // plays music for the level if there is any
1547 precache_sound (self.noise);
1548 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1553 // sorry, but using \ in macros breaks line numbers
1554 #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
1555 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1556 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1558 vector ExactTriggerHit_mins;
1559 vector ExactTriggerHit_maxs;
1560 float ExactTriggerHit_Recurse()
1566 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1569 if (trace_ent == self)
1574 se.solid = SOLID_NOT;
1575 f = ExactTriggerHit_Recurse();
1581 float ExactTriggerHit()
1585 if not(self.modelindex)
1589 self.solid = SOLID_BSP;
1590 ExactTriggerHit_mins = other.absmin;
1591 ExactTriggerHit_maxs = other.absmax;
1592 f = ExactTriggerHit_Recurse();
1598 // WARNING: this kills the trace globals
1599 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1600 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1602 #define INITPRIO_FIRST 0
1603 #define INITPRIO_GAMETYPE 0
1604 #define INITPRIO_GAMETYPE_FALLBACK 1
1605 #define INITPRIO_CVARS 5
1606 #define INITPRIO_FINDTARGET 10
1607 #define INITPRIO_DROPTOFLOOR 20
1608 #define INITPRIO_SETLOCATION 90
1609 #define INITPRIO_LINKDOORS 91
1610 #define INITPRIO_LAST 99
1612 .void(void) initialize_entity;
1613 .float initialize_entity_order;
1614 .entity initialize_entity_next;
1615 entity initialize_entity_first;
1617 void make_safe_for_remove(entity e)
1619 if (e.initialize_entity)
1622 for (ent = initialize_entity_first; ent; )
1624 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1626 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1627 // skip it in linked list
1630 prev.initialize_entity_next = ent.initialize_entity_next;
1631 ent = prev.initialize_entity_next;
1635 initialize_entity_first = ent.initialize_entity_next;
1636 ent = initialize_entity_first;
1642 ent = ent.initialize_entity_next;
1648 void objerror(string s)
1650 make_safe_for_remove(self);
1651 objerror_builtin(s);
1654 void remove_unsafely(entity e)
1659 void remove_safely(entity e)
1661 make_safe_for_remove(e);
1665 void InitializeEntity(entity e, void(void) func, float order)
1669 if (!e || e.initialize_entity)
1671 // make a proxy initializer entity
1675 e.classname = "initialize_entity";
1679 e.initialize_entity = func;
1680 e.initialize_entity_order = order;
1682 cur = initialize_entity_first;
1685 if (!cur || cur.initialize_entity_order > order)
1687 // insert between prev and cur
1689 prev.initialize_entity_next = e;
1691 initialize_entity_first = e;
1692 e.initialize_entity_next = cur;
1696 cur = cur.initialize_entity_next;
1699 void InitializeEntitiesRun()
1702 startoflist = initialize_entity_first;
1703 initialize_entity_first = world;
1704 for (self = startoflist; self; )
1707 var void(void) func;
1708 e = self.initialize_entity_next;
1709 func = self.initialize_entity;
1710 self.initialize_entity_order = 0;
1711 self.initialize_entity = func_null;
1712 self.initialize_entity_next = world;
1713 if (self.classname == "initialize_entity")
1717 remove_builtin(self);
1720 //dprint("Delayed initialization: ", self.classname, "\n");
1726 .float uncustomizeentityforclient_set;
1727 .void(void) uncustomizeentityforclient;
1728 void(void) SUB_Nullpointer = #0;
1729 void UncustomizeEntitiesRun()
1733 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1734 self.uncustomizeentityforclient();
1737 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1739 e.customizeentityforclient = customizer;
1740 e.uncustomizeentityforclient = uncustomizer;
1741 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1745 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1748 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1752 if (e.classname == "")
1753 e.classname = "net_linked";
1755 if (e.model == "" || self.modelindex == 0)
1759 setmodel(e, "null");
1763 e.SendEntity = sendfunc;
1764 e.SendFlags = 0xFFFFFF;
1767 e.effects |= EF_NODEPTHTEST;
1771 e.nextthink = time + dt;
1772 e.think = SUB_Remove;
1776 void adaptor_think2touch()
1785 void adaptor_think2use()
1797 // deferred dropping
1798 void DropToFloor_Handler()
1800 droptofloor_builtin();
1801 self.dropped_origin = self.origin;
1806 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1811 float trace_hits_box_a0, trace_hits_box_a1;
1813 float trace_hits_box_1d(float end, float thmi, float thma)
1817 // just check if x is in range
1825 // do the trace with respect to x
1826 // 0 -> end has to stay in thmi -> thma
1827 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1828 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1829 if (trace_hits_box_a0 > trace_hits_box_a1)
1835 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1840 // now it is a trace from 0 to end
1842 trace_hits_box_a0 = 0;
1843 trace_hits_box_a1 = 1;
1845 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1847 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1849 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1855 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1857 return trace_hits_box(start, end, thmi - ma, thma - mi);
1860 float SUB_NoImpactCheck()
1862 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1864 if (other == world && self.size != '0 0 0')
1867 tic = self.velocity * sys_ticrate;
1868 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1869 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1870 if (trace_fraction >= 1)
1872 dprint("Odd... did not hit...?\n");
1874 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1876 dprint("Detected and prevented the sky-grapple bug.\n");
1884 #define SUB_OwnerCheck() (other && (other == self.owner))
1886 #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)
1888 float MAX_IPBAN_URIS = 16;
1890 float URI_GET_DISCARD = 0;
1891 float URI_GET_IPBAN = 1;
1892 float URI_GET_IPBAN_END = 16;
1894 void URI_Get_Callback(float id, float status, string data)
1896 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1898 dprint("\nEnd of data.\n");
1900 if (id == URI_GET_DISCARD)
1904 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1907 OnlineBanList_URI_Get_Callback(id, status, data);
1911 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1915 void print_to(entity e, string s)
1918 sprint(e, strcat(s, "\n"));
1937 for (i = 0; i < MapInfo_count; ++i)
1939 if (MapInfo_Get_ByID(i))
1941 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1944 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1945 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1953 for (i = 0; i < MapInfo_count; ++i)
1955 if (MapInfo_Get_ByID(i))
1957 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1960 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1961 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1966 MapInfo_ClearTemps();
1969 return "No records are available on this server.\n";
1971 return strcat("Records on this server:\n", s);
1974 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1977 vector start, org, delta, end, enddown, mstart;
1979 m = e.dphitcontentsmask;
1980 e.dphitcontentsmask = goodcontents | badcontents;
1983 delta = world.maxs - world.mins;
1985 for (i = 0; i < attempts; ++i)
1987 start_x = org_x + random() * delta_x;
1988 start_y = org_y + random() * delta_y;
1989 start_z = org_z + random() * delta_z;
1991 // rule 1: start inside world bounds, and outside
1992 // solid, and don't start from somewhere where you can
1993 // fall down to evil
1994 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
1995 if (trace_fraction >= 1)
1997 if (trace_startsolid)
1999 if (trace_dphitcontents & badcontents)
2001 if (trace_dphitq3surfaceflags & badsurfaceflags)
2004 // rule 2: if we are too high, lower the point
2005 if (trace_fraction * delta_z > maxaboveground)
2006 start = trace_endpos + '0 0 1' * maxaboveground;
2007 enddown = trace_endpos;
2009 // rule 3: make sure we aren't outside the map. This only works
2010 // for somewhat well formed maps. A good rule of thumb is that
2011 // the map should have a convex outside hull.
2012 // these can be traceLINES as we already verified the starting box
2013 mstart = start + 0.5 * (e.mins + e.maxs);
2014 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2015 if (trace_fraction >= 1)
2017 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2018 if (trace_fraction >= 1)
2020 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2021 if (trace_fraction >= 1)
2023 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2024 if (trace_fraction >= 1)
2026 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2027 if (trace_fraction >= 1)
2030 // find a random vector to "look at"
2031 end_x = org_x + random() * delta_x;
2032 end_y = org_y + random() * delta_y;
2033 end_z = org_z + random() * delta_z;
2034 end = start + normalize(end - start) * vlen(delta);
2036 // rule 4: start TO end must not be too short
2037 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2038 if (trace_startsolid)
2040 if (trace_fraction < minviewdistance / vlen(delta))
2043 // rule 5: don't want to look at sky
2044 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2047 // rule 6: we must not end up in trigger_hurt
2048 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2050 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2057 e.dphitcontentsmask = m;
2061 setorigin(e, start);
2062 e.angles = vectoangles(end - start);
2063 dprint("Needed ", ftos(i + 1), " attempts\n");
2070 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2072 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2073 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2074 WriteShort(MSG_BROADCAST, effectno);
2075 WriteCoord(MSG_BROADCAST, start_x);
2076 WriteCoord(MSG_BROADCAST, start_y);
2077 WriteCoord(MSG_BROADCAST, start_z);
2078 WriteCoord(MSG_BROADCAST, end_x);
2079 WriteCoord(MSG_BROADCAST, end_y);
2080 WriteCoord(MSG_BROADCAST, end_z);
2081 WriteCoord(MSG_BROADCAST, end_dz);
2082 WriteShort(MSG_BROADCAST, spd / 16);
2085 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2088 vector vecxy, velxy;
2090 vecxy = end - start;
2095 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2097 trailparticles(world, effectno, start, end);
2101 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2102 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2105 string GetGametype(); // g_world.qc
2106 void write_recordmarker(entity pl, float tstart, float dt)
2108 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt / 10)));
2110 // also write a marker into demo files for demotc-race-record-extractor to find
2113 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2114 " ", ftos(tstart), " ", ftos(dt), "\n"));
2117 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2122 if (cvar("g_shootfromclient"))
2124 switch(self.owner.cvar_cl_gunalign)
2147 else if (cvar("g_shootfromeye"))
2160 else if (cvar("g_shootfromcenter"))
2165 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2180 void attach_sameorigin(entity e, entity to, string tag)
2182 vector org, t_forward, t_left, t_up, e_forward, e_up;
2189 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2190 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2191 t_forward = v_forward * tagscale;
2192 t_left = v_right * -tagscale;
2193 t_up = v_up * tagscale;
2195 e.origin_x = org * t_forward;
2196 e.origin_y = org * t_left;
2197 e.origin_z = org * t_up;
2199 // current forward and up directions
2200 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2201 e.angles_x = -e.angles_x;
2202 fixedmakevectors(e.angles);
2204 // untransform forward, up!
2205 e_forward_x = v_forward * t_forward;
2206 e_forward_y = v_forward * t_left;
2207 e_forward_z = v_forward * t_up;
2208 e_up_x = v_up * t_forward;
2209 e_up_y = v_up * t_left;
2210 e_up_z = v_up * t_up;
2212 e.angles = fixedvectoangles2(e_forward, e_up);
2213 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2214 e.angles_x = -e.angles_x;
2216 setattachment(e, to, tag);
2217 setorigin(e, e.origin);
2220 void detach_sameorigin(entity e)
2223 org = gettaginfo(e, 0);
2224 e.angles = fixedvectoangles2(v_forward, v_up);
2225 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2226 e.angles_x = -e.angles_x;
2228 setattachment(e, world, "");
2229 setorigin(e, e.origin);
2232 void follow_sameorigin(entity e, entity to)
2234 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2235 e.aiment = to; // make the hole follow bmodel
2236 e.punchangle = to.angles; // the original angles of bmodel
2237 e.view_ofs = e.origin - to.origin; // relative origin
2238 e.v_angle = e.angles - to.angles; // relative angles
2241 void unfollow_sameorigin(entity e)
2243 e.movetype = MOVETYPE_NONE;
2246 entity gettaginfo_relative_ent;
2247 vector gettaginfo_relative(entity e, float tag)
2249 if (!gettaginfo_relative_ent)
2251 gettaginfo_relative_ent = spawn();
2252 gettaginfo_relative_ent.effects = EF_NODRAW;
2254 gettaginfo_relative_ent.model = e.model;
2255 gettaginfo_relative_ent.modelindex = e.modelindex;
2256 gettaginfo_relative_ent.frame = e.frame;
2257 return gettaginfo(gettaginfo_relative_ent, tag);
2260 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2264 if (pl.soundentity.cnt & p)
2266 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2267 pl.soundentity.cnt |= p;
2270 void SoundEntity_StopSound(entity pl, float chan)
2274 if (pl.soundentity.cnt & p)
2276 stopsoundto(MSG_ALL, pl.soundentity, chan);
2277 pl.soundentity.cnt &~= p;
2281 void SoundEntity_Attach(entity pl)
2283 pl.soundentity = spawn();
2284 pl.soundentity.classname = "soundentity";
2285 pl.soundentity.owner = pl;
2286 setattachment(pl.soundentity, pl, "");
2287 setmodel(pl.soundentity, "null");
2290 void SoundEntity_Detach(entity pl)
2293 for (i = 0; i <= 7; ++i)
2294 SoundEntity_StopSound(pl, i);
2298 float ParseCommandPlayerSlotTarget_firsttoken;
2299 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2307 ParseCommandPlayerSlotTarget_firsttoken = -1;
2311 if (substring(argv(idx), 0, 1) == "#")
2313 s = substring(argv(idx), 1, -1);
2321 if (s == ftos(stof(s)))
2323 e = edict_num(stof(s));
2324 if (e.flags & FL_CLIENT)
2326 ParseCommandPlayerSlotTarget_firsttoken = idx;
2333 // it must be a nick name
2338 FOR_EACH_CLIENT(head)
2339 if (head.netname == s)
2346 ParseCommandPlayerSlotTarget_firsttoken = idx;
2350 s = strdecolorize(s);
2352 FOR_EACH_CLIENT(head)
2353 if (strdecolorize(head.netname) == s)
2360 ParseCommandPlayerSlotTarget_firsttoken = idx;