1 var void remove(entity e);
2 void objerror(string s);
4 .vector dropped_origin;
6 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
8 string ColoredTeamName(float t);
10 float DistributeEvenly_amount;
11 float DistributeEvenly_totalweight;
12 void DistributeEvenly_Init(float amount, float totalweight)
14 if (DistributeEvenly_amount)
16 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
17 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
20 DistributeEvenly_amount = 0;
22 DistributeEvenly_amount = amount;
23 DistributeEvenly_totalweight = totalweight;
25 float DistributeEvenly_Get(float weight)
30 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
31 DistributeEvenly_totalweight -= weight;
32 DistributeEvenly_amount -= f;
36 void move_out_of_solid_expand(entity e, vector by)
39 tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
42 if (trace_fraction < 1)
45 // adjust origin in the other direction...
46 e.origin = e.origin - by * (1 - trace_fraction);
50 float move_out_of_solid(entity e)
55 traceline(o, o, MOVE_WORLDONLY, e);
59 tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
60 if (!trace_startsolid)
67 move_out_of_solid_expand(e, '1 0 0' * m0_x);
69 move_out_of_solid_expand(e, '1 0 0' * m1_x);
71 move_out_of_solid_expand(e, '0 1 0' * m0_y);
73 move_out_of_solid_expand(e, '0 1 0' * m1_y);
75 move_out_of_solid_expand(e, '0 0 1' * m0_z);
77 move_out_of_solid_expand(e, '0 0 1' * m1_z);
79 setorigin(e, e.origin);
81 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
91 string STR_PLAYER = "player";
92 string STR_SPECTATOR = "spectator";
93 string STR_OBSERVER = "observer";
96 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
97 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
98 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
99 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
101 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
102 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
103 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
104 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
105 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
108 // copies a string to a tempstring (so one can strunzone it)
109 string strcat1(string s) = #115; // FRIK_FILE
114 void bcenterprint(string s)
116 // TODO replace by MSG_ALL (would show it to spectators too, though)?
118 FOR_EACH_PLAYER(head)
119 if (clienttype(head) == CLIENTTYPE_REAL)
120 centerprint(head, s);
123 void GameLogEcho(string s)
128 if (cvar("sv_eventlog_files"))
133 matches = cvar("sv_eventlog_files_counter") + 1;
134 cvar_set("sv_eventlog_files_counter", ftos(matches));
137 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
138 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
139 logfile = fopen(fn, FILE_APPEND);
140 fputs(logfile, ":logversion:3\n");
144 if (cvar("sv_eventlog_files_timestamps"))
145 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
147 fputs(logfile, strcat(s, "\n"));
150 if (cvar("sv_eventlog_console"))
159 // will be opened later
164 if (logfile_open && logfile >= 0)
171 float spawnpoint_nag;
172 void relocate_spawnpoint()
174 // nudge off the floor
175 setorigin(self, self.origin + '0 0 1');
177 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
178 if (trace_startsolid)
184 if (!move_out_of_solid(self))
185 objerror("could not get out of solid at all!");
186 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
187 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
188 print(" ", ftos(self.origin_y - o_y));
189 print(" ", ftos(self.origin_z - o_z), "'\n");
190 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
193 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
199 self.mins = self.maxs = '0 0 0';
200 objerror("player spawn point in solid, mapper sucks!\n");
205 if (cvar("g_spawnpoints_autodrop"))
207 setsize(self, PL_MIN, PL_MAX);
211 self.use = spawnpoint_use;
212 self.team_saved = self.team;
216 if (g_ctf || g_assault || g_onslaught || g_domination || g_nexball)
218 have_team_spawns = 1;
220 if (cvar("r_showbboxes"))
222 // show where spawnpoints point at too
223 makevectors(self.angles);
226 e.classname = "info_player_foo";
227 setorigin(e, self.origin + v_forward * 24);
228 setsize(e, '-8 -8 -8', '8 8 8');
229 e.solid = SOLID_TRIGGER;
233 #define strstr strstrofs
235 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
236 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
237 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
238 // BE CONSTANT OR strzoneD!
239 float strstr(string haystack, string needle, float offset)
243 len = strlen(needle);
244 endpos = strlen(haystack) - len;
245 while(offset <= endpos)
247 found = substring(haystack, offset, len);
256 float NUM_NEAREST_ENTITIES = 4;
257 entity nearest_entity[NUM_NEAREST_ENTITIES];
258 float nearest_length[NUM_NEAREST_ENTITIES];
259 entity findnearest(vector point, .string field, string value, vector axismod)
270 localhead = find(world, field, value);
273 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
274 dist = localhead.oldorigin;
276 dist = localhead.origin;
278 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
281 for (i = 0; i < num_nearest; ++i)
283 if (len < nearest_length[i])
287 // now i tells us where to insert at
288 // INSERTION SORT! YOU'VE SEEN IT! RUN!
289 if (i < NUM_NEAREST_ENTITIES)
291 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
293 nearest_length[j + 1] = nearest_length[j];
294 nearest_entity[j + 1] = nearest_entity[j];
296 nearest_length[i] = len;
297 nearest_entity[i] = localhead;
298 if (num_nearest < NUM_NEAREST_ENTITIES)
299 num_nearest = num_nearest + 1;
302 localhead = find(localhead, field, value);
305 // now use the first one from our list that we can see
306 for (i = 0; i < num_nearest; ++i)
308 traceline(point, nearest_entity[i].origin, TRUE, world);
309 if (trace_fraction == 1)
313 dprint("Nearest point (");
314 dprint(nearest_entity[0].netname);
315 dprint(") is not visible, using a visible one.\n");
317 return nearest_entity[i];
321 if (num_nearest == 0)
324 dprint("Not seeing any location point, using nearest as fallback.\n");
326 dprint("Candidates were: ");
327 for(j = 0; j < num_nearest; ++j)
331 dprint(nearest_entity[j].netname);
336 return nearest_entity[0];
339 void spawnfunc_target_location()
341 self.classname = "target_location";
342 // location name in netname
343 // eventually support: count, teamgame selectors, line of sight?
346 void spawnfunc_info_location()
348 self.classname = "target_location";
349 self.message = self.netname;
352 string NearestLocation(vector p)
357 loc = findnearest(p, classname, "target_location", '1 1 1');
364 loc = findnearest(p, target, "###item###", '1 1 4');
371 string formatmessage(string msg)
382 break; // too many replacements
384 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
385 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
395 replacement = substring(msg, p, 2);
396 escape = substring(msg, p + 1, 1);
399 else if (escape == "\\")
401 else if (escape == "n")
403 else if (escape == "a")
404 replacement = ftos(floor(self.armorvalue));
405 else if (escape == "h")
406 replacement = ftos(floor(self.health));
407 else if (escape == "l")
408 replacement = NearestLocation(self.origin);
409 else if (escape == "y")
410 replacement = NearestLocation(self.cursor_trace_endpos);
411 else if (escape == "d")
412 replacement = NearestLocation(self.death_origin);
413 else if (escape == "w")
418 wep = self.switchweapon;
421 replacement = W_Name(wep);
423 else if (escape == "W")
425 if (self.items & IT_SHELLS) replacement = "shells";
426 else if (self.items & IT_NAILS) replacement = "bullets";
427 else if (self.items & IT_ROCKETS) replacement = "rockets";
428 else if (self.items & IT_CELLS) replacement = "cells";
429 else replacement = "batteries"; // ;)
431 else if (escape == "x")
433 replacement = self.cursor_trace_ent.netname;
434 if (!replacement || !self.cursor_trace_ent)
435 replacement = "nothing";
437 else if (escape == "p")
439 if (self.last_selected_player)
440 replacement = self.last_selected_player.netname;
442 replacement = "(nobody)";
444 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
445 p = p + strlen(replacement);
456 >0: receives a cvar from name=argv(f) value=argv(f+1)
458 void GetCvars_handleString(string thisname, float f, .string field, string name)
463 strunzone(self.field);
464 self.field = string_null;
468 if (thisname == name)
471 strunzone(self.field);
472 self.field = strzone(argv(f + 1));
476 stuffcmd(self, strcat("sendcvar ", name, "\n"));
478 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
480 GetCvars_handleString(thisname, f, field, name);
481 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
482 if (thisname == name)
485 s = func(strcat1(self.field));
488 strunzone(self.field);
489 self.field = strzone(s);
493 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
500 if (thisname == name)
501 self.field = stof(argv(f + 1));
504 stuffcmd(self, strcat("sendcvar ", name, "\n"));
506 string W_FixWeaponOrder_ForceComplete(string s);
507 string W_FixWeaponOrder_AllowIncomplete(string s);
508 float w_getbestweapon(entity e);
509 void GetCvars(float f)
513 s = strcat1(argv(f));
514 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
515 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
516 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
517 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
518 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
519 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
520 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
521 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
522 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
523 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
524 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
525 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
526 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
527 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
528 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
529 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
530 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
531 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
532 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
533 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
534 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
535 #ifdef ALLOW_FORCEMODELS
536 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
537 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
539 GetCvars_handleFloat(s, f, cvar_cl_gunalign, "cl_gunalign");
542 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
545 if (s == "cl_weaponpriority")
546 self.switchweapon = w_getbestweapon(self);
550 float fexists(string f)
553 fh = fopen(f, FILE_READ);
560 void backtrace(string msg)
563 dev = cvar("developer");
564 cvar_set("developer", "1");
566 dprint("--- CUT HERE ---\nWARNING: ");
569 remove(world); // isn't there any better way to cause a backtrace?
570 dprint("\n--- CUT UNTIL HERE ---\n");
571 cvar_set("developer", ftos(dev));
574 string Team_ColorCode(float teamid)
576 if (teamid == COLOR_TEAM1)
578 else if (teamid == COLOR_TEAM2)
580 else if (teamid == COLOR_TEAM3)
582 else if (teamid == COLOR_TEAM4)
587 string Team_ColorName(float t)
589 // fixme: Search for team entities and get their .netname's!
590 if (t == COLOR_TEAM1)
592 if (t == COLOR_TEAM2)
594 if (t == COLOR_TEAM3)
596 if (t == COLOR_TEAM4)
600 string Team_ColorNameLowerCase(float t)
602 // fixme: Search for team entities and get their .netname's!
603 if (t == COLOR_TEAM1)
605 if (t == COLOR_TEAM2)
607 if (t == COLOR_TEAM3)
609 if (t == COLOR_TEAM4)
614 #define CENTERPRIO_POINT 1
615 #define CENTERPRIO_SPAM 2
616 #define CENTERPRIO_VOTE 4
617 #define CENTERPRIO_NORMAL 5
618 #define CENTERPRIO_SHIELDING 7
619 #define CENTERPRIO_MAPVOTE 9
620 #define CENTERPRIO_IDLEKICK 50
621 #define CENTERPRIO_ADMIN 99
622 .float centerprint_priority;
623 .float centerprint_expires;
624 void centerprint_atprio(entity e, float prio, string s)
626 if (intermission_running)
627 if (prio < CENTERPRIO_MAPVOTE)
629 if (time > e.centerprint_expires)
630 e.centerprint_priority = 0;
631 if (prio >= e.centerprint_priority)
633 e.centerprint_priority = prio;
634 if (timeoutStatus == 2)
635 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
637 e.centerprint_expires = time + e.cvar_scr_centertime;
638 centerprint_builtin(e, s);
641 void centerprint_expire(entity e, float prio)
643 if (prio == e.centerprint_priority)
645 e.centerprint_priority = 0;
646 centerprint_builtin(e, "");
649 void centerprint(entity e, string s)
651 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
654 // decolorizes and team colors the player name when needed
655 string playername(entity p)
658 if (teams_matter && !intermission_running && p.classname == "player")
660 t = Team_ColorCode(p.team);
661 return strcat(t, strdecolorize(p.netname));
667 vector randompos(vector m1, vector m2)
671 v_x = m2_x * random() + m1_x;
672 v_y = m2_y * random() + m1_y;
673 v_z = m2_z * random() + m1_z;
677 float g_pickup_shells;
678 float g_pickup_shells_max;
679 float g_pickup_nails;
680 float g_pickup_nails_max;
681 float g_pickup_rockets;
682 float g_pickup_rockets_max;
683 float g_pickup_cells;
684 float g_pickup_cells_max;
686 float g_pickup_fuel_jetpack;
687 float g_pickup_fuel_max;
688 float g_pickup_armorsmall;
689 float g_pickup_armorsmall_max;
690 float g_pickup_armormedium;
691 float g_pickup_armormedium_max;
692 float g_pickup_armorbig;
693 float g_pickup_armorbig_max;
694 float g_pickup_armorlarge;
695 float g_pickup_armorlarge_max;
696 float g_pickup_healthsmall;
697 float g_pickup_healthsmall_max;
698 float g_pickup_healthmedium;
699 float g_pickup_healthmedium_max;
700 float g_pickup_healthlarge;
701 float g_pickup_healthlarge_max;
702 float g_pickup_healthmega;
703 float g_pickup_healthmega_max;
705 string g_weaponarena_list;
706 float g_weaponspeedfactor;
707 float g_weapondamagefactor;
711 float start_ammo_shells;
712 float start_ammo_nails;
713 float start_ammo_rockets;
714 float start_ammo_cells;
715 float start_ammo_fuel;
717 float start_armorvalue;
718 float warmup_start_weapons;
719 float warmup_start_ammo_shells;
720 float warmup_start_ammo_nails;
721 float warmup_start_ammo_rockets;
722 float warmup_start_ammo_cells;
723 float warmup_start_ammo_fuel;
724 float warmup_start_health;
725 float warmup_start_armorvalue;
728 entity get_weaponinfo(float w);
730 float NixNex_CanChooseWeapon(float wpn);
731 void readplayerstartcvars()
737 // initialize starting values for players
740 start_ammo_shells = 0;
741 start_ammo_nails = 0;
742 start_ammo_rockets = 0;
743 start_ammo_cells = 0;
744 start_health = cvar("g_balance_health_start");
745 start_armorvalue = cvar("g_balance_armor_start");
748 s = cvar_string("g_weaponarena");
754 g_weaponarena_list = "All Weapons";
755 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
757 e = get_weaponinfo(j);
758 g_weaponarena |= e.weapons;
759 weapon_action(e.weapon, WR_PRECACHE);
762 else if (s == "most")
764 g_weaponarena_list = "Most Weapons";
765 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
767 e = get_weaponinfo(j);
768 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
770 g_weaponarena |= e.weapons;
771 weapon_action(e.weapon, WR_PRECACHE);
775 else if (s == "none")
777 g_weaponarena_list = "No Weapons";
778 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
782 t = tokenize_console(s);
783 g_weaponarena_list = "";
784 for (i = 0; i < t; ++i)
787 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
789 e = get_weaponinfo(j);
792 g_weaponarena |= e.weapons;
793 weapon_action(e.weapon, WR_PRECACHE);
794 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
800 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
803 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
809 // will be done later
810 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
811 if (NixNex_CanChooseWeapon(i))
812 weapon_action(i, WR_PRECACHE);
813 if(!cvar("g_use_ammunition"))
814 start_items |= IT_UNLIMITED_AMMO;
816 else if (g_weaponarena)
818 start_weapons = g_weaponarena;
819 start_ammo_rockets = 999;
820 start_ammo_shells = 999;
821 start_ammo_cells = 999;
822 start_ammo_nails = 999;
823 start_ammo_fuel = 999;
824 start_items |= IT_UNLIMITED_AMMO;
826 else if (g_minstagib)
829 start_armorvalue = 0;
830 start_weapons = WEPBIT_MINSTANEX;
831 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
832 start_ammo_cells = cvar("g_minstagib_ammo_start");
833 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
834 start_ammo_fuel = cvar("g_start_ammo_fuel");
836 if (g_minstagib_invis_alpha <= 0)
837 g_minstagib_invis_alpha = -1;
843 start_ammo_shells = cvar("g_lms_start_ammo_shells");
844 start_ammo_nails = cvar("g_lms_start_ammo_nails");
845 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
846 start_ammo_cells = cvar("g_lms_start_ammo_cells");
847 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
848 start_health = cvar("g_lms_start_health");
849 start_armorvalue = cvar("g_lms_start_armor");
851 else if (cvar("g_use_ammunition"))
853 start_ammo_shells = cvar("g_start_ammo_shells");
854 start_ammo_nails = cvar("g_start_ammo_nails");
855 start_ammo_rockets = cvar("g_start_ammo_rockets");
856 start_ammo_cells = cvar("g_start_ammo_cells");
857 start_ammo_fuel = cvar("g_start_ammo_fuel");
861 start_ammo_shells = cvar("g_pickup_shells_max");
862 start_ammo_nails = cvar("g_pickup_nails_max");
863 start_ammo_rockets = cvar("g_pickup_rockets_max");
864 start_ammo_cells = cvar("g_pickup_cells_max");
865 start_ammo_fuel = cvar("g_pickup_fuel_max");
866 start_items |= IT_UNLIMITED_AMMO;
869 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
871 e = get_weaponinfo(i);
875 t = cvar(strcat("g_start_weapon_", e.netname));
877 if (t < 0) // "default" weapon selection
880 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
881 else if (g_race || g_cts)
882 t = (i == WEP_LASER);
884 t = 0; // weapon is set a few lines later
886 t = (i == WEP_LASER || i == WEP_SHOTGUN);
887 if (g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
888 t += (i == WEP_HOOK);
891 if (g_nexball && i == WEP_PORTO)
896 start_weapons |= e.weapons;
897 weapon_action(e.weapon, WR_PRECACHE);
904 warmup_start_ammo_shells = start_ammo_shells;
905 warmup_start_ammo_nails = start_ammo_nails;
906 warmup_start_ammo_rockets = start_ammo_rockets;
907 warmup_start_ammo_cells = start_ammo_cells;
908 warmup_start_health = start_health;
909 warmup_start_armorvalue = start_armorvalue;
910 warmup_start_weapons = start_weapons;
912 if (!g_weaponarena && !g_nixnex && !g_minstagib)
914 if (cvar("g_use_ammunition"))
916 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
917 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
918 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
919 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
921 warmup_start_health = cvar("g_warmup_start_health");
922 warmup_start_armorvalue = cvar("g_warmup_start_armor");
923 if (cvar("g_warmup_allguns"))
925 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
927 e = get_weaponinfo(i);
930 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
932 warmup_start_weapons |= e.weapons;
933 weapon_action(e.weapon, WR_PRECACHE);
940 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
942 g_grappling_hook = 0; // these two can't coexist, as they use the same button
943 start_items |= IT_FUEL_REGEN;
944 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
948 start_items |= IT_JETPACK;
950 if (g_weapon_stay == 2)
952 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
953 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
954 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
955 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
956 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
957 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
958 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
959 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
960 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
961 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
964 start_ammo_shells = max(0, start_ammo_shells);
965 start_ammo_nails = max(0, start_ammo_nails);
966 start_ammo_cells = max(0, start_ammo_cells);
967 start_ammo_rockets = max(0, start_ammo_rockets);
968 start_ammo_fuel = max(0, start_ammo_fuel);
970 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
971 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
972 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
973 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
974 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
978 float g_bugrigs_planar_movement;
979 float g_bugrigs_planar_movement_car_jumping;
980 float g_bugrigs_reverse_spinning;
981 float g_bugrigs_reverse_speeding;
982 float g_bugrigs_reverse_stopping;
983 float g_bugrigs_air_steering;
984 float g_bugrigs_angle_smoothing;
985 float g_bugrigs_friction_floor;
986 float g_bugrigs_friction_brake;
987 float g_bugrigs_friction_air;
988 float g_bugrigs_accel;
989 float g_bugrigs_speed_ref;
990 float g_bugrigs_speed_pow;
991 float g_bugrigs_steer;
993 float g_touchexplode;
994 float g_touchexplode_radius;
995 float g_touchexplode_damage;
996 float g_touchexplode_edgedamage;
997 float g_touchexplode_force;
999 void readlevelcvars(void)
1001 g_bugrigs = cvar("g_bugrigs");
1002 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1003 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1004 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1005 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1006 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1007 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1008 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1009 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1010 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1011 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1012 g_bugrigs_accel = cvar("g_bugrigs_accel");
1013 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1014 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1015 g_bugrigs_steer = cvar("g_bugrigs_steer");
1017 g_touchexplode = cvar("g_touchexplode");
1018 g_touchexplode_radius = cvar("g_touchexplode_radius");
1019 g_touchexplode_damage = cvar("g_touchexplode_damage");
1020 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1021 g_touchexplode_force = cvar("g_touchexplode_force");
1023 #ifdef ALLOW_FORCEMODELS
1024 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1026 sv_loddistance1 = cvar("sv_loddistance1");
1027 sv_loddistance2 = cvar("sv_loddistance2");
1028 if(sv_loddistance2 <= sv_loddistance1)
1029 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1030 sv_clones = cvar("sv_clones");
1031 sv_cheats = cvar("sv_cheats");
1032 sv_gentle = cvar("sv_gentle");
1033 sv_foginterval = cvar("sv_foginterval");
1034 g_cloaked = cvar("g_cloaked");
1035 g_jump_grunt = cvar("g_jump_grunt");
1036 g_footsteps = cvar("g_footsteps");
1037 g_grappling_hook = cvar("g_grappling_hook");
1038 g_jetpack = cvar("g_jetpack");
1039 g_laserguided_missile = cvar("g_laserguided_missile");
1040 g_midair = cvar("g_midair");
1041 g_minstagib = cvar("g_minstagib");
1042 g_nixnex = cvar("g_nixnex");
1043 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1044 g_norecoil = cvar("g_norecoil");
1045 g_vampire = cvar("g_vampire");
1046 g_bloodloss = cvar("g_bloodloss");
1047 sv_maxidle = cvar("sv_maxidle");
1048 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1049 sv_pogostick = cvar("sv_pogostick");
1050 sv_doublejump = cvar("sv_doublejump");
1051 g_ctf_reverse = cvar("g_ctf_reverse");
1053 inWarmupStage = cvar("g_warmup");
1054 g_warmup_limit = cvar("g_warmup_limit");
1055 g_warmup_allguns = cvar("g_warmup_allguns");
1056 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1058 if ((g_race && g_race_qualifying == 2) || g_arena || g_assault || cvar("g_campaign"))
1059 inWarmupStage = 0; // these modes cannot work together, sorry
1061 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1062 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1063 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1064 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1065 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1066 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1067 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1068 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1069 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1070 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1071 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1072 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1074 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1075 if (g_nixnex) g_weaponarena = 0;
1078 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1079 g_weapondamagefactor = cvar("g_weapondamagefactor");
1081 g_pickup_shells = cvar("g_pickup_shells");
1082 g_pickup_shells_max = cvar("g_pickup_shells_max");
1083 g_pickup_nails = cvar("g_pickup_nails");
1084 g_pickup_nails_max = cvar("g_pickup_nails_max");
1085 g_pickup_rockets = cvar("g_pickup_rockets");
1086 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1087 g_pickup_cells = cvar("g_pickup_cells");
1088 g_pickup_cells_max = cvar("g_pickup_cells_max");
1089 g_pickup_fuel = cvar("g_pickup_fuel");
1090 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1091 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1092 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1093 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1094 g_pickup_armormedium = cvar("g_pickup_armormedium");
1095 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1096 g_pickup_armorbig = cvar("g_pickup_armorbig");
1097 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1098 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1099 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1100 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1101 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1102 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1103 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1104 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1105 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1106 g_pickup_healthmega = cvar("g_pickup_healthmega");
1107 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1109 g_pinata = cvar("g_pinata");
1111 g_weapon_stay = cvar("g_weapon_stay");
1112 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1115 if not(inWarmupStage)
1116 game_starttime = cvar("g_start_delay");
1118 readplayerstartcvars();
1122 // TODO sound pack system
1125 string precache_sound_builtin (string s) = #19;
1126 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1127 string precache_sound(string s)
1129 return precache_sound_builtin(strcat(soundpack, s));
1131 void play2(entity e, string filename)
1133 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1135 void sound(entity e, float chan, string samp, float vol, float atten)
1137 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1142 string precache_sound (string s) = #19;
1143 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1144 float precache_sound_index (string s) = #19;
1146 #define SND_VOLUME 1
1147 #define SND_ATTENUATION 2
1148 #define SND_LARGEENTITY 8
1149 #define SND_LARGESOUND 16
1151 float sound_allowed(float dest, entity e)
1153 // sounds from world may always pass
1156 if (e.classname == "body")
1158 if (e.owner && e.owner != e)
1163 // sounds to self may always pass
1164 if (dest == MSG_ONE)
1165 if (e == msg_entity)
1167 // sounds by players can be removed
1168 if (cvar("bot_sound_monopoly"))
1169 if (clienttype(e) == CLIENTTYPE_REAL)
1171 // anything else may pass
1175 void sound(entity e, float chan, string samp, float vol, float atten)
1177 if (!sound_allowed(MSG_BROADCAST, e))
1179 sound_builtin(e, chan, samp, vol, atten);
1181 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1185 if (!sound_allowed(dest, e))
1188 entno = num_for_edict(e);
1189 idx = precache_sound_index(samp);
1194 atten = floor(atten * 64);
1195 vol = floor(vol * 255);
1198 sflags |= SND_VOLUME;
1200 sflags |= SND_ATTENUATION;
1202 sflags |= SND_LARGEENTITY;
1204 sflags |= SND_LARGESOUND;
1206 WriteByte(dest, SVC_SOUND);
1207 WriteByte(dest, sflags);
1208 if (sflags & SND_VOLUME)
1209 WriteByte(dest, vol);
1210 if (sflags & SND_ATTENUATION)
1211 WriteByte(dest, atten);
1212 if (sflags & SND_LARGEENTITY)
1214 WriteShort(dest, entno);
1215 WriteByte(dest, chan);
1219 WriteShort(dest, entno * 8 + chan);
1221 if (sflags & SND_LARGESOUND)
1222 WriteShort(dest, idx);
1224 WriteByte(dest, idx);
1226 WriteCoord(dest, o_x);
1227 WriteCoord(dest, o_y);
1228 WriteCoord(dest, o_z);
1230 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1234 if (!sound_allowed(dest, e))
1237 o = e.origin + 0.5 * (e.mins + e.maxs);
1238 soundtoat(dest, e, o, chan, samp, vol, atten);
1240 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1242 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1244 void stopsoundto(float dest, entity e, float chan)
1248 if (!sound_allowed(dest, e))
1251 entno = num_for_edict(e);
1256 idx = precache_sound_index("misc/null.wav");
1257 sflags = SND_LARGEENTITY;
1259 sflags |= SND_LARGESOUND;
1260 WriteByte(dest, SVC_SOUND);
1261 WriteByte(dest, sflags);
1262 WriteShort(dest, entno);
1263 WriteByte(dest, chan);
1264 if (sflags & SND_LARGESOUND)
1265 WriteShort(dest, idx);
1267 WriteByte(dest, idx);
1268 WriteCoord(dest, e.origin_x);
1269 WriteCoord(dest, e.origin_y);
1270 WriteCoord(dest, e.origin_z);
1274 WriteByte(dest, SVC_STOPSOUND);
1275 WriteShort(dest, entno * 8 + chan);
1278 void stopsound(entity e, float chan)
1280 if (!sound_allowed(MSG_BROADCAST, e))
1283 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1284 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1287 void play2(entity e, string filename)
1289 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1291 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1294 .float announcetime;
1295 float announce(entity player, string msg)
1297 if (time > player.announcetime)
1298 if (clienttype(player) == CLIENTTYPE_REAL)
1300 player.announcetime = time + 0.8;
1306 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1307 float spamsound(entity e, float chan, string samp, float vol, float atten)
1309 if (!sound_allowed(MSG_BROADCAST, e))
1312 if (time > e.announcetime)
1314 e.announcetime = time;
1315 sound(e, chan, samp, vol, atten);
1321 void play2team(float t, string filename)
1325 if (cvar("bot_sound_monopoly"))
1328 FOR_EACH_REALPLAYER(head)
1331 play2(head, filename);
1335 void play2all(string samp)
1337 if (cvar("bot_sound_monopoly"))
1340 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1343 void PrecachePlayerSounds(string f);
1344 void precache_all_models(string pattern)
1346 float globhandle, i, n;
1349 globhandle = search_begin(pattern, TRUE, FALSE);
1352 n = search_getsize(globhandle);
1353 for (i = 0; i < n; ++i)
1355 //print(search_getfilename(globhandle, i), "\n");
1356 f = search_getfilename(globhandle, i);
1359 if(substring(f, -9,5) == "_lod1")
1361 if(substring(f, -9,5) == "_lod2")
1363 if(!sv_loddistance1)
1365 PrecachePlayerSounds(strcat(f, ".sounds"));
1367 search_end(globhandle);
1372 // gamemode related things
1373 precache_model ("models/misc/chatbubble.spr");
1374 precache_model ("models/misc/teambubble.spr");
1377 precache_model ("models/runematch/curse.mdl");
1378 precache_model ("models/runematch/rune.mdl");
1381 #ifdef TTURRETS_ENABLED
1382 if (cvar("g_turrets"))
1386 // Precache all player models if desired
1387 if (cvar("sv_precacheplayermodels"))
1389 PrecachePlayerSounds("sound/player/default.sounds");
1390 precache_all_models("models/player/*.zym");
1391 precache_all_models("models/player/*.dpm");
1392 precache_all_models("models/player/*.md3");
1393 precache_all_models("models/player/*.psk");
1394 //precache_model("models/player/carni.zym");
1395 //precache_model("models/player/crash.zym");
1396 //precache_model("models/player/grunt.zym");
1397 //precache_model("models/player/headhunter.zym");
1398 //precache_model("models/player/insurrectionist.zym");
1399 //precache_model("models/player/jeandarc.zym");
1400 //precache_model("models/player/lurk.zym");
1401 //precache_model("models/player/lycanthrope.zym");
1402 //precache_model("models/player/marine.zym");
1403 //precache_model("models/player/nexus.zym");
1404 //precache_model("models/player/pyria.zym");
1405 //precache_model("models/player/shock.zym");
1406 //precache_model("models/player/skadi.zym");
1407 //precache_model("models/player/specop.zym");
1408 //precache_model("models/player/visitant.zym");
1411 if (cvar("sv_defaultcharacter"))
1414 s = cvar_string("sv_defaultplayermodel_red");
1418 PrecachePlayerSounds(strcat(s, ".sounds"));
1420 s = cvar_string("sv_defaultplayermodel_blue");
1424 PrecachePlayerSounds(strcat(s, ".sounds"));
1426 s = cvar_string("sv_defaultplayermodel_yellow");
1430 PrecachePlayerSounds(strcat(s, ".sounds"));
1432 s = cvar_string("sv_defaultplayermodel_pink");
1436 PrecachePlayerSounds(strcat(s, ".sounds"));
1438 s = cvar_string("sv_defaultplayermodel");
1442 PrecachePlayerSounds(strcat(s, ".sounds"));
1448 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1449 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1452 // gore and miscellaneous sounds
1453 //precache_sound ("misc/h2ohit.wav");
1454 precache_model ("models/hook.md3");
1455 precache_sound ("misc/armorimpact.wav");
1456 precache_sound ("misc/bodyimpact1.wav");
1457 precache_sound ("misc/bodyimpact2.wav");
1458 precache_sound ("misc/gib.wav");
1459 precache_sound ("misc/gib_splat01.wav");
1460 precache_sound ("misc/gib_splat02.wav");
1461 precache_sound ("misc/gib_splat03.wav");
1462 precache_sound ("misc/gib_splat04.wav");
1463 precache_sound ("misc/hit.wav");
1464 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1465 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1466 precache_sound ("misc/null.wav");
1467 precache_sound ("misc/spawn.wav");
1468 precache_sound ("misc/talk.wav");
1469 precache_sound ("misc/teleport.wav");
1470 precache_sound ("misc/poweroff.wav");
1471 precache_sound ("player/lava.wav");
1472 precache_sound ("player/slime.wav");
1475 precache_sound ("misc/jetpack_fly.wav");
1477 // announcer sounds - male
1478 precache_sound ("announcer/male/electrobitch.wav");
1479 precache_sound ("announcer/male/airshot.wav");
1480 precache_sound ("announcer/male/03kills.wav");
1481 precache_sound ("announcer/male/05kills.wav");
1482 precache_sound ("announcer/male/10kills.wav");
1483 precache_sound ("announcer/male/15kills.wav");
1484 precache_sound ("announcer/male/20kills.wav");
1485 precache_sound ("announcer/male/25kills.wav");
1486 precache_sound ("announcer/male/30kills.wav");
1487 precache_sound ("announcer/male/botlike.wav");
1488 precache_sound ("announcer/male/yoda.wav");
1489 precache_sound ("announcer/male/amazing.wav");
1490 precache_sound ("announcer/male/awesome.wav");
1491 precache_sound ("announcer/male/headshot.wav");
1492 precache_sound ("announcer/male/impressive.wav");
1494 // announcer sounds - robotic
1495 precache_sound ("announcer/robotic/prepareforbattle.wav");
1496 precache_sound ("announcer/robotic/begin.wav");
1497 precache_sound ("announcer/robotic/timeoutcalled.wav");
1498 precache_sound ("announcer/robotic/1fragleft.wav");
1499 precache_sound ("announcer/robotic/2fragsleft.wav");
1500 precache_sound ("announcer/robotic/3fragsleft.wav");
1503 precache_sound ("announcer/robotic/lastsecond.wav");
1504 precache_sound ("announcer/robotic/narrowly.wav");
1507 precache_model ("models/sprites/0.spr32");
1508 precache_model ("models/sprites/1.spr32");
1509 precache_model ("models/sprites/2.spr32");
1510 precache_model ("models/sprites/3.spr32");
1511 precache_model ("models/sprites/4.spr32");
1512 precache_model ("models/sprites/5.spr32");
1513 precache_model ("models/sprites/6.spr32");
1514 precache_model ("models/sprites/7.spr32");
1515 precache_model ("models/sprites/8.spr32");
1516 precache_model ("models/sprites/9.spr32");
1517 precache_model ("models/sprites/10.spr32");
1518 precache_sound ("announcer/robotic/1.wav");
1519 precache_sound ("announcer/robotic/2.wav");
1520 precache_sound ("announcer/robotic/3.wav");
1521 precache_sound ("announcer/robotic/4.wav");
1522 precache_sound ("announcer/robotic/5.wav");
1523 precache_sound ("announcer/robotic/6.wav");
1524 precache_sound ("announcer/robotic/7.wav");
1525 precache_sound ("announcer/robotic/8.wav");
1526 precache_sound ("announcer/robotic/9.wav");
1527 precache_sound ("announcer/robotic/10.wav");
1529 // common weapon precaches
1530 precache_sound ("weapons/weapon_switch.wav");
1531 precache_sound ("weapons/weaponpickup.wav");
1532 precache_sound ("weapons/unavailable.wav");
1533 if (g_grappling_hook)
1535 precache_sound ("weapons/hook_fire.wav"); // hook
1536 precache_sound ("weapons/hook_impact.wav"); // hook
1539 if (cvar("sv_precacheweapons") || g_nixnex)
1541 //precache weapon models/sounds
1544 while (wep <= WEP_LAST)
1546 weapon_action(wep, WR_PRECACHE);
1551 precache_model("models/elaser.mdl");
1552 precache_model("models/laser.mdl");
1553 precache_model("models/ebomb.mdl");
1556 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1558 if (!self.noise && self.music) // quake 3 uses the music field
1559 self.noise = self.music;
1561 // plays music for the level if there is any
1564 precache_sound (self.noise);
1565 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1570 // sorry, but using \ in macros breaks line numbers
1571 #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
1572 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1573 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1575 vector ExactTriggerHit_mins;
1576 vector ExactTriggerHit_maxs;
1577 float ExactTriggerHit_Recurse()
1583 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1586 if (trace_ent == self)
1591 se.solid = SOLID_NOT;
1592 f = ExactTriggerHit_Recurse();
1598 float ExactTriggerHit()
1602 if not(self.modelindex)
1606 self.solid = SOLID_BSP;
1607 ExactTriggerHit_mins = other.absmin;
1608 ExactTriggerHit_maxs = other.absmax;
1609 f = ExactTriggerHit_Recurse();
1615 // WARNING: this kills the trace globals
1616 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1617 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1619 #define INITPRIO_FIRST 0
1620 #define INITPRIO_GAMETYPE 0
1621 #define INITPRIO_GAMETYPE_FALLBACK 1
1622 #define INITPRIO_CVARS 5
1623 #define INITPRIO_FINDTARGET 10
1624 #define INITPRIO_DROPTOFLOOR 20
1625 #define INITPRIO_SETLOCATION 90
1626 #define INITPRIO_LINKDOORS 91
1627 #define INITPRIO_LAST 99
1629 .void(void) initialize_entity;
1630 .float initialize_entity_order;
1631 .entity initialize_entity_next;
1632 entity initialize_entity_first;
1634 void make_safe_for_remove(entity e)
1636 if (e.initialize_entity)
1639 for (ent = initialize_entity_first; ent; )
1641 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1643 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1644 // skip it in linked list
1647 prev.initialize_entity_next = ent.initialize_entity_next;
1648 ent = prev.initialize_entity_next;
1652 initialize_entity_first = ent.initialize_entity_next;
1653 ent = initialize_entity_first;
1659 ent = ent.initialize_entity_next;
1665 void objerror(string s)
1667 make_safe_for_remove(self);
1668 objerror_builtin(s);
1671 void remove_unsafely(entity e)
1676 void remove_safely(entity e)
1678 make_safe_for_remove(e);
1682 void InitializeEntity(entity e, void(void) func, float order)
1686 if (!e || e.initialize_entity)
1688 // make a proxy initializer entity
1692 e.classname = "initialize_entity";
1696 e.initialize_entity = func;
1697 e.initialize_entity_order = order;
1699 cur = initialize_entity_first;
1702 if (!cur || cur.initialize_entity_order > order)
1704 // insert between prev and cur
1706 prev.initialize_entity_next = e;
1708 initialize_entity_first = e;
1709 e.initialize_entity_next = cur;
1713 cur = cur.initialize_entity_next;
1716 void InitializeEntitiesRun()
1719 startoflist = initialize_entity_first;
1720 initialize_entity_first = world;
1721 for (self = startoflist; self; )
1724 var void(void) func;
1725 e = self.initialize_entity_next;
1726 func = self.initialize_entity;
1727 self.initialize_entity_order = 0;
1728 self.initialize_entity = func_null;
1729 self.initialize_entity_next = world;
1730 if (self.classname == "initialize_entity")
1734 remove_builtin(self);
1737 //dprint("Delayed initialization: ", self.classname, "\n");
1743 .float uncustomizeentityforclient_set;
1744 .void(void) uncustomizeentityforclient;
1745 void(void) SUB_Nullpointer = #0;
1746 void UncustomizeEntitiesRun()
1750 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1751 self.uncustomizeentityforclient();
1754 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1756 e.customizeentityforclient = customizer;
1757 e.uncustomizeentityforclient = uncustomizer;
1758 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1762 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1765 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1769 if (e.classname == "")
1770 e.classname = "net_linked";
1772 if (e.model == "" || self.modelindex == 0)
1776 setmodel(e, "null");
1780 e.SendEntity = sendfunc;
1781 e.SendFlags = 0xFFFFFF;
1784 e.effects |= EF_NODEPTHTEST;
1788 e.nextthink = time + dt;
1789 e.think = SUB_Remove;
1793 void adaptor_think2touch()
1802 void adaptor_think2use()
1814 // deferred dropping
1815 void DropToFloor_Handler()
1817 droptofloor_builtin();
1818 self.dropped_origin = self.origin;
1823 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1828 float trace_hits_box_a0, trace_hits_box_a1;
1830 float trace_hits_box_1d(float end, float thmi, float thma)
1834 // just check if x is in range
1842 // do the trace with respect to x
1843 // 0 -> end has to stay in thmi -> thma
1844 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1845 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1846 if (trace_hits_box_a0 > trace_hits_box_a1)
1852 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1857 // now it is a trace from 0 to end
1859 trace_hits_box_a0 = 0;
1860 trace_hits_box_a1 = 1;
1862 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1864 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1866 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1872 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1874 return trace_hits_box(start, end, thmi - ma, thma - mi);
1877 float SUB_NoImpactCheck()
1879 // zero hitcontents = this is not the real impact, but either the
1880 // mirror-impact of something hitting the projectile instead of the
1881 // projectile hitting the something, or a touchareagrid one. Neither of
1882 // these stop the projectile from moving, so...
1883 if(trace_dphitcontents == 0)
1885 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1888 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1890 if (other == world && self.size != '0 0 0')
1893 tic = self.velocity * sys_ticrate;
1894 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1895 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1896 if (trace_fraction >= 1)
1898 dprint("Odd... did not hit...?\n");
1900 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1902 dprint("Detected and prevented the sky-grapple bug.\n");
1910 #define SUB_OwnerCheck() (other && (other == self.owner))
1912 #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)
1914 float MAX_IPBAN_URIS = 16;
1916 float URI_GET_DISCARD = 0;
1917 float URI_GET_IPBAN = 1;
1918 float URI_GET_IPBAN_END = 16;
1920 void URI_Get_Callback(float id, float status, string data)
1922 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1924 dprint("\nEnd of data.\n");
1926 if (id == URI_GET_DISCARD)
1930 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1933 OnlineBanList_URI_Get_Callback(id, status, data);
1937 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1941 void print_to(entity e, string s)
1944 sprint(e, strcat(s, "\n"));
1963 for (i = 0; i < MapInfo_count; ++i)
1965 if (MapInfo_Get_ByID(i))
1967 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1970 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1971 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1979 for (i = 0; i < MapInfo_count; ++i)
1981 if (MapInfo_Get_ByID(i))
1983 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
1986 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
1987 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
1995 for (i = 0; i < MapInfo_count; ++i)
1997 if (MapInfo_Get_ByID(i))
1999 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/time")));
2002 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/netname"));
2003 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2009 MapInfo_ClearTemps();
2012 return "No records are available on this server.\n";
2014 return strcat("Records on this server:\n", s);
2017 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2020 vector start, org, delta, end, enddown, mstart;
2022 m = e.dphitcontentsmask;
2023 e.dphitcontentsmask = goodcontents | badcontents;
2026 delta = world.maxs - world.mins;
2028 for (i = 0; i < attempts; ++i)
2030 start_x = org_x + random() * delta_x;
2031 start_y = org_y + random() * delta_y;
2032 start_z = org_z + random() * delta_z;
2034 // rule 1: start inside world bounds, and outside
2035 // solid, and don't start from somewhere where you can
2036 // fall down to evil
2037 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2038 if (trace_fraction >= 1)
2040 if (trace_startsolid)
2042 if (trace_dphitcontents & badcontents)
2044 if (trace_dphitq3surfaceflags & badsurfaceflags)
2047 // rule 2: if we are too high, lower the point
2048 if (trace_fraction * delta_z > maxaboveground)
2049 start = trace_endpos + '0 0 1' * maxaboveground;
2050 enddown = trace_endpos;
2052 // rule 3: make sure we aren't outside the map. This only works
2053 // for somewhat well formed maps. A good rule of thumb is that
2054 // the map should have a convex outside hull.
2055 // these can be traceLINES as we already verified the starting box
2056 mstart = start + 0.5 * (e.mins + e.maxs);
2057 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2058 if (trace_fraction >= 1)
2060 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2061 if (trace_fraction >= 1)
2063 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2064 if (trace_fraction >= 1)
2066 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2067 if (trace_fraction >= 1)
2069 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2070 if (trace_fraction >= 1)
2073 // find a random vector to "look at"
2074 end_x = org_x + random() * delta_x;
2075 end_y = org_y + random() * delta_y;
2076 end_z = org_z + random() * delta_z;
2077 end = start + normalize(end - start) * vlen(delta);
2079 // rule 4: start TO end must not be too short
2080 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2081 if (trace_startsolid)
2083 if (trace_fraction < minviewdistance / vlen(delta))
2086 // rule 5: don't want to look at sky
2087 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2090 // rule 6: we must not end up in trigger_hurt
2091 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2093 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2100 e.dphitcontentsmask = m;
2104 setorigin(e, start);
2105 e.angles = vectoangles(end - start);
2106 dprint("Needed ", ftos(i + 1), " attempts\n");
2113 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2115 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2116 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2117 WriteShort(MSG_BROADCAST, effectno);
2118 WriteCoord(MSG_BROADCAST, start_x);
2119 WriteCoord(MSG_BROADCAST, start_y);
2120 WriteCoord(MSG_BROADCAST, start_z);
2121 WriteCoord(MSG_BROADCAST, end_x);
2122 WriteCoord(MSG_BROADCAST, end_y);
2123 WriteCoord(MSG_BROADCAST, end_z);
2124 WriteCoord(MSG_BROADCAST, end_dz);
2125 WriteShort(MSG_BROADCAST, spd / 16);
2128 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2131 vector vecxy, velxy;
2133 vecxy = end - start;
2138 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2140 trailparticles(world, effectno, start, end);
2144 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2145 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2148 string GetGametype(); // g_world.qc
2149 void write_recordmarker(entity pl, float tstart, float dt)
2151 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2153 // also write a marker into demo files for demotc-race-record-extractor to find
2156 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2157 " ", ftos(tstart), " ", ftos(dt), "\n"));
2160 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2165 if (cvar("g_shootfromclient"))
2167 switch(self.owner.cvar_cl_gunalign)
2183 else if (cvar("g_shootfromeye"))
2196 else if (cvar("g_shootfromcenter"))
2201 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2216 void attach_sameorigin(entity e, entity to, string tag)
2218 vector org, t_forward, t_left, t_up, e_forward, e_up;
2225 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2226 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2227 t_forward = v_forward * tagscale;
2228 t_left = v_right * -tagscale;
2229 t_up = v_up * tagscale;
2231 e.origin_x = org * t_forward;
2232 e.origin_y = org * t_left;
2233 e.origin_z = org * t_up;
2235 // current forward and up directions
2236 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2237 e.angles_x = -e.angles_x;
2238 fixedmakevectors(e.angles);
2240 // untransform forward, up!
2241 e_forward_x = v_forward * t_forward;
2242 e_forward_y = v_forward * t_left;
2243 e_forward_z = v_forward * t_up;
2244 e_up_x = v_up * t_forward;
2245 e_up_y = v_up * t_left;
2246 e_up_z = v_up * t_up;
2248 e.angles = fixedvectoangles2(e_forward, e_up);
2249 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2250 e.angles_x = -e.angles_x;
2252 setattachment(e, to, tag);
2253 setorigin(e, e.origin);
2256 void detach_sameorigin(entity e)
2259 org = gettaginfo(e, 0);
2260 e.angles = fixedvectoangles2(v_forward, v_up);
2261 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2262 e.angles_x = -e.angles_x;
2264 setattachment(e, world, "");
2265 setorigin(e, e.origin);
2268 void follow_sameorigin(entity e, entity to)
2270 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2271 e.aiment = to; // make the hole follow bmodel
2272 e.punchangle = to.angles; // the original angles of bmodel
2273 e.view_ofs = e.origin - to.origin; // relative origin
2274 e.v_angle = e.angles - to.angles; // relative angles
2277 void unfollow_sameorigin(entity e)
2279 e.movetype = MOVETYPE_NONE;
2282 entity gettaginfo_relative_ent;
2283 vector gettaginfo_relative(entity e, float tag)
2285 if (!gettaginfo_relative_ent)
2287 gettaginfo_relative_ent = spawn();
2288 gettaginfo_relative_ent.effects = EF_NODRAW;
2290 gettaginfo_relative_ent.model = e.model;
2291 gettaginfo_relative_ent.modelindex = e.modelindex;
2292 gettaginfo_relative_ent.frame = e.frame;
2293 return gettaginfo(gettaginfo_relative_ent, tag);
2296 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2300 if (pl.soundentity.cnt & p)
2302 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2303 pl.soundentity.cnt |= p;
2306 void SoundEntity_StopSound(entity pl, float chan)
2310 if (pl.soundentity.cnt & p)
2312 stopsoundto(MSG_ALL, pl.soundentity, chan);
2313 pl.soundentity.cnt &~= p;
2317 void SoundEntity_Attach(entity pl)
2319 pl.soundentity = spawn();
2320 pl.soundentity.classname = "soundentity";
2321 pl.soundentity.owner = pl;
2322 setattachment(pl.soundentity, pl, "");
2323 setmodel(pl.soundentity, "null");
2326 void SoundEntity_Detach(entity pl)
2329 for (i = 0; i <= 7; ++i)
2330 SoundEntity_StopSound(pl, i);
2334 float ParseCommandPlayerSlotTarget_firsttoken;
2335 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2343 ParseCommandPlayerSlotTarget_firsttoken = -1;
2347 if (substring(argv(idx), 0, 1) == "#")
2349 s = substring(argv(idx), 1, -1);
2357 if (s == ftos(stof(s)))
2359 e = edict_num(stof(s));
2360 if (e.flags & FL_CLIENT)
2362 ParseCommandPlayerSlotTarget_firsttoken = idx;
2369 // it must be a nick name
2374 FOR_EACH_CLIENT(head)
2375 if (head.netname == s)
2382 ParseCommandPlayerSlotTarget_firsttoken = idx;
2386 s = strdecolorize(s);
2388 FOR_EACH_CLIENT(head)
2389 if (strdecolorize(head.netname) == s)
2396 ParseCommandPlayerSlotTarget_firsttoken = idx;