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);
1107 // is this a mutator? is this a mode?
1108 if(cvar("g_sandbox"))
1109 MUTATOR_ADD(sandbox);
1111 if(cvar("sv_allow_fullbright"))
1112 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1114 g_bugrigs = cvar("g_bugrigs");
1115 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1116 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1117 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1118 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1119 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1120 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1121 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1122 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1123 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1124 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1125 g_bugrigs_accel = cvar("g_bugrigs_accel");
1126 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1127 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1128 g_bugrigs_steer = cvar("g_bugrigs_steer");
1130 g_touchexplode = cvar("g_touchexplode");
1131 g_touchexplode_radius = cvar("g_touchexplode_radius");
1132 g_touchexplode_damage = cvar("g_touchexplode_damage");
1133 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1134 g_touchexplode_force = cvar("g_touchexplode_force");
1136 #ifdef ALLOW_FORCEMODELS
1137 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1139 sv_loddistance1 = cvar("sv_loddistance1");
1140 sv_loddistance2 = cvar("sv_loddistance2");
1142 if(sv_loddistance2 <= sv_loddistance1)
1143 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1145 sv_clones = cvar("sv_clones");
1146 sv_gentle = cvar("sv_gentle");
1147 sv_foginterval = cvar("sv_foginterval");
1148 g_cloaked = cvar("g_cloaked");
1150 g_cloaked = 1; // always enable cloak in CTS
1151 g_jump_grunt = cvar("g_jump_grunt");
1152 g_footsteps = cvar("g_footsteps");
1153 g_grappling_hook = cvar("g_grappling_hook");
1154 g_jetpack = cvar("g_jetpack");
1155 g_midair = cvar("g_midair");
1156 g_minstagib = cvar("g_minstagib");
1157 g_norecoil = cvar("g_norecoil");
1158 g_bloodloss = cvar("g_bloodloss");
1159 sv_maxidle = cvar("sv_maxidle");
1160 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1161 g_ctf_reverse = cvar("g_ctf_reverse");
1162 sv_autotaunt = cvar("sv_autotaunt");
1163 sv_taunt = cvar("sv_taunt");
1165 inWarmupStage = cvar("g_warmup");
1166 g_warmup_limit = cvar("g_warmup_limit");
1167 g_warmup_allguns = cvar("g_warmup_allguns");
1168 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1170 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1171 inWarmupStage = 0; // these modes cannot work together, sorry
1173 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1174 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1175 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1176 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1177 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1178 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1179 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1180 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1181 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1182 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1183 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1184 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1186 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1187 g_weaponratefactor = cvar("g_weaponratefactor");
1188 g_weapondamagefactor = cvar("g_weapondamagefactor");
1189 g_weaponforcefactor = cvar("g_weaponforcefactor");
1190 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1192 g_pickup_shells = cvar("g_pickup_shells");
1193 g_pickup_shells_max = cvar("g_pickup_shells_max");
1194 g_pickup_nails = cvar("g_pickup_nails");
1195 g_pickup_nails_max = cvar("g_pickup_nails_max");
1196 g_pickup_rockets = cvar("g_pickup_rockets");
1197 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1198 g_pickup_cells = cvar("g_pickup_cells");
1199 g_pickup_cells_max = cvar("g_pickup_cells_max");
1200 g_pickup_fuel = cvar("g_pickup_fuel");
1201 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1202 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1203 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1204 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1205 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1206 g_pickup_armormedium = cvar("g_pickup_armormedium");
1207 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1208 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1209 g_pickup_armorbig = cvar("g_pickup_armorbig");
1210 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1211 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1212 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1213 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1214 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1215 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1216 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1217 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1218 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1219 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1220 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1221 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1222 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1223 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1224 g_pickup_healthmega = cvar("g_pickup_healthmega");
1225 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1226 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1228 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1229 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1231 g_pinata = cvar("g_pinata");
1233 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1235 g_weapon_stay = cvar("g_weapon_stay");
1237 g_ghost_items = cvar("g_ghost_items");
1239 if(g_ghost_items >= 1)
1240 g_ghost_items = 0.25; // default alpha value
1242 if not(inWarmupStage && !g_ca)
1243 game_starttime = cvar("g_start_delay");
1245 sv_pitch_min = cvar("sv_pitch_min");
1246 sv_pitch_max = cvar("sv_pitch_max");
1247 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1249 readplayerstartcvars();
1255 string precache_sound (string s) = #19;
1256 float precache_sound_index (string s) = #19;
1258 #define SND_VOLUME 1
1259 #define SND_ATTENUATION 2
1260 #define SND_LARGEENTITY 8
1261 #define SND_LARGESOUND 16
1263 float sound_allowed(float dest, entity e)
1265 // sounds from world may always pass
1268 if (e.classname == "body")
1270 else if (e.realowner && e.realowner != e)
1272 else if (e.owner && e.owner != e)
1277 // sounds to self may always pass
1278 if (dest == MSG_ONE)
1279 if (e == msg_entity)
1281 // sounds by players can be removed
1282 if (autocvar_bot_sound_monopoly)
1283 if (clienttype(e) == CLIENTTYPE_REAL)
1285 // anything else may pass
1289 #ifdef COMPAT_XON010_CHANNELS
1290 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1291 void sound(entity e, float chan, string samp, float vol, float atten)
1293 if (!sound_allowed(MSG_BROADCAST, e))
1295 builtin_sound(e, chan, samp, vol, atten);
1299 void sound(entity e, float chan, string samp, float vol, float atten)
1301 if (!sound_allowed(MSG_BROADCAST, e))
1303 sound7(e, chan, samp, vol, atten, 0, 0);
1307 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1311 if (!sound_allowed(dest, e))
1314 entno = num_for_edict(e);
1315 idx = precache_sound_index(samp);
1320 atten = floor(atten * 64);
1321 vol = floor(vol * 255);
1324 sflags |= SND_VOLUME;
1326 sflags |= SND_ATTENUATION;
1327 if (entno >= 8192 || chan < 0 || chan > 7)
1328 sflags |= SND_LARGEENTITY;
1330 sflags |= SND_LARGESOUND;
1332 WriteByte(dest, SVC_SOUND);
1333 WriteByte(dest, sflags);
1334 if (sflags & SND_VOLUME)
1335 WriteByte(dest, vol);
1336 if (sflags & SND_ATTENUATION)
1337 WriteByte(dest, atten);
1338 if (sflags & SND_LARGEENTITY)
1340 WriteShort(dest, entno);
1341 WriteByte(dest, chan);
1345 WriteShort(dest, entno * 8 + chan);
1347 if (sflags & SND_LARGESOUND)
1348 WriteShort(dest, idx);
1350 WriteByte(dest, idx);
1352 WriteCoord(dest, o_x);
1353 WriteCoord(dest, o_y);
1354 WriteCoord(dest, o_z);
1356 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1360 if (!sound_allowed(dest, e))
1363 o = e.origin + 0.5 * (e.mins + e.maxs);
1364 soundtoat(dest, e, o, chan, samp, vol, atten);
1366 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1368 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1370 void stopsoundto(float dest, entity e, float chan)
1374 if (!sound_allowed(dest, e))
1377 entno = num_for_edict(e);
1379 if (entno >= 8192 || chan < 0 || chan > 7)
1382 idx = precache_sound_index("misc/null.wav");
1383 sflags = SND_LARGEENTITY;
1385 sflags |= SND_LARGESOUND;
1386 WriteByte(dest, SVC_SOUND);
1387 WriteByte(dest, sflags);
1388 WriteShort(dest, entno);
1389 WriteByte(dest, chan);
1390 if (sflags & SND_LARGESOUND)
1391 WriteShort(dest, idx);
1393 WriteByte(dest, idx);
1394 WriteCoord(dest, e.origin_x);
1395 WriteCoord(dest, e.origin_y);
1396 WriteCoord(dest, e.origin_z);
1400 WriteByte(dest, SVC_STOPSOUND);
1401 WriteShort(dest, entno * 8 + chan);
1404 void stopsound(entity e, float chan)
1406 if (!sound_allowed(MSG_BROADCAST, e))
1409 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1410 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1413 void play2(entity e, string filename)
1415 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1417 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1420 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1422 float spamsound(entity e, float chan, string samp, float vol, float atten)
1424 if (!sound_allowed(MSG_BROADCAST, e))
1427 if (time > e.spamtime)
1430 sound(e, chan, samp, vol, atten);
1436 void play2team(float t, string filename)
1440 if (autocvar_bot_sound_monopoly)
1443 FOR_EACH_REALPLAYER(head)
1446 play2(head, filename);
1450 void play2all(string samp)
1452 if (autocvar_bot_sound_monopoly)
1455 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1458 void PrecachePlayerSounds(string f);
1459 void precache_playermodel(string m)
1461 float globhandle, i, n;
1464 if(substring(m, -9,5) == "_lod1")
1466 if(substring(m, -9,5) == "_lod2")
1471 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1474 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1479 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1482 n = search_getsize(globhandle);
1483 for (i = 0; i < n; ++i)
1485 //print(search_getfilename(globhandle, i), "\n");
1486 f = search_getfilename(globhandle, i);
1487 PrecachePlayerSounds(f);
1489 search_end(globhandle);
1491 void precache_all_playermodels(string pattern)
1493 float globhandle, i, n;
1496 globhandle = search_begin(pattern, TRUE, FALSE);
1499 n = search_getsize(globhandle);
1500 for (i = 0; i < n; ++i)
1502 //print(search_getfilename(globhandle, i), "\n");
1503 f = search_getfilename(globhandle, i);
1504 precache_playermodel(f);
1506 search_end(globhandle);
1511 // gamemode related things
1512 precache_model ("models/misc/chatbubble.spr");
1515 precache_model ("models/runematch/curse.mdl");
1516 precache_model ("models/runematch/rune.mdl");
1519 #ifdef TTURRETS_ENABLED
1520 if (autocvar_g_turrets)
1524 // Precache all player models if desired
1525 if (autocvar_sv_precacheplayermodels)
1527 PrecachePlayerSounds("sound/player/default.sounds");
1528 precache_all_playermodels("models/player/*.zym");
1529 precache_all_playermodels("models/player/*.dpm");
1530 precache_all_playermodels("models/player/*.md3");
1531 precache_all_playermodels("models/player/*.psk");
1532 precache_all_playermodels("models/player/*.iqm");
1535 if (autocvar_sv_defaultcharacter)
1538 s = autocvar_sv_defaultplayermodel_red;
1540 precache_playermodel(s);
1541 s = autocvar_sv_defaultplayermodel_blue;
1543 precache_playermodel(s);
1544 s = autocvar_sv_defaultplayermodel_yellow;
1546 precache_playermodel(s);
1547 s = autocvar_sv_defaultplayermodel_pink;
1549 precache_playermodel(s);
1550 s = autocvar_sv_defaultplayermodel;
1552 precache_playermodel(s);
1557 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1558 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1561 // gore and miscellaneous sounds
1562 //precache_sound ("misc/h2ohit.wav");
1563 precache_model ("models/hook.md3");
1564 precache_sound ("misc/armorimpact.wav");
1565 precache_sound ("misc/bodyimpact1.wav");
1566 precache_sound ("misc/bodyimpact2.wav");
1567 precache_sound ("misc/gib.wav");
1568 precache_sound ("misc/gib_splat01.wav");
1569 precache_sound ("misc/gib_splat02.wav");
1570 precache_sound ("misc/gib_splat03.wav");
1571 precache_sound ("misc/gib_splat04.wav");
1572 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1573 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1574 precache_sound ("misc/null.wav");
1575 precache_sound ("misc/spawn.wav");
1576 precache_sound ("misc/talk.wav");
1577 precache_sound ("misc/teleport.wav");
1578 precache_sound ("misc/poweroff.wav");
1579 precache_sound ("player/lava.wav");
1580 precache_sound ("player/slime.wav");
1583 precache_sound ("misc/jetpack_fly.wav");
1585 precache_model ("models/sprites/0.spr32");
1586 precache_model ("models/sprites/1.spr32");
1587 precache_model ("models/sprites/2.spr32");
1588 precache_model ("models/sprites/3.spr32");
1589 precache_model ("models/sprites/4.spr32");
1590 precache_model ("models/sprites/5.spr32");
1591 precache_model ("models/sprites/6.spr32");
1592 precache_model ("models/sprites/7.spr32");
1593 precache_model ("models/sprites/8.spr32");
1594 precache_model ("models/sprites/9.spr32");
1595 precache_model ("models/sprites/10.spr32");
1597 // common weapon precaches
1598 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1599 precache_sound ("weapons/weapon_switch.wav");
1600 precache_sound ("weapons/weaponpickup.wav");
1601 precache_sound ("weapons/unavailable.wav");
1602 precache_sound ("weapons/dryfire.wav");
1603 if (g_grappling_hook)
1605 precache_sound ("weapons/hook_fire.wav"); // hook
1606 precache_sound ("weapons/hook_impact.wav"); // hook
1609 if(autocvar_sv_precacheweapons)
1611 //precache weapon models/sounds
1614 while (wep <= WEP_LAST)
1616 weapon_action(wep, WR_PRECACHE);
1621 precache_model("models/elaser.mdl");
1622 precache_model("models/laser.mdl");
1623 precache_model("models/ebomb.mdl");
1626 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1628 if (!self.noise && self.music) // quake 3 uses the music field
1629 self.noise = self.music;
1631 // plays music for the level if there is any
1634 precache_sound (self.noise);
1635 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1640 // sorry, but using \ in macros breaks line numbers
1641 #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
1642 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1643 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1646 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1648 if (clienttype(e) == CLIENTTYPE_REAL)
1651 WRITESPECTATABLE_MSG_ONE({
1652 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1653 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1654 WriteByte(MSG_ONE, id);
1655 WriteString(MSG_ONE, s);
1656 if (id != 0 && s != "")
1658 WriteByte(MSG_ONE, duration);
1659 WriteByte(MSG_ONE, countdown_num);
1664 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1666 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1668 // WARNING: this kills the trace globals
1669 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1670 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1672 #define INITPRIO_FIRST 0
1673 #define INITPRIO_GAMETYPE 0
1674 #define INITPRIO_GAMETYPE_FALLBACK 1
1675 #define INITPRIO_FINDTARGET 10
1676 #define INITPRIO_DROPTOFLOOR 20
1677 #define INITPRIO_SETLOCATION 90
1678 #define INITPRIO_LINKDOORS 91
1679 #define INITPRIO_LAST 99
1681 .void(void) initialize_entity;
1682 .float initialize_entity_order;
1683 .entity initialize_entity_next;
1684 entity initialize_entity_first;
1686 void make_safe_for_remove(entity e)
1688 if (e.initialize_entity)
1691 for (ent = initialize_entity_first; ent; )
1693 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1695 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1696 // skip it in linked list
1699 prev.initialize_entity_next = ent.initialize_entity_next;
1700 ent = prev.initialize_entity_next;
1704 initialize_entity_first = ent.initialize_entity_next;
1705 ent = initialize_entity_first;
1711 ent = ent.initialize_entity_next;
1717 void objerror(string s)
1719 make_safe_for_remove(self);
1720 builtin_objerror(s);
1723 .float remove_except_protected_forbidden;
1724 void remove_except_protected(entity e)
1726 if(e.remove_except_protected_forbidden)
1727 error("not allowed to remove this at this point");
1731 void remove_unsafely(entity e)
1733 if(e.classname == "spike")
1734 error("Removing spikes is forbidden (crylink bug), please report");
1738 void remove_safely(entity e)
1740 make_safe_for_remove(e);
1744 void InitializeEntity(entity e, void(void) func, float order)
1748 if (!e || e.initialize_entity)
1750 // make a proxy initializer entity
1754 e.classname = "initialize_entity";
1758 e.initialize_entity = func;
1759 e.initialize_entity_order = order;
1761 cur = initialize_entity_first;
1764 if (!cur || cur.initialize_entity_order > order)
1766 // insert between prev and cur
1768 prev.initialize_entity_next = e;
1770 initialize_entity_first = e;
1771 e.initialize_entity_next = cur;
1775 cur = cur.initialize_entity_next;
1778 void InitializeEntitiesRun()
1781 startoflist = initialize_entity_first;
1782 initialize_entity_first = world;
1783 remove = remove_except_protected;
1784 for (self = startoflist; self; self = self.initialize_entity_next)
1786 self.remove_except_protected_forbidden = 1;
1788 for (self = startoflist; self; )
1791 var void(void) func;
1792 e = self.initialize_entity_next;
1793 func = self.initialize_entity;
1794 self.initialize_entity_order = 0;
1795 self.initialize_entity = func_null;
1796 self.initialize_entity_next = world;
1797 self.remove_except_protected_forbidden = 0;
1798 if (self.classname == "initialize_entity")
1802 builtin_remove(self);
1805 //dprint("Delayed initialization: ", self.classname, "\n");
1806 if(func != func_null)
1811 backtrace(strcat("Null function in: ", self.classname, "\n"));
1815 remove = remove_unsafely;
1818 .float uncustomizeentityforclient_set;
1819 .void(void) uncustomizeentityforclient;
1820 void(void) SUB_Nullpointer = #0;
1821 void UncustomizeEntitiesRun()
1825 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1826 self.uncustomizeentityforclient();
1829 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1831 e.customizeentityforclient = customizer;
1832 e.uncustomizeentityforclient = uncustomizer;
1833 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1837 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1840 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1844 if (e.classname == "")
1845 e.classname = "net_linked";
1847 if (e.model == "" || self.modelindex == 0)
1851 setmodel(e, "null");
1855 e.SendEntity = sendfunc;
1856 e.SendFlags = 0xFFFFFF;
1859 e.effects |= EF_NODEPTHTEST;
1863 e.nextthink = time + dt;
1864 e.think = SUB_Remove;
1868 void adaptor_think2touch()
1877 void adaptor_think2use()
1889 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1891 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
1892 self.projectiledeathtype |= HITTYPE_SPLASH;
1893 adaptor_think2use();
1896 // deferred dropping
1897 void DropToFloor_Handler()
1899 builtin_droptofloor();
1900 self.dropped_origin = self.origin;
1905 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1910 float trace_hits_box_a0, trace_hits_box_a1;
1912 float trace_hits_box_1d(float end, float thmi, float thma)
1916 // just check if x is in range
1924 // do the trace with respect to x
1925 // 0 -> end has to stay in thmi -> thma
1926 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1927 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1928 if (trace_hits_box_a0 > trace_hits_box_a1)
1934 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1939 // now it is a trace from 0 to end
1941 trace_hits_box_a0 = 0;
1942 trace_hits_box_a1 = 1;
1944 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1946 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1948 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1954 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1956 return trace_hits_box(start, end, thmi - ma, thma - mi);
1959 float SUB_NoImpactCheck()
1961 // zero hitcontents = this is not the real impact, but either the
1962 // mirror-impact of something hitting the projectile instead of the
1963 // projectile hitting the something, or a touchareagrid one. Neither of
1964 // these stop the projectile from moving, so...
1965 if(trace_dphitcontents == 0)
1967 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1968 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)));
1971 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1973 if (other == world && self.size != '0 0 0')
1976 tic = self.velocity * sys_frametime;
1977 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1978 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1979 if (trace_fraction >= 1)
1981 dprint("Odd... did not hit...?\n");
1983 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1985 dprint("Detected and prevented the sky-grapple bug.\n");
1993 #define SUB_OwnerCheck() (other && (other == self.owner))
1995 void RemoveGrapplingHook(entity pl);
1996 void W_Crylink_Dequeue(entity e);
1997 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1999 if(SUB_OwnerCheck())
2001 if(SUB_NoImpactCheck())
2003 if(self.classname == "grapplinghook")
2004 RemoveGrapplingHook(self.realowner);
2005 else if(self.classname == "spike")
2007 W_Crylink_Dequeue(self);
2014 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2015 UpdateCSQCProjectile(self);
2018 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2020 float MAX_IPBAN_URIS = 16;
2022 float URI_GET_DISCARD = 0;
2023 float URI_GET_IPBAN = 1;
2024 float URI_GET_IPBAN_END = 16;
2026 void URI_Get_Callback(float id, float status, string data)
2028 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2030 dprint("\nEnd of data.\n");
2032 if(url_URI_Get_Callback(id, status, data))
2036 else if (id == URI_GET_DISCARD)
2040 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2043 OnlineBanList_URI_Get_Callback(id, status, data);
2047 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2051 string uid2name(string myuid) {
2053 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
2055 // FIXME remove this later after 0.6 release
2056 // convert old style broken records to correct style
2059 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2062 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
2063 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
2068 s = "^1Unregistered Player";
2072 float race_readTime(string map, float pos)
2080 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2083 string race_readUID(string map, float pos)
2091 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2094 float race_readPos(string map, float t) {
2096 for (i = 1; i <= RANKINGS_CNT; ++i)
2097 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2100 return 0; // pos is zero if unranked
2103 void race_writeTime(string map, float t, string myuid)
2112 newpos = race_readPos(map, t);
2115 for(i = 1; i <= RANKINGS_CNT; ++i)
2117 if(race_readUID(map, i) == myuid)
2120 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2121 for (i = prevpos; i > newpos; --i) {
2122 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2123 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2125 } else { // player has no ranked record yet
2126 for (i = RANKINGS_CNT; i > newpos; --i) {
2127 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2128 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2132 // store new time itself
2133 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2134 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2137 string race_readName(string map, float pos)
2145 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2148 string race_placeName(float pos) {
2149 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2151 if(mod(pos, 10) == 1)
2152 return strcat(ftos(pos), "st");
2153 else if(mod(pos, 10) == 2)
2154 return strcat(ftos(pos), "nd");
2155 else if(mod(pos, 10) == 3)
2156 return strcat(ftos(pos), "rd");
2158 return strcat(ftos(pos), "th");
2161 return strcat(ftos(pos), "th");
2163 string getrecords(float page) // 50 records per page
2177 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2179 if (MapInfo_Get_ByID(i))
2181 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2185 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2186 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2194 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2196 if (MapInfo_Get_ByID(i))
2198 r = race_readTime(MapInfo_Map_bspname, 1);
2201 h = race_readName(MapInfo_Map_bspname, 1);
2202 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2210 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2212 if (MapInfo_Get_ByID(i))
2214 r = race_readTime(MapInfo_Map_bspname, 1);
2217 h = race_readName(MapInfo_Map_bspname, 1);
2218 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2224 MapInfo_ClearTemps();
2226 if (s == "" && page == 0)
2227 return "No records are available on this server.\n";
2232 string getrankings()
2245 for (i = 1; i <= RANKINGS_CNT; ++i)
2247 t = race_readTime(map, i);
2250 n = race_readName(map, i);
2251 p = race_placeName(i);
2252 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2255 MapInfo_ClearTemps();
2258 return strcat("No records are available for the map: ", map, "\n");
2260 return strcat("Records for ", map, ":\n", s);
2263 #define LADDER_FIRSTPOINT 100
2264 #define LADDER_CNT 10
2265 // position X still gives LADDER_FIRSTPOINT/X points
2266 #define LADDER_SIZE 30
2267 // ladder shows the top X players
2268 string top_uids[LADDER_SIZE];
2269 float top_scores[LADDER_SIZE];
2272 float i, j, k, uidcnt;
2286 for (k = 0; k < MapInfo_count; ++k)
2288 if (MapInfo_Get_ByID(k))
2290 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2291 if(i == 0) // speed award
2293 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2296 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2298 else // normal record, if it exists (else break)
2300 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2303 myuid = race_readUID(MapInfo_Map_bspname, i);
2306 // string s contains:
2307 // arg 0 = # of speed recs
2308 // arg 1 = # of 1st place recs
2309 // arg 2 = # of 2nd place recs
2311 // LADDER_CNT+1 = total points
2313 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2316 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2318 for (j = 0; j <= LADDER_CNT + 1; ++j)
2320 if(j != LADDER_CNT + 1)
2321 temp_s = strcat(temp_s, "0 ");
2323 temp_s = strcat(temp_s, "0");
2327 tokenize_console(temp_s);
2330 if(i == 0) // speed award
2331 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2333 if(j == 0) // speed award
2334 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2336 s = strcat(s, " ", argv(j)); // just copy over everything else
2339 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2342 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2343 else if(j == i) // wanted rec!
2344 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2346 s = strcat(s, " ", argv(j)); // just copy over everything else
2349 // total points are (by default) calculated like this:
2350 // speedrec = floor(100 / 10) = 10 points
2351 // 1st place = floor(100 / 1) = 100 points
2352 // 2nd place = floor(100 / 2) = 50 points
2353 // 3rd place = floor(100 / 3) = 33 points
2354 // 4th place = floor(100 / 4) = 25 points
2355 // 5th place = floor(100 / 5) = 20 points
2359 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2361 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2363 db_put(TemporaryDB, strcat("ladder", myuid), s);
2370 for (i = 0; i <= uidcnt; ++i) // for each known uid
2372 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2373 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2374 tokenize_console(temp_s);
2375 thiscnt = stof(argv(LADDER_CNT+1));
2377 if(thiscnt > top_scores[LADDER_SIZE-1])
2378 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2380 if(thiscnt > top_scores[j])
2382 for (k = LADDER_SIZE-1; k >= j; --k)
2384 top_uids[k] = top_uids[k-1];
2385 top_scores[k] = top_scores[k-1];
2387 top_uids[j] = thisuid;
2388 top_scores[j] = thiscnt;
2394 s = "^3-----------------------\n\n";
2396 s = strcat(s, "Pos ^3|");
2397 s = strcat(s, " ^7Total ^3|");
2398 for (i = 1; i <= LADDER_CNT; ++i)
2400 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2402 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2404 s = strcat(s, "\n^3----+--------");
2405 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2407 s = strcat(s, "+-----");
2410 for (i = 1; i <= LADDER_CNT - 9; ++i)
2412 s = strcat(s, "+------");
2416 s = strcat(s, "+--------------+--------------------\n");
2418 for (i = 0; i < LADDER_SIZE; ++i)
2420 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2421 tokenize_console(temp_s);
2422 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2424 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2425 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2426 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2428 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2431 for (j = 10; j <= LADDER_CNT; ++j)
2433 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2437 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2438 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2441 MapInfo_ClearTemps();
2444 return "No ladder on this server!\n";
2446 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2450 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2453 vector start, org, delta, end, enddown, mstart;
2456 m = e.dphitcontentsmask;
2457 e.dphitcontentsmask = goodcontents | badcontents;
2460 delta = world.maxs - world.mins;
2462 for (i = 0; i < attempts; ++i)
2464 start_x = org_x + random() * delta_x;
2465 start_y = org_y + random() * delta_y;
2466 start_z = org_z + random() * delta_z;
2468 // rule 1: start inside world bounds, and outside
2469 // solid, and don't start from somewhere where you can
2470 // fall down to evil
2471 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2472 if (trace_fraction >= 1)
2474 if (trace_startsolid)
2476 if (trace_dphitcontents & badcontents)
2478 if (trace_dphitq3surfaceflags & badsurfaceflags)
2481 // rule 2: if we are too high, lower the point
2482 if (trace_fraction * delta_z > maxaboveground)
2483 start = trace_endpos + '0 0 1' * maxaboveground;
2484 enddown = trace_endpos;
2486 // rule 3: make sure we aren't outside the map. This only works
2487 // for somewhat well formed maps. A good rule of thumb is that
2488 // the map should have a convex outside hull.
2489 // these can be traceLINES as we already verified the starting box
2490 mstart = start + 0.5 * (e.mins + e.maxs);
2491 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2492 if (trace_fraction >= 1)
2494 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2495 if (trace_fraction >= 1)
2497 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2498 if (trace_fraction >= 1)
2500 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2501 if (trace_fraction >= 1)
2503 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2504 if (trace_fraction >= 1)
2507 // rule 4: we must "see" some spawnpoint
2508 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2509 if(checkpvs(mstart, sp))
2513 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2514 if(checkpvs(mstart, sp))
2520 // find a random vector to "look at"
2521 end_x = org_x + random() * delta_x;
2522 end_y = org_y + random() * delta_y;
2523 end_z = org_z + random() * delta_z;
2524 end = start + normalize(end - start) * vlen(delta);
2526 // rule 4: start TO end must not be too short
2527 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2528 if (trace_startsolid)
2530 if (trace_fraction < minviewdistance / vlen(delta))
2533 // rule 5: don't want to look at sky
2534 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2537 // rule 6: we must not end up in trigger_hurt
2538 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2544 e.dphitcontentsmask = m;
2548 setorigin(e, start);
2549 e.angles = vectoangles(end - start);
2550 dprint("Needed ", ftos(i + 1), " attempts\n");
2557 float zcurveparticles_effectno;
2558 vector zcurveparticles_start;
2559 float zcurveparticles_spd;
2561 void endzcurveparticles()
2563 if(zcurveparticles_effectno)
2566 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2568 zcurveparticles_effectno = 0;
2571 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2573 spd = bound(0, floor(spd / 16), 32767);
2574 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2576 endzcurveparticles();
2577 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2578 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2579 WriteShort(MSG_BROADCAST, effectno);
2580 WriteCoord(MSG_BROADCAST, start_x);
2581 WriteCoord(MSG_BROADCAST, start_y);
2582 WriteCoord(MSG_BROADCAST, start_z);
2583 zcurveparticles_effectno = effectno;
2584 zcurveparticles_start = start;
2587 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2588 WriteCoord(MSG_BROADCAST, end_x);
2589 WriteCoord(MSG_BROADCAST, end_y);
2590 WriteCoord(MSG_BROADCAST, end_z);
2591 WriteCoord(MSG_BROADCAST, end_dz);
2592 zcurveparticles_spd = spd;
2595 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2598 vector vecxy, velxy;
2600 vecxy = end - start;
2605 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2607 endzcurveparticles();
2608 trailparticles(world, effectno, start, end);
2612 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2613 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2616 void write_recordmarker(entity pl, float tstart, float dt)
2618 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2620 // also write a marker into demo files for demotc-race-record-extractor to find
2623 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2624 " ", ftos(tstart), " ", ftos(dt), "\n"));
2627 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2640 if(allowcenter) // 2: allow center handedness
2653 if(allowcenter) // 2: allow center handedness
2669 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2674 if (autocvar_g_shootfromeye)
2687 else if (autocvar_g_shootfromcenter)
2692 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2702 else if (autocvar_g_shootfromclient)
2704 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2709 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2711 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2715 void attach_sameorigin(entity e, entity to, string tag)
2717 vector org, t_forward, t_left, t_up, e_forward, e_up;
2724 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2725 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2726 t_forward = v_forward * tagscale;
2727 t_left = v_right * -tagscale;
2728 t_up = v_up * tagscale;
2730 e.origin_x = org * t_forward;
2731 e.origin_y = org * t_left;
2732 e.origin_z = org * t_up;
2734 // current forward and up directions
2735 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2736 e.angles = AnglesTransform_FromVAngles(e.angles);
2738 e.angles = AnglesTransform_FromAngles(e.angles);
2739 fixedmakevectors(e.angles);
2741 // untransform forward, up!
2742 e_forward_x = v_forward * t_forward;
2743 e_forward_y = v_forward * t_left;
2744 e_forward_z = v_forward * t_up;
2745 e_up_x = v_up * t_forward;
2746 e_up_y = v_up * t_left;
2747 e_up_z = v_up * t_up;
2749 e.angles = fixedvectoangles2(e_forward, e_up);
2750 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2751 e.angles = AnglesTransform_ToVAngles(e.angles);
2753 e.angles = AnglesTransform_ToAngles(e.angles);
2755 setattachment(e, to, tag);
2756 setorigin(e, e.origin);
2759 void detach_sameorigin(entity e)
2762 org = gettaginfo(e, 0);
2763 e.angles = fixedvectoangles2(v_forward, v_up);
2764 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2765 e.angles = AnglesTransform_ToVAngles(e.angles);
2767 e.angles = AnglesTransform_ToAngles(e.angles);
2769 setattachment(e, world, "");
2770 setorigin(e, e.origin);
2773 void follow_sameorigin(entity e, entity to)
2775 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2776 e.aiment = to; // make the hole follow bmodel
2777 e.punchangle = to.angles; // the original angles of bmodel
2778 e.view_ofs = e.origin - to.origin; // relative origin
2779 e.v_angle = e.angles - to.angles; // relative angles
2782 void unfollow_sameorigin(entity e)
2784 e.movetype = MOVETYPE_NONE;
2787 entity gettaginfo_relative_ent;
2788 vector gettaginfo_relative(entity e, float tag)
2790 if (!gettaginfo_relative_ent)
2792 gettaginfo_relative_ent = spawn();
2793 gettaginfo_relative_ent.effects = EF_NODRAW;
2795 gettaginfo_relative_ent.model = e.model;
2796 gettaginfo_relative_ent.modelindex = e.modelindex;
2797 gettaginfo_relative_ent.frame = e.frame;
2798 return gettaginfo(gettaginfo_relative_ent, tag);
2801 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2805 if (pl.soundentity.cnt & p)
2807 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2808 pl.soundentity.cnt |= p;
2811 void SoundEntity_StopSound(entity pl, float chan)
2815 if (pl.soundentity.cnt & p)
2817 stopsoundto(MSG_ALL, pl.soundentity, chan);
2818 pl.soundentity.cnt &~= p;
2822 void SoundEntity_Attach(entity pl)
2824 pl.soundentity = spawn();
2825 pl.soundentity.classname = "soundentity";
2826 pl.soundentity.owner = pl;
2827 setattachment(pl.soundentity, pl, "");
2828 setmodel(pl.soundentity, "null");
2831 void SoundEntity_Detach(entity pl)
2834 for (i = 0; i <= 7; ++i)
2835 SoundEntity_StopSound(pl, i);
2839 float ParseCommandPlayerSlotTarget_firsttoken;
2840 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2848 ParseCommandPlayerSlotTarget_firsttoken = -1;
2852 if (substring(argv(idx), 0, 1) == "#")
2854 s = substring(argv(idx), 1, -1);
2856 if (s == "") if (tokens > idx)
2861 ParseCommandPlayerSlotTarget_firsttoken = idx;
2863 if (s == ftos(n) && n > 0 && n <= maxclients)
2866 if (e.flags & FL_CLIENT)
2872 // it must be a nick name
2875 ParseCommandPlayerSlotTarget_firsttoken = idx;
2878 FOR_EACH_CLIENT(head)
2879 if (head.netname == s)
2887 s = strdecolorize(s);
2889 FOR_EACH_CLIENT(head)
2890 if (strdecolorize(head.netname) == s)
2905 float modeleffect_SendEntity(entity to, float sf)
2908 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2911 if(self.velocity != '0 0 0')
2913 if(self.angles != '0 0 0')
2915 if(self.avelocity != '0 0 0')
2918 WriteByte(MSG_ENTITY, f);
2919 WriteShort(MSG_ENTITY, self.modelindex);
2920 WriteByte(MSG_ENTITY, self.skin);
2921 WriteByte(MSG_ENTITY, self.frame);
2922 WriteCoord(MSG_ENTITY, self.origin_x);
2923 WriteCoord(MSG_ENTITY, self.origin_y);
2924 WriteCoord(MSG_ENTITY, self.origin_z);
2927 WriteCoord(MSG_ENTITY, self.velocity_x);
2928 WriteCoord(MSG_ENTITY, self.velocity_y);
2929 WriteCoord(MSG_ENTITY, self.velocity_z);
2933 WriteCoord(MSG_ENTITY, self.angles_x);
2934 WriteCoord(MSG_ENTITY, self.angles_y);
2935 WriteCoord(MSG_ENTITY, self.angles_z);
2939 WriteCoord(MSG_ENTITY, self.avelocity_x);
2940 WriteCoord(MSG_ENTITY, self.avelocity_y);
2941 WriteCoord(MSG_ENTITY, self.avelocity_z);
2943 WriteShort(MSG_ENTITY, self.scale * 256.0);
2944 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2945 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2946 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2947 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2952 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)
2957 e.classname = "modeleffect";
2965 e.teleport_time = t1;
2969 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2973 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2976 sz = max(e.scale, e.scale2);
2977 setsize(e, e.mins * sz, e.maxs * sz);
2978 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2981 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2983 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2986 float randombit(float bits)
2988 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2997 for(f = 1; f <= bits; f *= 2)
3006 r = (r - 1) / (n - 1);
3013 float randombits(float bits, float k, float error_return)
3017 while(k > 0 && bits != r)
3019 r += randombit(bits - r);
3028 void randombit_test(float bits, float iter)
3032 print(ftos(randombit(bits)), "\n");
3037 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3039 if(halflifedist > 0)
3040 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3041 else if(halflifedist < 0)
3042 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3051 #define cvar_string_normal builtin_cvar_string
3052 #define cvar_normal builtin_cvar
3054 string cvar_string_normal(string n)
3056 if not(cvar_type(n) & 1)
3057 backtrace(strcat("Attempt to access undefined cvar: ", n));
3058 return builtin_cvar_string(n);
3061 float cvar_normal(string n)
3063 return stof(cvar_string_normal(n));
3066 #define cvar_set_normal builtin_cvar_set
3074 oself.think = SUB_Remove;
3075 oself.nextthink = time;
3081 Execute func() after time + fdelay.
3082 self when func is executed = self when defer is called
3084 void defer(float fdelay, void() func)
3091 e.think = defer_think;
3092 e.nextthink = time + fdelay;
3095 .string aiment_classname;
3096 .float aiment_deadflag;
3097 void SetMovetypeFollow(entity ent, entity e)
3099 // FIXME this may not be warpzone aware
3100 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3101 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.
3102 ent.aiment = e; // make the hole follow bmodel
3103 ent.punchangle = e.angles; // the original angles of bmodel
3104 ent.view_ofs = ent.origin - e.origin; // relative origin
3105 ent.v_angle = ent.angles - e.angles; // relative angles
3106 ent.aiment_classname = strzone(e.classname);
3107 ent.aiment_deadflag = e.deadflag;
3109 void UnsetMovetypeFollow(entity ent)
3111 ent.movetype = MOVETYPE_FLY;
3112 PROJECTILE_MAKETRIGGER(ent);
3115 float LostMovetypeFollow(entity ent)
3118 if(ent.movetype != MOVETYPE_FOLLOW)
3124 if(ent.aiment.classname != ent.aiment_classname)
3126 if(ent.aiment.deadflag != ent.aiment_deadflag)
3132 float isPushable(entity e)
3139 case "droppedweapon":
3140 case "keepawayball":
3141 case "nexball_basketball":
3142 case "nexball_football":
3144 case "bullet": // antilagged bullets can't hit this either
3147 if (e.projectiledeathtype)