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 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
513 if (thisname == name)
517 self.field = stof(argv(f + 1));
526 stuffcmd(self, strcat("sendcvar ", name, "\n"));
529 string W_FixWeaponOrder_ForceComplete(string s);
530 string W_FixWeaponOrder_AllowIncomplete(string s);
531 float w_getbestweapon(entity e);
532 void GetCvars(float f)
536 s = strcat1(argv(f));
537 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
538 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
539 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
540 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
541 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
542 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
543 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
544 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
545 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
546 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
547 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
548 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
549 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
550 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
551 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
552 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
553 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
554 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
555 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
556 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
557 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
558 #ifdef ALLOW_FORCEMODELS
559 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
560 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
562 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
565 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
568 if (s == "cl_weaponpriority")
569 self.switchweapon = w_getbestweapon(self);
573 float fexists(string f)
576 fh = fopen(f, FILE_READ);
583 void backtrace(string msg)
586 dev = cvar("developer");
587 cvar_set("developer", "1");
589 dprint("--- CUT HERE ---\nWARNING: ");
592 remove(world); // isn't there any better way to cause a backtrace?
593 dprint("\n--- CUT UNTIL HERE ---\n");
594 cvar_set("developer", ftos(dev));
597 string Team_ColorCode(float teamid)
599 if (teamid == COLOR_TEAM1)
601 else if (teamid == COLOR_TEAM2)
603 else if (teamid == COLOR_TEAM3)
605 else if (teamid == COLOR_TEAM4)
610 string Team_ColorName(float t)
612 // fixme: Search for team entities and get their .netname's!
613 if (t == COLOR_TEAM1)
615 if (t == COLOR_TEAM2)
617 if (t == COLOR_TEAM3)
619 if (t == COLOR_TEAM4)
623 string Team_ColorNameLowerCase(float t)
625 // fixme: Search for team entities and get their .netname's!
626 if (t == COLOR_TEAM1)
628 if (t == COLOR_TEAM2)
630 if (t == COLOR_TEAM3)
632 if (t == COLOR_TEAM4)
637 #define CENTERPRIO_POINT 1
638 #define CENTERPRIO_SPAM 2
639 #define CENTERPRIO_VOTE 4
640 #define CENTERPRIO_NORMAL 5
641 #define CENTERPRIO_SHIELDING 7
642 #define CENTERPRIO_MAPVOTE 9
643 #define CENTERPRIO_IDLEKICK 50
644 #define CENTERPRIO_ADMIN 99
645 .float centerprint_priority;
646 .float centerprint_expires;
647 void centerprint_atprio(entity e, float prio, string s)
649 if (intermission_running)
650 if (prio < CENTERPRIO_MAPVOTE)
652 if (time > e.centerprint_expires)
653 e.centerprint_priority = 0;
654 if (prio >= e.centerprint_priority)
656 e.centerprint_priority = prio;
657 if (timeoutStatus == 2)
658 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
660 e.centerprint_expires = time + e.cvar_scr_centertime;
661 centerprint_builtin(e, s);
664 void centerprint_expire(entity e, float prio)
666 if (prio == e.centerprint_priority)
668 e.centerprint_priority = 0;
669 centerprint_builtin(e, "");
672 void centerprint(entity e, string s)
674 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
677 // decolorizes and team colors the player name when needed
678 string playername(entity p)
681 if (teams_matter && !intermission_running && p.classname == "player")
683 t = Team_ColorCode(p.team);
684 return strcat(t, strdecolorize(p.netname));
690 vector randompos(vector m1, vector m2)
694 v_x = m2_x * random() + m1_x;
695 v_y = m2_y * random() + m1_y;
696 v_z = m2_z * random() + m1_z;
700 float g_pickup_shells;
701 float g_pickup_shells_max;
702 float g_pickup_nails;
703 float g_pickup_nails_max;
704 float g_pickup_rockets;
705 float g_pickup_rockets_max;
706 float g_pickup_cells;
707 float g_pickup_cells_max;
709 float g_pickup_fuel_jetpack;
710 float g_pickup_fuel_max;
711 float g_pickup_armorsmall;
712 float g_pickup_armorsmall_max;
713 float g_pickup_armormedium;
714 float g_pickup_armormedium_max;
715 float g_pickup_armorbig;
716 float g_pickup_armorbig_max;
717 float g_pickup_armorlarge;
718 float g_pickup_armorlarge_max;
719 float g_pickup_healthsmall;
720 float g_pickup_healthsmall_max;
721 float g_pickup_healthmedium;
722 float g_pickup_healthmedium_max;
723 float g_pickup_healthlarge;
724 float g_pickup_healthlarge_max;
725 float g_pickup_healthmega;
726 float g_pickup_healthmega_max;
728 string g_weaponarena_list;
729 float g_weaponspeedfactor;
730 float g_weaponratefactor;
731 float g_weapondamagefactor;
732 float g_weaponforcefactor;
736 float start_ammo_shells;
737 float start_ammo_nails;
738 float start_ammo_rockets;
739 float start_ammo_cells;
740 float start_ammo_fuel;
742 float start_armorvalue;
743 float warmup_start_weapons;
744 float warmup_start_ammo_shells;
745 float warmup_start_ammo_nails;
746 float warmup_start_ammo_rockets;
747 float warmup_start_ammo_cells;
748 float warmup_start_ammo_fuel;
749 float warmup_start_health;
750 float warmup_start_armorvalue;
753 entity get_weaponinfo(float w);
755 float NixNex_CanChooseWeapon(float wpn);
756 void readplayerstartcvars()
762 // initialize starting values for players
765 start_ammo_shells = 0;
766 start_ammo_nails = 0;
767 start_ammo_rockets = 0;
768 start_ammo_cells = 0;
769 start_health = cvar("g_balance_health_start");
770 start_armorvalue = cvar("g_balance_armor_start");
773 s = cvar_string("g_weaponarena");
779 g_weaponarena_list = "All Weapons";
780 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
782 e = get_weaponinfo(j);
783 g_weaponarena |= e.weapons;
784 weapon_action(e.weapon, WR_PRECACHE);
787 else if (s == "most")
789 g_weaponarena_list = "Most Weapons";
790 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
792 e = get_weaponinfo(j);
793 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
795 g_weaponarena |= e.weapons;
796 weapon_action(e.weapon, WR_PRECACHE);
800 else if (s == "none")
802 g_weaponarena_list = "No Weapons";
803 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
807 t = tokenize_console(s);
808 g_weaponarena_list = "";
809 for (i = 0; i < t; ++i)
812 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
814 e = get_weaponinfo(j);
817 g_weaponarena |= e.weapons;
818 weapon_action(e.weapon, WR_PRECACHE);
819 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
825 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
828 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
834 // will be done later
835 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
836 if (NixNex_CanChooseWeapon(i))
837 weapon_action(i, WR_PRECACHE);
838 if(!cvar("g_use_ammunition"))
839 start_items |= IT_UNLIMITED_AMMO;
841 else if (g_weaponarena)
843 start_weapons = g_weaponarena;
844 start_ammo_rockets = 999;
845 start_ammo_shells = 999;
846 start_ammo_cells = 999;
847 start_ammo_nails = 999;
848 start_ammo_fuel = 999;
849 start_items |= IT_UNLIMITED_AMMO;
851 else if (g_minstagib)
854 start_armorvalue = 0;
855 start_weapons = WEPBIT_MINSTANEX;
856 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
857 start_ammo_cells = cvar("g_minstagib_ammo_start");
858 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
859 start_ammo_fuel = cvar("g_start_ammo_fuel");
861 if (g_minstagib_invis_alpha <= 0)
862 g_minstagib_invis_alpha = -1;
868 start_ammo_shells = cvar("g_lms_start_ammo_shells");
869 start_ammo_nails = cvar("g_lms_start_ammo_nails");
870 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
871 start_ammo_cells = cvar("g_lms_start_ammo_cells");
872 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
873 start_health = cvar("g_lms_start_health");
874 start_armorvalue = cvar("g_lms_start_armor");
876 else if (cvar("g_use_ammunition"))
878 start_ammo_shells = cvar("g_start_ammo_shells");
879 start_ammo_nails = cvar("g_start_ammo_nails");
880 start_ammo_rockets = cvar("g_start_ammo_rockets");
881 start_ammo_cells = cvar("g_start_ammo_cells");
882 start_ammo_fuel = cvar("g_start_ammo_fuel");
886 start_ammo_shells = cvar("g_pickup_shells_max");
887 start_ammo_nails = cvar("g_pickup_nails_max");
888 start_ammo_rockets = cvar("g_pickup_rockets_max");
889 start_ammo_cells = cvar("g_pickup_cells_max");
890 start_ammo_fuel = cvar("g_pickup_fuel_max");
891 start_items |= IT_UNLIMITED_AMMO;
894 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
896 e = get_weaponinfo(i);
900 t = cvar(strcat("g_start_weapon_", e.netname));
902 if (t < 0) // "default" weapon selection
905 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
906 else if (g_race || g_cts)
907 t = (i == WEP_LASER);
909 t = 0; // weapon is set a few lines later
911 t = (i == WEP_LASER || i == WEP_SHOTGUN);
912 if (g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
913 t += (i == WEP_HOOK);
916 if (g_nexball && i == WEP_PORTO)
921 start_weapons |= e.weapons;
922 weapon_action(e.weapon, WR_PRECACHE);
929 warmup_start_ammo_shells = start_ammo_shells;
930 warmup_start_ammo_nails = start_ammo_nails;
931 warmup_start_ammo_rockets = start_ammo_rockets;
932 warmup_start_ammo_cells = start_ammo_cells;
933 warmup_start_health = start_health;
934 warmup_start_armorvalue = start_armorvalue;
935 warmup_start_weapons = start_weapons;
937 if (!g_weaponarena && !g_nixnex && !g_minstagib)
939 if (cvar("g_use_ammunition"))
941 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
942 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
943 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
944 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
946 warmup_start_health = cvar("g_warmup_start_health");
947 warmup_start_armorvalue = cvar("g_warmup_start_armor");
948 if (cvar("g_warmup_allguns"))
950 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
952 e = get_weaponinfo(i);
955 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
957 warmup_start_weapons |= e.weapons;
958 weapon_action(e.weapon, WR_PRECACHE);
965 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
967 g_grappling_hook = 0; // these two can't coexist, as they use the same button
968 start_items |= IT_FUEL_REGEN;
969 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
973 start_items |= IT_JETPACK;
975 if (g_weapon_stay == 2)
977 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
978 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
979 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
980 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
981 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
982 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
983 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
984 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
985 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
986 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
989 start_ammo_shells = max(0, start_ammo_shells);
990 start_ammo_nails = max(0, start_ammo_nails);
991 start_ammo_cells = max(0, start_ammo_cells);
992 start_ammo_rockets = max(0, start_ammo_rockets);
993 start_ammo_fuel = max(0, start_ammo_fuel);
995 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
996 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
997 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
998 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
999 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1003 float g_bugrigs_planar_movement;
1004 float g_bugrigs_planar_movement_car_jumping;
1005 float g_bugrigs_reverse_spinning;
1006 float g_bugrigs_reverse_speeding;
1007 float g_bugrigs_reverse_stopping;
1008 float g_bugrigs_air_steering;
1009 float g_bugrigs_angle_smoothing;
1010 float g_bugrigs_friction_floor;
1011 float g_bugrigs_friction_brake;
1012 float g_bugrigs_friction_air;
1013 float g_bugrigs_accel;
1014 float g_bugrigs_speed_ref;
1015 float g_bugrigs_speed_pow;
1016 float g_bugrigs_steer;
1018 float g_touchexplode;
1019 float g_touchexplode_radius;
1020 float g_touchexplode_damage;
1021 float g_touchexplode_edgedamage;
1022 float g_touchexplode_force;
1024 void readlevelcvars(void)
1026 g_bugrigs = cvar("g_bugrigs");
1027 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1028 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1029 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1030 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1031 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1032 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1033 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1034 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1035 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1036 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1037 g_bugrigs_accel = cvar("g_bugrigs_accel");
1038 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1039 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1040 g_bugrigs_steer = cvar("g_bugrigs_steer");
1042 g_touchexplode = cvar("g_touchexplode");
1043 g_touchexplode_radius = cvar("g_touchexplode_radius");
1044 g_touchexplode_damage = cvar("g_touchexplode_damage");
1045 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1046 g_touchexplode_force = cvar("g_touchexplode_force");
1048 #ifdef ALLOW_FORCEMODELS
1049 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1051 sv_loddistance1 = cvar("sv_loddistance1");
1052 sv_loddistance2 = cvar("sv_loddistance2");
1053 if(sv_loddistance2 <= sv_loddistance1)
1054 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1055 sv_clones = cvar("sv_clones");
1056 sv_cheats = cvar("sv_cheats");
1057 sv_gentle = cvar("sv_gentle");
1058 sv_foginterval = cvar("sv_foginterval");
1059 g_cloaked = cvar("g_cloaked");
1060 g_jump_grunt = cvar("g_jump_grunt");
1061 g_footsteps = cvar("g_footsteps");
1062 g_grappling_hook = cvar("g_grappling_hook");
1063 g_jetpack = cvar("g_jetpack");
1064 g_laserguided_missile = cvar("g_laserguided_missile");
1065 g_midair = cvar("g_midair");
1066 g_minstagib = cvar("g_minstagib");
1067 g_nixnex = cvar("g_nixnex");
1068 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1069 g_norecoil = cvar("g_norecoil");
1070 g_vampire = cvar("g_vampire");
1071 g_bloodloss = cvar("g_bloodloss");
1072 sv_maxidle = cvar("sv_maxidle");
1073 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1074 sv_pogostick = cvar("sv_pogostick");
1075 sv_doublejump = cvar("sv_doublejump");
1076 g_ctf_reverse = cvar("g_ctf_reverse");
1078 inWarmupStage = cvar("g_warmup");
1079 g_warmup_limit = cvar("g_warmup_limit");
1080 g_warmup_allguns = cvar("g_warmup_allguns");
1081 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1083 if ((g_race && g_race_qualifying == 2) || g_arena || g_assault || cvar("g_campaign"))
1084 inWarmupStage = 0; // these modes cannot work together, sorry
1086 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1087 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1088 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1089 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1090 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1091 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1092 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1093 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1094 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1095 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1096 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1097 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1099 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1100 if (g_nixnex) g_weaponarena = 0;
1103 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1104 g_weaponratefactor = cvar("g_weaponratefactor");
1105 g_weapondamagefactor = cvar("g_weapondamagefactor");
1106 g_weaponforcefactor = cvar("g_weaponforcefactor");
1108 g_pickup_shells = cvar("g_pickup_shells");
1109 g_pickup_shells_max = cvar("g_pickup_shells_max");
1110 g_pickup_nails = cvar("g_pickup_nails");
1111 g_pickup_nails_max = cvar("g_pickup_nails_max");
1112 g_pickup_rockets = cvar("g_pickup_rockets");
1113 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1114 g_pickup_cells = cvar("g_pickup_cells");
1115 g_pickup_cells_max = cvar("g_pickup_cells_max");
1116 g_pickup_fuel = cvar("g_pickup_fuel");
1117 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1118 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1119 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1120 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1121 g_pickup_armormedium = cvar("g_pickup_armormedium");
1122 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1123 g_pickup_armorbig = cvar("g_pickup_armorbig");
1124 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1125 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1126 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1127 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1128 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1129 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1130 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1131 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1132 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1133 g_pickup_healthmega = cvar("g_pickup_healthmega");
1134 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1136 g_pinata = cvar("g_pinata");
1138 g_weapon_stay = cvar("g_weapon_stay");
1139 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1142 if not(inWarmupStage)
1143 game_starttime = cvar("g_start_delay");
1145 readplayerstartcvars();
1149 // TODO sound pack system
1152 string precache_sound_builtin (string s) = #19;
1153 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1154 string precache_sound(string s)
1156 return precache_sound_builtin(strcat(soundpack, s));
1158 void play2(entity e, string filename)
1160 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1162 void sound(entity e, float chan, string samp, float vol, float atten)
1164 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1169 string precache_sound (string s) = #19;
1170 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1171 float precache_sound_index (string s) = #19;
1173 #define SND_VOLUME 1
1174 #define SND_ATTENUATION 2
1175 #define SND_LARGEENTITY 8
1176 #define SND_LARGESOUND 16
1178 float sound_allowed(float dest, entity e)
1180 // sounds from world may always pass
1183 if (e.classname == "body")
1185 if (e.owner && e.owner != e)
1190 // sounds to self may always pass
1191 if (dest == MSG_ONE)
1192 if (e == msg_entity)
1194 // sounds by players can be removed
1195 if (cvar("bot_sound_monopoly"))
1196 if (clienttype(e) == CLIENTTYPE_REAL)
1198 // anything else may pass
1202 void sound(entity e, float chan, string samp, float vol, float atten)
1204 if (!sound_allowed(MSG_BROADCAST, e))
1206 sound_builtin(e, chan, samp, vol, atten);
1208 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1212 if (!sound_allowed(dest, e))
1215 entno = num_for_edict(e);
1216 idx = precache_sound_index(samp);
1221 atten = floor(atten * 64);
1222 vol = floor(vol * 255);
1225 sflags |= SND_VOLUME;
1227 sflags |= SND_ATTENUATION;
1229 sflags |= SND_LARGEENTITY;
1231 sflags |= SND_LARGESOUND;
1233 WriteByte(dest, SVC_SOUND);
1234 WriteByte(dest, sflags);
1235 if (sflags & SND_VOLUME)
1236 WriteByte(dest, vol);
1237 if (sflags & SND_ATTENUATION)
1238 WriteByte(dest, atten);
1239 if (sflags & SND_LARGEENTITY)
1241 WriteShort(dest, entno);
1242 WriteByte(dest, chan);
1246 WriteShort(dest, entno * 8 + chan);
1248 if (sflags & SND_LARGESOUND)
1249 WriteShort(dest, idx);
1251 WriteByte(dest, idx);
1253 WriteCoord(dest, o_x);
1254 WriteCoord(dest, o_y);
1255 WriteCoord(dest, o_z);
1257 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1261 if (!sound_allowed(dest, e))
1264 o = e.origin + 0.5 * (e.mins + e.maxs);
1265 soundtoat(dest, e, o, chan, samp, vol, atten);
1267 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1269 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1271 void stopsoundto(float dest, entity e, float chan)
1275 if (!sound_allowed(dest, e))
1278 entno = num_for_edict(e);
1283 idx = precache_sound_index("misc/null.wav");
1284 sflags = SND_LARGEENTITY;
1286 sflags |= SND_LARGESOUND;
1287 WriteByte(dest, SVC_SOUND);
1288 WriteByte(dest, sflags);
1289 WriteShort(dest, entno);
1290 WriteByte(dest, chan);
1291 if (sflags & SND_LARGESOUND)
1292 WriteShort(dest, idx);
1294 WriteByte(dest, idx);
1295 WriteCoord(dest, e.origin_x);
1296 WriteCoord(dest, e.origin_y);
1297 WriteCoord(dest, e.origin_z);
1301 WriteByte(dest, SVC_STOPSOUND);
1302 WriteShort(dest, entno * 8 + chan);
1305 void stopsound(entity e, float chan)
1307 if (!sound_allowed(MSG_BROADCAST, e))
1310 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1311 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1314 void play2(entity e, string filename)
1316 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1318 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1321 .float announcetime;
1322 float announce(entity player, string msg)
1324 if (time > player.announcetime)
1325 if (clienttype(player) == CLIENTTYPE_REAL)
1327 player.announcetime = time + 0.8;
1333 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1334 float spamsound(entity e, float chan, string samp, float vol, float atten)
1336 if (!sound_allowed(MSG_BROADCAST, e))
1339 if (time > e.announcetime)
1341 e.announcetime = time;
1342 sound(e, chan, samp, vol, atten);
1348 void play2team(float t, string filename)
1352 if (cvar("bot_sound_monopoly"))
1355 FOR_EACH_REALPLAYER(head)
1358 play2(head, filename);
1362 void play2all(string samp)
1364 if (cvar("bot_sound_monopoly"))
1367 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1370 void PrecachePlayerSounds(string f);
1371 void precache_all_models(string pattern)
1373 float globhandle, i, n;
1376 globhandle = search_begin(pattern, TRUE, FALSE);
1379 n = search_getsize(globhandle);
1380 for (i = 0; i < n; ++i)
1382 //print(search_getfilename(globhandle, i), "\n");
1383 f = search_getfilename(globhandle, i);
1386 if(substring(f, -9,5) == "_lod1")
1388 if(substring(f, -9,5) == "_lod2")
1390 if(!sv_loddistance1)
1392 PrecachePlayerSounds(strcat(f, ".sounds"));
1394 search_end(globhandle);
1399 // gamemode related things
1400 precache_model ("models/misc/chatbubble.spr");
1401 precache_model ("models/misc/teambubble.spr");
1404 precache_model ("models/runematch/curse.mdl");
1405 precache_model ("models/runematch/rune.mdl");
1408 #ifdef TTURRETS_ENABLED
1409 if (cvar("g_turrets"))
1413 // Precache all player models if desired
1414 if (cvar("sv_precacheplayermodels"))
1416 PrecachePlayerSounds("sound/player/default.sounds");
1417 precache_all_models("models/player/*.zym");
1418 precache_all_models("models/player/*.dpm");
1419 precache_all_models("models/player/*.md3");
1420 precache_all_models("models/player/*.psk");
1421 //precache_model("models/player/carni.zym");
1422 //precache_model("models/player/crash.zym");
1423 //precache_model("models/player/grunt.zym");
1424 //precache_model("models/player/headhunter.zym");
1425 //precache_model("models/player/insurrectionist.zym");
1426 //precache_model("models/player/jeandarc.zym");
1427 //precache_model("models/player/lurk.zym");
1428 //precache_model("models/player/lycanthrope.zym");
1429 //precache_model("models/player/marine.zym");
1430 //precache_model("models/player/nexus.zym");
1431 //precache_model("models/player/pyria.zym");
1432 //precache_model("models/player/shock.zym");
1433 //precache_model("models/player/skadi.zym");
1434 //precache_model("models/player/specop.zym");
1435 //precache_model("models/player/visitant.zym");
1438 if (cvar("sv_defaultcharacter"))
1441 s = cvar_string("sv_defaultplayermodel_red");
1445 PrecachePlayerSounds(strcat(s, ".sounds"));
1447 s = cvar_string("sv_defaultplayermodel_blue");
1451 PrecachePlayerSounds(strcat(s, ".sounds"));
1453 s = cvar_string("sv_defaultplayermodel_yellow");
1457 PrecachePlayerSounds(strcat(s, ".sounds"));
1459 s = cvar_string("sv_defaultplayermodel_pink");
1463 PrecachePlayerSounds(strcat(s, ".sounds"));
1465 s = cvar_string("sv_defaultplayermodel");
1469 PrecachePlayerSounds(strcat(s, ".sounds"));
1475 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1476 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1479 // gore and miscellaneous sounds
1480 //precache_sound ("misc/h2ohit.wav");
1481 precache_model ("models/hook.md3");
1482 precache_sound ("misc/armorimpact.wav");
1483 precache_sound ("misc/bodyimpact1.wav");
1484 precache_sound ("misc/bodyimpact2.wav");
1485 precache_sound ("misc/gib.wav");
1486 precache_sound ("misc/gib_splat01.wav");
1487 precache_sound ("misc/gib_splat02.wav");
1488 precache_sound ("misc/gib_splat03.wav");
1489 precache_sound ("misc/gib_splat04.wav");
1490 precache_sound ("misc/hit.wav");
1491 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1492 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1493 precache_sound ("misc/null.wav");
1494 precache_sound ("misc/spawn.wav");
1495 precache_sound ("misc/talk.wav");
1496 precache_sound ("misc/teleport.wav");
1497 precache_sound ("misc/poweroff.wav");
1498 precache_sound ("player/lava.wav");
1499 precache_sound ("player/slime.wav");
1502 precache_sound ("misc/jetpack_fly.wav");
1504 // announcer sounds - male
1505 precache_sound ("announcer/male/electrobitch.wav");
1506 precache_sound ("announcer/male/airshot.wav");
1507 precache_sound ("announcer/male/03kills.wav");
1508 precache_sound ("announcer/male/05kills.wav");
1509 precache_sound ("announcer/male/10kills.wav");
1510 precache_sound ("announcer/male/15kills.wav");
1511 precache_sound ("announcer/male/20kills.wav");
1512 precache_sound ("announcer/male/25kills.wav");
1513 precache_sound ("announcer/male/30kills.wav");
1514 precache_sound ("announcer/male/botlike.wav");
1515 precache_sound ("announcer/male/yoda.wav");
1516 precache_sound ("announcer/male/amazing.wav");
1517 precache_sound ("announcer/male/awesome.wav");
1518 precache_sound ("announcer/male/headshot.wav");
1519 precache_sound ("announcer/male/impressive.wav");
1521 // announcer sounds - robotic
1522 precache_sound ("announcer/robotic/prepareforbattle.wav");
1523 precache_sound ("announcer/robotic/begin.wav");
1524 precache_sound ("announcer/robotic/timeoutcalled.wav");
1525 precache_sound ("announcer/robotic/1fragleft.wav");
1526 precache_sound ("announcer/robotic/2fragsleft.wav");
1527 precache_sound ("announcer/robotic/3fragsleft.wav");
1530 precache_sound ("announcer/robotic/lastsecond.wav");
1531 precache_sound ("announcer/robotic/narrowly.wav");
1534 precache_model ("models/sprites/0.spr32");
1535 precache_model ("models/sprites/1.spr32");
1536 precache_model ("models/sprites/2.spr32");
1537 precache_model ("models/sprites/3.spr32");
1538 precache_model ("models/sprites/4.spr32");
1539 precache_model ("models/sprites/5.spr32");
1540 precache_model ("models/sprites/6.spr32");
1541 precache_model ("models/sprites/7.spr32");
1542 precache_model ("models/sprites/8.spr32");
1543 precache_model ("models/sprites/9.spr32");
1544 precache_model ("models/sprites/10.spr32");
1545 precache_sound ("announcer/robotic/1.wav");
1546 precache_sound ("announcer/robotic/2.wav");
1547 precache_sound ("announcer/robotic/3.wav");
1548 precache_sound ("announcer/robotic/4.wav");
1549 precache_sound ("announcer/robotic/5.wav");
1550 precache_sound ("announcer/robotic/6.wav");
1551 precache_sound ("announcer/robotic/7.wav");
1552 precache_sound ("announcer/robotic/8.wav");
1553 precache_sound ("announcer/robotic/9.wav");
1554 precache_sound ("announcer/robotic/10.wav");
1556 // common weapon precaches
1557 precache_sound ("weapons/weapon_switch.wav");
1558 precache_sound ("weapons/weaponpickup.wav");
1559 precache_sound ("weapons/unavailable.wav");
1560 if (g_grappling_hook)
1562 precache_sound ("weapons/hook_fire.wav"); // hook
1563 precache_sound ("weapons/hook_impact.wav"); // hook
1566 if (cvar("sv_precacheweapons") || g_nixnex)
1568 //precache weapon models/sounds
1571 while (wep <= WEP_LAST)
1573 weapon_action(wep, WR_PRECACHE);
1578 precache_model("models/elaser.mdl");
1579 precache_model("models/laser.mdl");
1580 precache_model("models/ebomb.mdl");
1583 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1585 if (!self.noise && self.music) // quake 3 uses the music field
1586 self.noise = self.music;
1588 // plays music for the level if there is any
1591 precache_sound (self.noise);
1592 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1597 // sorry, but using \ in macros breaks line numbers
1598 #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
1599 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1600 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1602 vector ExactTriggerHit_mins;
1603 vector ExactTriggerHit_maxs;
1604 float ExactTriggerHit_Recurse()
1610 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1613 if (trace_ent == self)
1618 se.solid = SOLID_NOT;
1619 f = ExactTriggerHit_Recurse();
1625 float ExactTriggerHit()
1629 if not(self.modelindex)
1633 self.solid = SOLID_BSP;
1634 ExactTriggerHit_mins = other.absmin;
1635 ExactTriggerHit_maxs = other.absmax;
1636 f = ExactTriggerHit_Recurse();
1642 // WARNING: this kills the trace globals
1643 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1644 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1646 #define INITPRIO_FIRST 0
1647 #define INITPRIO_GAMETYPE 0
1648 #define INITPRIO_GAMETYPE_FALLBACK 1
1649 #define INITPRIO_CVARS 5
1650 #define INITPRIO_FINDTARGET 10
1651 #define INITPRIO_DROPTOFLOOR 20
1652 #define INITPRIO_SETLOCATION 90
1653 #define INITPRIO_LINKDOORS 91
1654 #define INITPRIO_LAST 99
1656 .void(void) initialize_entity;
1657 .float initialize_entity_order;
1658 .entity initialize_entity_next;
1659 entity initialize_entity_first;
1661 void make_safe_for_remove(entity e)
1663 if (e.initialize_entity)
1666 for (ent = initialize_entity_first; ent; )
1668 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1670 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1671 // skip it in linked list
1674 prev.initialize_entity_next = ent.initialize_entity_next;
1675 ent = prev.initialize_entity_next;
1679 initialize_entity_first = ent.initialize_entity_next;
1680 ent = initialize_entity_first;
1686 ent = ent.initialize_entity_next;
1692 void objerror(string s)
1694 make_safe_for_remove(self);
1695 objerror_builtin(s);
1698 void remove_unsafely(entity e)
1703 void remove_safely(entity e)
1705 make_safe_for_remove(e);
1709 void InitializeEntity(entity e, void(void) func, float order)
1713 if (!e || e.initialize_entity)
1715 // make a proxy initializer entity
1719 e.classname = "initialize_entity";
1723 e.initialize_entity = func;
1724 e.initialize_entity_order = order;
1726 cur = initialize_entity_first;
1729 if (!cur || cur.initialize_entity_order > order)
1731 // insert between prev and cur
1733 prev.initialize_entity_next = e;
1735 initialize_entity_first = e;
1736 e.initialize_entity_next = cur;
1740 cur = cur.initialize_entity_next;
1743 void InitializeEntitiesRun()
1746 startoflist = initialize_entity_first;
1747 initialize_entity_first = world;
1748 for (self = startoflist; self; )
1751 var void(void) func;
1752 e = self.initialize_entity_next;
1753 func = self.initialize_entity;
1754 self.initialize_entity_order = 0;
1755 self.initialize_entity = func_null;
1756 self.initialize_entity_next = world;
1757 if (self.classname == "initialize_entity")
1761 remove_builtin(self);
1764 //dprint("Delayed initialization: ", self.classname, "\n");
1770 .float uncustomizeentityforclient_set;
1771 .void(void) uncustomizeentityforclient;
1772 void(void) SUB_Nullpointer = #0;
1773 void UncustomizeEntitiesRun()
1777 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1778 self.uncustomizeentityforclient();
1781 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1783 e.customizeentityforclient = customizer;
1784 e.uncustomizeentityforclient = uncustomizer;
1785 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1789 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1792 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1796 if (e.classname == "")
1797 e.classname = "net_linked";
1799 if (e.model == "" || self.modelindex == 0)
1803 setmodel(e, "null");
1807 e.SendEntity = sendfunc;
1808 e.SendFlags = 0xFFFFFF;
1811 e.effects |= EF_NODEPTHTEST;
1815 e.nextthink = time + dt;
1816 e.think = SUB_Remove;
1820 void adaptor_think2touch()
1829 void adaptor_think2use()
1841 // deferred dropping
1842 void DropToFloor_Handler()
1844 droptofloor_builtin();
1845 self.dropped_origin = self.origin;
1850 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1855 float trace_hits_box_a0, trace_hits_box_a1;
1857 float trace_hits_box_1d(float end, float thmi, float thma)
1861 // just check if x is in range
1869 // do the trace with respect to x
1870 // 0 -> end has to stay in thmi -> thma
1871 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1872 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1873 if (trace_hits_box_a0 > trace_hits_box_a1)
1879 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1884 // now it is a trace from 0 to end
1886 trace_hits_box_a0 = 0;
1887 trace_hits_box_a1 = 1;
1889 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1891 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1893 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1899 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1901 return trace_hits_box(start, end, thmi - ma, thma - mi);
1904 float SUB_NoImpactCheck()
1906 // zero hitcontents = this is not the real impact, but either the
1907 // mirror-impact of something hitting the projectile instead of the
1908 // projectile hitting the something, or a touchareagrid one. Neither of
1909 // these stop the projectile from moving, so...
1910 if(trace_dphitcontents == 0)
1912 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1915 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1917 if (other == world && self.size != '0 0 0')
1920 tic = self.velocity * sys_ticrate;
1921 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1922 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1923 if (trace_fraction >= 1)
1925 dprint("Odd... did not hit...?\n");
1927 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1929 dprint("Detected and prevented the sky-grapple bug.\n");
1937 #define SUB_OwnerCheck() (other && (other == self.owner))
1939 #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)
1941 float MAX_IPBAN_URIS = 16;
1943 float URI_GET_DISCARD = 0;
1944 float URI_GET_IPBAN = 1;
1945 float URI_GET_IPBAN_END = 16;
1947 void URI_Get_Callback(float id, float status, string data)
1949 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1951 dprint("\nEnd of data.\n");
1953 if (id == URI_GET_DISCARD)
1957 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1960 OnlineBanList_URI_Get_Callback(id, status, data);
1964 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1968 void print_to(entity e, string s)
1971 sprint(e, strcat(s, "\n"));
1990 for (i = 0; i < MapInfo_count; ++i)
1992 if (MapInfo_Get_ByID(i))
1994 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1997 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1998 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2006 for (i = 0; i < MapInfo_count; ++i)
2008 if (MapInfo_Get_ByID(i))
2010 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
2013 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
2014 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2022 for (i = 0; i < MapInfo_count; ++i)
2024 if (MapInfo_Get_ByID(i))
2026 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/time")));
2029 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/netname"));
2030 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2036 MapInfo_ClearTemps();
2039 return "No records are available on this server.\n";
2041 return strcat("Records on this server:\n", s);
2044 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2047 vector start, org, delta, end, enddown, mstart;
2049 m = e.dphitcontentsmask;
2050 e.dphitcontentsmask = goodcontents | badcontents;
2053 delta = world.maxs - world.mins;
2055 for (i = 0; i < attempts; ++i)
2057 start_x = org_x + random() * delta_x;
2058 start_y = org_y + random() * delta_y;
2059 start_z = org_z + random() * delta_z;
2061 // rule 1: start inside world bounds, and outside
2062 // solid, and don't start from somewhere where you can
2063 // fall down to evil
2064 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2065 if (trace_fraction >= 1)
2067 if (trace_startsolid)
2069 if (trace_dphitcontents & badcontents)
2071 if (trace_dphitq3surfaceflags & badsurfaceflags)
2074 // rule 2: if we are too high, lower the point
2075 if (trace_fraction * delta_z > maxaboveground)
2076 start = trace_endpos + '0 0 1' * maxaboveground;
2077 enddown = trace_endpos;
2079 // rule 3: make sure we aren't outside the map. This only works
2080 // for somewhat well formed maps. A good rule of thumb is that
2081 // the map should have a convex outside hull.
2082 // these can be traceLINES as we already verified the starting box
2083 mstart = start + 0.5 * (e.mins + e.maxs);
2084 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2085 if (trace_fraction >= 1)
2087 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2088 if (trace_fraction >= 1)
2090 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2091 if (trace_fraction >= 1)
2093 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2094 if (trace_fraction >= 1)
2096 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2097 if (trace_fraction >= 1)
2100 // find a random vector to "look at"
2101 end_x = org_x + random() * delta_x;
2102 end_y = org_y + random() * delta_y;
2103 end_z = org_z + random() * delta_z;
2104 end = start + normalize(end - start) * vlen(delta);
2106 // rule 4: start TO end must not be too short
2107 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2108 if (trace_startsolid)
2110 if (trace_fraction < minviewdistance / vlen(delta))
2113 // rule 5: don't want to look at sky
2114 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2117 // rule 6: we must not end up in trigger_hurt
2118 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2120 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2127 e.dphitcontentsmask = m;
2131 setorigin(e, start);
2132 e.angles = vectoangles(end - start);
2133 dprint("Needed ", ftos(i + 1), " attempts\n");
2140 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2142 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2143 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2144 WriteShort(MSG_BROADCAST, effectno);
2145 WriteCoord(MSG_BROADCAST, start_x);
2146 WriteCoord(MSG_BROADCAST, start_y);
2147 WriteCoord(MSG_BROADCAST, start_z);
2148 WriteCoord(MSG_BROADCAST, end_x);
2149 WriteCoord(MSG_BROADCAST, end_y);
2150 WriteCoord(MSG_BROADCAST, end_z);
2151 WriteCoord(MSG_BROADCAST, end_dz);
2152 WriteShort(MSG_BROADCAST, spd / 16);
2155 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2158 vector vecxy, velxy;
2160 vecxy = end - start;
2165 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2167 trailparticles(world, effectno, start, end);
2171 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2172 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2175 string GetGametype(); // g_world.qc
2176 void write_recordmarker(entity pl, float tstart, float dt)
2178 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2180 // also write a marker into demo files for demotc-race-record-extractor to find
2183 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2184 " ", ftos(tstart), " ", ftos(dt), "\n"));
2187 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2192 if (cvar("g_shootfromclient"))
2194 switch(self.owner.cvar_cl_gunalign)
2210 else if (cvar("g_shootfromeye"))
2223 else if (cvar("g_shootfromcenter"))
2228 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2243 void attach_sameorigin(entity e, entity to, string tag)
2245 vector org, t_forward, t_left, t_up, e_forward, e_up;
2252 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2253 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2254 t_forward = v_forward * tagscale;
2255 t_left = v_right * -tagscale;
2256 t_up = v_up * tagscale;
2258 e.origin_x = org * t_forward;
2259 e.origin_y = org * t_left;
2260 e.origin_z = org * t_up;
2262 // current forward and up directions
2263 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2264 e.angles_x = -e.angles_x;
2265 fixedmakevectors(e.angles);
2267 // untransform forward, up!
2268 e_forward_x = v_forward * t_forward;
2269 e_forward_y = v_forward * t_left;
2270 e_forward_z = v_forward * t_up;
2271 e_up_x = v_up * t_forward;
2272 e_up_y = v_up * t_left;
2273 e_up_z = v_up * t_up;
2275 e.angles = fixedvectoangles2(e_forward, e_up);
2276 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2277 e.angles_x = -e.angles_x;
2279 setattachment(e, to, tag);
2280 setorigin(e, e.origin);
2283 void detach_sameorigin(entity e)
2286 org = gettaginfo(e, 0);
2287 e.angles = fixedvectoangles2(v_forward, v_up);
2288 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2289 e.angles_x = -e.angles_x;
2291 setattachment(e, world, "");
2292 setorigin(e, e.origin);
2295 void follow_sameorigin(entity e, entity to)
2297 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2298 e.aiment = to; // make the hole follow bmodel
2299 e.punchangle = to.angles; // the original angles of bmodel
2300 e.view_ofs = e.origin - to.origin; // relative origin
2301 e.v_angle = e.angles - to.angles; // relative angles
2304 void unfollow_sameorigin(entity e)
2306 e.movetype = MOVETYPE_NONE;
2309 entity gettaginfo_relative_ent;
2310 vector gettaginfo_relative(entity e, float tag)
2312 if (!gettaginfo_relative_ent)
2314 gettaginfo_relative_ent = spawn();
2315 gettaginfo_relative_ent.effects = EF_NODRAW;
2317 gettaginfo_relative_ent.model = e.model;
2318 gettaginfo_relative_ent.modelindex = e.modelindex;
2319 gettaginfo_relative_ent.frame = e.frame;
2320 return gettaginfo(gettaginfo_relative_ent, tag);
2323 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2327 if (pl.soundentity.cnt & p)
2329 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2330 pl.soundentity.cnt |= p;
2333 void SoundEntity_StopSound(entity pl, float chan)
2337 if (pl.soundentity.cnt & p)
2339 stopsoundto(MSG_ALL, pl.soundentity, chan);
2340 pl.soundentity.cnt &~= p;
2344 void SoundEntity_Attach(entity pl)
2346 pl.soundentity = spawn();
2347 pl.soundentity.classname = "soundentity";
2348 pl.soundentity.owner = pl;
2349 setattachment(pl.soundentity, pl, "");
2350 setmodel(pl.soundentity, "null");
2353 void SoundEntity_Detach(entity pl)
2356 for (i = 0; i <= 7; ++i)
2357 SoundEntity_StopSound(pl, i);
2361 float ParseCommandPlayerSlotTarget_firsttoken;
2362 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2370 ParseCommandPlayerSlotTarget_firsttoken = -1;
2374 if (substring(argv(idx), 0, 1) == "#")
2376 s = substring(argv(idx), 1, -1);
2384 if (s == ftos(stof(s)))
2386 e = edict_num(stof(s));
2387 if (e.flags & FL_CLIENT)
2389 ParseCommandPlayerSlotTarget_firsttoken = idx;
2396 // it must be a nick name
2401 FOR_EACH_CLIENT(head)
2402 if (head.netname == s)
2409 ParseCommandPlayerSlotTarget_firsttoken = idx;
2413 s = strdecolorize(s);
2415 FOR_EACH_CLIENT(head)
2416 if (strdecolorize(head.netname) == s)
2423 ParseCommandPlayerSlotTarget_firsttoken = idx;