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
1078 if(cvar("g_invincible_projectiles"))
1079 MUTATOR_ADD(mutator_invincibleprojectiles);
1081 MUTATOR_ADD(mutator_nix);
1082 if(cvar("g_dodging"))
1083 MUTATOR_ADD(mutator_dodging);
1084 if(cvar("g_rocket_flying"))
1085 MUTATOR_ADD(mutator_rocketflying);
1086 if(cvar("g_vampire"))
1087 MUTATOR_ADD(mutator_vampire);
1089 if(cvar("sv_allow_fullbright"))
1090 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1092 g_bugrigs = cvar("g_bugrigs");
1093 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1094 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1095 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1096 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1097 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1098 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1099 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1100 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1101 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1102 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1103 g_bugrigs_accel = cvar("g_bugrigs_accel");
1104 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1105 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1106 g_bugrigs_steer = cvar("g_bugrigs_steer");
1108 g_touchexplode = cvar("g_touchexplode");
1109 g_touchexplode_radius = cvar("g_touchexplode_radius");
1110 g_touchexplode_damage = cvar("g_touchexplode_damage");
1111 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1112 g_touchexplode_force = cvar("g_touchexplode_force");
1114 #ifdef ALLOW_FORCEMODELS
1115 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1117 sv_loddistance1 = cvar("sv_loddistance1");
1118 sv_loddistance2 = cvar("sv_loddistance2");
1120 if(sv_loddistance2 <= sv_loddistance1)
1121 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1123 sv_clones = cvar("sv_clones");
1124 sv_gentle = cvar("sv_gentle");
1125 sv_foginterval = cvar("sv_foginterval");
1126 g_cloaked = cvar("g_cloaked");
1128 g_cloaked = 1; // always enable cloak in CTS
1129 g_jump_grunt = cvar("g_jump_grunt");
1130 g_footsteps = cvar("g_footsteps");
1131 g_grappling_hook = cvar("g_grappling_hook");
1132 g_jetpack = cvar("g_jetpack");
1133 g_midair = cvar("g_midair");
1134 g_minstagib = cvar("g_minstagib");
1135 g_norecoil = cvar("g_norecoil");
1136 g_bloodloss = cvar("g_bloodloss");
1137 sv_maxidle = cvar("sv_maxidle");
1138 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1139 g_ctf_reverse = cvar("g_ctf_reverse");
1140 sv_autotaunt = cvar("sv_autotaunt");
1141 sv_taunt = cvar("sv_taunt");
1143 inWarmupStage = cvar("g_warmup");
1144 g_warmup_limit = cvar("g_warmup_limit");
1145 g_warmup_allguns = cvar("g_warmup_allguns");
1146 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1148 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1149 inWarmupStage = 0; // these modes cannot work together, sorry
1151 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1152 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1153 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1154 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1155 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1156 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1157 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1158 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1159 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1160 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1161 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1162 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1164 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1165 g_weaponratefactor = cvar("g_weaponratefactor");
1166 g_weapondamagefactor = cvar("g_weapondamagefactor");
1167 g_weaponforcefactor = cvar("g_weaponforcefactor");
1168 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1170 g_pickup_shells = cvar("g_pickup_shells");
1171 g_pickup_shells_max = cvar("g_pickup_shells_max");
1172 g_pickup_nails = cvar("g_pickup_nails");
1173 g_pickup_nails_max = cvar("g_pickup_nails_max");
1174 g_pickup_rockets = cvar("g_pickup_rockets");
1175 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1176 g_pickup_cells = cvar("g_pickup_cells");
1177 g_pickup_cells_max = cvar("g_pickup_cells_max");
1178 g_pickup_fuel = cvar("g_pickup_fuel");
1179 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1180 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1181 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1182 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1183 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1184 g_pickup_armormedium = cvar("g_pickup_armormedium");
1185 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1186 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1187 g_pickup_armorbig = cvar("g_pickup_armorbig");
1188 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1189 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1190 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1191 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1192 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1193 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1194 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1195 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1196 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1197 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1198 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1199 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1200 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1201 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1202 g_pickup_healthmega = cvar("g_pickup_healthmega");
1203 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1204 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1206 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1207 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1209 g_pinata = cvar("g_pinata");
1211 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1213 g_weapon_stay = cvar("g_weapon_stay");
1215 g_ghost_items = cvar("g_ghost_items");
1217 if(g_ghost_items >= 1)
1218 g_ghost_items = 0.25; // default alpha value
1220 if not(inWarmupStage && !g_ca)
1221 game_starttime = cvar("g_start_delay");
1223 sv_pitch_min = cvar("sv_pitch_min");
1224 sv_pitch_max = cvar("sv_pitch_max");
1225 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1227 readplayerstartcvars();
1233 string precache_sound (string s) = #19;
1234 float precache_sound_index (string s) = #19;
1236 #define SND_VOLUME 1
1237 #define SND_ATTENUATION 2
1238 #define SND_LARGEENTITY 8
1239 #define SND_LARGESOUND 16
1241 float sound_allowed(float dest, entity e)
1243 // sounds from world may always pass
1246 if (e.classname == "body")
1248 if (e.owner && e.owner != e)
1253 // sounds to self may always pass
1254 if (dest == MSG_ONE)
1255 if (e == msg_entity)
1257 // sounds by players can be removed
1258 if (autocvar_bot_sound_monopoly)
1259 if (clienttype(e) == CLIENTTYPE_REAL)
1261 // anything else may pass
1265 #ifdef COMPAT_XON010_CHANNELS
1266 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1267 void sound(entity e, float chan, string samp, float vol, float atten)
1269 if (!sound_allowed(MSG_BROADCAST, e))
1271 sound_builtin(e, chan, samp, vol, atten);
1275 void sound(entity e, float chan, string samp, float vol, float atten)
1277 if (!sound_allowed(MSG_BROADCAST, e))
1279 sound7(e, chan, samp, vol, atten, 0, 0);
1283 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1287 if (!sound_allowed(dest, e))
1290 entno = num_for_edict(e);
1291 idx = precache_sound_index(samp);
1296 atten = floor(atten * 64);
1297 vol = floor(vol * 255);
1300 sflags |= SND_VOLUME;
1302 sflags |= SND_ATTENUATION;
1303 if (entno >= 8192 || chan < 0 || chan > 7)
1304 sflags |= SND_LARGEENTITY;
1306 sflags |= SND_LARGESOUND;
1308 WriteByte(dest, SVC_SOUND);
1309 WriteByte(dest, sflags);
1310 if (sflags & SND_VOLUME)
1311 WriteByte(dest, vol);
1312 if (sflags & SND_ATTENUATION)
1313 WriteByte(dest, atten);
1314 if (sflags & SND_LARGEENTITY)
1316 WriteShort(dest, entno);
1317 WriteByte(dest, chan);
1321 WriteShort(dest, entno * 8 + chan);
1323 if (sflags & SND_LARGESOUND)
1324 WriteShort(dest, idx);
1326 WriteByte(dest, idx);
1328 WriteCoord(dest, o_x);
1329 WriteCoord(dest, o_y);
1330 WriteCoord(dest, o_z);
1332 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1336 if (!sound_allowed(dest, e))
1339 o = e.origin + 0.5 * (e.mins + e.maxs);
1340 soundtoat(dest, e, o, chan, samp, vol, atten);
1342 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1344 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1346 void stopsoundto(float dest, entity e, float chan)
1350 if (!sound_allowed(dest, e))
1353 entno = num_for_edict(e);
1355 if (entno >= 8192 || chan < 0 || chan > 7)
1358 idx = precache_sound_index("misc/null.wav");
1359 sflags = SND_LARGEENTITY;
1361 sflags |= SND_LARGESOUND;
1362 WriteByte(dest, SVC_SOUND);
1363 WriteByte(dest, sflags);
1364 WriteShort(dest, entno);
1365 WriteByte(dest, chan);
1366 if (sflags & SND_LARGESOUND)
1367 WriteShort(dest, idx);
1369 WriteByte(dest, idx);
1370 WriteCoord(dest, e.origin_x);
1371 WriteCoord(dest, e.origin_y);
1372 WriteCoord(dest, e.origin_z);
1376 WriteByte(dest, SVC_STOPSOUND);
1377 WriteShort(dest, entno * 8 + chan);
1380 void stopsound(entity e, float chan)
1382 if (!sound_allowed(MSG_BROADCAST, e))
1385 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1386 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1389 void play2(entity e, string filename)
1391 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1393 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1396 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1398 float spamsound(entity e, float chan, string samp, float vol, float atten)
1400 if (!sound_allowed(MSG_BROADCAST, e))
1403 if (time > e.spamtime)
1406 sound(e, chan, samp, vol, atten);
1412 void play2team(float t, string filename)
1416 if (autocvar_bot_sound_monopoly)
1419 FOR_EACH_REALPLAYER(head)
1422 play2(head, filename);
1426 void play2all(string samp)
1428 if (autocvar_bot_sound_monopoly)
1431 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1434 void PrecachePlayerSounds(string f);
1435 void precache_playermodel(string m)
1437 float globhandle, i, n;
1440 if(substring(m, -9,5) == "_lod1")
1442 if(substring(m, -9,5) == "_lod2")
1447 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1450 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1455 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1458 n = search_getsize(globhandle);
1459 for (i = 0; i < n; ++i)
1461 //print(search_getfilename(globhandle, i), "\n");
1462 f = search_getfilename(globhandle, i);
1463 PrecachePlayerSounds(f);
1465 search_end(globhandle);
1467 void precache_all_playermodels(string pattern)
1469 float globhandle, i, n;
1472 globhandle = search_begin(pattern, TRUE, FALSE);
1475 n = search_getsize(globhandle);
1476 for (i = 0; i < n; ++i)
1478 //print(search_getfilename(globhandle, i), "\n");
1479 f = search_getfilename(globhandle, i);
1480 precache_playermodel(f);
1482 search_end(globhandle);
1487 // gamemode related things
1488 precache_model ("models/misc/chatbubble.spr");
1491 precache_model ("models/runematch/curse.mdl");
1492 precache_model ("models/runematch/rune.mdl");
1495 #ifdef TTURRETS_ENABLED
1496 if (autocvar_g_turrets)
1500 // Precache all player models if desired
1501 if (autocvar_sv_precacheplayermodels)
1503 PrecachePlayerSounds("sound/player/default.sounds");
1504 precache_all_playermodels("models/player/*.zym");
1505 precache_all_playermodels("models/player/*.dpm");
1506 precache_all_playermodels("models/player/*.md3");
1507 precache_all_playermodels("models/player/*.psk");
1508 precache_all_playermodels("models/player/*.iqm");
1511 if (autocvar_sv_defaultcharacter)
1514 s = autocvar_sv_defaultplayermodel_red;
1516 precache_playermodel(s);
1517 s = autocvar_sv_defaultplayermodel_blue;
1519 precache_playermodel(s);
1520 s = autocvar_sv_defaultplayermodel_yellow;
1522 precache_playermodel(s);
1523 s = autocvar_sv_defaultplayermodel_pink;
1525 precache_playermodel(s);
1526 s = autocvar_sv_defaultplayermodel;
1528 precache_playermodel(s);
1533 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1534 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1537 // gore and miscellaneous sounds
1538 //precache_sound ("misc/h2ohit.wav");
1539 precache_model ("models/hook.md3");
1540 precache_sound ("misc/armorimpact.wav");
1541 precache_sound ("misc/bodyimpact1.wav");
1542 precache_sound ("misc/bodyimpact2.wav");
1543 precache_sound ("misc/gib.wav");
1544 precache_sound ("misc/gib_splat01.wav");
1545 precache_sound ("misc/gib_splat02.wav");
1546 precache_sound ("misc/gib_splat03.wav");
1547 precache_sound ("misc/gib_splat04.wav");
1548 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1549 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1550 precache_sound ("misc/null.wav");
1551 precache_sound ("misc/spawn.wav");
1552 precache_sound ("misc/talk.wav");
1553 precache_sound ("misc/teleport.wav");
1554 precache_sound ("misc/poweroff.wav");
1555 precache_sound ("player/lava.wav");
1556 precache_sound ("player/slime.wav");
1559 precache_sound ("misc/jetpack_fly.wav");
1561 precache_model ("models/sprites/0.spr32");
1562 precache_model ("models/sprites/1.spr32");
1563 precache_model ("models/sprites/2.spr32");
1564 precache_model ("models/sprites/3.spr32");
1565 precache_model ("models/sprites/4.spr32");
1566 precache_model ("models/sprites/5.spr32");
1567 precache_model ("models/sprites/6.spr32");
1568 precache_model ("models/sprites/7.spr32");
1569 precache_model ("models/sprites/8.spr32");
1570 precache_model ("models/sprites/9.spr32");
1571 precache_model ("models/sprites/10.spr32");
1573 // common weapon precaches
1574 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1575 precache_sound ("weapons/weapon_switch.wav");
1576 precache_sound ("weapons/weaponpickup.wav");
1577 precache_sound ("weapons/unavailable.wav");
1578 precache_sound ("weapons/dryfire.wav");
1579 if (g_grappling_hook)
1581 precache_sound ("weapons/hook_fire.wav"); // hook
1582 precache_sound ("weapons/hook_impact.wav"); // hook
1585 if(autocvar_sv_precacheweapons)
1587 //precache weapon models/sounds
1590 while (wep <= WEP_LAST)
1592 weapon_action(wep, WR_PRECACHE);
1597 precache_model("models/elaser.mdl");
1598 precache_model("models/laser.mdl");
1599 precache_model("models/ebomb.mdl");
1602 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1604 if (!self.noise && self.music) // quake 3 uses the music field
1605 self.noise = self.music;
1607 // plays music for the level if there is any
1610 precache_sound (self.noise);
1611 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1616 // sorry, but using \ in macros breaks line numbers
1617 #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
1618 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1619 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1622 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1624 if (clienttype(e) == CLIENTTYPE_REAL)
1627 WRITESPECTATABLE_MSG_ONE({
1628 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1629 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1630 WriteByte(MSG_ONE, id);
1631 WriteString(MSG_ONE, s);
1632 if (id != 0 && s != "")
1634 WriteByte(MSG_ONE, duration);
1635 WriteByte(MSG_ONE, countdown_num);
1640 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1642 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1644 // WARNING: this kills the trace globals
1645 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1646 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1648 #define INITPRIO_FIRST 0
1649 #define INITPRIO_GAMETYPE 0
1650 #define INITPRIO_GAMETYPE_FALLBACK 1
1651 #define INITPRIO_FINDTARGET 10
1652 #define INITPRIO_DROPTOFLOOR 20
1653 #define INITPRIO_SETLOCATION 90
1654 #define INITPRIO_LINKDOORS 91
1655 #define INITPRIO_LAST 99
1657 .void(void) initialize_entity;
1658 .float initialize_entity_order;
1659 .entity initialize_entity_next;
1660 entity initialize_entity_first;
1662 void make_safe_for_remove(entity e)
1664 if (e.initialize_entity)
1667 for (ent = initialize_entity_first; ent; )
1669 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1671 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1672 // skip it in linked list
1675 prev.initialize_entity_next = ent.initialize_entity_next;
1676 ent = prev.initialize_entity_next;
1680 initialize_entity_first = ent.initialize_entity_next;
1681 ent = initialize_entity_first;
1687 ent = ent.initialize_entity_next;
1693 void objerror(string s)
1695 make_safe_for_remove(self);
1696 objerror_builtin(s);
1699 .float remove_except_protected_forbidden;
1700 void remove_except_protected(entity e)
1702 if(e.remove_except_protected_forbidden)
1703 error("not allowed to remove this at this point");
1707 void remove_unsafely(entity e)
1709 if(e.classname == "spike")
1710 error("Removing spikes is forbidden (crylink bug), please report");
1714 void remove_safely(entity e)
1716 make_safe_for_remove(e);
1720 void InitializeEntity(entity e, void(void) func, float order)
1724 if (!e || e.initialize_entity)
1726 // make a proxy initializer entity
1730 e.classname = "initialize_entity";
1734 e.initialize_entity = func;
1735 e.initialize_entity_order = order;
1737 cur = initialize_entity_first;
1740 if (!cur || cur.initialize_entity_order > order)
1742 // insert between prev and cur
1744 prev.initialize_entity_next = e;
1746 initialize_entity_first = e;
1747 e.initialize_entity_next = cur;
1751 cur = cur.initialize_entity_next;
1754 void InitializeEntitiesRun()
1757 startoflist = initialize_entity_first;
1758 initialize_entity_first = world;
1759 remove = remove_except_protected;
1760 for (self = startoflist; self; self = self.initialize_entity_next)
1762 self.remove_except_protected_forbidden = 1;
1764 for (self = startoflist; self; )
1767 var void(void) func;
1768 e = self.initialize_entity_next;
1769 func = self.initialize_entity;
1770 self.initialize_entity_order = 0;
1771 self.initialize_entity = func_null;
1772 self.initialize_entity_next = world;
1773 self.remove_except_protected_forbidden = 0;
1774 if (self.classname == "initialize_entity")
1778 remove_builtin(self);
1781 //dprint("Delayed initialization: ", self.classname, "\n");
1782 if(func != func_null)
1787 backtrace(strcat("Null function in: ", self.classname, "\n"));
1791 remove = remove_unsafely;
1794 .float uncustomizeentityforclient_set;
1795 .void(void) uncustomizeentityforclient;
1796 void(void) SUB_Nullpointer = #0;
1797 void UncustomizeEntitiesRun()
1801 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1802 self.uncustomizeentityforclient();
1805 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1807 e.customizeentityforclient = customizer;
1808 e.uncustomizeentityforclient = uncustomizer;
1809 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1813 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1816 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1820 if (e.classname == "")
1821 e.classname = "net_linked";
1823 if (e.model == "" || self.modelindex == 0)
1827 setmodel(e, "null");
1831 e.SendEntity = sendfunc;
1832 e.SendFlags = 0xFFFFFF;
1835 e.effects |= EF_NODEPTHTEST;
1839 e.nextthink = time + dt;
1840 e.think = SUB_Remove;
1844 void adaptor_think2touch()
1853 void adaptor_think2use()
1865 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1867 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
1868 self.projectiledeathtype |= HITTYPE_SPLASH;
1869 adaptor_think2use();
1872 // deferred dropping
1873 void DropToFloor_Handler()
1875 droptofloor_builtin();
1876 self.dropped_origin = self.origin;
1881 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1886 float trace_hits_box_a0, trace_hits_box_a1;
1888 float trace_hits_box_1d(float end, float thmi, float thma)
1892 // just check if x is in range
1900 // do the trace with respect to x
1901 // 0 -> end has to stay in thmi -> thma
1902 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1903 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1904 if (trace_hits_box_a0 > trace_hits_box_a1)
1910 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1915 // now it is a trace from 0 to end
1917 trace_hits_box_a0 = 0;
1918 trace_hits_box_a1 = 1;
1920 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1922 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1924 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1930 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1932 return trace_hits_box(start, end, thmi - ma, thma - mi);
1935 float SUB_NoImpactCheck()
1937 // zero hitcontents = this is not the real impact, but either the
1938 // mirror-impact of something hitting the projectile instead of the
1939 // projectile hitting the something, or a touchareagrid one. Neither of
1940 // these stop the projectile from moving, so...
1941 if(trace_dphitcontents == 0)
1943 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1944 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)));
1947 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1949 if (other == world && self.size != '0 0 0')
1952 tic = self.velocity * sys_frametime;
1953 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1954 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1955 if (trace_fraction >= 1)
1957 dprint("Odd... did not hit...?\n");
1959 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1961 dprint("Detected and prevented the sky-grapple bug.\n");
1969 #define SUB_OwnerCheck() (other && (other == self.owner))
1971 void RemoveGrapplingHook(entity pl);
1972 void W_Crylink_Dequeue(entity e);
1973 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1975 if(SUB_OwnerCheck())
1977 if(SUB_NoImpactCheck())
1979 if(self.classname == "grapplinghook")
1980 RemoveGrapplingHook(self.realowner);
1981 else if(self.classname == "spike")
1983 W_Crylink_Dequeue(self);
1990 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1991 UpdateCSQCProjectile(self);
1994 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1996 float MAX_IPBAN_URIS = 16;
1998 float URI_GET_DISCARD = 0;
1999 float URI_GET_IPBAN = 1;
2000 float URI_GET_IPBAN_END = 16;
2002 void URI_Get_Callback(float id, float status, string data)
2004 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2006 dprint("\nEnd of data.\n");
2008 if(url_URI_Get_Callback(id, status, data))
2012 else if (id == URI_GET_DISCARD)
2016 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2019 OnlineBanList_URI_Get_Callback(id, status, data);
2023 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2027 void print_to(entity e, string s)
2030 sprint(e, strcat(s, "\n"));
2035 string uid2name(string myuid) {
2037 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2040 s = "^1Unregistered Player";
2044 float race_readTime(string map, float pos)
2052 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2055 string race_readUID(string map, float pos)
2063 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2066 float race_readPos(string map, float t) {
2068 for (i = 1; i <= RANKINGS_CNT; ++i)
2069 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2072 return 0; // pos is zero if unranked
2075 void race_writeTime(string map, float t, string myuid)
2084 newpos = race_readPos(map, t);
2087 for(i = 1; i <= RANKINGS_CNT; ++i)
2089 if(race_readUID(map, i) == myuid)
2092 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2093 for (i = prevpos; i > newpos; --i) {
2094 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2095 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2097 } else { // player has no ranked record yet
2098 for (i = RANKINGS_CNT; i > newpos; --i) {
2099 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2100 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2104 // store new time itself
2105 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2106 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2109 string race_readName(string map, float pos)
2117 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2120 string race_placeName(float pos) {
2121 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2123 if(mod(pos, 10) == 1)
2124 return strcat(ftos(pos), "st");
2125 else if(mod(pos, 10) == 2)
2126 return strcat(ftos(pos), "nd");
2127 else if(mod(pos, 10) == 3)
2128 return strcat(ftos(pos), "rd");
2130 return strcat(ftos(pos), "th");
2133 return strcat(ftos(pos), "th");
2135 string getrecords(float page) // 50 records per page
2149 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2151 if (MapInfo_Get_ByID(i))
2153 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2157 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2158 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2166 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2168 if (MapInfo_Get_ByID(i))
2170 r = race_readTime(MapInfo_Map_bspname, 1);
2173 h = race_readName(MapInfo_Map_bspname, 1);
2174 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2182 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2184 if (MapInfo_Get_ByID(i))
2186 r = race_readTime(MapInfo_Map_bspname, 1);
2189 h = race_readName(MapInfo_Map_bspname, 1);
2190 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2196 MapInfo_ClearTemps();
2198 if (s == "" && page == 0)
2199 return "No records are available on this server.\n";
2204 string getrankings()
2217 for (i = 1; i <= RANKINGS_CNT; ++i)
2219 t = race_readTime(map, i);
2222 n = race_readName(map, i);
2223 p = race_placeName(i);
2224 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2227 MapInfo_ClearTemps();
2230 return strcat("No records are available for the map: ", map, "\n");
2232 return strcat("Records for ", map, ":\n", s);
2235 #define LADDER_FIRSTPOINT 100
2236 #define LADDER_CNT 10
2237 // position X still gives LADDER_FIRSTPOINT/X points
2238 #define LADDER_SIZE 30
2239 // ladder shows the top X players
2240 string top_uids[LADDER_SIZE];
2241 float top_scores[LADDER_SIZE];
2244 float i, j, k, uidcnt;
2258 for (k = 0; k < MapInfo_count; ++k)
2260 if (MapInfo_Get_ByID(k))
2262 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2263 if(i == 0) // speed award
2265 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2268 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2270 else // normal record, if it exists (else break)
2272 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2275 myuid = race_readUID(MapInfo_Map_bspname, i);
2278 // string s contains:
2279 // arg 0 = # of speed recs
2280 // arg 1 = # of 1st place recs
2281 // arg 2 = # of 2nd place recs
2283 // LADDER_CNT+1 = total points
2285 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2288 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2290 for (j = 0; j <= LADDER_CNT + 1; ++j)
2292 if(j != LADDER_CNT + 1)
2293 temp_s = strcat(temp_s, "0 ");
2295 temp_s = strcat(temp_s, "0");
2299 tokenize_console(temp_s);
2302 if(i == 0) // speed award
2303 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2305 if(j == 0) // speed award
2306 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2308 s = strcat(s, " ", argv(j)); // just copy over everything else
2311 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2314 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2315 else if(j == i) // wanted rec!
2316 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2318 s = strcat(s, " ", argv(j)); // just copy over everything else
2321 // total points are (by default) calculated like this:
2322 // speedrec = floor(100 / 10) = 10 points
2323 // 1st place = floor(100 / 1) = 100 points
2324 // 2nd place = floor(100 / 2) = 50 points
2325 // 3rd place = floor(100 / 3) = 33 points
2326 // 4th place = floor(100 / 4) = 25 points
2327 // 5th place = floor(100 / 5) = 20 points
2331 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2333 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2335 db_put(TemporaryDB, strcat("ladder", myuid), s);
2342 for (i = 0; i <= uidcnt; ++i) // for each known uid
2344 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2345 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2346 tokenize_console(temp_s);
2347 thiscnt = stof(argv(LADDER_CNT+1));
2349 if(thiscnt > top_scores[LADDER_SIZE-1])
2350 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2352 if(thiscnt > top_scores[j])
2354 for (k = LADDER_SIZE-1; k >= j; --k)
2356 top_uids[k] = top_uids[k-1];
2357 top_scores[k] = top_scores[k-1];
2359 top_uids[j] = thisuid;
2360 top_scores[j] = thiscnt;
2366 s = "^3-----------------------\n\n";
2368 s = strcat(s, "Pos ^3|");
2369 s = strcat(s, " ^7Total ^3|");
2370 for (i = 1; i <= LADDER_CNT; ++i)
2372 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2374 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2376 s = strcat(s, "\n^3----+--------");
2377 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2379 s = strcat(s, "+-----");
2382 for (i = 1; i <= LADDER_CNT - 9; ++i)
2384 s = strcat(s, "+------");
2388 s = strcat(s, "+--------------+--------------------\n");
2390 for (i = 0; i < LADDER_SIZE; ++i)
2392 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2393 tokenize_console(temp_s);
2394 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2396 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2397 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2398 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2400 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2403 for (j = 10; j <= LADDER_CNT; ++j)
2405 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2409 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2410 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2413 MapInfo_ClearTemps();
2416 return "No ladder on this server!\n";
2418 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2422 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2425 vector start, org, delta, end, enddown, mstart;
2428 m = e.dphitcontentsmask;
2429 e.dphitcontentsmask = goodcontents | badcontents;
2432 delta = world.maxs - world.mins;
2434 for (i = 0; i < attempts; ++i)
2436 start_x = org_x + random() * delta_x;
2437 start_y = org_y + random() * delta_y;
2438 start_z = org_z + random() * delta_z;
2440 // rule 1: start inside world bounds, and outside
2441 // solid, and don't start from somewhere where you can
2442 // fall down to evil
2443 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2444 if (trace_fraction >= 1)
2446 if (trace_startsolid)
2448 if (trace_dphitcontents & badcontents)
2450 if (trace_dphitq3surfaceflags & badsurfaceflags)
2453 // rule 2: if we are too high, lower the point
2454 if (trace_fraction * delta_z > maxaboveground)
2455 start = trace_endpos + '0 0 1' * maxaboveground;
2456 enddown = trace_endpos;
2458 // rule 3: make sure we aren't outside the map. This only works
2459 // for somewhat well formed maps. A good rule of thumb is that
2460 // the map should have a convex outside hull.
2461 // these can be traceLINES as we already verified the starting box
2462 mstart = start + 0.5 * (e.mins + e.maxs);
2463 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2464 if (trace_fraction >= 1)
2466 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2467 if (trace_fraction >= 1)
2469 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2470 if (trace_fraction >= 1)
2472 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2473 if (trace_fraction >= 1)
2475 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2476 if (trace_fraction >= 1)
2479 // rule 4: we must "see" some spawnpoint
2480 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2481 if(checkpvs(mstart, sp))
2485 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2486 if(checkpvs(mstart, sp))
2492 // find a random vector to "look at"
2493 end_x = org_x + random() * delta_x;
2494 end_y = org_y + random() * delta_y;
2495 end_z = org_z + random() * delta_z;
2496 end = start + normalize(end - start) * vlen(delta);
2498 // rule 4: start TO end must not be too short
2499 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2500 if (trace_startsolid)
2502 if (trace_fraction < minviewdistance / vlen(delta))
2505 // rule 5: don't want to look at sky
2506 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2509 // rule 6: we must not end up in trigger_hurt
2510 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2516 e.dphitcontentsmask = m;
2520 setorigin(e, start);
2521 e.angles = vectoangles(end - start);
2522 dprint("Needed ", ftos(i + 1), " attempts\n");
2529 float zcurveparticles_effectno;
2530 vector zcurveparticles_start;
2531 float zcurveparticles_spd;
2533 void endzcurveparticles()
2535 if(zcurveparticles_effectno)
2538 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2540 zcurveparticles_effectno = 0;
2543 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2545 spd = bound(0, floor(spd / 16), 32767);
2546 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2548 endzcurveparticles();
2549 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2550 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2551 WriteShort(MSG_BROADCAST, effectno);
2552 WriteCoord(MSG_BROADCAST, start_x);
2553 WriteCoord(MSG_BROADCAST, start_y);
2554 WriteCoord(MSG_BROADCAST, start_z);
2555 zcurveparticles_effectno = effectno;
2556 zcurveparticles_start = start;
2559 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2560 WriteCoord(MSG_BROADCAST, end_x);
2561 WriteCoord(MSG_BROADCAST, end_y);
2562 WriteCoord(MSG_BROADCAST, end_z);
2563 WriteCoord(MSG_BROADCAST, end_dz);
2564 zcurveparticles_spd = spd;
2567 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2570 vector vecxy, velxy;
2572 vecxy = end - start;
2577 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2579 endzcurveparticles();
2580 trailparticles(world, effectno, start, end);
2584 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2585 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2588 void write_recordmarker(entity pl, float tstart, float dt)
2590 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2592 // also write a marker into demo files for demotc-race-record-extractor to find
2595 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2596 " ", ftos(tstart), " ", ftos(dt), "\n"));
2599 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2612 if(allowcenter) // 2: allow center handedness
2625 if(allowcenter) // 2: allow center handedness
2641 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2646 if (autocvar_g_shootfromeye)
2659 else if (autocvar_g_shootfromcenter)
2664 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2674 else if (autocvar_g_shootfromclient)
2676 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2681 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2683 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2687 void attach_sameorigin(entity e, entity to, string tag)
2689 vector org, t_forward, t_left, t_up, e_forward, e_up;
2696 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2697 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2698 t_forward = v_forward * tagscale;
2699 t_left = v_right * -tagscale;
2700 t_up = v_up * tagscale;
2702 e.origin_x = org * t_forward;
2703 e.origin_y = org * t_left;
2704 e.origin_z = org * t_up;
2706 // current forward and up directions
2707 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2708 e.angles = AnglesTransform_FromVAngles(e.angles);
2710 e.angles = AnglesTransform_FromAngles(e.angles);
2711 fixedmakevectors(e.angles);
2713 // untransform forward, up!
2714 e_forward_x = v_forward * t_forward;
2715 e_forward_y = v_forward * t_left;
2716 e_forward_z = v_forward * t_up;
2717 e_up_x = v_up * t_forward;
2718 e_up_y = v_up * t_left;
2719 e_up_z = v_up * t_up;
2721 e.angles = fixedvectoangles2(e_forward, e_up);
2722 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2723 e.angles = AnglesTransform_ToVAngles(e.angles);
2725 e.angles = AnglesTransform_ToAngles(e.angles);
2727 setattachment(e, to, tag);
2728 setorigin(e, e.origin);
2731 void detach_sameorigin(entity e)
2734 org = gettaginfo(e, 0);
2735 e.angles = fixedvectoangles2(v_forward, v_up);
2736 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2737 e.angles = AnglesTransform_ToVAngles(e.angles);
2739 e.angles = AnglesTransform_ToAngles(e.angles);
2741 setattachment(e, world, "");
2742 setorigin(e, e.origin);
2745 void follow_sameorigin(entity e, entity to)
2747 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2748 e.aiment = to; // make the hole follow bmodel
2749 e.punchangle = to.angles; // the original angles of bmodel
2750 e.view_ofs = e.origin - to.origin; // relative origin
2751 e.v_angle = e.angles - to.angles; // relative angles
2754 void unfollow_sameorigin(entity e)
2756 e.movetype = MOVETYPE_NONE;
2759 entity gettaginfo_relative_ent;
2760 vector gettaginfo_relative(entity e, float tag)
2762 if (!gettaginfo_relative_ent)
2764 gettaginfo_relative_ent = spawn();
2765 gettaginfo_relative_ent.effects = EF_NODRAW;
2767 gettaginfo_relative_ent.model = e.model;
2768 gettaginfo_relative_ent.modelindex = e.modelindex;
2769 gettaginfo_relative_ent.frame = e.frame;
2770 return gettaginfo(gettaginfo_relative_ent, tag);
2773 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2777 if (pl.soundentity.cnt & p)
2779 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2780 pl.soundentity.cnt |= p;
2783 void SoundEntity_StopSound(entity pl, float chan)
2787 if (pl.soundentity.cnt & p)
2789 stopsoundto(MSG_ALL, pl.soundentity, chan);
2790 pl.soundentity.cnt &~= p;
2794 void SoundEntity_Attach(entity pl)
2796 pl.soundentity = spawn();
2797 pl.soundentity.classname = "soundentity";
2798 pl.soundentity.owner = pl;
2799 setattachment(pl.soundentity, pl, "");
2800 setmodel(pl.soundentity, "null");
2803 void SoundEntity_Detach(entity pl)
2806 for (i = 0; i <= 7; ++i)
2807 SoundEntity_StopSound(pl, i);
2811 float ParseCommandPlayerSlotTarget_firsttoken;
2812 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2820 ParseCommandPlayerSlotTarget_firsttoken = -1;
2824 if (substring(argv(idx), 0, 1) == "#")
2826 s = substring(argv(idx), 1, -1);
2828 if (s == "") if (tokens > idx)
2833 ParseCommandPlayerSlotTarget_firsttoken = idx;
2835 if (s == ftos(n) && n > 0 && n <= maxclients)
2838 if (e.flags & FL_CLIENT)
2844 // it must be a nick name
2847 ParseCommandPlayerSlotTarget_firsttoken = idx;
2850 FOR_EACH_CLIENT(head)
2851 if (head.netname == s)
2859 s = strdecolorize(s);
2861 FOR_EACH_CLIENT(head)
2862 if (strdecolorize(head.netname) == s)
2877 float modeleffect_SendEntity(entity to, float sf)
2880 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2883 if(self.velocity != '0 0 0')
2885 if(self.angles != '0 0 0')
2887 if(self.avelocity != '0 0 0')
2890 WriteByte(MSG_ENTITY, f);
2891 WriteShort(MSG_ENTITY, self.modelindex);
2892 WriteByte(MSG_ENTITY, self.skin);
2893 WriteByte(MSG_ENTITY, self.frame);
2894 WriteCoord(MSG_ENTITY, self.origin_x);
2895 WriteCoord(MSG_ENTITY, self.origin_y);
2896 WriteCoord(MSG_ENTITY, self.origin_z);
2899 WriteCoord(MSG_ENTITY, self.velocity_x);
2900 WriteCoord(MSG_ENTITY, self.velocity_y);
2901 WriteCoord(MSG_ENTITY, self.velocity_z);
2905 WriteCoord(MSG_ENTITY, self.angles_x);
2906 WriteCoord(MSG_ENTITY, self.angles_y);
2907 WriteCoord(MSG_ENTITY, self.angles_z);
2911 WriteCoord(MSG_ENTITY, self.avelocity_x);
2912 WriteCoord(MSG_ENTITY, self.avelocity_y);
2913 WriteCoord(MSG_ENTITY, self.avelocity_z);
2915 WriteShort(MSG_ENTITY, self.scale * 256.0);
2916 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2917 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2918 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2919 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2924 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)
2929 e.classname = "modeleffect";
2937 e.teleport_time = t1;
2941 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2945 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2948 sz = max(e.scale, e.scale2);
2949 setsize(e, e.mins * sz, e.maxs * sz);
2950 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2953 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2955 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2958 float randombit(float bits)
2960 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2969 for(f = 1; f <= bits; f *= 2)
2978 r = (r - 1) / (n - 1);
2985 float randombits(float bits, float k, float error_return)
2989 while(k > 0 && bits != r)
2991 r += randombit(bits - r);
3000 void randombit_test(float bits, float iter)
3004 print(ftos(randombit(bits)), "\n");
3009 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3011 if(halflifedist > 0)
3012 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3013 else if(halflifedist < 0)
3014 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3023 #define cvar_string_normal cvar_string_builtin
3024 #define cvar_normal cvar_builtin
3026 string cvar_string_normal(string n)
3028 if not(cvar_type(n) & 1)
3029 backtrace(strcat("Attempt to access undefined cvar: ", n));
3030 return cvar_string_builtin(n);
3033 float cvar_normal(string n)
3035 return stof(cvar_string_normal(n));
3038 #define cvar_set_normal cvar_set_builtin
3046 oself.think = SUB_Remove;
3047 oself.nextthink = time;
3053 Execute func() after time + fdelay.
3054 self when func is executed = self when defer is called
3056 void defer(float fdelay, void() func)
3063 e.think = defer_think;
3064 e.nextthink = time + fdelay;
3067 .string aiment_classname;
3068 .float aiment_deadflag;
3069 void SetMovetypeFollow(entity ent, entity e)
3071 // FIXME this may not be warpzone aware
3072 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3073 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.
3074 ent.aiment = e; // make the hole follow bmodel
3075 ent.punchangle = e.angles; // the original angles of bmodel
3076 ent.view_ofs = ent.origin - e.origin; // relative origin
3077 ent.v_angle = ent.angles - e.angles; // relative angles
3078 ent.aiment_classname = strzone(e.classname);
3079 ent.aiment_deadflag = e.deadflag;
3081 void UnsetMovetypeFollow(entity ent)
3083 ent.movetype = MOVETYPE_FLY;
3084 PROJECTILE_MAKETRIGGER(ent);
3087 float LostMovetypeFollow(entity ent)
3090 if(ent.movetype != MOVETYPE_FOLLOW)
3096 if(ent.aiment.classname != ent.aiment_classname)
3098 if(ent.aiment.deadflag != ent.aiment_deadflag)
3104 float isPushable(entity e)
3111 case "droppedweapon":
3112 case "keepawayball":
3113 case "nexball_basketball":
3114 case "nexball_football":
3116 case "bullet": // antilagged bullets can't hit this either
3119 if (e.projectiledeathtype)