1 var void remove(entity e);
2 void objerror(string s);
4 .vector dropped_origin;
6 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
7 void crosshair_trace(entity pl)
9 traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
11 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
12 void WarpZone_crosshair_trace(entity pl)
14 WarpZone_traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
17 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
18 void() spawnpoint_use;
20 string ColoredTeamName(float t);
22 string admin_name(void)
24 if(autocvar_sv_adminnick != "")
25 return autocvar_sv_adminnick;
27 return "SERVER ADMIN";
30 float DistributeEvenly_amount;
31 float DistributeEvenly_totalweight;
32 void DistributeEvenly_Init(float amount, float totalweight)
34 if (DistributeEvenly_amount)
36 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
37 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
40 DistributeEvenly_amount = 0;
42 DistributeEvenly_amount = amount;
43 DistributeEvenly_totalweight = totalweight;
45 float DistributeEvenly_Get(float weight)
50 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
51 DistributeEvenly_totalweight -= weight;
52 DistributeEvenly_amount -= f;
56 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
59 string STR_PLAYER = "player";
60 string STR_SPECTATOR = "spectator";
61 string STR_OBSERVER = "observer";
64 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
65 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
66 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
67 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
69 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
70 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
71 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
72 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
73 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
76 // copies a string to a tempstring (so one can strunzone it)
77 string strcat1(string s) = #115; // FRIK_FILE
82 void bcenterprint(string s)
84 // TODO replace by MSG_ALL (would show it to spectators too, though)?
87 if (clienttype(head) == CLIENTTYPE_REAL)
91 void GameLogEcho(string s)
96 if (autocvar_sv_eventlog_files)
101 matches = autocvar_sv_eventlog_files_counter + 1;
102 cvar_set("sv_eventlog_files_counter", ftos(matches));
105 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
106 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
107 logfile = fopen(fn, FILE_APPEND);
108 fputs(logfile, ":logversion:3\n");
112 if (autocvar_sv_eventlog_files_timestamps)
113 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
115 fputs(logfile, strcat(s, "\n"));
118 if (autocvar_sv_eventlog_console)
127 // will be opened later
132 if (logfile_open && logfile >= 0)
143 vector PL_CROUCH_VIEW_OFS;
144 vector PL_CROUCH_MIN;
145 vector PL_CROUCH_MAX;
147 float spawnpoint_nag;
148 void relocate_spawnpoint()
150 PL_VIEW_OFS = stov(autocvar_sv_player_viewoffset);
151 PL_MIN = stov(autocvar_sv_player_mins);
152 PL_MAX = stov(autocvar_sv_player_maxs);
153 PL_HEAD = stov(autocvar_sv_player_headsize);
154 PL_CROUCH_VIEW_OFS = stov(autocvar_sv_player_crouch_viewoffset);
155 PL_CROUCH_MIN = stov(autocvar_sv_player_crouch_mins);
156 PL_CROUCH_MAX = stov(autocvar_sv_player_crouch_maxs);
158 // nudge off the floor
159 setorigin(self, self.origin + '0 0 1');
161 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
162 if (trace_startsolid)
168 if (!move_out_of_solid(self))
169 objerror("could not get out of solid at all!");
170 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
171 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
172 print(" ", ftos(self.origin_y - o_y));
173 print(" ", ftos(self.origin_z - o_z), "'\n");
174 if (autocvar_g_spawnpoints_auto_move_out_of_solid)
177 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
183 self.mins = self.maxs = '0 0 0';
184 objerror("player spawn point in solid, mapper sucks!\n");
189 self.use = spawnpoint_use;
190 self.team_saved = self.team;
194 if (have_team_spawns != 0)
196 have_team_spawns = 1;
197 have_team_spawns_forteam[self.team] = 1;
199 if (autocvar_r_showbboxes)
201 // show where spawnpoints point at too
202 makevectors(self.angles);
205 e.classname = "info_player_foo";
206 setorigin(e, self.origin + v_forward * 24);
207 setsize(e, '-8 -8 -8', '8 8 8');
208 e.solid = SOLID_TRIGGER;
212 #define strstr strstrofs
214 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
215 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
216 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
217 // BE CONSTANT OR strzoneD!
218 float strstr(string haystack, string needle, float offset)
222 len = strlen(needle);
223 endpos = strlen(haystack) - len;
224 while(offset <= endpos)
226 found = substring(haystack, offset, len);
235 float NUM_NEAREST_ENTITIES = 4;
236 entity nearest_entity[NUM_NEAREST_ENTITIES];
237 float nearest_length[NUM_NEAREST_ENTITIES];
238 entity findnearest(vector point, .string field, string value, vector axismod)
249 localhead = find(world, field, value);
252 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
253 dist = localhead.oldorigin;
255 dist = localhead.origin;
257 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
260 for (i = 0; i < num_nearest; ++i)
262 if (len < nearest_length[i])
266 // now i tells us where to insert at
267 // INSERTION SORT! YOU'VE SEEN IT! RUN!
268 if (i < NUM_NEAREST_ENTITIES)
270 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
272 nearest_length[j + 1] = nearest_length[j];
273 nearest_entity[j + 1] = nearest_entity[j];
275 nearest_length[i] = len;
276 nearest_entity[i] = localhead;
277 if (num_nearest < NUM_NEAREST_ENTITIES)
278 num_nearest = num_nearest + 1;
281 localhead = find(localhead, field, value);
284 // now use the first one from our list that we can see
285 for (i = 0; i < num_nearest; ++i)
287 traceline(point, nearest_entity[i].origin, TRUE, world);
288 if (trace_fraction == 1)
292 dprint("Nearest point (");
293 dprint(nearest_entity[0].netname);
294 dprint(") is not visible, using a visible one.\n");
296 return nearest_entity[i];
300 if (num_nearest == 0)
303 dprint("Not seeing any location point, using nearest as fallback.\n");
305 dprint("Candidates were: ");
306 for(j = 0; j < num_nearest; ++j)
310 dprint(nearest_entity[j].netname);
315 return nearest_entity[0];
318 void spawnfunc_target_location()
320 self.classname = "target_location";
321 // location name in netname
322 // eventually support: count, teamgame selectors, line of sight?
325 void spawnfunc_info_location()
327 self.classname = "target_location";
328 self.message = self.netname;
331 string NearestLocation(vector p)
336 loc = findnearest(p, classname, "target_location", '1 1 1');
343 loc = findnearest(p, target, "###item###", '1 1 4');
350 string formatmessage(string msg)
361 WarpZone_crosshair_trace(self);
362 cursor = trace_endpos;
363 cursor_ent = trace_ent;
367 break; // too many replacements
370 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
371 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
384 replacement = substring(msg, p, 2);
385 escape = substring(msg, p + 1, 1);
389 else if (escape == "\\")
391 else if (escape == "n")
393 else if (escape == "a")
394 replacement = ftos(floor(self.armorvalue));
395 else if (escape == "h")
396 replacement = ftos(floor(self.health));
397 else if (escape == "l")
398 replacement = NearestLocation(self.origin);
399 else if (escape == "y")
400 replacement = NearestLocation(cursor);
401 else if (escape == "d")
402 replacement = NearestLocation(self.death_origin);
403 else if (escape == "w") {
407 wep = self.switchweapon;
410 replacement = W_Name(wep);
411 } else if (escape == "W") {
412 if (self.items & IT_SHELLS) replacement = "shells";
413 else if (self.items & IT_NAILS) replacement = "bullets";
414 else if (self.items & IT_ROCKETS) replacement = "rockets";
415 else if (self.items & IT_CELLS) replacement = "cells";
416 else replacement = "batteries"; // ;)
417 } else if (escape == "x") {
418 replacement = cursor_ent.netname;
419 if (!replacement || !cursor_ent)
420 replacement = "nothing";
421 } else if (escape == "s")
422 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
423 else if (escape == "S")
424 replacement = ftos(vlen(self.velocity));
426 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
427 p = p + strlen(replacement);
432 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
433 return (value == 0) ? FALSE : TRUE;
442 >0: receives a cvar from name=argv(f) value=argv(f+1)
444 void GetCvars_handleString(string thisname, float f, .string field, string name)
449 strunzone(self.field);
450 self.field = string_null;
454 if (thisname == name)
457 strunzone(self.field);
458 self.field = strzone(argv(f + 1));
462 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
464 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
466 GetCvars_handleString(thisname, f, field, name);
467 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
468 if (thisname == name)
471 s = func(strcat1(self.field));
474 strunzone(self.field);
475 self.field = strzone(s);
479 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
486 if (thisname == name)
487 self.field = stof(argv(f + 1));
490 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
492 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
499 if (thisname == name)
503 self.field = stof(argv(f + 1));
512 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
515 float w_getbestweapon(entity e);
516 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
519 o = W_FixWeaponOrder_ForceComplete(wo);
520 if(self.weaponorder_byimpulse)
522 strunzone(self.weaponorder_byimpulse);
523 self.weaponorder_byimpulse = string_null;
525 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
528 void GetCvars(float f)
533 s = strcat1(argv(f));
537 MUTATOR_CALLHOOK(GetCvars);
538 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
539 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
540 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
541 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
542 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
543 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
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_weaponimpulsemode, "cl_weaponimpulsemode");
555 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
556 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
557 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
558 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
559 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
560 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
562 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
563 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
565 #ifdef ALLOW_FORCEMODELS
566 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
567 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
569 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
570 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
571 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
572 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
573 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
575 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
578 if (s == "cl_weaponpriority")
579 self.switchweapon = w_getbestweapon(self);
580 if (s == "cl_allow_uidtracking")
581 PlayerStats_AddPlayer(self);
585 void backtrace(string msg)
588 dev = autocvar_developer;
589 war = autocvar_prvm_backtraceforwarnings;
590 cvar_set("developer", "1");
591 cvar_set("prvm_backtraceforwarnings", "1");
593 print("--- CUT HERE ---\nWARNING: ");
596 remove(world); // isn't there any better way to cause a backtrace?
597 print("\n--- CUT UNTIL HERE ---\n");
598 cvar_set("developer", ftos(dev));
599 cvar_set("prvm_backtraceforwarnings", ftos(war));
602 string Team_ColorCode(float teamid)
604 if (teamid == COLOR_TEAM1)
606 else if (teamid == COLOR_TEAM2)
608 else if (teamid == COLOR_TEAM3)
610 else if (teamid == COLOR_TEAM4)
616 string Team_ColorName(float t)
618 // fixme: Search for team entities and get their .netname's!
619 if (t == COLOR_TEAM1)
621 if (t == COLOR_TEAM2)
623 if (t == COLOR_TEAM3)
625 if (t == COLOR_TEAM4)
630 string Team_ColorNameLowerCase(float t)
632 // fixme: Search for team entities and get their .netname's!
633 if (t == COLOR_TEAM1)
635 if (t == COLOR_TEAM2)
637 if (t == COLOR_TEAM3)
639 if (t == COLOR_TEAM4)
644 float ColourToNumber(string team_colour)
646 if (team_colour == "red")
649 if (team_colour == "blue")
652 if (team_colour == "yellow")
655 if (team_colour == "pink")
658 if (team_colour == "auto")
664 float NumberToTeamNumber(float number)
681 // decolorizes and team colors the player name when needed
682 string playername(entity p)
685 if (teamplay && !intermission_running && p.classname == "player")
687 t = Team_ColorCode(p.team);
688 return strcat(t, strdecolorize(p.netname));
694 vector randompos(vector m1, vector m2)
698 v_x = m2_x * random() + m1_x;
699 v_y = m2_y * random() + m1_y;
700 v_z = m2_z * random() + m1_z;
704 //#NO AUTOCVARS START
706 float g_pickup_shells;
707 float g_pickup_shells_max;
708 float g_pickup_nails;
709 float g_pickup_nails_max;
710 float g_pickup_rockets;
711 float g_pickup_rockets_max;
712 float g_pickup_cells;
713 float g_pickup_cells_max;
715 float g_pickup_fuel_jetpack;
716 float g_pickup_fuel_max;
717 float g_pickup_armorsmall;
718 float g_pickup_armorsmall_max;
719 float g_pickup_armorsmall_anyway;
720 float g_pickup_armormedium;
721 float g_pickup_armormedium_max;
722 float g_pickup_armormedium_anyway;
723 float g_pickup_armorbig;
724 float g_pickup_armorbig_max;
725 float g_pickup_armorbig_anyway;
726 float g_pickup_armorlarge;
727 float g_pickup_armorlarge_max;
728 float g_pickup_armorlarge_anyway;
729 float g_pickup_healthsmall;
730 float g_pickup_healthsmall_max;
731 float g_pickup_healthsmall_anyway;
732 float g_pickup_healthmedium;
733 float g_pickup_healthmedium_max;
734 float g_pickup_healthmedium_anyway;
735 float g_pickup_healthlarge;
736 float g_pickup_healthlarge_max;
737 float g_pickup_healthlarge_anyway;
738 float g_pickup_healthmega;
739 float g_pickup_healthmega_max;
740 float g_pickup_healthmega_anyway;
741 float g_pickup_ammo_anyway;
742 float g_pickup_weapons_anyway;
744 float g_weaponarena_random;
745 float g_weaponarena_random_with_laser;
746 string g_weaponarena_list;
747 float g_weaponspeedfactor;
748 float g_weaponratefactor;
749 float g_weapondamagefactor;
750 float g_weaponforcefactor;
751 float g_weaponspreadfactor;
755 float start_ammo_shells;
756 float start_ammo_nails;
757 float start_ammo_rockets;
758 float start_ammo_cells;
759 float start_ammo_fuel;
761 float start_armorvalue;
762 float warmup_start_weapons;
763 float warmup_start_ammo_shells;
764 float warmup_start_ammo_nails;
765 float warmup_start_ammo_rockets;
766 float warmup_start_ammo_cells;
767 float warmup_start_ammo_fuel;
768 float warmup_start_health;
769 float warmup_start_armorvalue;
773 entity get_weaponinfo(float w);
775 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
777 var float i = weaponinfo.weapon;
782 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
784 if (t < 0) // "default" weapon selection
786 if (g_lms || g_ca || allguns)
787 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
791 t = (i == WEP_SHOTGUN);
793 t = 0; // weapon is set a few lines later
795 t = (i == WEP_LASER || i == WEP_SHOTGUN);
796 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
797 t |= (i == WEP_HOOK);
800 // we cannot disable porto in Nexball, we must force it
801 if(g_nexball && i == WEP_PORTO)
807 void readplayerstartcvars()
813 // initialize starting values for players
816 start_ammo_shells = 0;
817 start_ammo_nails = 0;
818 start_ammo_rockets = 0;
819 start_ammo_cells = 0;
820 start_health = cvar("g_balance_health_start");
821 start_armorvalue = cvar("g_balance_armor_start");
824 s = cvar_string("g_weaponarena");
825 if (s == "0" || s == "")
831 if (s == "0" || s == "")
837 // forcibly turn off weaponarena
841 g_weaponarena_list = "All Weapons";
842 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
844 e = get_weaponinfo(j);
845 g_weaponarena |= e.weapons;
846 weapon_action(e.weapon, WR_PRECACHE);
849 else if (s == "most")
851 g_weaponarena_list = "Most Weapons";
852 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
854 e = get_weaponinfo(j);
855 if (e.spawnflags & WEP_FLAG_NORMAL)
857 g_weaponarena |= e.weapons;
858 weapon_action(e.weapon, WR_PRECACHE);
862 else if (s == "none")
864 g_weaponarena_list = "No Weapons";
865 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
869 t = tokenize_console(s);
870 g_weaponarena_list = "";
871 for (i = 0; i < t; ++i)
874 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
876 e = get_weaponinfo(j);
879 g_weaponarena |= e.weapons;
880 weapon_action(e.weapon, WR_PRECACHE);
881 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
887 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
890 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
894 g_weaponarena_random = cvar("g_weaponarena_random");
896 g_weaponarena_random = 0;
897 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
901 start_weapons = g_weaponarena;
903 start_items |= IT_UNLIMITED_AMMO;
905 else if (g_minstagib)
908 start_armorvalue = 0;
909 start_weapons = WEPBIT_MINSTANEX;
910 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
911 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
913 if (g_minstagib_invis_alpha <= 0)
914 g_minstagib_invis_alpha = -1;
918 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
920 e = get_weaponinfo(i);
921 if(want_weapon("g_start_weapon_", e, FALSE))
922 start_weapons |= e.weapons;
926 if(!cvar("g_use_ammunition"))
927 start_items |= IT_UNLIMITED_AMMO;
931 start_ammo_cells = cvar("g_minstagib_ammo_start");
932 start_ammo_fuel = cvar("g_start_ammo_fuel");
934 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
936 start_ammo_rockets = 999;
937 start_ammo_shells = 999;
938 start_ammo_cells = 999;
939 start_ammo_nails = 999;
940 start_ammo_fuel = 999;
946 start_ammo_shells = cvar("g_lms_start_ammo_shells");
947 start_ammo_nails = cvar("g_lms_start_ammo_nails");
948 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
949 start_ammo_cells = cvar("g_lms_start_ammo_cells");
950 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
954 start_ammo_shells = cvar("g_start_ammo_shells");
955 start_ammo_nails = cvar("g_start_ammo_nails");
956 start_ammo_rockets = cvar("g_start_ammo_rockets");
957 start_ammo_cells = cvar("g_start_ammo_cells");
958 start_ammo_fuel = cvar("g_start_ammo_fuel");
964 start_health = cvar("g_lms_start_health");
965 start_armorvalue = cvar("g_lms_start_armor");
970 warmup_start_ammo_shells = start_ammo_shells;
971 warmup_start_ammo_nails = start_ammo_nails;
972 warmup_start_ammo_rockets = start_ammo_rockets;
973 warmup_start_ammo_cells = start_ammo_cells;
974 warmup_start_ammo_fuel = start_ammo_fuel;
975 warmup_start_health = start_health;
976 warmup_start_armorvalue = start_armorvalue;
977 warmup_start_weapons = start_weapons;
979 if (!g_weaponarena && !g_minstagib && !g_ca)
981 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
982 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
983 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
984 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
985 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
986 warmup_start_health = cvar("g_warmup_start_health");
987 warmup_start_armorvalue = cvar("g_warmup_start_armor");
988 warmup_start_weapons = 0;
989 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
991 e = get_weaponinfo(i);
992 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
993 warmup_start_weapons |= e.weapons;
998 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1000 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1001 start_items |= IT_FUEL_REGEN;
1002 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1003 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1007 start_items |= IT_JETPACK;
1009 if (g_weapon_stay == 2)
1011 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1012 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1013 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1014 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1015 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1016 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1017 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1018 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1019 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1020 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1023 MUTATOR_CALLHOOK(SetStartItems);
1025 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1027 e = get_weaponinfo(i);
1028 if(e.weapons & (start_weapons | warmup_start_weapons))
1029 weapon_action(e.weapon, WR_PRECACHE);
1032 start_ammo_shells = max(0, start_ammo_shells);
1033 start_ammo_nails = max(0, start_ammo_nails);
1034 start_ammo_cells = max(0, start_ammo_cells);
1035 start_ammo_rockets = max(0, start_ammo_rockets);
1036 start_ammo_fuel = max(0, start_ammo_fuel);
1038 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1039 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1040 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1041 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1042 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1046 float g_bugrigs_planar_movement;
1047 float g_bugrigs_planar_movement_car_jumping;
1048 float g_bugrigs_reverse_spinning;
1049 float g_bugrigs_reverse_speeding;
1050 float g_bugrigs_reverse_stopping;
1051 float g_bugrigs_air_steering;
1052 float g_bugrigs_angle_smoothing;
1053 float g_bugrigs_friction_floor;
1054 float g_bugrigs_friction_brake;
1055 float g_bugrigs_friction_air;
1056 float g_bugrigs_accel;
1057 float g_bugrigs_speed_ref;
1058 float g_bugrigs_speed_pow;
1059 float g_bugrigs_steer;
1061 float g_touchexplode;
1062 float g_touchexplode_radius;
1063 float g_touchexplode_damage;
1064 float g_touchexplode_edgedamage;
1065 float g_touchexplode_force;
1072 float sv_pitch_fixyaw;
1074 string GetGametype(); // g_world.qc
1075 void readlevelcvars(void)
1077 // first load all the mutators
1079 MUTATOR_ADD(mutator_nix);
1080 if(cvar("g_dodging"))
1081 MUTATOR_ADD(mutator_dodging);
1082 if(cvar("g_rocket_flying"))
1083 MUTATOR_ADD(mutator_rocketflying);
1084 if(cvar("g_vampire"))
1085 MUTATOR_ADD(mutator_vampire);
1087 if(cvar("sv_allow_fullbright"))
1088 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1090 g_bugrigs = cvar("g_bugrigs");
1091 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1092 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1093 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1094 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1095 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1096 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1097 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1098 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1099 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1100 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1101 g_bugrigs_accel = cvar("g_bugrigs_accel");
1102 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1103 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1104 g_bugrigs_steer = cvar("g_bugrigs_steer");
1106 g_touchexplode = cvar("g_touchexplode");
1107 g_touchexplode_radius = cvar("g_touchexplode_radius");
1108 g_touchexplode_damage = cvar("g_touchexplode_damage");
1109 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1110 g_touchexplode_force = cvar("g_touchexplode_force");
1112 #ifdef ALLOW_FORCEMODELS
1113 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1115 sv_loddistance1 = cvar("sv_loddistance1");
1116 sv_loddistance2 = cvar("sv_loddistance2");
1118 if(sv_loddistance2 <= sv_loddistance1)
1119 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1121 sv_clones = cvar("sv_clones");
1122 sv_gentle = cvar("sv_gentle");
1123 sv_foginterval = cvar("sv_foginterval");
1124 g_cloaked = cvar("g_cloaked");
1126 g_cloaked = 1; // always enable cloak in CTS
1127 g_jump_grunt = cvar("g_jump_grunt");
1128 g_footsteps = cvar("g_footsteps");
1129 g_grappling_hook = cvar("g_grappling_hook");
1130 g_jetpack = cvar("g_jetpack");
1131 g_midair = cvar("g_midair");
1132 g_minstagib = cvar("g_minstagib");
1133 g_norecoil = cvar("g_norecoil");
1134 g_bloodloss = cvar("g_bloodloss");
1135 sv_maxidle = cvar("sv_maxidle");
1136 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1137 g_ctf_reverse = cvar("g_ctf_reverse");
1138 sv_autotaunt = cvar("sv_autotaunt");
1139 sv_taunt = cvar("sv_taunt");
1141 inWarmupStage = cvar("g_warmup");
1142 g_warmup_limit = cvar("g_warmup_limit");
1143 g_warmup_allguns = cvar("g_warmup_allguns");
1144 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1146 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1147 inWarmupStage = 0; // these modes cannot work together, sorry
1149 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1150 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1151 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1152 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1153 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1154 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1155 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1156 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1157 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1158 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1159 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1160 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1162 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1163 g_weaponratefactor = cvar("g_weaponratefactor");
1164 g_weapondamagefactor = cvar("g_weapondamagefactor");
1165 g_weaponforcefactor = cvar("g_weaponforcefactor");
1166 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1168 g_pickup_shells = cvar("g_pickup_shells");
1169 g_pickup_shells_max = cvar("g_pickup_shells_max");
1170 g_pickup_nails = cvar("g_pickup_nails");
1171 g_pickup_nails_max = cvar("g_pickup_nails_max");
1172 g_pickup_rockets = cvar("g_pickup_rockets");
1173 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1174 g_pickup_cells = cvar("g_pickup_cells");
1175 g_pickup_cells_max = cvar("g_pickup_cells_max");
1176 g_pickup_fuel = cvar("g_pickup_fuel");
1177 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1178 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1179 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1180 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1181 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1182 g_pickup_armormedium = cvar("g_pickup_armormedium");
1183 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1184 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1185 g_pickup_armorbig = cvar("g_pickup_armorbig");
1186 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1187 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1188 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1189 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1190 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1191 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1192 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1193 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1194 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1195 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1196 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1197 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1198 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1199 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1200 g_pickup_healthmega = cvar("g_pickup_healthmega");
1201 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1202 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1204 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1205 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1207 g_pinata = cvar("g_pinata");
1209 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1211 g_weapon_stay = cvar("g_weapon_stay");
1213 g_ghost_items = cvar("g_ghost_items");
1215 if(g_ghost_items >= 1)
1216 g_ghost_items = 0.25; // default alpha value
1218 if not(inWarmupStage && !g_ca)
1219 game_starttime = cvar("g_start_delay");
1221 sv_pitch_min = cvar("sv_pitch_min");
1222 sv_pitch_max = cvar("sv_pitch_max");
1223 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1225 readplayerstartcvars();
1231 string precache_sound (string s) = #19;
1232 float precache_sound_index (string s) = #19;
1234 #define SND_VOLUME 1
1235 #define SND_ATTENUATION 2
1236 #define SND_LARGEENTITY 8
1237 #define SND_LARGESOUND 16
1239 float sound_allowed(float dest, entity e)
1241 // sounds from world may always pass
1244 if (e.classname == "body")
1246 if (e.owner && e.owner != e)
1251 // sounds to self may always pass
1252 if (dest == MSG_ONE)
1253 if (e == msg_entity)
1255 // sounds by players can be removed
1256 if (autocvar_bot_sound_monopoly)
1257 if (clienttype(e) == CLIENTTYPE_REAL)
1259 // anything else may pass
1263 #ifdef COMPAT_XON010_CHANNELS
1264 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1265 void sound(entity e, float chan, string samp, float vol, float atten)
1267 if (!sound_allowed(MSG_BROADCAST, e))
1269 sound_builtin(e, chan, samp, vol, atten);
1273 void sound(entity e, float chan, string samp, float vol, float atten)
1275 if (!sound_allowed(MSG_BROADCAST, e))
1277 sound7(e, chan, samp, vol, atten, 0, 0);
1281 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1285 if (!sound_allowed(dest, e))
1288 entno = num_for_edict(e);
1289 idx = precache_sound_index(samp);
1294 atten = floor(atten * 64);
1295 vol = floor(vol * 255);
1298 sflags |= SND_VOLUME;
1300 sflags |= SND_ATTENUATION;
1301 if (entno >= 8192 || chan < 0 || chan > 7)
1302 sflags |= SND_LARGEENTITY;
1304 sflags |= SND_LARGESOUND;
1306 WriteByte(dest, SVC_SOUND);
1307 WriteByte(dest, sflags);
1308 if (sflags & SND_VOLUME)
1309 WriteByte(dest, vol);
1310 if (sflags & SND_ATTENUATION)
1311 WriteByte(dest, atten);
1312 if (sflags & SND_LARGEENTITY)
1314 WriteShort(dest, entno);
1315 WriteByte(dest, chan);
1319 WriteShort(dest, entno * 8 + chan);
1321 if (sflags & SND_LARGESOUND)
1322 WriteShort(dest, idx);
1324 WriteByte(dest, idx);
1326 WriteCoord(dest, o_x);
1327 WriteCoord(dest, o_y);
1328 WriteCoord(dest, o_z);
1330 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1334 if (!sound_allowed(dest, e))
1337 o = e.origin + 0.5 * (e.mins + e.maxs);
1338 soundtoat(dest, e, o, chan, samp, vol, atten);
1340 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1342 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1344 void stopsoundto(float dest, entity e, float chan)
1348 if (!sound_allowed(dest, e))
1351 entno = num_for_edict(e);
1353 if (entno >= 8192 || chan < 0 || chan > 7)
1356 idx = precache_sound_index("misc/null.wav");
1357 sflags = SND_LARGEENTITY;
1359 sflags |= SND_LARGESOUND;
1360 WriteByte(dest, SVC_SOUND);
1361 WriteByte(dest, sflags);
1362 WriteShort(dest, entno);
1363 WriteByte(dest, chan);
1364 if (sflags & SND_LARGESOUND)
1365 WriteShort(dest, idx);
1367 WriteByte(dest, idx);
1368 WriteCoord(dest, e.origin_x);
1369 WriteCoord(dest, e.origin_y);
1370 WriteCoord(dest, e.origin_z);
1374 WriteByte(dest, SVC_STOPSOUND);
1375 WriteShort(dest, entno * 8 + chan);
1378 void stopsound(entity e, float chan)
1380 if (!sound_allowed(MSG_BROADCAST, e))
1383 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1384 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1387 void play2(entity e, string filename)
1389 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1391 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1394 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1396 float spamsound(entity e, float chan, string samp, float vol, float atten)
1398 if (!sound_allowed(MSG_BROADCAST, e))
1401 if (time > e.spamtime)
1404 sound(e, chan, samp, vol, atten);
1410 void play2team(float t, string filename)
1414 if (autocvar_bot_sound_monopoly)
1417 FOR_EACH_REALPLAYER(head)
1420 play2(head, filename);
1424 void play2all(string samp)
1426 if (autocvar_bot_sound_monopoly)
1429 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1432 void PrecachePlayerSounds(string f);
1433 void precache_playermodel(string m)
1435 float globhandle, i, n;
1438 if(substring(m, -9,5) == "_lod1")
1440 if(substring(m, -9,5) == "_lod2")
1445 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1448 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1453 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1456 n = search_getsize(globhandle);
1457 for (i = 0; i < n; ++i)
1459 //print(search_getfilename(globhandle, i), "\n");
1460 f = search_getfilename(globhandle, i);
1461 PrecachePlayerSounds(f);
1463 search_end(globhandle);
1465 void precache_all_playermodels(string pattern)
1467 float globhandle, i, n;
1470 globhandle = search_begin(pattern, TRUE, FALSE);
1473 n = search_getsize(globhandle);
1474 for (i = 0; i < n; ++i)
1476 //print(search_getfilename(globhandle, i), "\n");
1477 f = search_getfilename(globhandle, i);
1478 precache_playermodel(f);
1480 search_end(globhandle);
1485 // gamemode related things
1486 precache_model ("models/misc/chatbubble.spr");
1489 precache_model ("models/runematch/curse.mdl");
1490 precache_model ("models/runematch/rune.mdl");
1493 #ifdef TTURRETS_ENABLED
1494 if (autocvar_g_turrets)
1498 // Precache all player models if desired
1499 if (autocvar_sv_precacheplayermodels)
1501 PrecachePlayerSounds("sound/player/default.sounds");
1502 precache_all_playermodels("models/player/*.zym");
1503 precache_all_playermodels("models/player/*.dpm");
1504 precache_all_playermodels("models/player/*.md3");
1505 precache_all_playermodels("models/player/*.psk");
1506 precache_all_playermodels("models/player/*.iqm");
1509 if (autocvar_sv_defaultcharacter)
1512 s = autocvar_sv_defaultplayermodel_red;
1514 precache_playermodel(s);
1515 s = autocvar_sv_defaultplayermodel_blue;
1517 precache_playermodel(s);
1518 s = autocvar_sv_defaultplayermodel_yellow;
1520 precache_playermodel(s);
1521 s = autocvar_sv_defaultplayermodel_pink;
1523 precache_playermodel(s);
1524 s = autocvar_sv_defaultplayermodel;
1526 precache_playermodel(s);
1531 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1532 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1535 // gore and miscellaneous sounds
1536 //precache_sound ("misc/h2ohit.wav");
1537 precache_model ("models/hook.md3");
1538 precache_sound ("misc/armorimpact.wav");
1539 precache_sound ("misc/bodyimpact1.wav");
1540 precache_sound ("misc/bodyimpact2.wav");
1541 precache_sound ("misc/gib.wav");
1542 precache_sound ("misc/gib_splat01.wav");
1543 precache_sound ("misc/gib_splat02.wav");
1544 precache_sound ("misc/gib_splat03.wav");
1545 precache_sound ("misc/gib_splat04.wav");
1546 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1547 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1548 precache_sound ("misc/null.wav");
1549 precache_sound ("misc/spawn.wav");
1550 precache_sound ("misc/talk.wav");
1551 precache_sound ("misc/teleport.wav");
1552 precache_sound ("misc/poweroff.wav");
1553 precache_sound ("player/lava.wav");
1554 precache_sound ("player/slime.wav");
1557 precache_sound ("misc/jetpack_fly.wav");
1559 precache_model ("models/sprites/0.spr32");
1560 precache_model ("models/sprites/1.spr32");
1561 precache_model ("models/sprites/2.spr32");
1562 precache_model ("models/sprites/3.spr32");
1563 precache_model ("models/sprites/4.spr32");
1564 precache_model ("models/sprites/5.spr32");
1565 precache_model ("models/sprites/6.spr32");
1566 precache_model ("models/sprites/7.spr32");
1567 precache_model ("models/sprites/8.spr32");
1568 precache_model ("models/sprites/9.spr32");
1569 precache_model ("models/sprites/10.spr32");
1571 // common weapon precaches
1572 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1573 precache_sound ("weapons/weapon_switch.wav");
1574 precache_sound ("weapons/weaponpickup.wav");
1575 precache_sound ("weapons/unavailable.wav");
1576 precache_sound ("weapons/dryfire.wav");
1577 if (g_grappling_hook)
1579 precache_sound ("weapons/hook_fire.wav"); // hook
1580 precache_sound ("weapons/hook_impact.wav"); // hook
1583 if(autocvar_sv_precacheweapons)
1585 //precache weapon models/sounds
1588 while (wep <= WEP_LAST)
1590 weapon_action(wep, WR_PRECACHE);
1595 precache_model("models/elaser.mdl");
1596 precache_model("models/laser.mdl");
1597 precache_model("models/ebomb.mdl");
1600 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1602 if (!self.noise && self.music) // quake 3 uses the music field
1603 self.noise = self.music;
1605 // plays music for the level if there is any
1608 precache_sound (self.noise);
1609 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1614 // sorry, but using \ in macros breaks line numbers
1615 #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
1616 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1617 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1620 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1622 if (clienttype(e) == CLIENTTYPE_REAL)
1625 WRITESPECTATABLE_MSG_ONE({
1626 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1627 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1628 WriteByte(MSG_ONE, id);
1629 WriteString(MSG_ONE, s);
1630 if (id != 0 && s != "")
1632 WriteByte(MSG_ONE, duration);
1633 WriteByte(MSG_ONE, countdown_num);
1638 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1640 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1642 // WARNING: this kills the trace globals
1643 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1644 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1646 #define INITPRIO_FIRST 0
1647 #define INITPRIO_GAMETYPE 0
1648 #define INITPRIO_GAMETYPE_FALLBACK 1
1649 #define INITPRIO_FINDTARGET 10
1650 #define INITPRIO_DROPTOFLOOR 20
1651 #define INITPRIO_SETLOCATION 90
1652 #define INITPRIO_LINKDOORS 91
1653 #define INITPRIO_LAST 99
1655 .void(void) initialize_entity;
1656 .float initialize_entity_order;
1657 .entity initialize_entity_next;
1658 entity initialize_entity_first;
1660 void make_safe_for_remove(entity e)
1662 if (e.initialize_entity)
1665 for (ent = initialize_entity_first; ent; )
1667 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1669 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1670 // skip it in linked list
1673 prev.initialize_entity_next = ent.initialize_entity_next;
1674 ent = prev.initialize_entity_next;
1678 initialize_entity_first = ent.initialize_entity_next;
1679 ent = initialize_entity_first;
1685 ent = ent.initialize_entity_next;
1691 void objerror(string s)
1693 make_safe_for_remove(self);
1694 objerror_builtin(s);
1697 .float remove_except_protected_forbidden;
1698 void remove_except_protected(entity e)
1700 if(e.remove_except_protected_forbidden)
1701 error("not allowed to remove this at this point");
1705 void remove_unsafely(entity e)
1707 if(e.classname == "spike")
1708 error("Removing spikes is forbidden (crylink bug), please report");
1712 void remove_safely(entity e)
1714 make_safe_for_remove(e);
1718 void InitializeEntity(entity e, void(void) func, float order)
1722 if (!e || e.initialize_entity)
1724 // make a proxy initializer entity
1728 e.classname = "initialize_entity";
1732 e.initialize_entity = func;
1733 e.initialize_entity_order = order;
1735 cur = initialize_entity_first;
1738 if (!cur || cur.initialize_entity_order > order)
1740 // insert between prev and cur
1742 prev.initialize_entity_next = e;
1744 initialize_entity_first = e;
1745 e.initialize_entity_next = cur;
1749 cur = cur.initialize_entity_next;
1752 void InitializeEntitiesRun()
1755 startoflist = initialize_entity_first;
1756 initialize_entity_first = world;
1757 remove = remove_except_protected;
1758 for (self = startoflist; self; self = self.initialize_entity_next)
1760 self.remove_except_protected_forbidden = 1;
1762 for (self = startoflist; self; )
1765 var void(void) func;
1766 e = self.initialize_entity_next;
1767 func = self.initialize_entity;
1768 self.initialize_entity_order = 0;
1769 self.initialize_entity = func_null;
1770 self.initialize_entity_next = world;
1771 self.remove_except_protected_forbidden = 0;
1772 if (self.classname == "initialize_entity")
1776 remove_builtin(self);
1779 //dprint("Delayed initialization: ", self.classname, "\n");
1780 if(func != func_null)
1785 backtrace(strcat("Null function in: ", self.classname, "\n"));
1789 remove = remove_unsafely;
1792 .float uncustomizeentityforclient_set;
1793 .void(void) uncustomizeentityforclient;
1794 void(void) SUB_Nullpointer = #0;
1795 void UncustomizeEntitiesRun()
1799 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1800 self.uncustomizeentityforclient();
1803 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1805 e.customizeentityforclient = customizer;
1806 e.uncustomizeentityforclient = uncustomizer;
1807 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1811 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1814 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1818 if (e.classname == "")
1819 e.classname = "net_linked";
1821 if (e.model == "" || self.modelindex == 0)
1825 setmodel(e, "null");
1829 e.SendEntity = sendfunc;
1830 e.SendFlags = 0xFFFFFF;
1833 e.effects |= EF_NODEPTHTEST;
1837 e.nextthink = time + dt;
1838 e.think = SUB_Remove;
1842 void adaptor_think2touch()
1851 void adaptor_think2use()
1863 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1865 if not(self.flags & FL_ONGROUND) // if onground, we ARE touching something, but HITTYPE_SPLASH is to be networked if the damage causing projectile is not touching ANYTHING
1866 self.projectiledeathtype |= HITTYPE_SPLASH;
1867 adaptor_think2use();
1870 // deferred dropping
1871 void DropToFloor_Handler()
1873 droptofloor_builtin();
1874 self.dropped_origin = self.origin;
1879 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1884 float trace_hits_box_a0, trace_hits_box_a1;
1886 float trace_hits_box_1d(float end, float thmi, float thma)
1890 // just check if x is in range
1898 // do the trace with respect to x
1899 // 0 -> end has to stay in thmi -> thma
1900 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1901 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1902 if (trace_hits_box_a0 > trace_hits_box_a1)
1908 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1913 // now it is a trace from 0 to end
1915 trace_hits_box_a0 = 0;
1916 trace_hits_box_a1 = 1;
1918 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1920 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1922 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1928 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1930 return trace_hits_box(start, end, thmi - ma, thma - mi);
1933 float SUB_NoImpactCheck()
1935 // zero hitcontents = this is not the real impact, but either the
1936 // mirror-impact of something hitting the projectile instead of the
1937 // projectile hitting the something, or a touchareagrid one. Neither of
1938 // these stop the projectile from moving, so...
1939 if(trace_dphitcontents == 0)
1941 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1942 dprint(sprintf(_("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Profectile will self-destruct. (edict: %d, classname: %s, origin: %s)\n"), num_for_edict(self), self.classname, vtos(self.origin)));
1945 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1947 if (other == world && self.size != '0 0 0')
1950 tic = self.velocity * sys_frametime;
1951 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1952 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1953 if (trace_fraction >= 1)
1955 dprint("Odd... did not hit...?\n");
1957 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1959 dprint("Detected and prevented the sky-grapple bug.\n");
1967 #define SUB_OwnerCheck() (other && (other == self.owner))
1969 void RemoveGrapplingHook(entity pl);
1970 void W_Crylink_Dequeue(entity e);
1971 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1973 if(SUB_OwnerCheck())
1975 if(SUB_NoImpactCheck())
1977 if(self.classname == "grapplinghook")
1978 RemoveGrapplingHook(self.realowner);
1979 else if(self.classname == "spike")
1981 W_Crylink_Dequeue(self);
1988 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1989 UpdateCSQCProjectile(self);
1992 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1994 float MAX_IPBAN_URIS = 16;
1996 float URI_GET_DISCARD = 0;
1997 float URI_GET_IPBAN = 1;
1998 float URI_GET_IPBAN_END = 16;
2000 void URI_Get_Callback(float id, float status, string data)
2002 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2004 dprint("\nEnd of data.\n");
2006 if(url_URI_Get_Callback(id, status, data))
2010 else if (id == URI_GET_DISCARD)
2014 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2017 OnlineBanList_URI_Get_Callback(id, status, data);
2021 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2025 void print_to(entity e, string s)
2028 sprint(e, strcat(s, "\n"));
2033 string uid2name(string myuid) {
2035 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2038 s = "^1Unregistered Player";
2042 float race_readTime(string map, float pos)
2050 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2053 string race_readUID(string map, float pos)
2061 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2064 float race_readPos(string map, float t) {
2066 for (i = 1; i <= RANKINGS_CNT; ++i)
2067 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2070 return 0; // pos is zero if unranked
2073 void race_writeTime(string map, float t, string myuid)
2082 newpos = race_readPos(map, t);
2085 for(i = 1; i <= RANKINGS_CNT; ++i)
2087 if(race_readUID(map, i) == myuid)
2090 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2091 for (i = prevpos; i > newpos; --i) {
2092 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2093 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2095 } else { // player has no ranked record yet
2096 for (i = RANKINGS_CNT; i > newpos; --i) {
2097 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2098 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2102 // store new time itself
2103 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2104 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2107 string race_readName(string map, float pos)
2115 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2118 string race_placeName(float pos) {
2119 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2121 if(mod(pos, 10) == 1)
2122 return strcat(ftos(pos), "st");
2123 else if(mod(pos, 10) == 2)
2124 return strcat(ftos(pos), "nd");
2125 else if(mod(pos, 10) == 3)
2126 return strcat(ftos(pos), "rd");
2128 return strcat(ftos(pos), "th");
2131 return strcat(ftos(pos), "th");
2133 string getrecords(float page) // 50 records per page
2147 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2149 if (MapInfo_Get_ByID(i))
2151 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2155 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2156 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2164 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2166 if (MapInfo_Get_ByID(i))
2168 r = race_readTime(MapInfo_Map_bspname, 1);
2171 h = race_readName(MapInfo_Map_bspname, 1);
2172 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2180 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2182 if (MapInfo_Get_ByID(i))
2184 r = race_readTime(MapInfo_Map_bspname, 1);
2187 h = race_readName(MapInfo_Map_bspname, 1);
2188 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2194 MapInfo_ClearTemps();
2196 if (s == "" && page == 0)
2197 return "No records are available on this server.\n";
2202 string getrankings()
2215 for (i = 1; i <= RANKINGS_CNT; ++i)
2217 t = race_readTime(map, i);
2220 n = race_readName(map, i);
2221 p = race_placeName(i);
2222 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2225 MapInfo_ClearTemps();
2228 return strcat("No records are available for the map: ", map, "\n");
2230 return strcat("Records for ", map, ":\n", s);
2233 #define LADDER_FIRSTPOINT 100
2234 #define LADDER_CNT 10
2235 // position X still gives LADDER_FIRSTPOINT/X points
2236 #define LADDER_SIZE 30
2237 // ladder shows the top X players
2238 string top_uids[LADDER_SIZE];
2239 float top_scores[LADDER_SIZE];
2242 float i, j, k, uidcnt;
2256 for (k = 0; k < MapInfo_count; ++k)
2258 if (MapInfo_Get_ByID(k))
2260 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2261 if(i == 0) // speed award
2263 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2266 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2268 else // normal record, if it exists (else break)
2270 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2273 myuid = race_readUID(MapInfo_Map_bspname, i);
2276 // string s contains:
2277 // arg 0 = # of speed recs
2278 // arg 1 = # of 1st place recs
2279 // arg 2 = # of 2nd place recs
2281 // LADDER_CNT+1 = total points
2283 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2286 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2288 for (j = 0; j <= LADDER_CNT + 1; ++j)
2290 if(j != LADDER_CNT + 1)
2291 temp_s = strcat(temp_s, "0 ");
2293 temp_s = strcat(temp_s, "0");
2297 tokenize_console(temp_s);
2300 if(i == 0) // speed award
2301 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2303 if(j == 0) // speed award
2304 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2306 s = strcat(s, " ", argv(j)); // just copy over everything else
2309 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2312 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2313 else if(j == i) // wanted rec!
2314 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2316 s = strcat(s, " ", argv(j)); // just copy over everything else
2319 // total points are (by default) calculated like this:
2320 // speedrec = floor(100 / 10) = 10 points
2321 // 1st place = floor(100 / 1) = 100 points
2322 // 2nd place = floor(100 / 2) = 50 points
2323 // 3rd place = floor(100 / 3) = 33 points
2324 // 4th place = floor(100 / 4) = 25 points
2325 // 5th place = floor(100 / 5) = 20 points
2329 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2331 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2333 db_put(TemporaryDB, strcat("ladder", myuid), s);
2340 for (i = 0; i <= uidcnt; ++i) // for each known uid
2342 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2343 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2344 tokenize_console(temp_s);
2345 thiscnt = stof(argv(LADDER_CNT+1));
2347 if(thiscnt > top_scores[LADDER_SIZE-1])
2348 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2350 if(thiscnt > top_scores[j])
2352 for (k = LADDER_SIZE-1; k >= j; --k)
2354 top_uids[k] = top_uids[k-1];
2355 top_scores[k] = top_scores[k-1];
2357 top_uids[j] = thisuid;
2358 top_scores[j] = thiscnt;
2364 s = "^3-----------------------\n\n";
2366 s = strcat(s, "Pos ^3|");
2367 s = strcat(s, " ^7Total ^3|");
2368 for (i = 1; i <= LADDER_CNT; ++i)
2370 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2372 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2374 s = strcat(s, "\n^3----+--------");
2375 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2377 s = strcat(s, "+-----");
2380 for (i = 1; i <= LADDER_CNT - 9; ++i)
2382 s = strcat(s, "+------");
2386 s = strcat(s, "+--------------+--------------------\n");
2388 for (i = 0; i < LADDER_SIZE; ++i)
2390 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2391 tokenize_console(temp_s);
2392 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2394 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2395 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2396 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2398 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2401 for (j = 10; j <= LADDER_CNT; ++j)
2403 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2407 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2408 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2411 MapInfo_ClearTemps();
2414 return "No ladder on this server!\n";
2416 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2420 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2423 vector start, org, delta, end, enddown, mstart;
2426 m = e.dphitcontentsmask;
2427 e.dphitcontentsmask = goodcontents | badcontents;
2430 delta = world.maxs - world.mins;
2432 for (i = 0; i < attempts; ++i)
2434 start_x = org_x + random() * delta_x;
2435 start_y = org_y + random() * delta_y;
2436 start_z = org_z + random() * delta_z;
2438 // rule 1: start inside world bounds, and outside
2439 // solid, and don't start from somewhere where you can
2440 // fall down to evil
2441 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2442 if (trace_fraction >= 1)
2444 if (trace_startsolid)
2446 if (trace_dphitcontents & badcontents)
2448 if (trace_dphitq3surfaceflags & badsurfaceflags)
2451 // rule 2: if we are too high, lower the point
2452 if (trace_fraction * delta_z > maxaboveground)
2453 start = trace_endpos + '0 0 1' * maxaboveground;
2454 enddown = trace_endpos;
2456 // rule 3: make sure we aren't outside the map. This only works
2457 // for somewhat well formed maps. A good rule of thumb is that
2458 // the map should have a convex outside hull.
2459 // these can be traceLINES as we already verified the starting box
2460 mstart = start + 0.5 * (e.mins + e.maxs);
2461 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2462 if (trace_fraction >= 1)
2464 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2465 if (trace_fraction >= 1)
2467 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2468 if (trace_fraction >= 1)
2470 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2471 if (trace_fraction >= 1)
2473 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2474 if (trace_fraction >= 1)
2477 // rule 4: we must "see" some spawnpoint
2478 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2479 if(checkpvs(mstart, sp))
2483 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2484 if(checkpvs(mstart, sp))
2490 // find a random vector to "look at"
2491 end_x = org_x + random() * delta_x;
2492 end_y = org_y + random() * delta_y;
2493 end_z = org_z + random() * delta_z;
2494 end = start + normalize(end - start) * vlen(delta);
2496 // rule 4: start TO end must not be too short
2497 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2498 if (trace_startsolid)
2500 if (trace_fraction < minviewdistance / vlen(delta))
2503 // rule 5: don't want to look at sky
2504 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2507 // rule 6: we must not end up in trigger_hurt
2508 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2514 e.dphitcontentsmask = m;
2518 setorigin(e, start);
2519 e.angles = vectoangles(end - start);
2520 dprint("Needed ", ftos(i + 1), " attempts\n");
2527 float zcurveparticles_effectno;
2528 vector zcurveparticles_start;
2529 float zcurveparticles_spd;
2531 void endzcurveparticles()
2533 if(zcurveparticles_effectno)
2536 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2538 zcurveparticles_effectno = 0;
2541 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2543 spd = bound(0, floor(spd / 16), 32767);
2544 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2546 endzcurveparticles();
2547 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2548 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2549 WriteShort(MSG_BROADCAST, effectno);
2550 WriteCoord(MSG_BROADCAST, start_x);
2551 WriteCoord(MSG_BROADCAST, start_y);
2552 WriteCoord(MSG_BROADCAST, start_z);
2553 zcurveparticles_effectno = effectno;
2554 zcurveparticles_start = start;
2557 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2558 WriteCoord(MSG_BROADCAST, end_x);
2559 WriteCoord(MSG_BROADCAST, end_y);
2560 WriteCoord(MSG_BROADCAST, end_z);
2561 WriteCoord(MSG_BROADCAST, end_dz);
2562 zcurveparticles_spd = spd;
2565 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2568 vector vecxy, velxy;
2570 vecxy = end - start;
2575 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2577 endzcurveparticles();
2578 trailparticles(world, effectno, start, end);
2582 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2583 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2586 void write_recordmarker(entity pl, float tstart, float dt)
2588 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2590 // also write a marker into demo files for demotc-race-record-extractor to find
2593 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2594 " ", ftos(tstart), " ", ftos(dt), "\n"));
2597 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2610 if(allowcenter) // 2: allow center handedness
2623 if(allowcenter) // 2: allow center handedness
2639 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2644 if (autocvar_g_shootfromeye)
2657 else if (autocvar_g_shootfromcenter)
2662 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2672 else if (autocvar_g_shootfromclient)
2674 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2679 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2681 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2685 void attach_sameorigin(entity e, entity to, string tag)
2687 vector org, t_forward, t_left, t_up, e_forward, e_up;
2694 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2695 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2696 t_forward = v_forward * tagscale;
2697 t_left = v_right * -tagscale;
2698 t_up = v_up * tagscale;
2700 e.origin_x = org * t_forward;
2701 e.origin_y = org * t_left;
2702 e.origin_z = org * t_up;
2704 // current forward and up directions
2705 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2706 e.angles = AnglesTransform_FromVAngles(e.angles);
2708 e.angles = AnglesTransform_FromAngles(e.angles);
2709 fixedmakevectors(e.angles);
2711 // untransform forward, up!
2712 e_forward_x = v_forward * t_forward;
2713 e_forward_y = v_forward * t_left;
2714 e_forward_z = v_forward * t_up;
2715 e_up_x = v_up * t_forward;
2716 e_up_y = v_up * t_left;
2717 e_up_z = v_up * t_up;
2719 e.angles = fixedvectoangles2(e_forward, e_up);
2720 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2721 e.angles = AnglesTransform_ToVAngles(e.angles);
2723 e.angles = AnglesTransform_ToAngles(e.angles);
2725 setattachment(e, to, tag);
2726 setorigin(e, e.origin);
2729 void detach_sameorigin(entity e)
2732 org = gettaginfo(e, 0);
2733 e.angles = fixedvectoangles2(v_forward, v_up);
2734 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2735 e.angles = AnglesTransform_ToVAngles(e.angles);
2737 e.angles = AnglesTransform_ToAngles(e.angles);
2739 setattachment(e, world, "");
2740 setorigin(e, e.origin);
2743 void follow_sameorigin(entity e, entity to)
2745 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2746 e.aiment = to; // make the hole follow bmodel
2747 e.punchangle = to.angles; // the original angles of bmodel
2748 e.view_ofs = e.origin - to.origin; // relative origin
2749 e.v_angle = e.angles - to.angles; // relative angles
2752 void unfollow_sameorigin(entity e)
2754 e.movetype = MOVETYPE_NONE;
2757 entity gettaginfo_relative_ent;
2758 vector gettaginfo_relative(entity e, float tag)
2760 if (!gettaginfo_relative_ent)
2762 gettaginfo_relative_ent = spawn();
2763 gettaginfo_relative_ent.effects = EF_NODRAW;
2765 gettaginfo_relative_ent.model = e.model;
2766 gettaginfo_relative_ent.modelindex = e.modelindex;
2767 gettaginfo_relative_ent.frame = e.frame;
2768 return gettaginfo(gettaginfo_relative_ent, tag);
2771 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2775 if (pl.soundentity.cnt & p)
2777 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2778 pl.soundentity.cnt |= p;
2781 void SoundEntity_StopSound(entity pl, float chan)
2785 if (pl.soundentity.cnt & p)
2787 stopsoundto(MSG_ALL, pl.soundentity, chan);
2788 pl.soundentity.cnt &~= p;
2792 void SoundEntity_Attach(entity pl)
2794 pl.soundentity = spawn();
2795 pl.soundentity.classname = "soundentity";
2796 pl.soundentity.owner = pl;
2797 setattachment(pl.soundentity, pl, "");
2798 setmodel(pl.soundentity, "null");
2801 void SoundEntity_Detach(entity pl)
2804 for (i = 0; i <= 7; ++i)
2805 SoundEntity_StopSound(pl, i);
2809 float ParseCommandPlayerSlotTarget_firsttoken;
2810 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2818 ParseCommandPlayerSlotTarget_firsttoken = -1;
2822 if (substring(argv(idx), 0, 1) == "#")
2824 s = substring(argv(idx), 1, -1);
2826 if (s == "") if (tokens > idx)
2831 ParseCommandPlayerSlotTarget_firsttoken = idx;
2833 if (s == ftos(n) && n > 0 && n <= maxclients)
2836 if (e.flags & FL_CLIENT)
2842 // it must be a nick name
2845 ParseCommandPlayerSlotTarget_firsttoken = idx;
2848 FOR_EACH_CLIENT(head)
2849 if (head.netname == s)
2857 s = strdecolorize(s);
2859 FOR_EACH_CLIENT(head)
2860 if (strdecolorize(head.netname) == s)
2875 float modeleffect_SendEntity(entity to, float sf)
2878 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2881 if(self.velocity != '0 0 0')
2883 if(self.angles != '0 0 0')
2885 if(self.avelocity != '0 0 0')
2888 WriteByte(MSG_ENTITY, f);
2889 WriteShort(MSG_ENTITY, self.modelindex);
2890 WriteByte(MSG_ENTITY, self.skin);
2891 WriteByte(MSG_ENTITY, self.frame);
2892 WriteCoord(MSG_ENTITY, self.origin_x);
2893 WriteCoord(MSG_ENTITY, self.origin_y);
2894 WriteCoord(MSG_ENTITY, self.origin_z);
2897 WriteCoord(MSG_ENTITY, self.velocity_x);
2898 WriteCoord(MSG_ENTITY, self.velocity_y);
2899 WriteCoord(MSG_ENTITY, self.velocity_z);
2903 WriteCoord(MSG_ENTITY, self.angles_x);
2904 WriteCoord(MSG_ENTITY, self.angles_y);
2905 WriteCoord(MSG_ENTITY, self.angles_z);
2909 WriteCoord(MSG_ENTITY, self.avelocity_x);
2910 WriteCoord(MSG_ENTITY, self.avelocity_y);
2911 WriteCoord(MSG_ENTITY, self.avelocity_z);
2913 WriteShort(MSG_ENTITY, self.scale * 256.0);
2914 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2915 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2916 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2917 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2922 void modeleffect_spawn(string m, float s, float f, vector o, vector v, vector ang, vector angv, float s0, float s2, float a, float t1, float t2)
2927 e.classname = "modeleffect";
2935 e.teleport_time = t1;
2939 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2943 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2946 sz = max(e.scale, e.scale2);
2947 setsize(e, e.mins * sz, e.maxs * sz);
2948 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2951 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2953 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2956 float randombit(float bits)
2958 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2967 for(f = 1; f <= bits; f *= 2)
2976 r = (r - 1) / (n - 1);
2983 float randombits(float bits, float k, float error_return)
2987 while(k > 0 && bits != r)
2989 r += randombit(bits - r);
2998 void randombit_test(float bits, float iter)
3002 print(ftos(randombit(bits)), "\n");
3007 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3009 if(halflifedist > 0)
3010 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3011 else if(halflifedist < 0)
3012 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3021 #define cvar_string_normal cvar_string_builtin
3022 #define cvar_normal cvar_builtin
3024 string cvar_string_normal(string n)
3026 if not(cvar_type(n) & 1)
3027 backtrace(strcat("Attempt to access undefined cvar: ", n));
3028 return cvar_string_builtin(n);
3031 float cvar_normal(string n)
3033 return stof(cvar_string_normal(n));
3036 #define cvar_set_normal cvar_set_builtin
3044 oself.think = SUB_Remove;
3045 oself.nextthink = time;
3051 Execute func() after time + fdelay.
3052 self when func is executed = self when defer is called
3054 void defer(float fdelay, void() func)
3061 e.think = defer_think;
3062 e.nextthink = time + fdelay;
3065 .string aiment_classname;
3066 .float aiment_deadflag;
3067 void SetMovetypeFollow(entity ent, entity e)
3069 // FIXME this may not be warpzone aware
3070 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3071 ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported.
3072 ent.aiment = e; // make the hole follow bmodel
3073 ent.punchangle = e.angles; // the original angles of bmodel
3074 ent.view_ofs = ent.origin - e.origin; // relative origin
3075 ent.v_angle = ent.angles - e.angles; // relative angles
3076 ent.aiment_classname = strzone(e.classname);
3077 ent.aiment_deadflag = e.deadflag;
3079 void UnsetMovetypeFollow(entity ent)
3081 ent.movetype = MOVETYPE_FLY;
3082 PROJECTILE_MAKETRIGGER(ent);
3085 float LostMovetypeFollow(entity ent)
3088 if(ent.movetype != MOVETYPE_FOLLOW)
3094 if(ent.aiment.classname != ent.aiment_classname)
3096 if(ent.aiment.deadflag != ent.aiment_deadflag)
3102 float isPushable(entity e)
3109 case "droppedweapon":
3110 case "keepawayball":
3111 case "nexball_basketball":
3112 case "nexball_football":
3114 case "bullet": // antilagged bullets can't hit this either
3117 if (e.projectiledeathtype)