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 else if (e.realowner && e.realowner != e)
1250 else if (e.owner && e.owner != e)
1255 // sounds to self may always pass
1256 if (dest == MSG_ONE)
1257 if (e == msg_entity)
1259 // sounds by players can be removed
1260 if (autocvar_bot_sound_monopoly)
1261 if (clienttype(e) == CLIENTTYPE_REAL)
1263 // anything else may pass
1267 #ifdef COMPAT_XON010_CHANNELS
1268 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1269 void sound(entity e, float chan, string samp, float vol, float atten)
1271 if (!sound_allowed(MSG_BROADCAST, e))
1273 sound_builtin(e, chan, samp, vol, atten);
1277 void sound(entity e, float chan, string samp, float vol, float atten)
1279 if (!sound_allowed(MSG_BROADCAST, e))
1281 sound7(e, chan, samp, vol, atten, 0, 0);
1285 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1289 if (!sound_allowed(dest, e))
1292 entno = num_for_edict(e);
1293 idx = precache_sound_index(samp);
1298 atten = floor(atten * 64);
1299 vol = floor(vol * 255);
1302 sflags |= SND_VOLUME;
1304 sflags |= SND_ATTENUATION;
1305 if (entno >= 8192 || chan < 0 || chan > 7)
1306 sflags |= SND_LARGEENTITY;
1308 sflags |= SND_LARGESOUND;
1310 WriteByte(dest, SVC_SOUND);
1311 WriteByte(dest, sflags);
1312 if (sflags & SND_VOLUME)
1313 WriteByte(dest, vol);
1314 if (sflags & SND_ATTENUATION)
1315 WriteByte(dest, atten);
1316 if (sflags & SND_LARGEENTITY)
1318 WriteShort(dest, entno);
1319 WriteByte(dest, chan);
1323 WriteShort(dest, entno * 8 + chan);
1325 if (sflags & SND_LARGESOUND)
1326 WriteShort(dest, idx);
1328 WriteByte(dest, idx);
1330 WriteCoord(dest, o_x);
1331 WriteCoord(dest, o_y);
1332 WriteCoord(dest, o_z);
1334 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1338 if (!sound_allowed(dest, e))
1341 o = e.origin + 0.5 * (e.mins + e.maxs);
1342 soundtoat(dest, e, o, chan, samp, vol, atten);
1344 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1346 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1348 void stopsoundto(float dest, entity e, float chan)
1352 if (!sound_allowed(dest, e))
1355 entno = num_for_edict(e);
1357 if (entno >= 8192 || chan < 0 || chan > 7)
1360 idx = precache_sound_index("misc/null.wav");
1361 sflags = SND_LARGEENTITY;
1363 sflags |= SND_LARGESOUND;
1364 WriteByte(dest, SVC_SOUND);
1365 WriteByte(dest, sflags);
1366 WriteShort(dest, entno);
1367 WriteByte(dest, chan);
1368 if (sflags & SND_LARGESOUND)
1369 WriteShort(dest, idx);
1371 WriteByte(dest, idx);
1372 WriteCoord(dest, e.origin_x);
1373 WriteCoord(dest, e.origin_y);
1374 WriteCoord(dest, e.origin_z);
1378 WriteByte(dest, SVC_STOPSOUND);
1379 WriteShort(dest, entno * 8 + chan);
1382 void stopsound(entity e, float chan)
1384 if (!sound_allowed(MSG_BROADCAST, e))
1387 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1388 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1391 void play2(entity e, string filename)
1393 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1395 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1398 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1400 float spamsound(entity e, float chan, string samp, float vol, float atten)
1402 if (!sound_allowed(MSG_BROADCAST, e))
1405 if (time > e.spamtime)
1408 sound(e, chan, samp, vol, atten);
1414 void play2team(float t, string filename)
1418 if (autocvar_bot_sound_monopoly)
1421 FOR_EACH_REALPLAYER(head)
1424 play2(head, filename);
1428 void play2all(string samp)
1430 if (autocvar_bot_sound_monopoly)
1433 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1436 void PrecachePlayerSounds(string f);
1437 void precache_playermodel(string m)
1439 float globhandle, i, n;
1442 if(substring(m, -9,5) == "_lod1")
1444 if(substring(m, -9,5) == "_lod2")
1449 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1452 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1457 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1460 n = search_getsize(globhandle);
1461 for (i = 0; i < n; ++i)
1463 //print(search_getfilename(globhandle, i), "\n");
1464 f = search_getfilename(globhandle, i);
1465 PrecachePlayerSounds(f);
1467 search_end(globhandle);
1469 void precache_all_playermodels(string pattern)
1471 float globhandle, i, n;
1474 globhandle = search_begin(pattern, TRUE, FALSE);
1477 n = search_getsize(globhandle);
1478 for (i = 0; i < n; ++i)
1480 //print(search_getfilename(globhandle, i), "\n");
1481 f = search_getfilename(globhandle, i);
1482 precache_playermodel(f);
1484 search_end(globhandle);
1489 // gamemode related things
1490 precache_model ("models/misc/chatbubble.spr");
1493 precache_model ("models/runematch/curse.mdl");
1494 precache_model ("models/runematch/rune.mdl");
1497 #ifdef TTURRETS_ENABLED
1498 if (autocvar_g_turrets)
1502 // Precache all player models if desired
1503 if (autocvar_sv_precacheplayermodels)
1505 PrecachePlayerSounds("sound/player/default.sounds");
1506 precache_all_playermodels("models/player/*.zym");
1507 precache_all_playermodels("models/player/*.dpm");
1508 precache_all_playermodels("models/player/*.md3");
1509 precache_all_playermodels("models/player/*.psk");
1510 precache_all_playermodels("models/player/*.iqm");
1513 if (autocvar_sv_defaultcharacter)
1516 s = autocvar_sv_defaultplayermodel_red;
1518 precache_playermodel(s);
1519 s = autocvar_sv_defaultplayermodel_blue;
1521 precache_playermodel(s);
1522 s = autocvar_sv_defaultplayermodel_yellow;
1524 precache_playermodel(s);
1525 s = autocvar_sv_defaultplayermodel_pink;
1527 precache_playermodel(s);
1528 s = autocvar_sv_defaultplayermodel;
1530 precache_playermodel(s);
1535 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1536 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1539 // gore and miscellaneous sounds
1540 //precache_sound ("misc/h2ohit.wav");
1541 precache_model ("models/hook.md3");
1542 precache_sound ("misc/armorimpact.wav");
1543 precache_sound ("misc/bodyimpact1.wav");
1544 precache_sound ("misc/bodyimpact2.wav");
1545 precache_sound ("misc/gib.wav");
1546 precache_sound ("misc/gib_splat01.wav");
1547 precache_sound ("misc/gib_splat02.wav");
1548 precache_sound ("misc/gib_splat03.wav");
1549 precache_sound ("misc/gib_splat04.wav");
1550 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1551 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1552 precache_sound ("misc/null.wav");
1553 precache_sound ("misc/spawn.wav");
1554 precache_sound ("misc/talk.wav");
1555 precache_sound ("misc/teleport.wav");
1556 precache_sound ("misc/poweroff.wav");
1557 precache_sound ("player/lava.wav");
1558 precache_sound ("player/slime.wav");
1561 precache_sound ("misc/jetpack_fly.wav");
1563 precache_model ("models/sprites/0.spr32");
1564 precache_model ("models/sprites/1.spr32");
1565 precache_model ("models/sprites/2.spr32");
1566 precache_model ("models/sprites/3.spr32");
1567 precache_model ("models/sprites/4.spr32");
1568 precache_model ("models/sprites/5.spr32");
1569 precache_model ("models/sprites/6.spr32");
1570 precache_model ("models/sprites/7.spr32");
1571 precache_model ("models/sprites/8.spr32");
1572 precache_model ("models/sprites/9.spr32");
1573 precache_model ("models/sprites/10.spr32");
1575 // common weapon precaches
1576 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1577 precache_sound ("weapons/weapon_switch.wav");
1578 precache_sound ("weapons/weaponpickup.wav");
1579 precache_sound ("weapons/unavailable.wav");
1580 precache_sound ("weapons/dryfire.wav");
1581 if (g_grappling_hook)
1583 precache_sound ("weapons/hook_fire.wav"); // hook
1584 precache_sound ("weapons/hook_impact.wav"); // hook
1587 if(autocvar_sv_precacheweapons)
1589 //precache weapon models/sounds
1592 while (wep <= WEP_LAST)
1594 weapon_action(wep, WR_PRECACHE);
1599 precache_model("models/elaser.mdl");
1600 precache_model("models/laser.mdl");
1601 precache_model("models/ebomb.mdl");
1604 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1606 if (!self.noise && self.music) // quake 3 uses the music field
1607 self.noise = self.music;
1609 // plays music for the level if there is any
1612 precache_sound (self.noise);
1613 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1618 // sorry, but using \ in macros breaks line numbers
1619 #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
1620 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1621 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1624 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1626 if (clienttype(e) == CLIENTTYPE_REAL)
1629 WRITESPECTATABLE_MSG_ONE({
1630 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1631 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1632 WriteByte(MSG_ONE, id);
1633 WriteString(MSG_ONE, s);
1634 if (id != 0 && s != "")
1636 WriteByte(MSG_ONE, duration);
1637 WriteByte(MSG_ONE, countdown_num);
1642 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1644 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1646 // WARNING: this kills the trace globals
1647 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1648 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1650 #define INITPRIO_FIRST 0
1651 #define INITPRIO_GAMETYPE 0
1652 #define INITPRIO_GAMETYPE_FALLBACK 1
1653 #define INITPRIO_FINDTARGET 10
1654 #define INITPRIO_DROPTOFLOOR 20
1655 #define INITPRIO_SETLOCATION 90
1656 #define INITPRIO_LINKDOORS 91
1657 #define INITPRIO_LAST 99
1659 .void(void) initialize_entity;
1660 .float initialize_entity_order;
1661 .entity initialize_entity_next;
1662 entity initialize_entity_first;
1664 void make_safe_for_remove(entity e)
1666 if (e.initialize_entity)
1669 for (ent = initialize_entity_first; ent; )
1671 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1673 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1674 // skip it in linked list
1677 prev.initialize_entity_next = ent.initialize_entity_next;
1678 ent = prev.initialize_entity_next;
1682 initialize_entity_first = ent.initialize_entity_next;
1683 ent = initialize_entity_first;
1689 ent = ent.initialize_entity_next;
1695 void objerror(string s)
1697 make_safe_for_remove(self);
1698 objerror_builtin(s);
1701 .float remove_except_protected_forbidden;
1702 void remove_except_protected(entity e)
1704 if(e.remove_except_protected_forbidden)
1705 error("not allowed to remove this at this point");
1709 void remove_unsafely(entity e)
1711 if(e.classname == "spike")
1712 error("Removing spikes is forbidden (crylink bug), please report");
1716 void remove_safely(entity e)
1718 make_safe_for_remove(e);
1722 void InitializeEntity(entity e, void(void) func, float order)
1726 if (!e || e.initialize_entity)
1728 // make a proxy initializer entity
1732 e.classname = "initialize_entity";
1736 e.initialize_entity = func;
1737 e.initialize_entity_order = order;
1739 cur = initialize_entity_first;
1742 if (!cur || cur.initialize_entity_order > order)
1744 // insert between prev and cur
1746 prev.initialize_entity_next = e;
1748 initialize_entity_first = e;
1749 e.initialize_entity_next = cur;
1753 cur = cur.initialize_entity_next;
1756 void InitializeEntitiesRun()
1759 startoflist = initialize_entity_first;
1760 initialize_entity_first = world;
1761 remove = remove_except_protected;
1762 for (self = startoflist; self; self = self.initialize_entity_next)
1764 self.remove_except_protected_forbidden = 1;
1766 for (self = startoflist; self; )
1769 var void(void) func;
1770 e = self.initialize_entity_next;
1771 func = self.initialize_entity;
1772 self.initialize_entity_order = 0;
1773 self.initialize_entity = func_null;
1774 self.initialize_entity_next = world;
1775 self.remove_except_protected_forbidden = 0;
1776 if (self.classname == "initialize_entity")
1780 remove_builtin(self);
1783 //dprint("Delayed initialization: ", self.classname, "\n");
1784 if(func != func_null)
1789 backtrace(strcat("Null function in: ", self.classname, "\n"));
1793 remove = remove_unsafely;
1796 .float uncustomizeentityforclient_set;
1797 .void(void) uncustomizeentityforclient;
1798 void(void) SUB_Nullpointer = #0;
1799 void UncustomizeEntitiesRun()
1803 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1804 self.uncustomizeentityforclient();
1807 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1809 e.customizeentityforclient = customizer;
1810 e.uncustomizeentityforclient = uncustomizer;
1811 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1815 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1818 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1822 if (e.classname == "")
1823 e.classname = "net_linked";
1825 if (e.model == "" || self.modelindex == 0)
1829 setmodel(e, "null");
1833 e.SendEntity = sendfunc;
1834 e.SendFlags = 0xFFFFFF;
1837 e.effects |= EF_NODEPTHTEST;
1841 e.nextthink = time + dt;
1842 e.think = SUB_Remove;
1846 void adaptor_think2touch()
1855 void adaptor_think2use()
1867 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1869 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
1870 self.projectiledeathtype |= HITTYPE_SPLASH;
1871 adaptor_think2use();
1874 // deferred dropping
1875 void DropToFloor_Handler()
1877 droptofloor_builtin();
1878 self.dropped_origin = self.origin;
1883 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1888 float trace_hits_box_a0, trace_hits_box_a1;
1890 float trace_hits_box_1d(float end, float thmi, float thma)
1894 // just check if x is in range
1902 // do the trace with respect to x
1903 // 0 -> end has to stay in thmi -> thma
1904 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1905 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1906 if (trace_hits_box_a0 > trace_hits_box_a1)
1912 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1917 // now it is a trace from 0 to end
1919 trace_hits_box_a0 = 0;
1920 trace_hits_box_a1 = 1;
1922 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1924 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1926 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1932 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1934 return trace_hits_box(start, end, thmi - ma, thma - mi);
1937 float SUB_NoImpactCheck()
1939 // zero hitcontents = this is not the real impact, but either the
1940 // mirror-impact of something hitting the projectile instead of the
1941 // projectile hitting the something, or a touchareagrid one. Neither of
1942 // these stop the projectile from moving, so...
1943 if(trace_dphitcontents == 0)
1945 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1946 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)));
1949 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1951 if (other == world && self.size != '0 0 0')
1954 tic = self.velocity * sys_frametime;
1955 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1956 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1957 if (trace_fraction >= 1)
1959 dprint("Odd... did not hit...?\n");
1961 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1963 dprint("Detected and prevented the sky-grapple bug.\n");
1971 #define SUB_OwnerCheck() (other && (other == self.owner))
1973 void RemoveGrapplingHook(entity pl);
1974 void W_Crylink_Dequeue(entity e);
1975 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1977 if(SUB_OwnerCheck())
1979 if(SUB_NoImpactCheck())
1981 if(self.classname == "grapplinghook")
1982 RemoveGrapplingHook(self.realowner);
1983 else if(self.classname == "spike")
1985 W_Crylink_Dequeue(self);
1992 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1993 UpdateCSQCProjectile(self);
1996 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1998 float MAX_IPBAN_URIS = 16;
2000 float URI_GET_DISCARD = 0;
2001 float URI_GET_IPBAN = 1;
2002 float URI_GET_IPBAN_END = 16;
2004 void URI_Get_Callback(float id, float status, string data)
2006 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2008 dprint("\nEnd of data.\n");
2010 if(url_URI_Get_Callback(id, status, data))
2014 else if (id == URI_GET_DISCARD)
2018 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2021 OnlineBanList_URI_Get_Callback(id, status, data);
2025 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2029 void print_to(entity e, string s)
2032 sprint(e, strcat(s, "\n"));
2037 string uid2name(string myuid) {
2039 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2042 s = "^1Unregistered Player";
2046 float race_readTime(string map, float pos)
2054 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2057 string race_readUID(string map, float pos)
2065 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2068 float race_readPos(string map, float t) {
2070 for (i = 1; i <= RANKINGS_CNT; ++i)
2071 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2074 return 0; // pos is zero if unranked
2077 void race_writeTime(string map, float t, string myuid)
2086 newpos = race_readPos(map, t);
2089 for(i = 1; i <= RANKINGS_CNT; ++i)
2091 if(race_readUID(map, i) == myuid)
2094 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2095 for (i = prevpos; i > newpos; --i) {
2096 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2097 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2099 } else { // player has no ranked record yet
2100 for (i = RANKINGS_CNT; i > newpos; --i) {
2101 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2102 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2106 // store new time itself
2107 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2108 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2111 string race_readName(string map, float pos)
2119 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2122 string race_placeName(float pos) {
2123 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2125 if(mod(pos, 10) == 1)
2126 return strcat(ftos(pos), "st");
2127 else if(mod(pos, 10) == 2)
2128 return strcat(ftos(pos), "nd");
2129 else if(mod(pos, 10) == 3)
2130 return strcat(ftos(pos), "rd");
2132 return strcat(ftos(pos), "th");
2135 return strcat(ftos(pos), "th");
2137 string getrecords(float page) // 50 records per page
2151 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2153 if (MapInfo_Get_ByID(i))
2155 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2159 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2160 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2168 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2170 if (MapInfo_Get_ByID(i))
2172 r = race_readTime(MapInfo_Map_bspname, 1);
2175 h = race_readName(MapInfo_Map_bspname, 1);
2176 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2184 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2186 if (MapInfo_Get_ByID(i))
2188 r = race_readTime(MapInfo_Map_bspname, 1);
2191 h = race_readName(MapInfo_Map_bspname, 1);
2192 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2198 MapInfo_ClearTemps();
2200 if (s == "" && page == 0)
2201 return "No records are available on this server.\n";
2206 string getrankings()
2219 for (i = 1; i <= RANKINGS_CNT; ++i)
2221 t = race_readTime(map, i);
2224 n = race_readName(map, i);
2225 p = race_placeName(i);
2226 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2229 MapInfo_ClearTemps();
2232 return strcat("No records are available for the map: ", map, "\n");
2234 return strcat("Records for ", map, ":\n", s);
2237 #define LADDER_FIRSTPOINT 100
2238 #define LADDER_CNT 10
2239 // position X still gives LADDER_FIRSTPOINT/X points
2240 #define LADDER_SIZE 30
2241 // ladder shows the top X players
2242 string top_uids[LADDER_SIZE];
2243 float top_scores[LADDER_SIZE];
2246 float i, j, k, uidcnt;
2260 for (k = 0; k < MapInfo_count; ++k)
2262 if (MapInfo_Get_ByID(k))
2264 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2265 if(i == 0) // speed award
2267 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2270 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2272 else // normal record, if it exists (else break)
2274 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2277 myuid = race_readUID(MapInfo_Map_bspname, i);
2280 // string s contains:
2281 // arg 0 = # of speed recs
2282 // arg 1 = # of 1st place recs
2283 // arg 2 = # of 2nd place recs
2285 // LADDER_CNT+1 = total points
2287 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2290 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2292 for (j = 0; j <= LADDER_CNT + 1; ++j)
2294 if(j != LADDER_CNT + 1)
2295 temp_s = strcat(temp_s, "0 ");
2297 temp_s = strcat(temp_s, "0");
2301 tokenize_console(temp_s);
2304 if(i == 0) // speed award
2305 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2307 if(j == 0) // speed award
2308 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2310 s = strcat(s, " ", argv(j)); // just copy over everything else
2313 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2316 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2317 else if(j == i) // wanted rec!
2318 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2320 s = strcat(s, " ", argv(j)); // just copy over everything else
2323 // total points are (by default) calculated like this:
2324 // speedrec = floor(100 / 10) = 10 points
2325 // 1st place = floor(100 / 1) = 100 points
2326 // 2nd place = floor(100 / 2) = 50 points
2327 // 3rd place = floor(100 / 3) = 33 points
2328 // 4th place = floor(100 / 4) = 25 points
2329 // 5th place = floor(100 / 5) = 20 points
2333 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2335 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2337 db_put(TemporaryDB, strcat("ladder", myuid), s);
2344 for (i = 0; i <= uidcnt; ++i) // for each known uid
2346 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2347 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2348 tokenize_console(temp_s);
2349 thiscnt = stof(argv(LADDER_CNT+1));
2351 if(thiscnt > top_scores[LADDER_SIZE-1])
2352 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2354 if(thiscnt > top_scores[j])
2356 for (k = LADDER_SIZE-1; k >= j; --k)
2358 top_uids[k] = top_uids[k-1];
2359 top_scores[k] = top_scores[k-1];
2361 top_uids[j] = thisuid;
2362 top_scores[j] = thiscnt;
2368 s = "^3-----------------------\n\n";
2370 s = strcat(s, "Pos ^3|");
2371 s = strcat(s, " ^7Total ^3|");
2372 for (i = 1; i <= LADDER_CNT; ++i)
2374 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2376 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2378 s = strcat(s, "\n^3----+--------");
2379 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2381 s = strcat(s, "+-----");
2384 for (i = 1; i <= LADDER_CNT - 9; ++i)
2386 s = strcat(s, "+------");
2390 s = strcat(s, "+--------------+--------------------\n");
2392 for (i = 0; i < LADDER_SIZE; ++i)
2394 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2395 tokenize_console(temp_s);
2396 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2398 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2399 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2400 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2402 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2405 for (j = 10; j <= LADDER_CNT; ++j)
2407 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2411 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2412 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2415 MapInfo_ClearTemps();
2418 return "No ladder on this server!\n";
2420 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2424 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2427 vector start, org, delta, end, enddown, mstart;
2430 m = e.dphitcontentsmask;
2431 e.dphitcontentsmask = goodcontents | badcontents;
2434 delta = world.maxs - world.mins;
2436 for (i = 0; i < attempts; ++i)
2438 start_x = org_x + random() * delta_x;
2439 start_y = org_y + random() * delta_y;
2440 start_z = org_z + random() * delta_z;
2442 // rule 1: start inside world bounds, and outside
2443 // solid, and don't start from somewhere where you can
2444 // fall down to evil
2445 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2446 if (trace_fraction >= 1)
2448 if (trace_startsolid)
2450 if (trace_dphitcontents & badcontents)
2452 if (trace_dphitq3surfaceflags & badsurfaceflags)
2455 // rule 2: if we are too high, lower the point
2456 if (trace_fraction * delta_z > maxaboveground)
2457 start = trace_endpos + '0 0 1' * maxaboveground;
2458 enddown = trace_endpos;
2460 // rule 3: make sure we aren't outside the map. This only works
2461 // for somewhat well formed maps. A good rule of thumb is that
2462 // the map should have a convex outside hull.
2463 // these can be traceLINES as we already verified the starting box
2464 mstart = start + 0.5 * (e.mins + e.maxs);
2465 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2466 if (trace_fraction >= 1)
2468 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2469 if (trace_fraction >= 1)
2471 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2472 if (trace_fraction >= 1)
2474 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2475 if (trace_fraction >= 1)
2477 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2478 if (trace_fraction >= 1)
2481 // rule 4: we must "see" some spawnpoint
2482 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2483 if(checkpvs(mstart, sp))
2487 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2488 if(checkpvs(mstart, sp))
2494 // find a random vector to "look at"
2495 end_x = org_x + random() * delta_x;
2496 end_y = org_y + random() * delta_y;
2497 end_z = org_z + random() * delta_z;
2498 end = start + normalize(end - start) * vlen(delta);
2500 // rule 4: start TO end must not be too short
2501 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2502 if (trace_startsolid)
2504 if (trace_fraction < minviewdistance / vlen(delta))
2507 // rule 5: don't want to look at sky
2508 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2511 // rule 6: we must not end up in trigger_hurt
2512 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2518 e.dphitcontentsmask = m;
2522 setorigin(e, start);
2523 e.angles = vectoangles(end - start);
2524 dprint("Needed ", ftos(i + 1), " attempts\n");
2531 float zcurveparticles_effectno;
2532 vector zcurveparticles_start;
2533 float zcurveparticles_spd;
2535 void endzcurveparticles()
2537 if(zcurveparticles_effectno)
2540 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2542 zcurveparticles_effectno = 0;
2545 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2547 spd = bound(0, floor(spd / 16), 32767);
2548 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2550 endzcurveparticles();
2551 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2552 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2553 WriteShort(MSG_BROADCAST, effectno);
2554 WriteCoord(MSG_BROADCAST, start_x);
2555 WriteCoord(MSG_BROADCAST, start_y);
2556 WriteCoord(MSG_BROADCAST, start_z);
2557 zcurveparticles_effectno = effectno;
2558 zcurveparticles_start = start;
2561 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2562 WriteCoord(MSG_BROADCAST, end_x);
2563 WriteCoord(MSG_BROADCAST, end_y);
2564 WriteCoord(MSG_BROADCAST, end_z);
2565 WriteCoord(MSG_BROADCAST, end_dz);
2566 zcurveparticles_spd = spd;
2569 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2572 vector vecxy, velxy;
2574 vecxy = end - start;
2579 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2581 endzcurveparticles();
2582 trailparticles(world, effectno, start, end);
2586 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2587 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2590 void write_recordmarker(entity pl, float tstart, float dt)
2592 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2594 // also write a marker into demo files for demotc-race-record-extractor to find
2597 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2598 " ", ftos(tstart), " ", ftos(dt), "\n"));
2601 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2614 if(allowcenter) // 2: allow center handedness
2627 if(allowcenter) // 2: allow center handedness
2643 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2648 if (autocvar_g_shootfromeye)
2661 else if (autocvar_g_shootfromcenter)
2666 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2676 else if (autocvar_g_shootfromclient)
2678 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2683 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2685 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2689 void attach_sameorigin(entity e, entity to, string tag)
2691 vector org, t_forward, t_left, t_up, e_forward, e_up;
2698 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2699 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2700 t_forward = v_forward * tagscale;
2701 t_left = v_right * -tagscale;
2702 t_up = v_up * tagscale;
2704 e.origin_x = org * t_forward;
2705 e.origin_y = org * t_left;
2706 e.origin_z = org * t_up;
2708 // current forward and up directions
2709 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2710 e.angles = AnglesTransform_FromVAngles(e.angles);
2712 e.angles = AnglesTransform_FromAngles(e.angles);
2713 fixedmakevectors(e.angles);
2715 // untransform forward, up!
2716 e_forward_x = v_forward * t_forward;
2717 e_forward_y = v_forward * t_left;
2718 e_forward_z = v_forward * t_up;
2719 e_up_x = v_up * t_forward;
2720 e_up_y = v_up * t_left;
2721 e_up_z = v_up * t_up;
2723 e.angles = fixedvectoangles2(e_forward, e_up);
2724 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2725 e.angles = AnglesTransform_ToVAngles(e.angles);
2727 e.angles = AnglesTransform_ToAngles(e.angles);
2729 setattachment(e, to, tag);
2730 setorigin(e, e.origin);
2733 void detach_sameorigin(entity e)
2736 org = gettaginfo(e, 0);
2737 e.angles = fixedvectoangles2(v_forward, v_up);
2738 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2739 e.angles = AnglesTransform_ToVAngles(e.angles);
2741 e.angles = AnglesTransform_ToAngles(e.angles);
2743 setattachment(e, world, "");
2744 setorigin(e, e.origin);
2747 void follow_sameorigin(entity e, entity to)
2749 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2750 e.aiment = to; // make the hole follow bmodel
2751 e.punchangle = to.angles; // the original angles of bmodel
2752 e.view_ofs = e.origin - to.origin; // relative origin
2753 e.v_angle = e.angles - to.angles; // relative angles
2756 void unfollow_sameorigin(entity e)
2758 e.movetype = MOVETYPE_NONE;
2761 entity gettaginfo_relative_ent;
2762 vector gettaginfo_relative(entity e, float tag)
2764 if (!gettaginfo_relative_ent)
2766 gettaginfo_relative_ent = spawn();
2767 gettaginfo_relative_ent.effects = EF_NODRAW;
2769 gettaginfo_relative_ent.model = e.model;
2770 gettaginfo_relative_ent.modelindex = e.modelindex;
2771 gettaginfo_relative_ent.frame = e.frame;
2772 return gettaginfo(gettaginfo_relative_ent, tag);
2775 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2779 if (pl.soundentity.cnt & p)
2781 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2782 pl.soundentity.cnt |= p;
2785 void SoundEntity_StopSound(entity pl, float chan)
2789 if (pl.soundentity.cnt & p)
2791 stopsoundto(MSG_ALL, pl.soundentity, chan);
2792 pl.soundentity.cnt &~= p;
2796 void SoundEntity_Attach(entity pl)
2798 pl.soundentity = spawn();
2799 pl.soundentity.classname = "soundentity";
2800 pl.soundentity.owner = pl;
2801 setattachment(pl.soundentity, pl, "");
2802 setmodel(pl.soundentity, "null");
2805 void SoundEntity_Detach(entity pl)
2808 for (i = 0; i <= 7; ++i)
2809 SoundEntity_StopSound(pl, i);
2813 float ParseCommandPlayerSlotTarget_firsttoken;
2814 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2822 ParseCommandPlayerSlotTarget_firsttoken = -1;
2826 if (substring(argv(idx), 0, 1) == "#")
2828 s = substring(argv(idx), 1, -1);
2830 if (s == "") if (tokens > idx)
2835 ParseCommandPlayerSlotTarget_firsttoken = idx;
2837 if (s == ftos(n) && n > 0 && n <= maxclients)
2840 if (e.flags & FL_CLIENT)
2846 // it must be a nick name
2849 ParseCommandPlayerSlotTarget_firsttoken = idx;
2852 FOR_EACH_CLIENT(head)
2853 if (head.netname == s)
2861 s = strdecolorize(s);
2863 FOR_EACH_CLIENT(head)
2864 if (strdecolorize(head.netname) == s)
2879 float modeleffect_SendEntity(entity to, float sf)
2882 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2885 if(self.velocity != '0 0 0')
2887 if(self.angles != '0 0 0')
2889 if(self.avelocity != '0 0 0')
2892 WriteByte(MSG_ENTITY, f);
2893 WriteShort(MSG_ENTITY, self.modelindex);
2894 WriteByte(MSG_ENTITY, self.skin);
2895 WriteByte(MSG_ENTITY, self.frame);
2896 WriteCoord(MSG_ENTITY, self.origin_x);
2897 WriteCoord(MSG_ENTITY, self.origin_y);
2898 WriteCoord(MSG_ENTITY, self.origin_z);
2901 WriteCoord(MSG_ENTITY, self.velocity_x);
2902 WriteCoord(MSG_ENTITY, self.velocity_y);
2903 WriteCoord(MSG_ENTITY, self.velocity_z);
2907 WriteCoord(MSG_ENTITY, self.angles_x);
2908 WriteCoord(MSG_ENTITY, self.angles_y);
2909 WriteCoord(MSG_ENTITY, self.angles_z);
2913 WriteCoord(MSG_ENTITY, self.avelocity_x);
2914 WriteCoord(MSG_ENTITY, self.avelocity_y);
2915 WriteCoord(MSG_ENTITY, self.avelocity_z);
2917 WriteShort(MSG_ENTITY, self.scale * 256.0);
2918 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2919 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2920 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2921 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2926 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)
2931 e.classname = "modeleffect";
2939 e.teleport_time = t1;
2943 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2947 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2950 sz = max(e.scale, e.scale2);
2951 setsize(e, e.mins * sz, e.maxs * sz);
2952 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2955 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2957 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2960 float randombit(float bits)
2962 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2971 for(f = 1; f <= bits; f *= 2)
2980 r = (r - 1) / (n - 1);
2987 float randombits(float bits, float k, float error_return)
2991 while(k > 0 && bits != r)
2993 r += randombit(bits - r);
3002 void randombit_test(float bits, float iter)
3006 print(ftos(randombit(bits)), "\n");
3011 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3013 if(halflifedist > 0)
3014 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3015 else if(halflifedist < 0)
3016 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3025 #define cvar_string_normal cvar_string_builtin
3026 #define cvar_normal cvar_builtin
3028 string cvar_string_normal(string n)
3030 if not(cvar_type(n) & 1)
3031 backtrace(strcat("Attempt to access undefined cvar: ", n));
3032 return cvar_string_builtin(n);
3035 float cvar_normal(string n)
3037 return stof(cvar_string_normal(n));
3040 #define cvar_set_normal cvar_set_builtin
3048 oself.think = SUB_Remove;
3049 oself.nextthink = time;
3055 Execute func() after time + fdelay.
3056 self when func is executed = self when defer is called
3058 void defer(float fdelay, void() func)
3065 e.think = defer_think;
3066 e.nextthink = time + fdelay;
3069 .string aiment_classname;
3070 .float aiment_deadflag;
3071 void SetMovetypeFollow(entity ent, entity e)
3073 // FIXME this may not be warpzone aware
3074 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3075 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.
3076 ent.aiment = e; // make the hole follow bmodel
3077 ent.punchangle = e.angles; // the original angles of bmodel
3078 ent.view_ofs = ent.origin - e.origin; // relative origin
3079 ent.v_angle = ent.angles - e.angles; // relative angles
3080 ent.aiment_classname = strzone(e.classname);
3081 ent.aiment_deadflag = e.deadflag;
3083 void UnsetMovetypeFollow(entity ent)
3085 ent.movetype = MOVETYPE_FLY;
3086 PROJECTILE_MAKETRIGGER(ent);
3089 float LostMovetypeFollow(entity ent)
3092 if(ent.movetype != MOVETYPE_FOLLOW)
3098 if(ent.aiment.classname != ent.aiment_classname)
3100 if(ent.aiment.deadflag != ent.aiment_deadflag)
3106 float isPushable(entity e)
3113 case "droppedweapon":
3114 case "keepawayball":
3115 case "nexball_basketball":
3116 case "nexball_football":
3118 case "bullet": // antilagged bullets can't hit this either
3121 if (e.projectiledeathtype)