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_autoscreenshot, "cl_autoscreenshot");
540 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
541 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
542 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
543 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
544 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
545 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
546 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
547 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
548 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
549 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
550 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
551 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
552 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
553 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
554 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
555 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
556 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
557 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
558 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
559 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
560 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
561 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
563 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
564 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
566 #ifdef ALLOW_FORCEMODELS
567 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
568 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
570 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
571 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
572 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
573 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
574 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
576 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
579 if (s == "cl_weaponpriority")
580 self.switchweapon = w_getbestweapon(self);
581 if (s == "cl_allow_uidtracking")
582 PlayerStats_AddPlayer(self);
586 void backtrace(string msg)
589 dev = autocvar_developer;
590 war = autocvar_prvm_backtraceforwarnings;
591 cvar_set("developer", "1");
592 cvar_set("prvm_backtraceforwarnings", "1");
594 print("--- CUT HERE ---\nWARNING: ");
597 remove(world); // isn't there any better way to cause a backtrace?
598 print("\n--- CUT UNTIL HERE ---\n");
599 cvar_set("developer", ftos(dev));
600 cvar_set("prvm_backtraceforwarnings", ftos(war));
603 string Team_ColorCode(float teamid)
605 if (teamid == COLOR_TEAM1)
607 else if (teamid == COLOR_TEAM2)
609 else if (teamid == COLOR_TEAM3)
611 else if (teamid == COLOR_TEAM4)
617 string Team_ColorName(float t)
619 // fixme: Search for team entities and get their .netname's!
620 if (t == COLOR_TEAM1)
622 if (t == COLOR_TEAM2)
624 if (t == COLOR_TEAM3)
626 if (t == COLOR_TEAM4)
631 string Team_ColorNameLowerCase(float t)
633 // fixme: Search for team entities and get their .netname's!
634 if (t == COLOR_TEAM1)
636 if (t == COLOR_TEAM2)
638 if (t == COLOR_TEAM3)
640 if (t == COLOR_TEAM4)
645 float ColourToNumber(string team_colour)
647 if (team_colour == "red")
650 if (team_colour == "blue")
653 if (team_colour == "yellow")
656 if (team_colour == "pink")
659 if (team_colour == "auto")
665 float NumberToTeamNumber(float number)
682 // decolorizes and team colors the player name when needed
683 string playername(entity p)
686 if (teamplay && !intermission_running && p.classname == "player")
688 t = Team_ColorCode(p.team);
689 return strcat(t, strdecolorize(p.netname));
695 vector randompos(vector m1, vector m2)
699 v_x = m2_x * random() + m1_x;
700 v_y = m2_y * random() + m1_y;
701 v_z = m2_z * random() + m1_z;
705 //#NO AUTOCVARS START
707 float g_pickup_shells;
708 float g_pickup_shells_max;
709 float g_pickup_nails;
710 float g_pickup_nails_max;
711 float g_pickup_rockets;
712 float g_pickup_rockets_max;
713 float g_pickup_cells;
714 float g_pickup_cells_max;
716 float g_pickup_fuel_jetpack;
717 float g_pickup_fuel_max;
718 float g_pickup_armorsmall;
719 float g_pickup_armorsmall_max;
720 float g_pickup_armorsmall_anyway;
721 float g_pickup_armormedium;
722 float g_pickup_armormedium_max;
723 float g_pickup_armormedium_anyway;
724 float g_pickup_armorbig;
725 float g_pickup_armorbig_max;
726 float g_pickup_armorbig_anyway;
727 float g_pickup_armorlarge;
728 float g_pickup_armorlarge_max;
729 float g_pickup_armorlarge_anyway;
730 float g_pickup_healthsmall;
731 float g_pickup_healthsmall_max;
732 float g_pickup_healthsmall_anyway;
733 float g_pickup_healthmedium;
734 float g_pickup_healthmedium_max;
735 float g_pickup_healthmedium_anyway;
736 float g_pickup_healthlarge;
737 float g_pickup_healthlarge_max;
738 float g_pickup_healthlarge_anyway;
739 float g_pickup_healthmega;
740 float g_pickup_healthmega_max;
741 float g_pickup_healthmega_anyway;
742 float g_pickup_ammo_anyway;
743 float g_pickup_weapons_anyway;
745 float g_weaponarena_random;
746 float g_weaponarena_random_with_laser;
747 string g_weaponarena_list;
748 float g_weaponspeedfactor;
749 float g_weaponratefactor;
750 float g_weapondamagefactor;
751 float g_weaponforcefactor;
752 float g_weaponspreadfactor;
756 float start_ammo_shells;
757 float start_ammo_nails;
758 float start_ammo_rockets;
759 float start_ammo_cells;
760 float start_ammo_fuel;
762 float start_armorvalue;
763 float warmup_start_weapons;
764 float warmup_start_ammo_shells;
765 float warmup_start_ammo_nails;
766 float warmup_start_ammo_rockets;
767 float warmup_start_ammo_cells;
768 float warmup_start_ammo_fuel;
769 float warmup_start_health;
770 float warmup_start_armorvalue;
774 entity get_weaponinfo(float w);
776 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
778 var float i = weaponinfo.weapon;
783 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
785 if (t < 0) // "default" weapon selection
787 if (g_lms || g_ca || allguns)
788 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
792 t = (i == WEP_SHOTGUN);
794 t = 0; // weapon is set a few lines later
796 t = (i == WEP_LASER || i == WEP_SHOTGUN);
797 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
798 t |= (i == WEP_HOOK);
801 // we cannot disable porto in Nexball, we must force it
802 if(g_nexball && i == WEP_PORTO)
808 void readplayerstartcvars()
814 // initialize starting values for players
817 start_ammo_shells = 0;
818 start_ammo_nails = 0;
819 start_ammo_rockets = 0;
820 start_ammo_cells = 0;
821 start_health = cvar("g_balance_health_start");
822 start_armorvalue = cvar("g_balance_armor_start");
825 s = cvar_string("g_weaponarena");
826 if (s == "0" || s == "")
832 if (s == "0" || s == "")
838 // forcibly turn off weaponarena
842 g_weaponarena_list = "All Weapons";
843 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
845 e = get_weaponinfo(j);
846 g_weaponarena |= e.weapons;
847 weapon_action(e.weapon, WR_PRECACHE);
850 else if (s == "most")
852 g_weaponarena_list = "Most Weapons";
853 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
855 e = get_weaponinfo(j);
856 if (e.spawnflags & WEP_FLAG_NORMAL)
858 g_weaponarena |= e.weapons;
859 weapon_action(e.weapon, WR_PRECACHE);
863 else if (s == "none")
865 g_weaponarena_list = "No Weapons";
866 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
870 t = tokenize_console(s);
871 g_weaponarena_list = "";
872 for (i = 0; i < t; ++i)
875 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
877 e = get_weaponinfo(j);
880 g_weaponarena |= e.weapons;
881 weapon_action(e.weapon, WR_PRECACHE);
882 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
888 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
891 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
895 g_weaponarena_random = cvar("g_weaponarena_random");
897 g_weaponarena_random = 0;
898 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
902 start_weapons = g_weaponarena;
904 start_items |= IT_UNLIMITED_AMMO;
906 else if (g_minstagib)
909 start_armorvalue = 0;
910 start_weapons = WEPBIT_MINSTANEX;
911 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
912 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
914 if (g_minstagib_invis_alpha <= 0)
915 g_minstagib_invis_alpha = -1;
919 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
921 e = get_weaponinfo(i);
922 if(want_weapon("g_start_weapon_", e, FALSE))
923 start_weapons |= e.weapons;
927 if(!cvar("g_use_ammunition"))
928 start_items |= IT_UNLIMITED_AMMO;
932 start_ammo_cells = cvar("g_minstagib_ammo_start");
933 start_ammo_fuel = cvar("g_start_ammo_fuel");
935 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
937 start_ammo_rockets = 999;
938 start_ammo_shells = 999;
939 start_ammo_cells = 999;
940 start_ammo_nails = 999;
941 start_ammo_fuel = 999;
947 start_ammo_shells = cvar("g_lms_start_ammo_shells");
948 start_ammo_nails = cvar("g_lms_start_ammo_nails");
949 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
950 start_ammo_cells = cvar("g_lms_start_ammo_cells");
951 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
955 start_ammo_shells = cvar("g_start_ammo_shells");
956 start_ammo_nails = cvar("g_start_ammo_nails");
957 start_ammo_rockets = cvar("g_start_ammo_rockets");
958 start_ammo_cells = cvar("g_start_ammo_cells");
959 start_ammo_fuel = cvar("g_start_ammo_fuel");
965 start_health = cvar("g_lms_start_health");
966 start_armorvalue = cvar("g_lms_start_armor");
971 warmup_start_ammo_shells = start_ammo_shells;
972 warmup_start_ammo_nails = start_ammo_nails;
973 warmup_start_ammo_rockets = start_ammo_rockets;
974 warmup_start_ammo_cells = start_ammo_cells;
975 warmup_start_ammo_fuel = start_ammo_fuel;
976 warmup_start_health = start_health;
977 warmup_start_armorvalue = start_armorvalue;
978 warmup_start_weapons = start_weapons;
980 if (!g_weaponarena && !g_minstagib && !g_ca)
982 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
983 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
984 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
985 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
986 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
987 warmup_start_health = cvar("g_warmup_start_health");
988 warmup_start_armorvalue = cvar("g_warmup_start_armor");
989 warmup_start_weapons = 0;
990 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
992 e = get_weaponinfo(i);
993 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
994 warmup_start_weapons |= e.weapons;
999 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1001 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1002 start_items |= IT_FUEL_REGEN;
1003 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1004 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1008 start_items |= IT_JETPACK;
1010 if (g_weapon_stay == 2)
1012 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1013 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1014 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1015 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1016 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1017 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1018 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1019 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1020 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1021 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1024 MUTATOR_CALLHOOK(SetStartItems);
1026 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1028 e = get_weaponinfo(i);
1029 if(e.weapons & (start_weapons | warmup_start_weapons))
1030 weapon_action(e.weapon, WR_PRECACHE);
1033 start_ammo_shells = max(0, start_ammo_shells);
1034 start_ammo_nails = max(0, start_ammo_nails);
1035 start_ammo_cells = max(0, start_ammo_cells);
1036 start_ammo_rockets = max(0, start_ammo_rockets);
1037 start_ammo_fuel = max(0, start_ammo_fuel);
1039 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1040 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1041 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1042 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1043 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1047 float g_bugrigs_planar_movement;
1048 float g_bugrigs_planar_movement_car_jumping;
1049 float g_bugrigs_reverse_spinning;
1050 float g_bugrigs_reverse_speeding;
1051 float g_bugrigs_reverse_stopping;
1052 float g_bugrigs_air_steering;
1053 float g_bugrigs_angle_smoothing;
1054 float g_bugrigs_friction_floor;
1055 float g_bugrigs_friction_brake;
1056 float g_bugrigs_friction_air;
1057 float g_bugrigs_accel;
1058 float g_bugrigs_speed_ref;
1059 float g_bugrigs_speed_pow;
1060 float g_bugrigs_steer;
1062 float g_touchexplode;
1063 float g_touchexplode_radius;
1064 float g_touchexplode_damage;
1065 float g_touchexplode_edgedamage;
1066 float g_touchexplode_force;
1073 float sv_pitch_fixyaw;
1075 string GetGametype(); // g_world.qc
1076 void readlevelcvars(void)
1078 // first load all the mutators
1079 if(cvar("g_invincible_projectiles"))
1080 MUTATOR_ADD(mutator_invincibleprojectiles);
1082 MUTATOR_ADD(mutator_nix);
1083 if(cvar("g_dodging"))
1084 MUTATOR_ADD(mutator_dodging);
1085 if(cvar("g_rocket_flying"))
1086 MUTATOR_ADD(mutator_rocketflying);
1087 if(cvar("g_vampire"))
1088 MUTATOR_ADD(mutator_vampire);
1090 if(cvar("sv_allow_fullbright"))
1091 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1093 g_bugrigs = cvar("g_bugrigs");
1094 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1095 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1096 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1097 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1098 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1099 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1100 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1101 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1102 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1103 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1104 g_bugrigs_accel = cvar("g_bugrigs_accel");
1105 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1106 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1107 g_bugrigs_steer = cvar("g_bugrigs_steer");
1109 g_touchexplode = cvar("g_touchexplode");
1110 g_touchexplode_radius = cvar("g_touchexplode_radius");
1111 g_touchexplode_damage = cvar("g_touchexplode_damage");
1112 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1113 g_touchexplode_force = cvar("g_touchexplode_force");
1115 #ifdef ALLOW_FORCEMODELS
1116 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1118 sv_loddistance1 = cvar("sv_loddistance1");
1119 sv_loddistance2 = cvar("sv_loddistance2");
1121 if(sv_loddistance2 <= sv_loddistance1)
1122 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1124 sv_clones = cvar("sv_clones");
1125 sv_gentle = cvar("sv_gentle");
1126 sv_foginterval = cvar("sv_foginterval");
1127 g_cloaked = cvar("g_cloaked");
1129 g_cloaked = 1; // always enable cloak in CTS
1130 g_jump_grunt = cvar("g_jump_grunt");
1131 g_footsteps = cvar("g_footsteps");
1132 g_grappling_hook = cvar("g_grappling_hook");
1133 g_jetpack = cvar("g_jetpack");
1134 g_midair = cvar("g_midair");
1135 g_minstagib = cvar("g_minstagib");
1136 g_norecoil = cvar("g_norecoil");
1137 g_bloodloss = cvar("g_bloodloss");
1138 sv_maxidle = cvar("sv_maxidle");
1139 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1140 g_ctf_reverse = cvar("g_ctf_reverse");
1141 sv_autotaunt = cvar("sv_autotaunt");
1142 sv_taunt = cvar("sv_taunt");
1144 inWarmupStage = cvar("g_warmup");
1145 g_warmup_limit = cvar("g_warmup_limit");
1146 g_warmup_allguns = cvar("g_warmup_allguns");
1147 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1149 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1150 inWarmupStage = 0; // these modes cannot work together, sorry
1152 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1153 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1154 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1155 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1156 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1157 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1158 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1159 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1160 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1161 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1162 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1163 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1165 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1166 g_weaponratefactor = cvar("g_weaponratefactor");
1167 g_weapondamagefactor = cvar("g_weapondamagefactor");
1168 g_weaponforcefactor = cvar("g_weaponforcefactor");
1169 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1171 g_pickup_shells = cvar("g_pickup_shells");
1172 g_pickup_shells_max = cvar("g_pickup_shells_max");
1173 g_pickup_nails = cvar("g_pickup_nails");
1174 g_pickup_nails_max = cvar("g_pickup_nails_max");
1175 g_pickup_rockets = cvar("g_pickup_rockets");
1176 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1177 g_pickup_cells = cvar("g_pickup_cells");
1178 g_pickup_cells_max = cvar("g_pickup_cells_max");
1179 g_pickup_fuel = cvar("g_pickup_fuel");
1180 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1181 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1182 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1183 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1184 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1185 g_pickup_armormedium = cvar("g_pickup_armormedium");
1186 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1187 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1188 g_pickup_armorbig = cvar("g_pickup_armorbig");
1189 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1190 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1191 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1192 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1193 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1194 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1195 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1196 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1197 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1198 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1199 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1200 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1201 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1202 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1203 g_pickup_healthmega = cvar("g_pickup_healthmega");
1204 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1205 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1207 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1208 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1210 g_pinata = cvar("g_pinata");
1212 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1214 g_weapon_stay = cvar("g_weapon_stay");
1216 g_ghost_items = cvar("g_ghost_items");
1218 if(g_ghost_items >= 1)
1219 g_ghost_items = 0.25; // default alpha value
1221 if not(inWarmupStage && !g_ca)
1222 game_starttime = cvar("g_start_delay");
1224 sv_pitch_min = cvar("sv_pitch_min");
1225 sv_pitch_max = cvar("sv_pitch_max");
1226 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1228 readplayerstartcvars();
1234 string precache_sound (string s) = #19;
1235 float precache_sound_index (string s) = #19;
1237 #define SND_VOLUME 1
1238 #define SND_ATTENUATION 2
1239 #define SND_LARGEENTITY 8
1240 #define SND_LARGESOUND 16
1242 float sound_allowed(float dest, entity e)
1244 // sounds from world may always pass
1247 if (e.classname == "body")
1249 else if (e.realowner && e.realowner != e)
1251 else if (e.owner && e.owner != e)
1256 // sounds to self may always pass
1257 if (dest == MSG_ONE)
1258 if (e == msg_entity)
1260 // sounds by players can be removed
1261 if (autocvar_bot_sound_monopoly)
1262 if (clienttype(e) == CLIENTTYPE_REAL)
1264 // anything else may pass
1268 #ifdef COMPAT_XON010_CHANNELS
1269 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1270 void sound(entity e, float chan, string samp, float vol, float atten)
1272 if (!sound_allowed(MSG_BROADCAST, e))
1274 sound_builtin(e, chan, samp, vol, atten);
1278 void sound(entity e, float chan, string samp, float vol, float atten)
1280 if (!sound_allowed(MSG_BROADCAST, e))
1282 sound7(e, chan, samp, vol, atten, 0, 0);
1286 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1290 if (!sound_allowed(dest, e))
1293 entno = num_for_edict(e);
1294 idx = precache_sound_index(samp);
1299 atten = floor(atten * 64);
1300 vol = floor(vol * 255);
1303 sflags |= SND_VOLUME;
1305 sflags |= SND_ATTENUATION;
1306 if (entno >= 8192 || chan < 0 || chan > 7)
1307 sflags |= SND_LARGEENTITY;
1309 sflags |= SND_LARGESOUND;
1311 WriteByte(dest, SVC_SOUND);
1312 WriteByte(dest, sflags);
1313 if (sflags & SND_VOLUME)
1314 WriteByte(dest, vol);
1315 if (sflags & SND_ATTENUATION)
1316 WriteByte(dest, atten);
1317 if (sflags & SND_LARGEENTITY)
1319 WriteShort(dest, entno);
1320 WriteByte(dest, chan);
1324 WriteShort(dest, entno * 8 + chan);
1326 if (sflags & SND_LARGESOUND)
1327 WriteShort(dest, idx);
1329 WriteByte(dest, idx);
1331 WriteCoord(dest, o_x);
1332 WriteCoord(dest, o_y);
1333 WriteCoord(dest, o_z);
1335 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1339 if (!sound_allowed(dest, e))
1342 o = e.origin + 0.5 * (e.mins + e.maxs);
1343 soundtoat(dest, e, o, chan, samp, vol, atten);
1345 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1347 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1349 void stopsoundto(float dest, entity e, float chan)
1353 if (!sound_allowed(dest, e))
1356 entno = num_for_edict(e);
1358 if (entno >= 8192 || chan < 0 || chan > 7)
1361 idx = precache_sound_index("misc/null.wav");
1362 sflags = SND_LARGEENTITY;
1364 sflags |= SND_LARGESOUND;
1365 WriteByte(dest, SVC_SOUND);
1366 WriteByte(dest, sflags);
1367 WriteShort(dest, entno);
1368 WriteByte(dest, chan);
1369 if (sflags & SND_LARGESOUND)
1370 WriteShort(dest, idx);
1372 WriteByte(dest, idx);
1373 WriteCoord(dest, e.origin_x);
1374 WriteCoord(dest, e.origin_y);
1375 WriteCoord(dest, e.origin_z);
1379 WriteByte(dest, SVC_STOPSOUND);
1380 WriteShort(dest, entno * 8 + chan);
1383 void stopsound(entity e, float chan)
1385 if (!sound_allowed(MSG_BROADCAST, e))
1388 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1389 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1392 void play2(entity e, string filename)
1394 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1396 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1399 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1401 float spamsound(entity e, float chan, string samp, float vol, float atten)
1403 if (!sound_allowed(MSG_BROADCAST, e))
1406 if (time > e.spamtime)
1409 sound(e, chan, samp, vol, atten);
1415 void play2team(float t, string filename)
1419 if (autocvar_bot_sound_monopoly)
1422 FOR_EACH_REALPLAYER(head)
1425 play2(head, filename);
1429 void play2all(string samp)
1431 if (autocvar_bot_sound_monopoly)
1434 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1437 void PrecachePlayerSounds(string f);
1438 void precache_playermodel(string m)
1440 float globhandle, i, n;
1443 if(substring(m, -9,5) == "_lod1")
1445 if(substring(m, -9,5) == "_lod2")
1450 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1453 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1458 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1461 n = search_getsize(globhandle);
1462 for (i = 0; i < n; ++i)
1464 //print(search_getfilename(globhandle, i), "\n");
1465 f = search_getfilename(globhandle, i);
1466 PrecachePlayerSounds(f);
1468 search_end(globhandle);
1470 void precache_all_playermodels(string pattern)
1472 float globhandle, i, n;
1475 globhandle = search_begin(pattern, TRUE, FALSE);
1478 n = search_getsize(globhandle);
1479 for (i = 0; i < n; ++i)
1481 //print(search_getfilename(globhandle, i), "\n");
1482 f = search_getfilename(globhandle, i);
1483 precache_playermodel(f);
1485 search_end(globhandle);
1490 // gamemode related things
1491 precache_model ("models/misc/chatbubble.spr");
1494 precache_model ("models/runematch/curse.mdl");
1495 precache_model ("models/runematch/rune.mdl");
1498 #ifdef TTURRETS_ENABLED
1499 if (autocvar_g_turrets)
1503 // Precache all player models if desired
1504 if (autocvar_sv_precacheplayermodels)
1506 PrecachePlayerSounds("sound/player/default.sounds");
1507 precache_all_playermodels("models/player/*.zym");
1508 precache_all_playermodels("models/player/*.dpm");
1509 precache_all_playermodels("models/player/*.md3");
1510 precache_all_playermodels("models/player/*.psk");
1511 precache_all_playermodels("models/player/*.iqm");
1514 if (autocvar_sv_defaultcharacter)
1517 s = autocvar_sv_defaultplayermodel_red;
1519 precache_playermodel(s);
1520 s = autocvar_sv_defaultplayermodel_blue;
1522 precache_playermodel(s);
1523 s = autocvar_sv_defaultplayermodel_yellow;
1525 precache_playermodel(s);
1526 s = autocvar_sv_defaultplayermodel_pink;
1528 precache_playermodel(s);
1529 s = autocvar_sv_defaultplayermodel;
1531 precache_playermodel(s);
1536 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1537 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1540 // gore and miscellaneous sounds
1541 //precache_sound ("misc/h2ohit.wav");
1542 precache_model ("models/hook.md3");
1543 precache_sound ("misc/armorimpact.wav");
1544 precache_sound ("misc/bodyimpact1.wav");
1545 precache_sound ("misc/bodyimpact2.wav");
1546 precache_sound ("misc/gib.wav");
1547 precache_sound ("misc/gib_splat01.wav");
1548 precache_sound ("misc/gib_splat02.wav");
1549 precache_sound ("misc/gib_splat03.wav");
1550 precache_sound ("misc/gib_splat04.wav");
1551 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1552 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1553 precache_sound ("misc/null.wav");
1554 precache_sound ("misc/spawn.wav");
1555 precache_sound ("misc/talk.wav");
1556 precache_sound ("misc/teleport.wav");
1557 precache_sound ("misc/poweroff.wav");
1558 precache_sound ("player/lava.wav");
1559 precache_sound ("player/slime.wav");
1562 precache_sound ("misc/jetpack_fly.wav");
1564 precache_model ("models/sprites/0.spr32");
1565 precache_model ("models/sprites/1.spr32");
1566 precache_model ("models/sprites/2.spr32");
1567 precache_model ("models/sprites/3.spr32");
1568 precache_model ("models/sprites/4.spr32");
1569 precache_model ("models/sprites/5.spr32");
1570 precache_model ("models/sprites/6.spr32");
1571 precache_model ("models/sprites/7.spr32");
1572 precache_model ("models/sprites/8.spr32");
1573 precache_model ("models/sprites/9.spr32");
1574 precache_model ("models/sprites/10.spr32");
1576 // common weapon precaches
1577 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1578 precache_sound ("weapons/weapon_switch.wav");
1579 precache_sound ("weapons/weaponpickup.wav");
1580 precache_sound ("weapons/unavailable.wav");
1581 precache_sound ("weapons/dryfire.wav");
1582 if (g_grappling_hook)
1584 precache_sound ("weapons/hook_fire.wav"); // hook
1585 precache_sound ("weapons/hook_impact.wav"); // hook
1588 if(autocvar_sv_precacheweapons)
1590 //precache weapon models/sounds
1593 while (wep <= WEP_LAST)
1595 weapon_action(wep, WR_PRECACHE);
1600 precache_model("models/elaser.mdl");
1601 precache_model("models/laser.mdl");
1602 precache_model("models/ebomb.mdl");
1605 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1607 if (!self.noise && self.music) // quake 3 uses the music field
1608 self.noise = self.music;
1610 // plays music for the level if there is any
1613 precache_sound (self.noise);
1614 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1619 // sorry, but using \ in macros breaks line numbers
1620 #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
1621 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1622 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1625 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1627 if (clienttype(e) == CLIENTTYPE_REAL)
1630 WRITESPECTATABLE_MSG_ONE({
1631 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1632 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1633 WriteByte(MSG_ONE, id);
1634 WriteString(MSG_ONE, s);
1635 if (id != 0 && s != "")
1637 WriteByte(MSG_ONE, duration);
1638 WriteByte(MSG_ONE, countdown_num);
1643 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1645 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1647 // WARNING: this kills the trace globals
1648 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1649 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1651 #define INITPRIO_FIRST 0
1652 #define INITPRIO_GAMETYPE 0
1653 #define INITPRIO_GAMETYPE_FALLBACK 1
1654 #define INITPRIO_FINDTARGET 10
1655 #define INITPRIO_DROPTOFLOOR 20
1656 #define INITPRIO_SETLOCATION 90
1657 #define INITPRIO_LINKDOORS 91
1658 #define INITPRIO_LAST 99
1660 .void(void) initialize_entity;
1661 .float initialize_entity_order;
1662 .entity initialize_entity_next;
1663 entity initialize_entity_first;
1665 void make_safe_for_remove(entity e)
1667 if (e.initialize_entity)
1670 for (ent = initialize_entity_first; ent; )
1672 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1674 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1675 // skip it in linked list
1678 prev.initialize_entity_next = ent.initialize_entity_next;
1679 ent = prev.initialize_entity_next;
1683 initialize_entity_first = ent.initialize_entity_next;
1684 ent = initialize_entity_first;
1690 ent = ent.initialize_entity_next;
1696 void objerror(string s)
1698 make_safe_for_remove(self);
1699 objerror_builtin(s);
1702 .float remove_except_protected_forbidden;
1703 void remove_except_protected(entity e)
1705 if(e.remove_except_protected_forbidden)
1706 error("not allowed to remove this at this point");
1710 void remove_unsafely(entity e)
1712 if(e.classname == "spike")
1713 error("Removing spikes is forbidden (crylink bug), please report");
1717 void remove_safely(entity e)
1719 make_safe_for_remove(e);
1723 void InitializeEntity(entity e, void(void) func, float order)
1727 if (!e || e.initialize_entity)
1729 // make a proxy initializer entity
1733 e.classname = "initialize_entity";
1737 e.initialize_entity = func;
1738 e.initialize_entity_order = order;
1740 cur = initialize_entity_first;
1743 if (!cur || cur.initialize_entity_order > order)
1745 // insert between prev and cur
1747 prev.initialize_entity_next = e;
1749 initialize_entity_first = e;
1750 e.initialize_entity_next = cur;
1754 cur = cur.initialize_entity_next;
1757 void InitializeEntitiesRun()
1760 startoflist = initialize_entity_first;
1761 initialize_entity_first = world;
1762 remove = remove_except_protected;
1763 for (self = startoflist; self; self = self.initialize_entity_next)
1765 self.remove_except_protected_forbidden = 1;
1767 for (self = startoflist; self; )
1770 var void(void) func;
1771 e = self.initialize_entity_next;
1772 func = self.initialize_entity;
1773 self.initialize_entity_order = 0;
1774 self.initialize_entity = func_null;
1775 self.initialize_entity_next = world;
1776 self.remove_except_protected_forbidden = 0;
1777 if (self.classname == "initialize_entity")
1781 remove_builtin(self);
1784 //dprint("Delayed initialization: ", self.classname, "\n");
1785 if(func != func_null)
1790 backtrace(strcat("Null function in: ", self.classname, "\n"));
1794 remove = remove_unsafely;
1797 .float uncustomizeentityforclient_set;
1798 .void(void) uncustomizeentityforclient;
1799 void(void) SUB_Nullpointer = #0;
1800 void UncustomizeEntitiesRun()
1804 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1805 self.uncustomizeentityforclient();
1808 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1810 e.customizeentityforclient = customizer;
1811 e.uncustomizeentityforclient = uncustomizer;
1812 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1816 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1819 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1823 if (e.classname == "")
1824 e.classname = "net_linked";
1826 if (e.model == "" || self.modelindex == 0)
1830 setmodel(e, "null");
1834 e.SendEntity = sendfunc;
1835 e.SendFlags = 0xFFFFFF;
1838 e.effects |= EF_NODEPTHTEST;
1842 e.nextthink = time + dt;
1843 e.think = SUB_Remove;
1847 void adaptor_think2touch()
1856 void adaptor_think2use()
1868 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1870 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
1871 self.projectiledeathtype |= HITTYPE_SPLASH;
1872 adaptor_think2use();
1875 // deferred dropping
1876 void DropToFloor_Handler()
1878 droptofloor_builtin();
1879 self.dropped_origin = self.origin;
1884 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1889 float trace_hits_box_a0, trace_hits_box_a1;
1891 float trace_hits_box_1d(float end, float thmi, float thma)
1895 // just check if x is in range
1903 // do the trace with respect to x
1904 // 0 -> end has to stay in thmi -> thma
1905 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1906 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1907 if (trace_hits_box_a0 > trace_hits_box_a1)
1913 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1918 // now it is a trace from 0 to end
1920 trace_hits_box_a0 = 0;
1921 trace_hits_box_a1 = 1;
1923 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1925 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1927 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1933 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1935 return trace_hits_box(start, end, thmi - ma, thma - mi);
1938 float SUB_NoImpactCheck()
1940 // zero hitcontents = this is not the real impact, but either the
1941 // mirror-impact of something hitting the projectile instead of the
1942 // projectile hitting the something, or a touchareagrid one. Neither of
1943 // these stop the projectile from moving, so...
1944 if(trace_dphitcontents == 0)
1946 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1947 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)));
1950 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1952 if (other == world && self.size != '0 0 0')
1955 tic = self.velocity * sys_frametime;
1956 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1957 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1958 if (trace_fraction >= 1)
1960 dprint("Odd... did not hit...?\n");
1962 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1964 dprint("Detected and prevented the sky-grapple bug.\n");
1972 #define SUB_OwnerCheck() (other && (other == self.owner))
1974 void RemoveGrapplingHook(entity pl);
1975 void W_Crylink_Dequeue(entity e);
1976 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1978 if(SUB_OwnerCheck())
1980 if(SUB_NoImpactCheck())
1982 if(self.classname == "grapplinghook")
1983 RemoveGrapplingHook(self.realowner);
1984 else if(self.classname == "spike")
1986 W_Crylink_Dequeue(self);
1993 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1994 UpdateCSQCProjectile(self);
1997 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1999 float MAX_IPBAN_URIS = 16;
2001 float URI_GET_DISCARD = 0;
2002 float URI_GET_IPBAN = 1;
2003 float URI_GET_IPBAN_END = 16;
2005 void URI_Get_Callback(float id, float status, string data)
2007 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2009 dprint("\nEnd of data.\n");
2011 if(url_URI_Get_Callback(id, status, data))
2015 else if (id == URI_GET_DISCARD)
2019 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2022 OnlineBanList_URI_Get_Callback(id, status, data);
2026 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2030 void print_to(entity e, string s)
2033 sprint(e, strcat(s, "\n"));
2038 string uid2name(string myuid) {
2040 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2043 s = "^1Unregistered Player";
2047 float race_readTime(string map, float pos)
2055 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2058 string race_readUID(string map, float pos)
2066 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2069 float race_readPos(string map, float t) {
2071 for (i = 1; i <= RANKINGS_CNT; ++i)
2072 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2075 return 0; // pos is zero if unranked
2078 void race_writeTime(string map, float t, string myuid)
2087 newpos = race_readPos(map, t);
2090 for(i = 1; i <= RANKINGS_CNT; ++i)
2092 if(race_readUID(map, i) == myuid)
2095 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2096 for (i = prevpos; i > newpos; --i) {
2097 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2098 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2100 } else { // player has no ranked record yet
2101 for (i = RANKINGS_CNT; i > newpos; --i) {
2102 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2103 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2107 // store new time itself
2108 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2109 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2112 string race_readName(string map, float pos)
2120 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2123 string race_placeName(float pos) {
2124 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2126 if(mod(pos, 10) == 1)
2127 return strcat(ftos(pos), "st");
2128 else if(mod(pos, 10) == 2)
2129 return strcat(ftos(pos), "nd");
2130 else if(mod(pos, 10) == 3)
2131 return strcat(ftos(pos), "rd");
2133 return strcat(ftos(pos), "th");
2136 return strcat(ftos(pos), "th");
2138 string getrecords(float page) // 50 records per page
2152 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2154 if (MapInfo_Get_ByID(i))
2156 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2160 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2161 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2169 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2171 if (MapInfo_Get_ByID(i))
2173 r = race_readTime(MapInfo_Map_bspname, 1);
2176 h = race_readName(MapInfo_Map_bspname, 1);
2177 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2185 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2187 if (MapInfo_Get_ByID(i))
2189 r = race_readTime(MapInfo_Map_bspname, 1);
2192 h = race_readName(MapInfo_Map_bspname, 1);
2193 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2199 MapInfo_ClearTemps();
2201 if (s == "" && page == 0)
2202 return "No records are available on this server.\n";
2207 string getrankings()
2220 for (i = 1; i <= RANKINGS_CNT; ++i)
2222 t = race_readTime(map, i);
2225 n = race_readName(map, i);
2226 p = race_placeName(i);
2227 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2230 MapInfo_ClearTemps();
2233 return strcat("No records are available for the map: ", map, "\n");
2235 return strcat("Records for ", map, ":\n", s);
2238 #define LADDER_FIRSTPOINT 100
2239 #define LADDER_CNT 10
2240 // position X still gives LADDER_FIRSTPOINT/X points
2241 #define LADDER_SIZE 30
2242 // ladder shows the top X players
2243 string top_uids[LADDER_SIZE];
2244 float top_scores[LADDER_SIZE];
2247 float i, j, k, uidcnt;
2261 for (k = 0; k < MapInfo_count; ++k)
2263 if (MapInfo_Get_ByID(k))
2265 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2266 if(i == 0) // speed award
2268 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2271 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2273 else // normal record, if it exists (else break)
2275 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2278 myuid = race_readUID(MapInfo_Map_bspname, i);
2281 // string s contains:
2282 // arg 0 = # of speed recs
2283 // arg 1 = # of 1st place recs
2284 // arg 2 = # of 2nd place recs
2286 // LADDER_CNT+1 = total points
2288 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2291 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2293 for (j = 0; j <= LADDER_CNT + 1; ++j)
2295 if(j != LADDER_CNT + 1)
2296 temp_s = strcat(temp_s, "0 ");
2298 temp_s = strcat(temp_s, "0");
2302 tokenize_console(temp_s);
2305 if(i == 0) // speed award
2306 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2308 if(j == 0) // speed award
2309 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2311 s = strcat(s, " ", argv(j)); // just copy over everything else
2314 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2317 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2318 else if(j == i) // wanted rec!
2319 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2321 s = strcat(s, " ", argv(j)); // just copy over everything else
2324 // total points are (by default) calculated like this:
2325 // speedrec = floor(100 / 10) = 10 points
2326 // 1st place = floor(100 / 1) = 100 points
2327 // 2nd place = floor(100 / 2) = 50 points
2328 // 3rd place = floor(100 / 3) = 33 points
2329 // 4th place = floor(100 / 4) = 25 points
2330 // 5th place = floor(100 / 5) = 20 points
2334 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2336 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2338 db_put(TemporaryDB, strcat("ladder", myuid), s);
2345 for (i = 0; i <= uidcnt; ++i) // for each known uid
2347 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2348 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2349 tokenize_console(temp_s);
2350 thiscnt = stof(argv(LADDER_CNT+1));
2352 if(thiscnt > top_scores[LADDER_SIZE-1])
2353 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2355 if(thiscnt > top_scores[j])
2357 for (k = LADDER_SIZE-1; k >= j; --k)
2359 top_uids[k] = top_uids[k-1];
2360 top_scores[k] = top_scores[k-1];
2362 top_uids[j] = thisuid;
2363 top_scores[j] = thiscnt;
2369 s = "^3-----------------------\n\n";
2371 s = strcat(s, "Pos ^3|");
2372 s = strcat(s, " ^7Total ^3|");
2373 for (i = 1; i <= LADDER_CNT; ++i)
2375 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2377 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2379 s = strcat(s, "\n^3----+--------");
2380 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2382 s = strcat(s, "+-----");
2385 for (i = 1; i <= LADDER_CNT - 9; ++i)
2387 s = strcat(s, "+------");
2391 s = strcat(s, "+--------------+--------------------\n");
2393 for (i = 0; i < LADDER_SIZE; ++i)
2395 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2396 tokenize_console(temp_s);
2397 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2399 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2400 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2401 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2403 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2406 for (j = 10; j <= LADDER_CNT; ++j)
2408 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2412 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2413 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2416 MapInfo_ClearTemps();
2419 return "No ladder on this server!\n";
2421 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2425 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2428 vector start, org, delta, end, enddown, mstart;
2431 m = e.dphitcontentsmask;
2432 e.dphitcontentsmask = goodcontents | badcontents;
2435 delta = world.maxs - world.mins;
2437 for (i = 0; i < attempts; ++i)
2439 start_x = org_x + random() * delta_x;
2440 start_y = org_y + random() * delta_y;
2441 start_z = org_z + random() * delta_z;
2443 // rule 1: start inside world bounds, and outside
2444 // solid, and don't start from somewhere where you can
2445 // fall down to evil
2446 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2447 if (trace_fraction >= 1)
2449 if (trace_startsolid)
2451 if (trace_dphitcontents & badcontents)
2453 if (trace_dphitq3surfaceflags & badsurfaceflags)
2456 // rule 2: if we are too high, lower the point
2457 if (trace_fraction * delta_z > maxaboveground)
2458 start = trace_endpos + '0 0 1' * maxaboveground;
2459 enddown = trace_endpos;
2461 // rule 3: make sure we aren't outside the map. This only works
2462 // for somewhat well formed maps. A good rule of thumb is that
2463 // the map should have a convex outside hull.
2464 // these can be traceLINES as we already verified the starting box
2465 mstart = start + 0.5 * (e.mins + e.maxs);
2466 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2467 if (trace_fraction >= 1)
2469 traceline(mstart, mstart - '1 0 0' * delta_x, 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 1 0' * delta_y, MOVE_NORMAL, e);
2476 if (trace_fraction >= 1)
2478 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2479 if (trace_fraction >= 1)
2482 // rule 4: we must "see" some spawnpoint
2483 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2484 if(checkpvs(mstart, sp))
2488 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2489 if(checkpvs(mstart, sp))
2495 // find a random vector to "look at"
2496 end_x = org_x + random() * delta_x;
2497 end_y = org_y + random() * delta_y;
2498 end_z = org_z + random() * delta_z;
2499 end = start + normalize(end - start) * vlen(delta);
2501 // rule 4: start TO end must not be too short
2502 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2503 if (trace_startsolid)
2505 if (trace_fraction < minviewdistance / vlen(delta))
2508 // rule 5: don't want to look at sky
2509 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2512 // rule 6: we must not end up in trigger_hurt
2513 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2519 e.dphitcontentsmask = m;
2523 setorigin(e, start);
2524 e.angles = vectoangles(end - start);
2525 dprint("Needed ", ftos(i + 1), " attempts\n");
2532 float zcurveparticles_effectno;
2533 vector zcurveparticles_start;
2534 float zcurveparticles_spd;
2536 void endzcurveparticles()
2538 if(zcurveparticles_effectno)
2541 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2543 zcurveparticles_effectno = 0;
2546 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2548 spd = bound(0, floor(spd / 16), 32767);
2549 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2551 endzcurveparticles();
2552 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2553 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2554 WriteShort(MSG_BROADCAST, effectno);
2555 WriteCoord(MSG_BROADCAST, start_x);
2556 WriteCoord(MSG_BROADCAST, start_y);
2557 WriteCoord(MSG_BROADCAST, start_z);
2558 zcurveparticles_effectno = effectno;
2559 zcurveparticles_start = start;
2562 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2563 WriteCoord(MSG_BROADCAST, end_x);
2564 WriteCoord(MSG_BROADCAST, end_y);
2565 WriteCoord(MSG_BROADCAST, end_z);
2566 WriteCoord(MSG_BROADCAST, end_dz);
2567 zcurveparticles_spd = spd;
2570 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2573 vector vecxy, velxy;
2575 vecxy = end - start;
2580 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2582 endzcurveparticles();
2583 trailparticles(world, effectno, start, end);
2587 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2588 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2591 void write_recordmarker(entity pl, float tstart, float dt)
2593 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2595 // also write a marker into demo files for demotc-race-record-extractor to find
2598 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2599 " ", ftos(tstart), " ", ftos(dt), "\n"));
2602 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2615 if(allowcenter) // 2: allow center handedness
2628 if(allowcenter) // 2: allow center handedness
2644 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2649 if (autocvar_g_shootfromeye)
2662 else if (autocvar_g_shootfromcenter)
2667 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2677 else if (autocvar_g_shootfromclient)
2679 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2684 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2686 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2690 void attach_sameorigin(entity e, entity to, string tag)
2692 vector org, t_forward, t_left, t_up, e_forward, e_up;
2699 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2700 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2701 t_forward = v_forward * tagscale;
2702 t_left = v_right * -tagscale;
2703 t_up = v_up * tagscale;
2705 e.origin_x = org * t_forward;
2706 e.origin_y = org * t_left;
2707 e.origin_z = org * t_up;
2709 // current forward and up directions
2710 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2711 e.angles = AnglesTransform_FromVAngles(e.angles);
2713 e.angles = AnglesTransform_FromAngles(e.angles);
2714 fixedmakevectors(e.angles);
2716 // untransform forward, up!
2717 e_forward_x = v_forward * t_forward;
2718 e_forward_y = v_forward * t_left;
2719 e_forward_z = v_forward * t_up;
2720 e_up_x = v_up * t_forward;
2721 e_up_y = v_up * t_left;
2722 e_up_z = v_up * t_up;
2724 e.angles = fixedvectoangles2(e_forward, e_up);
2725 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2726 e.angles = AnglesTransform_ToVAngles(e.angles);
2728 e.angles = AnglesTransform_ToAngles(e.angles);
2730 setattachment(e, to, tag);
2731 setorigin(e, e.origin);
2734 void detach_sameorigin(entity e)
2737 org = gettaginfo(e, 0);
2738 e.angles = fixedvectoangles2(v_forward, v_up);
2739 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2740 e.angles = AnglesTransform_ToVAngles(e.angles);
2742 e.angles = AnglesTransform_ToAngles(e.angles);
2744 setattachment(e, world, "");
2745 setorigin(e, e.origin);
2748 void follow_sameorigin(entity e, entity to)
2750 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2751 e.aiment = to; // make the hole follow bmodel
2752 e.punchangle = to.angles; // the original angles of bmodel
2753 e.view_ofs = e.origin - to.origin; // relative origin
2754 e.v_angle = e.angles - to.angles; // relative angles
2757 void unfollow_sameorigin(entity e)
2759 e.movetype = MOVETYPE_NONE;
2762 entity gettaginfo_relative_ent;
2763 vector gettaginfo_relative(entity e, float tag)
2765 if (!gettaginfo_relative_ent)
2767 gettaginfo_relative_ent = spawn();
2768 gettaginfo_relative_ent.effects = EF_NODRAW;
2770 gettaginfo_relative_ent.model = e.model;
2771 gettaginfo_relative_ent.modelindex = e.modelindex;
2772 gettaginfo_relative_ent.frame = e.frame;
2773 return gettaginfo(gettaginfo_relative_ent, tag);
2776 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2780 if (pl.soundentity.cnt & p)
2782 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2783 pl.soundentity.cnt |= p;
2786 void SoundEntity_StopSound(entity pl, float chan)
2790 if (pl.soundentity.cnt & p)
2792 stopsoundto(MSG_ALL, pl.soundentity, chan);
2793 pl.soundentity.cnt &~= p;
2797 void SoundEntity_Attach(entity pl)
2799 pl.soundentity = spawn();
2800 pl.soundentity.classname = "soundentity";
2801 pl.soundentity.owner = pl;
2802 setattachment(pl.soundentity, pl, "");
2803 setmodel(pl.soundentity, "null");
2806 void SoundEntity_Detach(entity pl)
2809 for (i = 0; i <= 7; ++i)
2810 SoundEntity_StopSound(pl, i);
2814 float ParseCommandPlayerSlotTarget_firsttoken;
2815 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2823 ParseCommandPlayerSlotTarget_firsttoken = -1;
2827 if (substring(argv(idx), 0, 1) == "#")
2829 s = substring(argv(idx), 1, -1);
2831 if (s == "") if (tokens > idx)
2836 ParseCommandPlayerSlotTarget_firsttoken = idx;
2838 if (s == ftos(n) && n > 0 && n <= maxclients)
2841 if (e.flags & FL_CLIENT)
2847 // it must be a nick name
2850 ParseCommandPlayerSlotTarget_firsttoken = idx;
2853 FOR_EACH_CLIENT(head)
2854 if (head.netname == s)
2862 s = strdecolorize(s);
2864 FOR_EACH_CLIENT(head)
2865 if (strdecolorize(head.netname) == s)
2880 float modeleffect_SendEntity(entity to, float sf)
2883 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2886 if(self.velocity != '0 0 0')
2888 if(self.angles != '0 0 0')
2890 if(self.avelocity != '0 0 0')
2893 WriteByte(MSG_ENTITY, f);
2894 WriteShort(MSG_ENTITY, self.modelindex);
2895 WriteByte(MSG_ENTITY, self.skin);
2896 WriteByte(MSG_ENTITY, self.frame);
2897 WriteCoord(MSG_ENTITY, self.origin_x);
2898 WriteCoord(MSG_ENTITY, self.origin_y);
2899 WriteCoord(MSG_ENTITY, self.origin_z);
2902 WriteCoord(MSG_ENTITY, self.velocity_x);
2903 WriteCoord(MSG_ENTITY, self.velocity_y);
2904 WriteCoord(MSG_ENTITY, self.velocity_z);
2908 WriteCoord(MSG_ENTITY, self.angles_x);
2909 WriteCoord(MSG_ENTITY, self.angles_y);
2910 WriteCoord(MSG_ENTITY, self.angles_z);
2914 WriteCoord(MSG_ENTITY, self.avelocity_x);
2915 WriteCoord(MSG_ENTITY, self.avelocity_y);
2916 WriteCoord(MSG_ENTITY, self.avelocity_z);
2918 WriteShort(MSG_ENTITY, self.scale * 256.0);
2919 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2920 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2921 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2922 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2927 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)
2932 e.classname = "modeleffect";
2940 e.teleport_time = t1;
2944 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2948 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2951 sz = max(e.scale, e.scale2);
2952 setsize(e, e.mins * sz, e.maxs * sz);
2953 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2956 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2958 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2961 float randombit(float bits)
2963 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2972 for(f = 1; f <= bits; f *= 2)
2981 r = (r - 1) / (n - 1);
2988 float randombits(float bits, float k, float error_return)
2992 while(k > 0 && bits != r)
2994 r += randombit(bits - r);
3003 void randombit_test(float bits, float iter)
3007 print(ftos(randombit(bits)), "\n");
3012 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3014 if(halflifedist > 0)
3015 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3016 else if(halflifedist < 0)
3017 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3026 #define cvar_string_normal cvar_string_builtin
3027 #define cvar_normal cvar_builtin
3029 string cvar_string_normal(string n)
3031 if not(cvar_type(n) & 1)
3032 backtrace(strcat("Attempt to access undefined cvar: ", n));
3033 return cvar_string_builtin(n);
3036 float cvar_normal(string n)
3038 return stof(cvar_string_normal(n));
3041 #define cvar_set_normal cvar_set_builtin
3049 oself.think = SUB_Remove;
3050 oself.nextthink = time;
3056 Execute func() after time + fdelay.
3057 self when func is executed = self when defer is called
3059 void defer(float fdelay, void() func)
3066 e.think = defer_think;
3067 e.nextthink = time + fdelay;
3070 .string aiment_classname;
3071 .float aiment_deadflag;
3072 void SetMovetypeFollow(entity ent, entity e)
3074 // FIXME this may not be warpzone aware
3075 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3076 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.
3077 ent.aiment = e; // make the hole follow bmodel
3078 ent.punchangle = e.angles; // the original angles of bmodel
3079 ent.view_ofs = ent.origin - e.origin; // relative origin
3080 ent.v_angle = ent.angles - e.angles; // relative angles
3081 ent.aiment_classname = strzone(e.classname);
3082 ent.aiment_deadflag = e.deadflag;
3084 void UnsetMovetypeFollow(entity ent)
3086 ent.movetype = MOVETYPE_FLY;
3087 PROJECTILE_MAKETRIGGER(ent);
3090 float LostMovetypeFollow(entity ent)
3093 if(ent.movetype != MOVETYPE_FOLLOW)
3099 if(ent.aiment.classname != ent.aiment_classname)
3101 if(ent.aiment.deadflag != ent.aiment_deadflag)
3107 float isPushable(entity e)
3114 case "droppedweapon":
3115 case "keepawayball":
3116 case "nexball_basketball":
3117 case "nexball_football":
3119 case "bullet": // antilagged bullets can't hit this either
3122 if (e.projectiledeathtype)