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");
1528 precache_sound ("announcer/robotic/terminated.wav");
1531 precache_sound ("announcer/robotic/lastsecond.wav");
1532 precache_sound ("announcer/robotic/narrowly.wav");
1535 precache_model ("models/sprites/0.spr32");
1536 precache_model ("models/sprites/1.spr32");
1537 precache_model ("models/sprites/2.spr32");
1538 precache_model ("models/sprites/3.spr32");
1539 precache_model ("models/sprites/4.spr32");
1540 precache_model ("models/sprites/5.spr32");
1541 precache_model ("models/sprites/6.spr32");
1542 precache_model ("models/sprites/7.spr32");
1543 precache_model ("models/sprites/8.spr32");
1544 precache_model ("models/sprites/9.spr32");
1545 precache_model ("models/sprites/10.spr32");
1546 precache_sound ("announcer/robotic/1.wav");
1547 precache_sound ("announcer/robotic/2.wav");
1548 precache_sound ("announcer/robotic/3.wav");
1549 precache_sound ("announcer/robotic/4.wav");
1550 precache_sound ("announcer/robotic/5.wav");
1551 precache_sound ("announcer/robotic/6.wav");
1552 precache_sound ("announcer/robotic/7.wav");
1553 precache_sound ("announcer/robotic/8.wav");
1554 precache_sound ("announcer/robotic/9.wav");
1555 precache_sound ("announcer/robotic/10.wav");
1557 // common weapon precaches
1558 precache_sound ("weapons/weapon_switch.wav");
1559 precache_sound ("weapons/weaponpickup.wav");
1560 precache_sound ("weapons/unavailable.wav");
1561 if (g_grappling_hook)
1563 precache_sound ("weapons/hook_fire.wav"); // hook
1564 precache_sound ("weapons/hook_impact.wav"); // hook
1567 if (cvar("sv_precacheweapons") || g_nixnex)
1569 //precache weapon models/sounds
1572 while (wep <= WEP_LAST)
1574 weapon_action(wep, WR_PRECACHE);
1579 precache_model("models/elaser.mdl");
1580 precache_model("models/laser.mdl");
1581 precache_model("models/ebomb.mdl");
1584 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1586 if (!self.noise && self.music) // quake 3 uses the music field
1587 self.noise = self.music;
1589 // plays music for the level if there is any
1592 precache_sound (self.noise);
1593 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1598 // sorry, but using \ in macros breaks line numbers
1599 #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
1600 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1601 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1603 vector ExactTriggerHit_mins;
1604 vector ExactTriggerHit_maxs;
1605 float ExactTriggerHit_Recurse()
1611 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1614 if (trace_ent == self)
1619 se.solid = SOLID_NOT;
1620 f = ExactTriggerHit_Recurse();
1626 float ExactTriggerHit()
1630 if not(self.modelindex)
1634 self.solid = SOLID_BSP;
1635 ExactTriggerHit_mins = other.absmin;
1636 ExactTriggerHit_maxs = other.absmax;
1637 f = ExactTriggerHit_Recurse();
1643 // WARNING: this kills the trace globals
1644 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1645 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1647 #define INITPRIO_FIRST 0
1648 #define INITPRIO_GAMETYPE 0
1649 #define INITPRIO_GAMETYPE_FALLBACK 1
1650 #define INITPRIO_CVARS 5
1651 #define INITPRIO_FINDTARGET 10
1652 #define INITPRIO_DROPTOFLOOR 20
1653 #define INITPRIO_SETLOCATION 90
1654 #define INITPRIO_LINKDOORS 91
1655 #define INITPRIO_LAST 99
1657 .void(void) initialize_entity;
1658 .float initialize_entity_order;
1659 .entity initialize_entity_next;
1660 entity initialize_entity_first;
1662 void make_safe_for_remove(entity e)
1664 if (e.initialize_entity)
1667 for (ent = initialize_entity_first; ent; )
1669 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1671 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1672 // skip it in linked list
1675 prev.initialize_entity_next = ent.initialize_entity_next;
1676 ent = prev.initialize_entity_next;
1680 initialize_entity_first = ent.initialize_entity_next;
1681 ent = initialize_entity_first;
1687 ent = ent.initialize_entity_next;
1693 void objerror(string s)
1695 make_safe_for_remove(self);
1696 objerror_builtin(s);
1699 void remove_unsafely(entity e)
1704 void remove_safely(entity e)
1706 make_safe_for_remove(e);
1710 void InitializeEntity(entity e, void(void) func, float order)
1714 if (!e || e.initialize_entity)
1716 // make a proxy initializer entity
1720 e.classname = "initialize_entity";
1724 e.initialize_entity = func;
1725 e.initialize_entity_order = order;
1727 cur = initialize_entity_first;
1730 if (!cur || cur.initialize_entity_order > order)
1732 // insert between prev and cur
1734 prev.initialize_entity_next = e;
1736 initialize_entity_first = e;
1737 e.initialize_entity_next = cur;
1741 cur = cur.initialize_entity_next;
1744 void InitializeEntitiesRun()
1747 startoflist = initialize_entity_first;
1748 initialize_entity_first = world;
1749 for (self = startoflist; self; )
1752 var void(void) func;
1753 e = self.initialize_entity_next;
1754 func = self.initialize_entity;
1755 self.initialize_entity_order = 0;
1756 self.initialize_entity = func_null;
1757 self.initialize_entity_next = world;
1758 if (self.classname == "initialize_entity")
1762 remove_builtin(self);
1765 //dprint("Delayed initialization: ", self.classname, "\n");
1771 .float uncustomizeentityforclient_set;
1772 .void(void) uncustomizeentityforclient;
1773 void(void) SUB_Nullpointer = #0;
1774 void UncustomizeEntitiesRun()
1778 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1779 self.uncustomizeentityforclient();
1782 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1784 e.customizeentityforclient = customizer;
1785 e.uncustomizeentityforclient = uncustomizer;
1786 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1790 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1793 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1797 if (e.classname == "")
1798 e.classname = "net_linked";
1800 if (e.model == "" || self.modelindex == 0)
1804 setmodel(e, "null");
1808 e.SendEntity = sendfunc;
1809 e.SendFlags = 0xFFFFFF;
1812 e.effects |= EF_NODEPTHTEST;
1816 e.nextthink = time + dt;
1817 e.think = SUB_Remove;
1821 void adaptor_think2touch()
1830 void adaptor_think2use()
1842 // deferred dropping
1843 void DropToFloor_Handler()
1845 droptofloor_builtin();
1846 self.dropped_origin = self.origin;
1851 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1856 float trace_hits_box_a0, trace_hits_box_a1;
1858 float trace_hits_box_1d(float end, float thmi, float thma)
1862 // just check if x is in range
1870 // do the trace with respect to x
1871 // 0 -> end has to stay in thmi -> thma
1872 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1873 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1874 if (trace_hits_box_a0 > trace_hits_box_a1)
1880 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1885 // now it is a trace from 0 to end
1887 trace_hits_box_a0 = 0;
1888 trace_hits_box_a1 = 1;
1890 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1892 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1894 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1900 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1902 return trace_hits_box(start, end, thmi - ma, thma - mi);
1905 float SUB_NoImpactCheck()
1907 // zero hitcontents = this is not the real impact, but either the
1908 // mirror-impact of something hitting the projectile instead of the
1909 // projectile hitting the something, or a touchareagrid one. Neither of
1910 // these stop the projectile from moving, so...
1911 if(trace_dphitcontents == 0)
1913 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1916 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1918 if (other == world && self.size != '0 0 0')
1921 tic = self.velocity * sys_ticrate;
1922 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1923 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1924 if (trace_fraction >= 1)
1926 dprint("Odd... did not hit...?\n");
1928 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1930 dprint("Detected and prevented the sky-grapple bug.\n");
1938 #define SUB_OwnerCheck() (other && (other == self.owner))
1940 #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)
1942 float MAX_IPBAN_URIS = 16;
1944 float URI_GET_DISCARD = 0;
1945 float URI_GET_IPBAN = 1;
1946 float URI_GET_IPBAN_END = 16;
1948 void URI_Get_Callback(float id, float status, string data)
1950 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1952 dprint("\nEnd of data.\n");
1954 if (id == URI_GET_DISCARD)
1958 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1961 OnlineBanList_URI_Get_Callback(id, status, data);
1965 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1969 void print_to(entity e, string s)
1972 sprint(e, strcat(s, "\n"));
1991 for (i = 0; i < MapInfo_count; ++i)
1993 if (MapInfo_Get_ByID(i))
1995 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1998 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1999 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2007 for (i = 0; i < MapInfo_count; ++i)
2009 if (MapInfo_Get_ByID(i))
2011 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
2014 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
2015 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2023 for (i = 0; i < MapInfo_count; ++i)
2025 if (MapInfo_Get_ByID(i))
2027 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/time")));
2030 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/netname"));
2031 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2037 MapInfo_ClearTemps();
2040 return "No records are available on this server.\n";
2042 return strcat("Records on this server:\n", s);
2045 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2048 vector start, org, delta, end, enddown, mstart;
2050 m = e.dphitcontentsmask;
2051 e.dphitcontentsmask = goodcontents | badcontents;
2054 delta = world.maxs - world.mins;
2056 for (i = 0; i < attempts; ++i)
2058 start_x = org_x + random() * delta_x;
2059 start_y = org_y + random() * delta_y;
2060 start_z = org_z + random() * delta_z;
2062 // rule 1: start inside world bounds, and outside
2063 // solid, and don't start from somewhere where you can
2064 // fall down to evil
2065 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2066 if (trace_fraction >= 1)
2068 if (trace_startsolid)
2070 if (trace_dphitcontents & badcontents)
2072 if (trace_dphitq3surfaceflags & badsurfaceflags)
2075 // rule 2: if we are too high, lower the point
2076 if (trace_fraction * delta_z > maxaboveground)
2077 start = trace_endpos + '0 0 1' * maxaboveground;
2078 enddown = trace_endpos;
2080 // rule 3: make sure we aren't outside the map. This only works
2081 // for somewhat well formed maps. A good rule of thumb is that
2082 // the map should have a convex outside hull.
2083 // these can be traceLINES as we already verified the starting box
2084 mstart = start + 0.5 * (e.mins + e.maxs);
2085 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2086 if (trace_fraction >= 1)
2088 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2089 if (trace_fraction >= 1)
2091 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2092 if (trace_fraction >= 1)
2094 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2095 if (trace_fraction >= 1)
2097 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2098 if (trace_fraction >= 1)
2101 // find a random vector to "look at"
2102 end_x = org_x + random() * delta_x;
2103 end_y = org_y + random() * delta_y;
2104 end_z = org_z + random() * delta_z;
2105 end = start + normalize(end - start) * vlen(delta);
2107 // rule 4: start TO end must not be too short
2108 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2109 if (trace_startsolid)
2111 if (trace_fraction < minviewdistance / vlen(delta))
2114 // rule 5: don't want to look at sky
2115 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2118 // rule 6: we must not end up in trigger_hurt
2119 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2121 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2128 e.dphitcontentsmask = m;
2132 setorigin(e, start);
2133 e.angles = vectoangles(end - start);
2134 dprint("Needed ", ftos(i + 1), " attempts\n");
2141 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2143 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2144 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2145 WriteShort(MSG_BROADCAST, effectno);
2146 WriteCoord(MSG_BROADCAST, start_x);
2147 WriteCoord(MSG_BROADCAST, start_y);
2148 WriteCoord(MSG_BROADCAST, start_z);
2149 WriteCoord(MSG_BROADCAST, end_x);
2150 WriteCoord(MSG_BROADCAST, end_y);
2151 WriteCoord(MSG_BROADCAST, end_z);
2152 WriteCoord(MSG_BROADCAST, end_dz);
2153 WriteShort(MSG_BROADCAST, spd / 16);
2156 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2159 vector vecxy, velxy;
2161 vecxy = end - start;
2166 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2168 trailparticles(world, effectno, start, end);
2172 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2173 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2176 string GetGametype(); // g_world.qc
2177 void write_recordmarker(entity pl, float tstart, float dt)
2179 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2181 // also write a marker into demo files for demotc-race-record-extractor to find
2184 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2185 " ", ftos(tstart), " ", ftos(dt), "\n"));
2188 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2190 switch(self.owner.cvar_cl_gunalign)
2201 if(allowcenter) // 2: allow center handedness
2214 if(allowcenter) // 2: allow center handedness
2230 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2235 if (cvar("g_shootfromeye"))
2239 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2247 else if (cvar("g_shootfromcenter"))
2251 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2259 else if (cvar("g_shootfromclient"))
2261 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2263 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2278 void attach_sameorigin(entity e, entity to, string tag)
2280 vector org, t_forward, t_left, t_up, e_forward, e_up;
2287 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2288 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2289 t_forward = v_forward * tagscale;
2290 t_left = v_right * -tagscale;
2291 t_up = v_up * tagscale;
2293 e.origin_x = org * t_forward;
2294 e.origin_y = org * t_left;
2295 e.origin_z = org * t_up;
2297 // current forward and up directions
2298 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2299 e.angles_x = -e.angles_x;
2300 fixedmakevectors(e.angles);
2302 // untransform forward, up!
2303 e_forward_x = v_forward * t_forward;
2304 e_forward_y = v_forward * t_left;
2305 e_forward_z = v_forward * t_up;
2306 e_up_x = v_up * t_forward;
2307 e_up_y = v_up * t_left;
2308 e_up_z = v_up * t_up;
2310 e.angles = fixedvectoangles2(e_forward, e_up);
2311 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2312 e.angles_x = -e.angles_x;
2314 setattachment(e, to, tag);
2315 setorigin(e, e.origin);
2318 void detach_sameorigin(entity e)
2321 org = gettaginfo(e, 0);
2322 e.angles = fixedvectoangles2(v_forward, v_up);
2323 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2324 e.angles_x = -e.angles_x;
2326 setattachment(e, world, "");
2327 setorigin(e, e.origin);
2330 void follow_sameorigin(entity e, entity to)
2332 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2333 e.aiment = to; // make the hole follow bmodel
2334 e.punchangle = to.angles; // the original angles of bmodel
2335 e.view_ofs = e.origin - to.origin; // relative origin
2336 e.v_angle = e.angles - to.angles; // relative angles
2339 void unfollow_sameorigin(entity e)
2341 e.movetype = MOVETYPE_NONE;
2344 entity gettaginfo_relative_ent;
2345 vector gettaginfo_relative(entity e, float tag)
2347 if (!gettaginfo_relative_ent)
2349 gettaginfo_relative_ent = spawn();
2350 gettaginfo_relative_ent.effects = EF_NODRAW;
2352 gettaginfo_relative_ent.model = e.model;
2353 gettaginfo_relative_ent.modelindex = e.modelindex;
2354 gettaginfo_relative_ent.frame = e.frame;
2355 return gettaginfo(gettaginfo_relative_ent, tag);
2358 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2362 if (pl.soundentity.cnt & p)
2364 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2365 pl.soundentity.cnt |= p;
2368 void SoundEntity_StopSound(entity pl, float chan)
2372 if (pl.soundentity.cnt & p)
2374 stopsoundto(MSG_ALL, pl.soundentity, chan);
2375 pl.soundentity.cnt &~= p;
2379 void SoundEntity_Attach(entity pl)
2381 pl.soundentity = spawn();
2382 pl.soundentity.classname = "soundentity";
2383 pl.soundentity.owner = pl;
2384 setattachment(pl.soundentity, pl, "");
2385 setmodel(pl.soundentity, "null");
2388 void SoundEntity_Detach(entity pl)
2391 for (i = 0; i <= 7; ++i)
2392 SoundEntity_StopSound(pl, i);
2396 float ParseCommandPlayerSlotTarget_firsttoken;
2397 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2405 ParseCommandPlayerSlotTarget_firsttoken = -1;
2409 if (substring(argv(idx), 0, 1) == "#")
2411 s = substring(argv(idx), 1, -1);
2419 if (s == ftos(stof(s)))
2421 e = edict_num(stof(s));
2422 if (e.flags & FL_CLIENT)
2424 ParseCommandPlayerSlotTarget_firsttoken = idx;
2431 // it must be a nick name
2436 FOR_EACH_CLIENT(head)
2437 if (head.netname == s)
2444 ParseCommandPlayerSlotTarget_firsttoken = idx;
2448 s = strdecolorize(s);
2450 FOR_EACH_CLIENT(head)
2451 if (strdecolorize(head.netname) == s)
2458 ParseCommandPlayerSlotTarget_firsttoken = idx;