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 crosshair_trace_plusvisibletriggers(entity pl)
15 first = findchainfloat(solid, SOLID_TRIGGER);
17 for (e = first; e; e = e.chain)
23 for (e = first; e; e = e.chain)
24 e.solid = SOLID_TRIGGER;
26 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
27 void WarpZone_crosshair_trace(entity pl)
29 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));
32 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
33 void() spawnpoint_use;
35 string ColoredTeamName(float t);
37 string admin_name(void)
39 if(autocvar_sv_adminnick != "")
40 return autocvar_sv_adminnick;
42 return "SERVER ADMIN";
45 float DistributeEvenly_amount;
46 float DistributeEvenly_totalweight;
47 void DistributeEvenly_Init(float amount, float totalweight)
49 if (DistributeEvenly_amount)
51 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
52 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
55 DistributeEvenly_amount = 0;
57 DistributeEvenly_amount = amount;
58 DistributeEvenly_totalweight = totalweight;
60 float DistributeEvenly_Get(float weight)
65 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
66 DistributeEvenly_totalweight -= weight;
67 DistributeEvenly_amount -= f;
71 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
74 string STR_PLAYER = "player";
75 string STR_SPECTATOR = "spectator";
76 string STR_OBSERVER = "observer";
79 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
80 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
81 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
82 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
84 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
85 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
86 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
87 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
88 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
91 // copies a string to a tempstring (so one can strunzone it)
92 string strcat1(string s) = #115; // FRIK_FILE
97 void bcenterprint(string s)
99 // TODO replace by MSG_ALL (would show it to spectators too, though)?
101 FOR_EACH_PLAYER(head)
102 if (clienttype(head) == CLIENTTYPE_REAL)
103 centerprint(head, s);
106 void GameLogEcho(string s)
111 if (autocvar_sv_eventlog_files)
116 matches = autocvar_sv_eventlog_files_counter + 1;
117 cvar_set("sv_eventlog_files_counter", ftos(matches));
120 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
121 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
122 logfile = fopen(fn, FILE_APPEND);
123 fputs(logfile, ":logversion:3\n");
127 if (autocvar_sv_eventlog_files_timestamps)
128 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
130 fputs(logfile, strcat(s, "\n"));
133 if (autocvar_sv_eventlog_console)
142 // will be opened later
147 if (logfile_open && logfile >= 0)
158 vector PL_CROUCH_VIEW_OFS;
159 vector PL_CROUCH_MIN;
160 vector PL_CROUCH_MAX;
162 float spawnpoint_nag;
163 void relocate_spawnpoint()
165 PL_VIEW_OFS = stov(autocvar_sv_player_viewoffset);
166 PL_MIN = stov(autocvar_sv_player_mins);
167 PL_MAX = stov(autocvar_sv_player_maxs);
168 PL_HEAD = stov(autocvar_sv_player_headsize);
169 PL_CROUCH_VIEW_OFS = stov(autocvar_sv_player_crouch_viewoffset);
170 PL_CROUCH_MIN = stov(autocvar_sv_player_crouch_mins);
171 PL_CROUCH_MAX = stov(autocvar_sv_player_crouch_maxs);
173 // nudge off the floor
174 setorigin(self, self.origin + '0 0 1');
176 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
177 if (trace_startsolid)
183 if (!move_out_of_solid(self))
184 objerror("could not get out of solid at all!");
185 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
186 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
187 print(" ", ftos(self.origin_y - o_y));
188 print(" ", ftos(self.origin_z - o_z), "'\n");
189 if (autocvar_g_spawnpoints_auto_move_out_of_solid)
192 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
198 self.mins = self.maxs = '0 0 0';
199 objerror("player spawn point in solid, mapper sucks!\n");
204 self.use = spawnpoint_use;
205 self.team_saved = self.team;
209 if (have_team_spawns != 0)
211 have_team_spawns = 1;
212 have_team_spawns_forteam[self.team] = 1;
214 if (autocvar_r_showbboxes)
216 // show where spawnpoints point at too
217 makevectors(self.angles);
220 e.classname = "info_player_foo";
221 setorigin(e, self.origin + v_forward * 24);
222 setsize(e, '-8 -8 -8', '8 8 8');
223 e.solid = SOLID_TRIGGER;
227 #define strstr strstrofs
229 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
230 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
231 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
232 // BE CONSTANT OR strzoneD!
233 float strstr(string haystack, string needle, float offset)
237 len = strlen(needle);
238 endpos = strlen(haystack) - len;
239 while(offset <= endpos)
241 found = substring(haystack, offset, len);
250 float NUM_NEAREST_ENTITIES = 4;
251 entity nearest_entity[NUM_NEAREST_ENTITIES];
252 float nearest_length[NUM_NEAREST_ENTITIES];
253 entity findnearest(vector point, .string field, string value, vector axismod)
264 localhead = find(world, field, value);
267 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
268 dist = localhead.oldorigin;
270 dist = localhead.origin;
272 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
275 for (i = 0; i < num_nearest; ++i)
277 if (len < nearest_length[i])
281 // now i tells us where to insert at
282 // INSERTION SORT! YOU'VE SEEN IT! RUN!
283 if (i < NUM_NEAREST_ENTITIES)
285 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
287 nearest_length[j + 1] = nearest_length[j];
288 nearest_entity[j + 1] = nearest_entity[j];
290 nearest_length[i] = len;
291 nearest_entity[i] = localhead;
292 if (num_nearest < NUM_NEAREST_ENTITIES)
293 num_nearest = num_nearest + 1;
296 localhead = find(localhead, field, value);
299 // now use the first one from our list that we can see
300 for (i = 0; i < num_nearest; ++i)
302 traceline(point, nearest_entity[i].origin, TRUE, world);
303 if (trace_fraction == 1)
307 dprint("Nearest point (");
308 dprint(nearest_entity[0].netname);
309 dprint(") is not visible, using a visible one.\n");
311 return nearest_entity[i];
315 if (num_nearest == 0)
318 dprint("Not seeing any location point, using nearest as fallback.\n");
320 dprint("Candidates were: ");
321 for(j = 0; j < num_nearest; ++j)
325 dprint(nearest_entity[j].netname);
330 return nearest_entity[0];
333 void spawnfunc_target_location()
335 self.classname = "target_location";
336 // location name in netname
337 // eventually support: count, teamgame selectors, line of sight?
340 void spawnfunc_info_location()
342 self.classname = "target_location";
343 self.message = self.netname;
346 string NearestLocation(vector p)
351 loc = findnearest(p, classname, "target_location", '1 1 1');
358 loc = findnearest(p, target, "###item###", '1 1 4');
365 string formatmessage(string msg)
376 WarpZone_crosshair_trace(self);
377 cursor = trace_endpos;
378 cursor_ent = trace_ent;
382 break; // too many replacements
385 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
386 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
399 replacement = substring(msg, p, 2);
400 escape = substring(msg, p + 1, 1);
404 else if (escape == "\\")
406 else if (escape == "n")
408 else if (escape == "a")
409 replacement = ftos(floor(self.armorvalue));
410 else if (escape == "h")
411 replacement = ftos(floor(self.health));
412 else if (escape == "l")
413 replacement = NearestLocation(self.origin);
414 else if (escape == "y")
415 replacement = NearestLocation(cursor);
416 else if (escape == "d")
417 replacement = NearestLocation(self.death_origin);
418 else if (escape == "w") {
422 wep = self.switchweapon;
425 replacement = W_Name(wep);
426 } else if (escape == "W") {
427 if (self.items & IT_SHELLS) replacement = "shells";
428 else if (self.items & IT_NAILS) replacement = "bullets";
429 else if (self.items & IT_ROCKETS) replacement = "rockets";
430 else if (self.items & IT_CELLS) replacement = "cells";
431 else replacement = "batteries"; // ;)
432 } else if (escape == "x") {
433 replacement = cursor_ent.netname;
434 if (!replacement || !cursor_ent)
435 replacement = "nothing";
436 } else if (escape == "s")
437 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
438 else if (escape == "S")
439 replacement = ftos(vlen(self.velocity));
441 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
442 p = p + strlen(replacement);
447 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
448 return (value == 0) ? FALSE : TRUE;
457 >0: receives a cvar from name=argv(f) value=argv(f+1)
459 void GetCvars_handleString(string thisname, float f, .string field, string name)
464 strunzone(self.field);
465 self.field = string_null;
469 if (thisname == name)
472 strunzone(self.field);
473 self.field = strzone(argv(f + 1));
477 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
479 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
481 GetCvars_handleString(thisname, f, field, name);
482 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
483 if (thisname == name)
486 s = func(strcat1(self.field));
489 strunzone(self.field);
490 self.field = strzone(s);
494 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
501 if (thisname == name)
502 self.field = stof(argv(f + 1));
505 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
507 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
514 if (thisname == name)
518 self.field = stof(argv(f + 1));
527 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
530 float w_getbestweapon(entity e);
531 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
534 o = W_FixWeaponOrder_ForceComplete(wo);
535 if(self.weaponorder_byimpulse)
537 strunzone(self.weaponorder_byimpulse);
538 self.weaponorder_byimpulse = string_null;
540 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
543 void GetCvars(float f)
548 s = strcat1(argv(f));
552 MUTATOR_CALLHOOK(GetCvars);
553 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
554 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
555 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
556 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
557 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
558 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
559 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
560 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
561 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
562 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
563 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
564 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
565 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
566 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
567 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
568 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
569 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
570 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
571 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
572 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
573 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
574 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
575 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
576 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
578 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
579 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
581 #ifdef ALLOW_FORCEMODELS
582 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
583 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
585 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
586 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
587 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
588 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
589 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
591 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
594 if (s == "cl_weaponpriority")
595 self.switchweapon = w_getbestweapon(self);
596 if (s == "cl_allow_uidtracking")
597 PlayerStats_AddPlayer(self);
601 void backtrace(string msg)
604 dev = autocvar_developer;
605 war = autocvar_prvm_backtraceforwarnings;
606 cvar_set("developer", "1");
607 cvar_set("prvm_backtraceforwarnings", "1");
609 print("--- CUT HERE ---\nWARNING: ");
612 remove(world); // isn't there any better way to cause a backtrace?
613 print("\n--- CUT UNTIL HERE ---\n");
614 cvar_set("developer", ftos(dev));
615 cvar_set("prvm_backtraceforwarnings", ftos(war));
618 string Team_ColorCode(float teamid)
620 if (teamid == COLOR_TEAM1)
622 else if (teamid == COLOR_TEAM2)
624 else if (teamid == COLOR_TEAM3)
626 else if (teamid == COLOR_TEAM4)
632 string Team_ColorName(float t)
634 // fixme: Search for team entities and get their .netname's!
635 if (t == COLOR_TEAM1)
637 if (t == COLOR_TEAM2)
639 if (t == COLOR_TEAM3)
641 if (t == COLOR_TEAM4)
646 string Team_ColorNameLowerCase(float t)
648 // fixme: Search for team entities and get their .netname's!
649 if (t == COLOR_TEAM1)
651 if (t == COLOR_TEAM2)
653 if (t == COLOR_TEAM3)
655 if (t == COLOR_TEAM4)
660 float ColourToNumber(string team_colour)
662 if (team_colour == "red")
665 if (team_colour == "blue")
668 if (team_colour == "yellow")
671 if (team_colour == "pink")
674 if (team_colour == "auto")
680 float NumberToTeamNumber(float number)
697 // decolorizes and team colors the player name when needed
698 string playername(entity p)
701 if (teamplay && !intermission_running && p.classname == "player")
703 t = Team_ColorCode(p.team);
704 return strcat(t, strdecolorize(p.netname));
710 vector randompos(vector m1, vector m2)
714 v_x = m2_x * random() + m1_x;
715 v_y = m2_y * random() + m1_y;
716 v_z = m2_z * random() + m1_z;
720 //#NO AUTOCVARS START
722 float g_pickup_shells;
723 float g_pickup_shells_max;
724 float g_pickup_nails;
725 float g_pickup_nails_max;
726 float g_pickup_rockets;
727 float g_pickup_rockets_max;
728 float g_pickup_cells;
729 float g_pickup_cells_max;
731 float g_pickup_fuel_jetpack;
732 float g_pickup_fuel_max;
733 float g_pickup_armorsmall;
734 float g_pickup_armorsmall_max;
735 float g_pickup_armorsmall_anyway;
736 float g_pickup_armormedium;
737 float g_pickup_armormedium_max;
738 float g_pickup_armormedium_anyway;
739 float g_pickup_armorbig;
740 float g_pickup_armorbig_max;
741 float g_pickup_armorbig_anyway;
742 float g_pickup_armorlarge;
743 float g_pickup_armorlarge_max;
744 float g_pickup_armorlarge_anyway;
745 float g_pickup_healthsmall;
746 float g_pickup_healthsmall_max;
747 float g_pickup_healthsmall_anyway;
748 float g_pickup_healthmedium;
749 float g_pickup_healthmedium_max;
750 float g_pickup_healthmedium_anyway;
751 float g_pickup_healthlarge;
752 float g_pickup_healthlarge_max;
753 float g_pickup_healthlarge_anyway;
754 float g_pickup_healthmega;
755 float g_pickup_healthmega_max;
756 float g_pickup_healthmega_anyway;
757 float g_pickup_ammo_anyway;
758 float g_pickup_weapons_anyway;
760 float g_weaponarena_random;
761 float g_weaponarena_random_with_laser;
762 string g_weaponarena_list;
763 float g_weaponspeedfactor;
764 float g_weaponratefactor;
765 float g_weapondamagefactor;
766 float g_weaponforcefactor;
767 float g_weaponspreadfactor;
771 float start_ammo_shells;
772 float start_ammo_nails;
773 float start_ammo_rockets;
774 float start_ammo_cells;
775 float start_ammo_fuel;
777 float start_armorvalue;
778 float warmup_start_weapons;
779 float warmup_start_ammo_shells;
780 float warmup_start_ammo_nails;
781 float warmup_start_ammo_rockets;
782 float warmup_start_ammo_cells;
783 float warmup_start_ammo_fuel;
784 float warmup_start_health;
785 float warmup_start_armorvalue;
789 entity get_weaponinfo(float w);
791 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
793 var float i = weaponinfo.weapon;
798 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
800 if (t < 0) // "default" weapon selection
802 if (g_lms || g_ca || allguns)
803 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
807 t = (i == WEP_SHOTGUN);
809 t = 0; // weapon is set a few lines later
811 t = (i == WEP_LASER || i == WEP_SHOTGUN);
812 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
813 t |= (i == WEP_HOOK);
816 // we cannot disable porto in Nexball, we must force it
817 if(g_nexball && i == WEP_PORTO)
823 void readplayerstartcvars()
829 // initialize starting values for players
832 start_ammo_shells = 0;
833 start_ammo_nails = 0;
834 start_ammo_rockets = 0;
835 start_ammo_cells = 0;
836 start_health = cvar("g_balance_health_start");
837 start_armorvalue = cvar("g_balance_armor_start");
840 s = cvar_string("g_weaponarena");
841 if (s == "0" || s == "")
847 if (s == "0" || s == "")
853 // forcibly turn off weaponarena
857 g_weaponarena_list = "All Weapons";
858 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
860 e = get_weaponinfo(j);
861 g_weaponarena |= e.weapons;
862 weapon_action(e.weapon, WR_PRECACHE);
865 else if (s == "most")
867 g_weaponarena_list = "Most Weapons";
868 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
870 e = get_weaponinfo(j);
871 if (e.spawnflags & WEP_FLAG_NORMAL)
873 g_weaponarena |= e.weapons;
874 weapon_action(e.weapon, WR_PRECACHE);
878 else if (s == "none")
880 g_weaponarena_list = "No Weapons";
881 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
885 t = tokenize_console(s);
886 g_weaponarena_list = "";
887 for (i = 0; i < t; ++i)
890 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
892 e = get_weaponinfo(j);
895 g_weaponarena |= e.weapons;
896 weapon_action(e.weapon, WR_PRECACHE);
897 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
903 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
906 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
910 g_weaponarena_random = cvar("g_weaponarena_random");
912 g_weaponarena_random = 0;
913 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
917 start_weapons = g_weaponarena;
919 start_items |= IT_UNLIMITED_AMMO;
921 else if (g_minstagib)
924 start_armorvalue = 0;
925 start_weapons = WEPBIT_MINSTANEX;
926 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
927 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
929 if (g_minstagib_invis_alpha <= 0)
930 g_minstagib_invis_alpha = -1;
934 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
936 e = get_weaponinfo(i);
937 if(want_weapon("g_start_weapon_", e, FALSE))
938 start_weapons |= e.weapons;
942 if(!cvar("g_use_ammunition"))
943 start_items |= IT_UNLIMITED_AMMO;
947 start_ammo_cells = cvar("g_minstagib_ammo_start");
948 start_ammo_fuel = cvar("g_start_ammo_fuel");
950 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
952 start_ammo_rockets = 999;
953 start_ammo_shells = 999;
954 start_ammo_cells = 999;
955 start_ammo_nails = 999;
956 start_ammo_fuel = 999;
962 start_ammo_shells = cvar("g_lms_start_ammo_shells");
963 start_ammo_nails = cvar("g_lms_start_ammo_nails");
964 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
965 start_ammo_cells = cvar("g_lms_start_ammo_cells");
966 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
970 start_ammo_shells = cvar("g_start_ammo_shells");
971 start_ammo_nails = cvar("g_start_ammo_nails");
972 start_ammo_rockets = cvar("g_start_ammo_rockets");
973 start_ammo_cells = cvar("g_start_ammo_cells");
974 start_ammo_fuel = cvar("g_start_ammo_fuel");
980 start_health = cvar("g_lms_start_health");
981 start_armorvalue = cvar("g_lms_start_armor");
986 warmup_start_ammo_shells = start_ammo_shells;
987 warmup_start_ammo_nails = start_ammo_nails;
988 warmup_start_ammo_rockets = start_ammo_rockets;
989 warmup_start_ammo_cells = start_ammo_cells;
990 warmup_start_ammo_fuel = start_ammo_fuel;
991 warmup_start_health = start_health;
992 warmup_start_armorvalue = start_armorvalue;
993 warmup_start_weapons = start_weapons;
995 if (!g_weaponarena && !g_minstagib && !g_ca)
997 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
998 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
999 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1000 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1001 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1002 warmup_start_health = cvar("g_warmup_start_health");
1003 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1004 warmup_start_weapons = 0;
1005 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1007 e = get_weaponinfo(i);
1008 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1009 warmup_start_weapons |= e.weapons;
1014 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1016 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1017 start_items |= IT_FUEL_REGEN;
1018 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1019 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1023 start_items |= IT_JETPACK;
1025 if (g_weapon_stay == 2)
1027 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1028 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1029 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1030 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1031 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1032 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1033 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1034 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1035 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1036 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1039 MUTATOR_CALLHOOK(SetStartItems);
1041 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1043 e = get_weaponinfo(i);
1044 if(e.weapons & (start_weapons | warmup_start_weapons))
1045 weapon_action(e.weapon, WR_PRECACHE);
1048 start_ammo_shells = max(0, start_ammo_shells);
1049 start_ammo_nails = max(0, start_ammo_nails);
1050 start_ammo_cells = max(0, start_ammo_cells);
1051 start_ammo_rockets = max(0, start_ammo_rockets);
1052 start_ammo_fuel = max(0, start_ammo_fuel);
1054 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1055 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1056 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1057 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1058 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1062 float g_bugrigs_planar_movement;
1063 float g_bugrigs_planar_movement_car_jumping;
1064 float g_bugrigs_reverse_spinning;
1065 float g_bugrigs_reverse_speeding;
1066 float g_bugrigs_reverse_stopping;
1067 float g_bugrigs_air_steering;
1068 float g_bugrigs_angle_smoothing;
1069 float g_bugrigs_friction_floor;
1070 float g_bugrigs_friction_brake;
1071 float g_bugrigs_friction_air;
1072 float g_bugrigs_accel;
1073 float g_bugrigs_speed_ref;
1074 float g_bugrigs_speed_pow;
1075 float g_bugrigs_steer;
1077 float g_touchexplode;
1078 float g_touchexplode_radius;
1079 float g_touchexplode_damage;
1080 float g_touchexplode_edgedamage;
1081 float g_touchexplode_force;
1088 float sv_pitch_fixyaw;
1090 string GetGametype(); // g_world.qc
1091 void readlevelcvars(void)
1093 // first load all the mutators
1094 if(cvar("g_invincible_projectiles"))
1095 MUTATOR_ADD(mutator_invincibleprojectiles);
1097 MUTATOR_ADD(mutator_nix);
1098 if(cvar("g_dodging"))
1099 MUTATOR_ADD(mutator_dodging);
1100 if(cvar("g_rocket_flying"))
1101 MUTATOR_ADD(mutator_rocketflying);
1102 if(cvar("g_vampire"))
1103 MUTATOR_ADD(mutator_vampire);
1104 if(cvar("g_spawn_near_teammate"))
1105 MUTATOR_ADD(mutator_spawn_near_teammate);
1106 if(cvar("g_sandbox"))
1107 MUTATOR_ADD(sandbox);
1109 if(cvar("sv_allow_fullbright"))
1110 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1112 g_bugrigs = cvar("g_bugrigs");
1113 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1114 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1115 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1116 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1117 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1118 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1119 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1120 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1121 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1122 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1123 g_bugrigs_accel = cvar("g_bugrigs_accel");
1124 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1125 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1126 g_bugrigs_steer = cvar("g_bugrigs_steer");
1128 g_touchexplode = cvar("g_touchexplode");
1129 g_touchexplode_radius = cvar("g_touchexplode_radius");
1130 g_touchexplode_damage = cvar("g_touchexplode_damage");
1131 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1132 g_touchexplode_force = cvar("g_touchexplode_force");
1134 #ifdef ALLOW_FORCEMODELS
1135 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1137 sv_loddistance1 = cvar("sv_loddistance1");
1138 sv_loddistance2 = cvar("sv_loddistance2");
1140 if(sv_loddistance2 <= sv_loddistance1)
1141 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1143 sv_clones = cvar("sv_clones");
1144 sv_gentle = cvar("sv_gentle");
1145 sv_foginterval = cvar("sv_foginterval");
1146 g_cloaked = cvar("g_cloaked");
1148 g_cloaked = 1; // always enable cloak in CTS
1149 g_jump_grunt = cvar("g_jump_grunt");
1150 g_footsteps = cvar("g_footsteps");
1151 g_grappling_hook = cvar("g_grappling_hook");
1152 g_jetpack = cvar("g_jetpack");
1153 g_midair = cvar("g_midair");
1154 g_minstagib = cvar("g_minstagib");
1155 g_norecoil = cvar("g_norecoil");
1156 g_bloodloss = cvar("g_bloodloss");
1157 sv_maxidle = cvar("sv_maxidle");
1158 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1159 g_ctf_reverse = cvar("g_ctf_reverse");
1160 sv_autotaunt = cvar("sv_autotaunt");
1161 sv_taunt = cvar("sv_taunt");
1163 inWarmupStage = cvar("g_warmup");
1164 g_warmup_limit = cvar("g_warmup_limit");
1165 g_warmup_allguns = cvar("g_warmup_allguns");
1166 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1168 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1169 inWarmupStage = 0; // these modes cannot work together, sorry
1171 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1172 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1173 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1174 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1175 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1176 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1177 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1178 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1179 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1180 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1181 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1182 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1184 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1185 g_weaponratefactor = cvar("g_weaponratefactor");
1186 g_weapondamagefactor = cvar("g_weapondamagefactor");
1187 g_weaponforcefactor = cvar("g_weaponforcefactor");
1188 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1190 g_pickup_shells = cvar("g_pickup_shells");
1191 g_pickup_shells_max = cvar("g_pickup_shells_max");
1192 g_pickup_nails = cvar("g_pickup_nails");
1193 g_pickup_nails_max = cvar("g_pickup_nails_max");
1194 g_pickup_rockets = cvar("g_pickup_rockets");
1195 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1196 g_pickup_cells = cvar("g_pickup_cells");
1197 g_pickup_cells_max = cvar("g_pickup_cells_max");
1198 g_pickup_fuel = cvar("g_pickup_fuel");
1199 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1200 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1201 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1202 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1203 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1204 g_pickup_armormedium = cvar("g_pickup_armormedium");
1205 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1206 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1207 g_pickup_armorbig = cvar("g_pickup_armorbig");
1208 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1209 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1210 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1211 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1212 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1213 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1214 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1215 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1216 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1217 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1218 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1219 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1220 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1221 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1222 g_pickup_healthmega = cvar("g_pickup_healthmega");
1223 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1224 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1226 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1227 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1229 g_pinata = cvar("g_pinata");
1231 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1233 g_weapon_stay = cvar("g_weapon_stay");
1235 g_ghost_items = cvar("g_ghost_items");
1237 if(g_ghost_items >= 1)
1238 g_ghost_items = 0.25; // default alpha value
1240 if not(inWarmupStage && !g_ca)
1241 game_starttime = cvar("g_start_delay");
1243 sv_pitch_min = cvar("sv_pitch_min");
1244 sv_pitch_max = cvar("sv_pitch_max");
1245 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1247 readplayerstartcvars();
1253 string precache_sound (string s) = #19;
1254 float precache_sound_index (string s) = #19;
1256 #define SND_VOLUME 1
1257 #define SND_ATTENUATION 2
1258 #define SND_LARGEENTITY 8
1259 #define SND_LARGESOUND 16
1261 float sound_allowed(float dest, entity e)
1263 // sounds from world may always pass
1266 if (e.classname == "body")
1268 else if (e.realowner && e.realowner != e)
1270 else if (e.owner && e.owner != e)
1275 // sounds to self may always pass
1276 if (dest == MSG_ONE)
1277 if (e == msg_entity)
1279 // sounds by players can be removed
1280 if (autocvar_bot_sound_monopoly)
1281 if (clienttype(e) == CLIENTTYPE_REAL)
1283 // anything else may pass
1287 #ifdef COMPAT_XON010_CHANNELS
1288 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1289 void sound(entity e, float chan, string samp, float vol, float atten)
1291 if (!sound_allowed(MSG_BROADCAST, e))
1293 builtin_sound(e, chan, samp, vol, atten);
1297 void sound(entity e, float chan, string samp, float vol, float atten)
1299 if (!sound_allowed(MSG_BROADCAST, e))
1301 sound7(e, chan, samp, vol, atten, 0, 0);
1305 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1309 if (!sound_allowed(dest, e))
1312 entno = num_for_edict(e);
1313 idx = precache_sound_index(samp);
1318 atten = floor(atten * 64);
1319 vol = floor(vol * 255);
1322 sflags |= SND_VOLUME;
1324 sflags |= SND_ATTENUATION;
1325 if (entno >= 8192 || chan < 0 || chan > 7)
1326 sflags |= SND_LARGEENTITY;
1328 sflags |= SND_LARGESOUND;
1330 WriteByte(dest, SVC_SOUND);
1331 WriteByte(dest, sflags);
1332 if (sflags & SND_VOLUME)
1333 WriteByte(dest, vol);
1334 if (sflags & SND_ATTENUATION)
1335 WriteByte(dest, atten);
1336 if (sflags & SND_LARGEENTITY)
1338 WriteShort(dest, entno);
1339 WriteByte(dest, chan);
1343 WriteShort(dest, entno * 8 + chan);
1345 if (sflags & SND_LARGESOUND)
1346 WriteShort(dest, idx);
1348 WriteByte(dest, idx);
1350 WriteCoord(dest, o_x);
1351 WriteCoord(dest, o_y);
1352 WriteCoord(dest, o_z);
1354 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1358 if (!sound_allowed(dest, e))
1361 o = e.origin + 0.5 * (e.mins + e.maxs);
1362 soundtoat(dest, e, o, chan, samp, vol, atten);
1364 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1366 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1368 void stopsoundto(float dest, entity e, float chan)
1372 if (!sound_allowed(dest, e))
1375 entno = num_for_edict(e);
1377 if (entno >= 8192 || chan < 0 || chan > 7)
1380 idx = precache_sound_index("misc/null.wav");
1381 sflags = SND_LARGEENTITY;
1383 sflags |= SND_LARGESOUND;
1384 WriteByte(dest, SVC_SOUND);
1385 WriteByte(dest, sflags);
1386 WriteShort(dest, entno);
1387 WriteByte(dest, chan);
1388 if (sflags & SND_LARGESOUND)
1389 WriteShort(dest, idx);
1391 WriteByte(dest, idx);
1392 WriteCoord(dest, e.origin_x);
1393 WriteCoord(dest, e.origin_y);
1394 WriteCoord(dest, e.origin_z);
1398 WriteByte(dest, SVC_STOPSOUND);
1399 WriteShort(dest, entno * 8 + chan);
1402 void stopsound(entity e, float chan)
1404 if (!sound_allowed(MSG_BROADCAST, e))
1407 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1408 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1411 void play2(entity e, string filename)
1413 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1415 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1418 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1420 float spamsound(entity e, float chan, string samp, float vol, float atten)
1422 if (!sound_allowed(MSG_BROADCAST, e))
1425 if (time > e.spamtime)
1428 sound(e, chan, samp, vol, atten);
1434 void play2team(float t, string filename)
1438 if (autocvar_bot_sound_monopoly)
1441 FOR_EACH_REALPLAYER(head)
1444 play2(head, filename);
1448 void play2all(string samp)
1450 if (autocvar_bot_sound_monopoly)
1453 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1456 void PrecachePlayerSounds(string f);
1457 void precache_playermodel(string m)
1459 float globhandle, i, n;
1462 if(substring(m, -9,5) == "_lod1")
1464 if(substring(m, -9,5) == "_lod2")
1469 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1472 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1477 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1480 n = search_getsize(globhandle);
1481 for (i = 0; i < n; ++i)
1483 //print(search_getfilename(globhandle, i), "\n");
1484 f = search_getfilename(globhandle, i);
1485 PrecachePlayerSounds(f);
1487 search_end(globhandle);
1489 void precache_all_playermodels(string pattern)
1491 float globhandle, i, n;
1494 globhandle = search_begin(pattern, TRUE, FALSE);
1497 n = search_getsize(globhandle);
1498 for (i = 0; i < n; ++i)
1500 //print(search_getfilename(globhandle, i), "\n");
1501 f = search_getfilename(globhandle, i);
1502 precache_playermodel(f);
1504 search_end(globhandle);
1509 // gamemode related things
1510 precache_model ("models/misc/chatbubble.spr");
1513 precache_model ("models/runematch/curse.mdl");
1514 precache_model ("models/runematch/rune.mdl");
1517 #ifdef TTURRETS_ENABLED
1518 if (autocvar_g_turrets)
1522 // Precache all player models if desired
1523 if (autocvar_sv_precacheplayermodels)
1525 PrecachePlayerSounds("sound/player/default.sounds");
1526 precache_all_playermodels("models/player/*.zym");
1527 precache_all_playermodels("models/player/*.dpm");
1528 precache_all_playermodels("models/player/*.md3");
1529 precache_all_playermodels("models/player/*.psk");
1530 precache_all_playermodels("models/player/*.iqm");
1533 if (autocvar_sv_defaultcharacter)
1536 s = autocvar_sv_defaultplayermodel_red;
1538 precache_playermodel(s);
1539 s = autocvar_sv_defaultplayermodel_blue;
1541 precache_playermodel(s);
1542 s = autocvar_sv_defaultplayermodel_yellow;
1544 precache_playermodel(s);
1545 s = autocvar_sv_defaultplayermodel_pink;
1547 precache_playermodel(s);
1548 s = autocvar_sv_defaultplayermodel;
1550 precache_playermodel(s);
1555 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1556 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1559 // gore and miscellaneous sounds
1560 //precache_sound ("misc/h2ohit.wav");
1561 precache_model ("models/hook.md3");
1562 precache_sound ("misc/armorimpact.wav");
1563 precache_sound ("misc/bodyimpact1.wav");
1564 precache_sound ("misc/bodyimpact2.wav");
1565 precache_sound ("misc/gib.wav");
1566 precache_sound ("misc/gib_splat01.wav");
1567 precache_sound ("misc/gib_splat02.wav");
1568 precache_sound ("misc/gib_splat03.wav");
1569 precache_sound ("misc/gib_splat04.wav");
1570 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1571 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1572 precache_sound ("misc/null.wav");
1573 precache_sound ("misc/spawn.wav");
1574 precache_sound ("misc/talk.wav");
1575 precache_sound ("misc/teleport.wav");
1576 precache_sound ("misc/poweroff.wav");
1577 precache_sound ("player/lava.wav");
1578 precache_sound ("player/slime.wav");
1581 precache_sound ("misc/jetpack_fly.wav");
1583 precache_model ("models/sprites/0.spr32");
1584 precache_model ("models/sprites/1.spr32");
1585 precache_model ("models/sprites/2.spr32");
1586 precache_model ("models/sprites/3.spr32");
1587 precache_model ("models/sprites/4.spr32");
1588 precache_model ("models/sprites/5.spr32");
1589 precache_model ("models/sprites/6.spr32");
1590 precache_model ("models/sprites/7.spr32");
1591 precache_model ("models/sprites/8.spr32");
1592 precache_model ("models/sprites/9.spr32");
1593 precache_model ("models/sprites/10.spr32");
1595 // common weapon precaches
1596 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1597 precache_sound ("weapons/weapon_switch.wav");
1598 precache_sound ("weapons/weaponpickup.wav");
1599 precache_sound ("weapons/unavailable.wav");
1600 precache_sound ("weapons/dryfire.wav");
1601 if (g_grappling_hook)
1603 precache_sound ("weapons/hook_fire.wav"); // hook
1604 precache_sound ("weapons/hook_impact.wav"); // hook
1607 if(autocvar_sv_precacheweapons)
1609 //precache weapon models/sounds
1612 while (wep <= WEP_LAST)
1614 weapon_action(wep, WR_PRECACHE);
1619 precache_model("models/elaser.mdl");
1620 precache_model("models/laser.mdl");
1621 precache_model("models/ebomb.mdl");
1624 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1626 if (!self.noise && self.music) // quake 3 uses the music field
1627 self.noise = self.music;
1629 // plays music for the level if there is any
1632 precache_sound (self.noise);
1633 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1638 // sorry, but using \ in macros breaks line numbers
1639 #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
1640 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1641 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1644 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1646 if (clienttype(e) == CLIENTTYPE_REAL)
1649 WRITESPECTATABLE_MSG_ONE({
1650 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1651 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1652 WriteByte(MSG_ONE, id);
1653 WriteString(MSG_ONE, s);
1654 if (id != 0 && s != "")
1656 WriteByte(MSG_ONE, duration);
1657 WriteByte(MSG_ONE, countdown_num);
1662 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1664 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1666 // WARNING: this kills the trace globals
1667 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1668 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1670 #define INITPRIO_FIRST 0
1671 #define INITPRIO_GAMETYPE 0
1672 #define INITPRIO_GAMETYPE_FALLBACK 1
1673 #define INITPRIO_FINDTARGET 10
1674 #define INITPRIO_DROPTOFLOOR 20
1675 #define INITPRIO_SETLOCATION 90
1676 #define INITPRIO_LINKDOORS 91
1677 #define INITPRIO_LAST 99
1679 .void(void) initialize_entity;
1680 .float initialize_entity_order;
1681 .entity initialize_entity_next;
1682 entity initialize_entity_first;
1684 void make_safe_for_remove(entity e)
1686 if (e.initialize_entity)
1689 for (ent = initialize_entity_first; ent; )
1691 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1693 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1694 // skip it in linked list
1697 prev.initialize_entity_next = ent.initialize_entity_next;
1698 ent = prev.initialize_entity_next;
1702 initialize_entity_first = ent.initialize_entity_next;
1703 ent = initialize_entity_first;
1709 ent = ent.initialize_entity_next;
1715 void objerror(string s)
1717 make_safe_for_remove(self);
1718 builtin_objerror(s);
1721 .float remove_except_protected_forbidden;
1722 void remove_except_protected(entity e)
1724 if(e.remove_except_protected_forbidden)
1725 error("not allowed to remove this at this point");
1729 void remove_unsafely(entity e)
1731 if(e.classname == "spike")
1732 error("Removing spikes is forbidden (crylink bug), please report");
1736 void remove_safely(entity e)
1738 make_safe_for_remove(e);
1742 void InitializeEntity(entity e, void(void) func, float order)
1746 if (!e || e.initialize_entity)
1748 // make a proxy initializer entity
1752 e.classname = "initialize_entity";
1756 e.initialize_entity = func;
1757 e.initialize_entity_order = order;
1759 cur = initialize_entity_first;
1762 if (!cur || cur.initialize_entity_order > order)
1764 // insert between prev and cur
1766 prev.initialize_entity_next = e;
1768 initialize_entity_first = e;
1769 e.initialize_entity_next = cur;
1773 cur = cur.initialize_entity_next;
1776 void InitializeEntitiesRun()
1779 startoflist = initialize_entity_first;
1780 initialize_entity_first = world;
1781 remove = remove_except_protected;
1782 for (self = startoflist; self; self = self.initialize_entity_next)
1784 self.remove_except_protected_forbidden = 1;
1786 for (self = startoflist; self; )
1789 var void(void) func;
1790 e = self.initialize_entity_next;
1791 func = self.initialize_entity;
1792 self.initialize_entity_order = 0;
1793 self.initialize_entity = func_null;
1794 self.initialize_entity_next = world;
1795 self.remove_except_protected_forbidden = 0;
1796 if (self.classname == "initialize_entity")
1800 builtin_remove(self);
1803 //dprint("Delayed initialization: ", self.classname, "\n");
1804 if(func != func_null)
1809 backtrace(strcat("Null function in: ", self.classname, "\n"));
1813 remove = remove_unsafely;
1816 .float uncustomizeentityforclient_set;
1817 .void(void) uncustomizeentityforclient;
1818 void(void) SUB_Nullpointer = #0;
1819 void UncustomizeEntitiesRun()
1823 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1824 self.uncustomizeentityforclient();
1827 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1829 e.customizeentityforclient = customizer;
1830 e.uncustomizeentityforclient = uncustomizer;
1831 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1835 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1838 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1842 if (e.classname == "")
1843 e.classname = "net_linked";
1845 if (e.model == "" || self.modelindex == 0)
1849 setmodel(e, "null");
1853 e.SendEntity = sendfunc;
1854 e.SendFlags = 0xFFFFFF;
1857 e.effects |= EF_NODEPTHTEST;
1861 e.nextthink = time + dt;
1862 e.think = SUB_Remove;
1866 void adaptor_think2touch()
1875 void adaptor_think2use()
1887 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1889 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
1890 self.projectiledeathtype |= HITTYPE_SPLASH;
1891 adaptor_think2use();
1894 // deferred dropping
1895 void DropToFloor_Handler()
1897 builtin_droptofloor();
1898 self.dropped_origin = self.origin;
1903 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1908 float trace_hits_box_a0, trace_hits_box_a1;
1910 float trace_hits_box_1d(float end, float thmi, float thma)
1914 // just check if x is in range
1922 // do the trace with respect to x
1923 // 0 -> end has to stay in thmi -> thma
1924 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1925 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1926 if (trace_hits_box_a0 > trace_hits_box_a1)
1932 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1937 // now it is a trace from 0 to end
1939 trace_hits_box_a0 = 0;
1940 trace_hits_box_a1 = 1;
1942 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1944 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1946 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1952 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1954 return trace_hits_box(start, end, thmi - ma, thma - mi);
1957 float SUB_NoImpactCheck()
1959 // zero hitcontents = this is not the real impact, but either the
1960 // mirror-impact of something hitting the projectile instead of the
1961 // projectile hitting the something, or a touchareagrid one. Neither of
1962 // these stop the projectile from moving, so...
1963 if(trace_dphitcontents == 0)
1965 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1966 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)));
1969 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1971 if (other == world && self.size != '0 0 0')
1974 tic = self.velocity * sys_frametime;
1975 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1976 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1977 if (trace_fraction >= 1)
1979 dprint("Odd... did not hit...?\n");
1981 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1983 dprint("Detected and prevented the sky-grapple bug.\n");
1991 #define SUB_OwnerCheck() (other && (other == self.owner))
1993 void RemoveGrapplingHook(entity pl);
1994 void W_Crylink_Dequeue(entity e);
1995 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1997 if(SUB_OwnerCheck())
1999 if(SUB_NoImpactCheck())
2001 if(self.classname == "grapplinghook")
2002 RemoveGrapplingHook(self.realowner);
2003 else if(self.classname == "spike")
2005 W_Crylink_Dequeue(self);
2012 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2013 UpdateCSQCProjectile(self);
2016 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2018 float MAX_IPBAN_URIS = 16;
2020 float URI_GET_DISCARD = 0;
2021 float URI_GET_IPBAN = 1;
2022 float URI_GET_IPBAN_END = 16;
2024 void URI_Get_Callback(float id, float status, string data)
2026 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2028 dprint("\nEnd of data.\n");
2030 if(url_URI_Get_Callback(id, status, data))
2034 else if (id == URI_GET_DISCARD)
2038 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2041 OnlineBanList_URI_Get_Callback(id, status, data);
2045 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2049 void print_to(entity e, string s)
2052 sprint(e, strcat(s, "\n"));
2057 string uid2name(string myuid) {
2059 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
2061 // FIXME remove this later after 0.6 release
2062 // convert old style broken records to correct style
2065 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2068 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
2069 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
2074 s = "^1Unregistered Player";
2078 float race_readTime(string map, float pos)
2086 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2089 string race_readUID(string map, float pos)
2097 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2100 float race_readPos(string map, float t) {
2102 for (i = 1; i <= RANKINGS_CNT; ++i)
2103 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2106 return 0; // pos is zero if unranked
2109 void race_writeTime(string map, float t, string myuid)
2118 newpos = race_readPos(map, t);
2121 for(i = 1; i <= RANKINGS_CNT; ++i)
2123 if(race_readUID(map, i) == myuid)
2126 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2127 for (i = prevpos; i > newpos; --i) {
2128 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2129 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2131 } else { // player has no ranked record yet
2132 for (i = RANKINGS_CNT; i > newpos; --i) {
2133 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2134 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2138 // store new time itself
2139 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2140 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2143 string race_readName(string map, float pos)
2151 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2154 string race_placeName(float pos) {
2155 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2157 if(mod(pos, 10) == 1)
2158 return strcat(ftos(pos), "st");
2159 else if(mod(pos, 10) == 2)
2160 return strcat(ftos(pos), "nd");
2161 else if(mod(pos, 10) == 3)
2162 return strcat(ftos(pos), "rd");
2164 return strcat(ftos(pos), "th");
2167 return strcat(ftos(pos), "th");
2169 string getrecords(float page) // 50 records per page
2183 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2185 if (MapInfo_Get_ByID(i))
2187 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2191 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2192 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2200 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2202 if (MapInfo_Get_ByID(i))
2204 r = race_readTime(MapInfo_Map_bspname, 1);
2207 h = race_readName(MapInfo_Map_bspname, 1);
2208 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2216 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2218 if (MapInfo_Get_ByID(i))
2220 r = race_readTime(MapInfo_Map_bspname, 1);
2223 h = race_readName(MapInfo_Map_bspname, 1);
2224 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2230 MapInfo_ClearTemps();
2232 if (s == "" && page == 0)
2233 return "No records are available on this server.\n";
2238 string getrankings()
2251 for (i = 1; i <= RANKINGS_CNT; ++i)
2253 t = race_readTime(map, i);
2256 n = race_readName(map, i);
2257 p = race_placeName(i);
2258 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2261 MapInfo_ClearTemps();
2264 return strcat("No records are available for the map: ", map, "\n");
2266 return strcat("Records for ", map, ":\n", s);
2269 #define LADDER_FIRSTPOINT 100
2270 #define LADDER_CNT 10
2271 // position X still gives LADDER_FIRSTPOINT/X points
2272 #define LADDER_SIZE 30
2273 // ladder shows the top X players
2274 string top_uids[LADDER_SIZE];
2275 float top_scores[LADDER_SIZE];
2278 float i, j, k, uidcnt;
2292 for (k = 0; k < MapInfo_count; ++k)
2294 if (MapInfo_Get_ByID(k))
2296 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2297 if(i == 0) // speed award
2299 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2302 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2304 else // normal record, if it exists (else break)
2306 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2309 myuid = race_readUID(MapInfo_Map_bspname, i);
2312 // string s contains:
2313 // arg 0 = # of speed recs
2314 // arg 1 = # of 1st place recs
2315 // arg 2 = # of 2nd place recs
2317 // LADDER_CNT+1 = total points
2319 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2322 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2324 for (j = 0; j <= LADDER_CNT + 1; ++j)
2326 if(j != LADDER_CNT + 1)
2327 temp_s = strcat(temp_s, "0 ");
2329 temp_s = strcat(temp_s, "0");
2333 tokenize_console(temp_s);
2336 if(i == 0) // speed award
2337 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2339 if(j == 0) // speed award
2340 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2342 s = strcat(s, " ", argv(j)); // just copy over everything else
2345 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2348 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2349 else if(j == i) // wanted rec!
2350 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2352 s = strcat(s, " ", argv(j)); // just copy over everything else
2355 // total points are (by default) calculated like this:
2356 // speedrec = floor(100 / 10) = 10 points
2357 // 1st place = floor(100 / 1) = 100 points
2358 // 2nd place = floor(100 / 2) = 50 points
2359 // 3rd place = floor(100 / 3) = 33 points
2360 // 4th place = floor(100 / 4) = 25 points
2361 // 5th place = floor(100 / 5) = 20 points
2365 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2367 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2369 db_put(TemporaryDB, strcat("ladder", myuid), s);
2376 for (i = 0; i <= uidcnt; ++i) // for each known uid
2378 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2379 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2380 tokenize_console(temp_s);
2381 thiscnt = stof(argv(LADDER_CNT+1));
2383 if(thiscnt > top_scores[LADDER_SIZE-1])
2384 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2386 if(thiscnt > top_scores[j])
2388 for (k = LADDER_SIZE-1; k >= j; --k)
2390 top_uids[k] = top_uids[k-1];
2391 top_scores[k] = top_scores[k-1];
2393 top_uids[j] = thisuid;
2394 top_scores[j] = thiscnt;
2400 s = "^3-----------------------\n\n";
2402 s = strcat(s, "Pos ^3|");
2403 s = strcat(s, " ^7Total ^3|");
2404 for (i = 1; i <= LADDER_CNT; ++i)
2406 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2408 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2410 s = strcat(s, "\n^3----+--------");
2411 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2413 s = strcat(s, "+-----");
2416 for (i = 1; i <= LADDER_CNT - 9; ++i)
2418 s = strcat(s, "+------");
2422 s = strcat(s, "+--------------+--------------------\n");
2424 for (i = 0; i < LADDER_SIZE; ++i)
2426 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2427 tokenize_console(temp_s);
2428 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2430 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2431 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2432 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2434 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2437 for (j = 10; j <= LADDER_CNT; ++j)
2439 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2443 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2444 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2447 MapInfo_ClearTemps();
2450 return "No ladder on this server!\n";
2452 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2456 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2459 vector start, org, delta, end, enddown, mstart;
2462 m = e.dphitcontentsmask;
2463 e.dphitcontentsmask = goodcontents | badcontents;
2466 delta = world.maxs - world.mins;
2468 for (i = 0; i < attempts; ++i)
2470 start_x = org_x + random() * delta_x;
2471 start_y = org_y + random() * delta_y;
2472 start_z = org_z + random() * delta_z;
2474 // rule 1: start inside world bounds, and outside
2475 // solid, and don't start from somewhere where you can
2476 // fall down to evil
2477 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2478 if (trace_fraction >= 1)
2480 if (trace_startsolid)
2482 if (trace_dphitcontents & badcontents)
2484 if (trace_dphitq3surfaceflags & badsurfaceflags)
2487 // rule 2: if we are too high, lower the point
2488 if (trace_fraction * delta_z > maxaboveground)
2489 start = trace_endpos + '0 0 1' * maxaboveground;
2490 enddown = trace_endpos;
2492 // rule 3: make sure we aren't outside the map. This only works
2493 // for somewhat well formed maps. A good rule of thumb is that
2494 // the map should have a convex outside hull.
2495 // these can be traceLINES as we already verified the starting box
2496 mstart = start + 0.5 * (e.mins + e.maxs);
2497 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2498 if (trace_fraction >= 1)
2500 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2501 if (trace_fraction >= 1)
2503 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2504 if (trace_fraction >= 1)
2506 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2507 if (trace_fraction >= 1)
2509 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2510 if (trace_fraction >= 1)
2513 // rule 4: we must "see" some spawnpoint
2514 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2515 if(checkpvs(mstart, sp))
2519 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2520 if(checkpvs(mstart, sp))
2526 // find a random vector to "look at"
2527 end_x = org_x + random() * delta_x;
2528 end_y = org_y + random() * delta_y;
2529 end_z = org_z + random() * delta_z;
2530 end = start + normalize(end - start) * vlen(delta);
2532 // rule 4: start TO end must not be too short
2533 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2534 if (trace_startsolid)
2536 if (trace_fraction < minviewdistance / vlen(delta))
2539 // rule 5: don't want to look at sky
2540 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2543 // rule 6: we must not end up in trigger_hurt
2544 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2550 e.dphitcontentsmask = m;
2554 setorigin(e, start);
2555 e.angles = vectoangles(end - start);
2556 dprint("Needed ", ftos(i + 1), " attempts\n");
2563 float zcurveparticles_effectno;
2564 vector zcurveparticles_start;
2565 float zcurveparticles_spd;
2567 void endzcurveparticles()
2569 if(zcurveparticles_effectno)
2572 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2574 zcurveparticles_effectno = 0;
2577 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2579 spd = bound(0, floor(spd / 16), 32767);
2580 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2582 endzcurveparticles();
2583 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2584 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2585 WriteShort(MSG_BROADCAST, effectno);
2586 WriteCoord(MSG_BROADCAST, start_x);
2587 WriteCoord(MSG_BROADCAST, start_y);
2588 WriteCoord(MSG_BROADCAST, start_z);
2589 zcurveparticles_effectno = effectno;
2590 zcurveparticles_start = start;
2593 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2594 WriteCoord(MSG_BROADCAST, end_x);
2595 WriteCoord(MSG_BROADCAST, end_y);
2596 WriteCoord(MSG_BROADCAST, end_z);
2597 WriteCoord(MSG_BROADCAST, end_dz);
2598 zcurveparticles_spd = spd;
2601 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2604 vector vecxy, velxy;
2606 vecxy = end - start;
2611 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2613 endzcurveparticles();
2614 trailparticles(world, effectno, start, end);
2618 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2619 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2622 void write_recordmarker(entity pl, float tstart, float dt)
2624 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2626 // also write a marker into demo files for demotc-race-record-extractor to find
2629 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2630 " ", ftos(tstart), " ", ftos(dt), "\n"));
2633 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2646 if(allowcenter) // 2: allow center handedness
2659 if(allowcenter) // 2: allow center handedness
2675 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2680 if (autocvar_g_shootfromeye)
2693 else if (autocvar_g_shootfromcenter)
2698 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2708 else if (autocvar_g_shootfromclient)
2710 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2715 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2717 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2721 void attach_sameorigin(entity e, entity to, string tag)
2723 vector org, t_forward, t_left, t_up, e_forward, e_up;
2730 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2731 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2732 t_forward = v_forward * tagscale;
2733 t_left = v_right * -tagscale;
2734 t_up = v_up * tagscale;
2736 e.origin_x = org * t_forward;
2737 e.origin_y = org * t_left;
2738 e.origin_z = org * t_up;
2740 // current forward and up directions
2741 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2742 e.angles = AnglesTransform_FromVAngles(e.angles);
2744 e.angles = AnglesTransform_FromAngles(e.angles);
2745 fixedmakevectors(e.angles);
2747 // untransform forward, up!
2748 e_forward_x = v_forward * t_forward;
2749 e_forward_y = v_forward * t_left;
2750 e_forward_z = v_forward * t_up;
2751 e_up_x = v_up * t_forward;
2752 e_up_y = v_up * t_left;
2753 e_up_z = v_up * t_up;
2755 e.angles = fixedvectoangles2(e_forward, e_up);
2756 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2757 e.angles = AnglesTransform_ToVAngles(e.angles);
2759 e.angles = AnglesTransform_ToAngles(e.angles);
2761 setattachment(e, to, tag);
2762 setorigin(e, e.origin);
2765 void detach_sameorigin(entity e)
2768 org = gettaginfo(e, 0);
2769 e.angles = fixedvectoangles2(v_forward, v_up);
2770 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2771 e.angles = AnglesTransform_ToVAngles(e.angles);
2773 e.angles = AnglesTransform_ToAngles(e.angles);
2775 setattachment(e, world, "");
2776 setorigin(e, e.origin);
2779 void follow_sameorigin(entity e, entity to)
2781 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2782 e.aiment = to; // make the hole follow bmodel
2783 e.punchangle = to.angles; // the original angles of bmodel
2784 e.view_ofs = e.origin - to.origin; // relative origin
2785 e.v_angle = e.angles - to.angles; // relative angles
2788 void unfollow_sameorigin(entity e)
2790 e.movetype = MOVETYPE_NONE;
2793 entity gettaginfo_relative_ent;
2794 vector gettaginfo_relative(entity e, float tag)
2796 if (!gettaginfo_relative_ent)
2798 gettaginfo_relative_ent = spawn();
2799 gettaginfo_relative_ent.effects = EF_NODRAW;
2801 gettaginfo_relative_ent.model = e.model;
2802 gettaginfo_relative_ent.modelindex = e.modelindex;
2803 gettaginfo_relative_ent.frame = e.frame;
2804 return gettaginfo(gettaginfo_relative_ent, tag);
2807 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2811 if (pl.soundentity.cnt & p)
2813 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2814 pl.soundentity.cnt |= p;
2817 void SoundEntity_StopSound(entity pl, float chan)
2821 if (pl.soundentity.cnt & p)
2823 stopsoundto(MSG_ALL, pl.soundentity, chan);
2824 pl.soundentity.cnt &~= p;
2828 void SoundEntity_Attach(entity pl)
2830 pl.soundentity = spawn();
2831 pl.soundentity.classname = "soundentity";
2832 pl.soundentity.owner = pl;
2833 setattachment(pl.soundentity, pl, "");
2834 setmodel(pl.soundentity, "null");
2837 void SoundEntity_Detach(entity pl)
2840 for (i = 0; i <= 7; ++i)
2841 SoundEntity_StopSound(pl, i);
2845 float ParseCommandPlayerSlotTarget_firsttoken;
2846 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2854 ParseCommandPlayerSlotTarget_firsttoken = -1;
2858 if (substring(argv(idx), 0, 1) == "#")
2860 s = substring(argv(idx), 1, -1);
2862 if (s == "") if (tokens > idx)
2867 ParseCommandPlayerSlotTarget_firsttoken = idx;
2869 if (s == ftos(n) && n > 0 && n <= maxclients)
2872 if (e.flags & FL_CLIENT)
2878 // it must be a nick name
2881 ParseCommandPlayerSlotTarget_firsttoken = idx;
2884 FOR_EACH_CLIENT(head)
2885 if (head.netname == s)
2893 s = strdecolorize(s);
2895 FOR_EACH_CLIENT(head)
2896 if (strdecolorize(head.netname) == s)
2911 float modeleffect_SendEntity(entity to, float sf)
2914 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2917 if(self.velocity != '0 0 0')
2919 if(self.angles != '0 0 0')
2921 if(self.avelocity != '0 0 0')
2924 WriteByte(MSG_ENTITY, f);
2925 WriteShort(MSG_ENTITY, self.modelindex);
2926 WriteByte(MSG_ENTITY, self.skin);
2927 WriteByte(MSG_ENTITY, self.frame);
2928 WriteCoord(MSG_ENTITY, self.origin_x);
2929 WriteCoord(MSG_ENTITY, self.origin_y);
2930 WriteCoord(MSG_ENTITY, self.origin_z);
2933 WriteCoord(MSG_ENTITY, self.velocity_x);
2934 WriteCoord(MSG_ENTITY, self.velocity_y);
2935 WriteCoord(MSG_ENTITY, self.velocity_z);
2939 WriteCoord(MSG_ENTITY, self.angles_x);
2940 WriteCoord(MSG_ENTITY, self.angles_y);
2941 WriteCoord(MSG_ENTITY, self.angles_z);
2945 WriteCoord(MSG_ENTITY, self.avelocity_x);
2946 WriteCoord(MSG_ENTITY, self.avelocity_y);
2947 WriteCoord(MSG_ENTITY, self.avelocity_z);
2949 WriteShort(MSG_ENTITY, self.scale * 256.0);
2950 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2951 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2952 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2953 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2958 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)
2963 e.classname = "modeleffect";
2971 e.teleport_time = t1;
2975 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2979 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2982 sz = max(e.scale, e.scale2);
2983 setsize(e, e.mins * sz, e.maxs * sz);
2984 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2987 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2989 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2992 float randombit(float bits)
2994 if not(bits & (bits-1)) // this ONLY holds for powers of two!
3003 for(f = 1; f <= bits; f *= 2)
3012 r = (r - 1) / (n - 1);
3019 float randombits(float bits, float k, float error_return)
3023 while(k > 0 && bits != r)
3025 r += randombit(bits - r);
3034 void randombit_test(float bits, float iter)
3038 print(ftos(randombit(bits)), "\n");
3043 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3045 if(halflifedist > 0)
3046 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3047 else if(halflifedist < 0)
3048 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3057 #define cvar_string_normal builtin_cvar_string
3058 #define cvar_normal builtin_cvar
3060 string cvar_string_normal(string n)
3062 if not(cvar_type(n) & 1)
3063 backtrace(strcat("Attempt to access undefined cvar: ", n));
3064 return builtin_cvar_string(n);
3067 float cvar_normal(string n)
3069 return stof(cvar_string_normal(n));
3072 #define cvar_set_normal builtin_cvar_set
3080 oself.think = SUB_Remove;
3081 oself.nextthink = time;
3087 Execute func() after time + fdelay.
3088 self when func is executed = self when defer is called
3090 void defer(float fdelay, void() func)
3097 e.think = defer_think;
3098 e.nextthink = time + fdelay;
3101 .string aiment_classname;
3102 .float aiment_deadflag;
3103 void SetMovetypeFollow(entity ent, entity e)
3105 // FIXME this may not be warpzone aware
3106 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3107 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.
3108 ent.aiment = e; // make the hole follow bmodel
3109 ent.punchangle = e.angles; // the original angles of bmodel
3110 ent.view_ofs = ent.origin - e.origin; // relative origin
3111 ent.v_angle = ent.angles - e.angles; // relative angles
3112 ent.aiment_classname = strzone(e.classname);
3113 ent.aiment_deadflag = e.deadflag;
3115 void UnsetMovetypeFollow(entity ent)
3117 ent.movetype = MOVETYPE_FLY;
3118 PROJECTILE_MAKETRIGGER(ent);
3121 float LostMovetypeFollow(entity ent)
3124 if(ent.movetype != MOVETYPE_FOLLOW)
3130 if(ent.aiment.classname != ent.aiment_classname)
3132 if(ent.aiment.deadflag != ent.aiment_deadflag)
3138 float isPushable(entity e)
3145 case "droppedweapon":
3146 case "keepawayball":
3147 case "nexball_basketball":
3148 case "nexball_football":
3150 case "bullet": // antilagged bullets can't hit this either
3153 if (e.projectiledeathtype)