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));
2042 // FIXME remove this later after 0.6 release
2043 // convert old style broken records to correct style
2046 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2049 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
2050 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
2055 s = "^1Unregistered Player";
2059 float race_readTime(string map, float pos)
2067 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2070 string race_readUID(string map, float pos)
2078 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2081 float race_readPos(string map, float t) {
2083 for (i = 1; i <= RANKINGS_CNT; ++i)
2084 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2087 return 0; // pos is zero if unranked
2090 void race_writeTime(string map, float t, string myuid)
2099 newpos = race_readPos(map, t);
2102 for(i = 1; i <= RANKINGS_CNT; ++i)
2104 if(race_readUID(map, i) == myuid)
2107 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2108 for (i = prevpos; i > newpos; --i) {
2109 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2110 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2112 } else { // player has no ranked record yet
2113 for (i = RANKINGS_CNT; i > newpos; --i) {
2114 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2115 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2119 // store new time itself
2120 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2121 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2124 string race_readName(string map, float pos)
2132 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2135 string race_placeName(float pos) {
2136 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2138 if(mod(pos, 10) == 1)
2139 return strcat(ftos(pos), "st");
2140 else if(mod(pos, 10) == 2)
2141 return strcat(ftos(pos), "nd");
2142 else if(mod(pos, 10) == 3)
2143 return strcat(ftos(pos), "rd");
2145 return strcat(ftos(pos), "th");
2148 return strcat(ftos(pos), "th");
2150 string getrecords(float page) // 50 records per page
2164 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2166 if (MapInfo_Get_ByID(i))
2168 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2172 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2173 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2181 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2183 if (MapInfo_Get_ByID(i))
2185 r = race_readTime(MapInfo_Map_bspname, 1);
2188 h = race_readName(MapInfo_Map_bspname, 1);
2189 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2197 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2199 if (MapInfo_Get_ByID(i))
2201 r = race_readTime(MapInfo_Map_bspname, 1);
2204 h = race_readName(MapInfo_Map_bspname, 1);
2205 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2211 MapInfo_ClearTemps();
2213 if (s == "" && page == 0)
2214 return "No records are available on this server.\n";
2219 string getrankings()
2232 for (i = 1; i <= RANKINGS_CNT; ++i)
2234 t = race_readTime(map, i);
2237 n = race_readName(map, i);
2238 p = race_placeName(i);
2239 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2242 MapInfo_ClearTemps();
2245 return strcat("No records are available for the map: ", map, "\n");
2247 return strcat("Records for ", map, ":\n", s);
2250 #define LADDER_FIRSTPOINT 100
2251 #define LADDER_CNT 10
2252 // position X still gives LADDER_FIRSTPOINT/X points
2253 #define LADDER_SIZE 30
2254 // ladder shows the top X players
2255 string top_uids[LADDER_SIZE];
2256 float top_scores[LADDER_SIZE];
2259 float i, j, k, uidcnt;
2273 for (k = 0; k < MapInfo_count; ++k)
2275 if (MapInfo_Get_ByID(k))
2277 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2278 if(i == 0) // speed award
2280 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2283 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2285 else // normal record, if it exists (else break)
2287 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2290 myuid = race_readUID(MapInfo_Map_bspname, i);
2293 // string s contains:
2294 // arg 0 = # of speed recs
2295 // arg 1 = # of 1st place recs
2296 // arg 2 = # of 2nd place recs
2298 // LADDER_CNT+1 = total points
2300 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2303 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2305 for (j = 0; j <= LADDER_CNT + 1; ++j)
2307 if(j != LADDER_CNT + 1)
2308 temp_s = strcat(temp_s, "0 ");
2310 temp_s = strcat(temp_s, "0");
2314 tokenize_console(temp_s);
2317 if(i == 0) // speed award
2318 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2320 if(j == 0) // speed award
2321 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2323 s = strcat(s, " ", argv(j)); // just copy over everything else
2326 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2329 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2330 else if(j == i) // wanted rec!
2331 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2333 s = strcat(s, " ", argv(j)); // just copy over everything else
2336 // total points are (by default) calculated like this:
2337 // speedrec = floor(100 / 10) = 10 points
2338 // 1st place = floor(100 / 1) = 100 points
2339 // 2nd place = floor(100 / 2) = 50 points
2340 // 3rd place = floor(100 / 3) = 33 points
2341 // 4th place = floor(100 / 4) = 25 points
2342 // 5th place = floor(100 / 5) = 20 points
2346 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2348 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2350 db_put(TemporaryDB, strcat("ladder", myuid), s);
2357 for (i = 0; i <= uidcnt; ++i) // for each known uid
2359 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2360 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2361 tokenize_console(temp_s);
2362 thiscnt = stof(argv(LADDER_CNT+1));
2364 if(thiscnt > top_scores[LADDER_SIZE-1])
2365 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2367 if(thiscnt > top_scores[j])
2369 for (k = LADDER_SIZE-1; k >= j; --k)
2371 top_uids[k] = top_uids[k-1];
2372 top_scores[k] = top_scores[k-1];
2374 top_uids[j] = thisuid;
2375 top_scores[j] = thiscnt;
2381 s = "^3-----------------------\n\n";
2383 s = strcat(s, "Pos ^3|");
2384 s = strcat(s, " ^7Total ^3|");
2385 for (i = 1; i <= LADDER_CNT; ++i)
2387 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2389 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2391 s = strcat(s, "\n^3----+--------");
2392 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2394 s = strcat(s, "+-----");
2397 for (i = 1; i <= LADDER_CNT - 9; ++i)
2399 s = strcat(s, "+------");
2403 s = strcat(s, "+--------------+--------------------\n");
2405 for (i = 0; i < LADDER_SIZE; ++i)
2407 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2408 tokenize_console(temp_s);
2409 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2411 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2412 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2413 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2415 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2418 for (j = 10; j <= LADDER_CNT; ++j)
2420 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2424 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2425 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2428 MapInfo_ClearTemps();
2431 return "No ladder on this server!\n";
2433 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2437 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2440 vector start, org, delta, end, enddown, mstart;
2443 m = e.dphitcontentsmask;
2444 e.dphitcontentsmask = goodcontents | badcontents;
2447 delta = world.maxs - world.mins;
2449 for (i = 0; i < attempts; ++i)
2451 start_x = org_x + random() * delta_x;
2452 start_y = org_y + random() * delta_y;
2453 start_z = org_z + random() * delta_z;
2455 // rule 1: start inside world bounds, and outside
2456 // solid, and don't start from somewhere where you can
2457 // fall down to evil
2458 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2459 if (trace_fraction >= 1)
2461 if (trace_startsolid)
2463 if (trace_dphitcontents & badcontents)
2465 if (trace_dphitq3surfaceflags & badsurfaceflags)
2468 // rule 2: if we are too high, lower the point
2469 if (trace_fraction * delta_z > maxaboveground)
2470 start = trace_endpos + '0 0 1' * maxaboveground;
2471 enddown = trace_endpos;
2473 // rule 3: make sure we aren't outside the map. This only works
2474 // for somewhat well formed maps. A good rule of thumb is that
2475 // the map should have a convex outside hull.
2476 // these can be traceLINES as we already verified the starting box
2477 mstart = start + 0.5 * (e.mins + e.maxs);
2478 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2479 if (trace_fraction >= 1)
2481 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2482 if (trace_fraction >= 1)
2484 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2485 if (trace_fraction >= 1)
2487 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2488 if (trace_fraction >= 1)
2490 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2491 if (trace_fraction >= 1)
2494 // rule 4: we must "see" some spawnpoint
2495 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2496 if(checkpvs(mstart, sp))
2500 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2501 if(checkpvs(mstart, sp))
2507 // find a random vector to "look at"
2508 end_x = org_x + random() * delta_x;
2509 end_y = org_y + random() * delta_y;
2510 end_z = org_z + random() * delta_z;
2511 end = start + normalize(end - start) * vlen(delta);
2513 // rule 4: start TO end must not be too short
2514 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2515 if (trace_startsolid)
2517 if (trace_fraction < minviewdistance / vlen(delta))
2520 // rule 5: don't want to look at sky
2521 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2524 // rule 6: we must not end up in trigger_hurt
2525 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2531 e.dphitcontentsmask = m;
2535 setorigin(e, start);
2536 e.angles = vectoangles(end - start);
2537 dprint("Needed ", ftos(i + 1), " attempts\n");
2544 float zcurveparticles_effectno;
2545 vector zcurveparticles_start;
2546 float zcurveparticles_spd;
2548 void endzcurveparticles()
2550 if(zcurveparticles_effectno)
2553 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2555 zcurveparticles_effectno = 0;
2558 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2560 spd = bound(0, floor(spd / 16), 32767);
2561 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2563 endzcurveparticles();
2564 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2565 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2566 WriteShort(MSG_BROADCAST, effectno);
2567 WriteCoord(MSG_BROADCAST, start_x);
2568 WriteCoord(MSG_BROADCAST, start_y);
2569 WriteCoord(MSG_BROADCAST, start_z);
2570 zcurveparticles_effectno = effectno;
2571 zcurveparticles_start = start;
2574 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2575 WriteCoord(MSG_BROADCAST, end_x);
2576 WriteCoord(MSG_BROADCAST, end_y);
2577 WriteCoord(MSG_BROADCAST, end_z);
2578 WriteCoord(MSG_BROADCAST, end_dz);
2579 zcurveparticles_spd = spd;
2582 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2585 vector vecxy, velxy;
2587 vecxy = end - start;
2592 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2594 endzcurveparticles();
2595 trailparticles(world, effectno, start, end);
2599 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2600 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2603 void write_recordmarker(entity pl, float tstart, float dt)
2605 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2607 // also write a marker into demo files for demotc-race-record-extractor to find
2610 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2611 " ", ftos(tstart), " ", ftos(dt), "\n"));
2614 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2627 if(allowcenter) // 2: allow center handedness
2640 if(allowcenter) // 2: allow center handedness
2656 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2661 if (autocvar_g_shootfromeye)
2674 else if (autocvar_g_shootfromcenter)
2679 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2689 else if (autocvar_g_shootfromclient)
2691 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2696 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2698 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2702 void attach_sameorigin(entity e, entity to, string tag)
2704 vector org, t_forward, t_left, t_up, e_forward, e_up;
2711 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2712 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2713 t_forward = v_forward * tagscale;
2714 t_left = v_right * -tagscale;
2715 t_up = v_up * tagscale;
2717 e.origin_x = org * t_forward;
2718 e.origin_y = org * t_left;
2719 e.origin_z = org * t_up;
2721 // current forward and up directions
2722 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2723 e.angles = AnglesTransform_FromVAngles(e.angles);
2725 e.angles = AnglesTransform_FromAngles(e.angles);
2726 fixedmakevectors(e.angles);
2728 // untransform forward, up!
2729 e_forward_x = v_forward * t_forward;
2730 e_forward_y = v_forward * t_left;
2731 e_forward_z = v_forward * t_up;
2732 e_up_x = v_up * t_forward;
2733 e_up_y = v_up * t_left;
2734 e_up_z = v_up * t_up;
2736 e.angles = fixedvectoangles2(e_forward, e_up);
2737 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2738 e.angles = AnglesTransform_ToVAngles(e.angles);
2740 e.angles = AnglesTransform_ToAngles(e.angles);
2742 setattachment(e, to, tag);
2743 setorigin(e, e.origin);
2746 void detach_sameorigin(entity e)
2749 org = gettaginfo(e, 0);
2750 e.angles = fixedvectoangles2(v_forward, v_up);
2751 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2752 e.angles = AnglesTransform_ToVAngles(e.angles);
2754 e.angles = AnglesTransform_ToAngles(e.angles);
2756 setattachment(e, world, "");
2757 setorigin(e, e.origin);
2760 void follow_sameorigin(entity e, entity to)
2762 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2763 e.aiment = to; // make the hole follow bmodel
2764 e.punchangle = to.angles; // the original angles of bmodel
2765 e.view_ofs = e.origin - to.origin; // relative origin
2766 e.v_angle = e.angles - to.angles; // relative angles
2769 void unfollow_sameorigin(entity e)
2771 e.movetype = MOVETYPE_NONE;
2774 entity gettaginfo_relative_ent;
2775 vector gettaginfo_relative(entity e, float tag)
2777 if (!gettaginfo_relative_ent)
2779 gettaginfo_relative_ent = spawn();
2780 gettaginfo_relative_ent.effects = EF_NODRAW;
2782 gettaginfo_relative_ent.model = e.model;
2783 gettaginfo_relative_ent.modelindex = e.modelindex;
2784 gettaginfo_relative_ent.frame = e.frame;
2785 return gettaginfo(gettaginfo_relative_ent, tag);
2788 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2792 if (pl.soundentity.cnt & p)
2794 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2795 pl.soundentity.cnt |= p;
2798 void SoundEntity_StopSound(entity pl, float chan)
2802 if (pl.soundentity.cnt & p)
2804 stopsoundto(MSG_ALL, pl.soundentity, chan);
2805 pl.soundentity.cnt &~= p;
2809 void SoundEntity_Attach(entity pl)
2811 pl.soundentity = spawn();
2812 pl.soundentity.classname = "soundentity";
2813 pl.soundentity.owner = pl;
2814 setattachment(pl.soundentity, pl, "");
2815 setmodel(pl.soundentity, "null");
2818 void SoundEntity_Detach(entity pl)
2821 for (i = 0; i <= 7; ++i)
2822 SoundEntity_StopSound(pl, i);
2826 float ParseCommandPlayerSlotTarget_firsttoken;
2827 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2835 ParseCommandPlayerSlotTarget_firsttoken = -1;
2839 if (substring(argv(idx), 0, 1) == "#")
2841 s = substring(argv(idx), 1, -1);
2843 if (s == "") if (tokens > idx)
2848 ParseCommandPlayerSlotTarget_firsttoken = idx;
2850 if (s == ftos(n) && n > 0 && n <= maxclients)
2853 if (e.flags & FL_CLIENT)
2859 // it must be a nick name
2862 ParseCommandPlayerSlotTarget_firsttoken = idx;
2865 FOR_EACH_CLIENT(head)
2866 if (head.netname == s)
2874 s = strdecolorize(s);
2876 FOR_EACH_CLIENT(head)
2877 if (strdecolorize(head.netname) == s)
2892 float modeleffect_SendEntity(entity to, float sf)
2895 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2898 if(self.velocity != '0 0 0')
2900 if(self.angles != '0 0 0')
2902 if(self.avelocity != '0 0 0')
2905 WriteByte(MSG_ENTITY, f);
2906 WriteShort(MSG_ENTITY, self.modelindex);
2907 WriteByte(MSG_ENTITY, self.skin);
2908 WriteByte(MSG_ENTITY, self.frame);
2909 WriteCoord(MSG_ENTITY, self.origin_x);
2910 WriteCoord(MSG_ENTITY, self.origin_y);
2911 WriteCoord(MSG_ENTITY, self.origin_z);
2914 WriteCoord(MSG_ENTITY, self.velocity_x);
2915 WriteCoord(MSG_ENTITY, self.velocity_y);
2916 WriteCoord(MSG_ENTITY, self.velocity_z);
2920 WriteCoord(MSG_ENTITY, self.angles_x);
2921 WriteCoord(MSG_ENTITY, self.angles_y);
2922 WriteCoord(MSG_ENTITY, self.angles_z);
2926 WriteCoord(MSG_ENTITY, self.avelocity_x);
2927 WriteCoord(MSG_ENTITY, self.avelocity_y);
2928 WriteCoord(MSG_ENTITY, self.avelocity_z);
2930 WriteShort(MSG_ENTITY, self.scale * 256.0);
2931 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2932 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2933 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2934 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2939 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)
2944 e.classname = "modeleffect";
2952 e.teleport_time = t1;
2956 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2960 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2963 sz = max(e.scale, e.scale2);
2964 setsize(e, e.mins * sz, e.maxs * sz);
2965 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2968 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2970 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2973 float randombit(float bits)
2975 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2984 for(f = 1; f <= bits; f *= 2)
2993 r = (r - 1) / (n - 1);
3000 float randombits(float bits, float k, float error_return)
3004 while(k > 0 && bits != r)
3006 r += randombit(bits - r);
3015 void randombit_test(float bits, float iter)
3019 print(ftos(randombit(bits)), "\n");
3024 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3026 if(halflifedist > 0)
3027 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3028 else if(halflifedist < 0)
3029 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3038 #define cvar_string_normal cvar_string_builtin
3039 #define cvar_normal cvar_builtin
3041 string cvar_string_normal(string n)
3043 if not(cvar_type(n) & 1)
3044 backtrace(strcat("Attempt to access undefined cvar: ", n));
3045 return cvar_string_builtin(n);
3048 float cvar_normal(string n)
3050 return stof(cvar_string_normal(n));
3053 #define cvar_set_normal cvar_set_builtin
3061 oself.think = SUB_Remove;
3062 oself.nextthink = time;
3068 Execute func() after time + fdelay.
3069 self when func is executed = self when defer is called
3071 void defer(float fdelay, void() func)
3078 e.think = defer_think;
3079 e.nextthink = time + fdelay;
3082 .string aiment_classname;
3083 .float aiment_deadflag;
3084 void SetMovetypeFollow(entity ent, entity e)
3086 // FIXME this may not be warpzone aware
3087 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3088 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.
3089 ent.aiment = e; // make the hole follow bmodel
3090 ent.punchangle = e.angles; // the original angles of bmodel
3091 ent.view_ofs = ent.origin - e.origin; // relative origin
3092 ent.v_angle = ent.angles - e.angles; // relative angles
3093 ent.aiment_classname = strzone(e.classname);
3094 ent.aiment_deadflag = e.deadflag;
3096 void UnsetMovetypeFollow(entity ent)
3098 ent.movetype = MOVETYPE_FLY;
3099 PROJECTILE_MAKETRIGGER(ent);
3102 float LostMovetypeFollow(entity ent)
3105 if(ent.movetype != MOVETYPE_FOLLOW)
3111 if(ent.aiment.classname != ent.aiment_classname)
3113 if(ent.aiment.deadflag != ent.aiment_deadflag)
3119 float isPushable(entity e)
3126 case "droppedweapon":
3127 case "keepawayball":
3128 case "nexball_basketball":
3129 case "nexball_football":
3131 case "bullet": // antilagged bullets can't hit this either
3134 if (e.projectiledeathtype)