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_sandbox"))
1105 MUTATOR_ADD(sandbox);
1107 if(cvar("sv_allow_fullbright"))
1108 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1110 g_bugrigs = cvar("g_bugrigs");
1111 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1112 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1113 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1114 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1115 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1116 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1117 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1118 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1119 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1120 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1121 g_bugrigs_accel = cvar("g_bugrigs_accel");
1122 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1123 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1124 g_bugrigs_steer = cvar("g_bugrigs_steer");
1126 g_touchexplode = cvar("g_touchexplode");
1127 g_touchexplode_radius = cvar("g_touchexplode_radius");
1128 g_touchexplode_damage = cvar("g_touchexplode_damage");
1129 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1130 g_touchexplode_force = cvar("g_touchexplode_force");
1132 #ifdef ALLOW_FORCEMODELS
1133 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1135 sv_loddistance1 = cvar("sv_loddistance1");
1136 sv_loddistance2 = cvar("sv_loddistance2");
1138 if(sv_loddistance2 <= sv_loddistance1)
1139 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1141 sv_clones = cvar("sv_clones");
1142 sv_gentle = cvar("sv_gentle");
1143 sv_foginterval = cvar("sv_foginterval");
1144 g_cloaked = cvar("g_cloaked");
1146 g_cloaked = 1; // always enable cloak in CTS
1147 g_jump_grunt = cvar("g_jump_grunt");
1148 g_footsteps = cvar("g_footsteps");
1149 g_grappling_hook = cvar("g_grappling_hook");
1150 g_jetpack = cvar("g_jetpack");
1151 g_midair = cvar("g_midair");
1152 g_minstagib = cvar("g_minstagib");
1153 g_norecoil = cvar("g_norecoil");
1154 g_bloodloss = cvar("g_bloodloss");
1155 sv_maxidle = cvar("sv_maxidle");
1156 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1157 g_ctf_reverse = cvar("g_ctf_reverse");
1158 sv_autotaunt = cvar("sv_autotaunt");
1159 sv_taunt = cvar("sv_taunt");
1161 inWarmupStage = cvar("g_warmup");
1162 g_warmup_limit = cvar("g_warmup_limit");
1163 g_warmup_allguns = cvar("g_warmup_allguns");
1164 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1166 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1167 inWarmupStage = 0; // these modes cannot work together, sorry
1169 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1170 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1171 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1172 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1173 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1174 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1175 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1176 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1177 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1178 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1179 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1180 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1182 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1183 g_weaponratefactor = cvar("g_weaponratefactor");
1184 g_weapondamagefactor = cvar("g_weapondamagefactor");
1185 g_weaponforcefactor = cvar("g_weaponforcefactor");
1186 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1188 g_pickup_shells = cvar("g_pickup_shells");
1189 g_pickup_shells_max = cvar("g_pickup_shells_max");
1190 g_pickup_nails = cvar("g_pickup_nails");
1191 g_pickup_nails_max = cvar("g_pickup_nails_max");
1192 g_pickup_rockets = cvar("g_pickup_rockets");
1193 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1194 g_pickup_cells = cvar("g_pickup_cells");
1195 g_pickup_cells_max = cvar("g_pickup_cells_max");
1196 g_pickup_fuel = cvar("g_pickup_fuel");
1197 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1198 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1199 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1200 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1201 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1202 g_pickup_armormedium = cvar("g_pickup_armormedium");
1203 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1204 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1205 g_pickup_armorbig = cvar("g_pickup_armorbig");
1206 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1207 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1208 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1209 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1210 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1211 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1212 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1213 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1214 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1215 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1216 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1217 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1218 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1219 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1220 g_pickup_healthmega = cvar("g_pickup_healthmega");
1221 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1222 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1224 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1225 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1227 g_pinata = cvar("g_pinata");
1229 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1231 g_weapon_stay = cvar("g_weapon_stay");
1233 g_ghost_items = cvar("g_ghost_items");
1235 if(g_ghost_items >= 1)
1236 g_ghost_items = 0.25; // default alpha value
1238 if not(inWarmupStage && !g_ca)
1239 game_starttime = cvar("g_start_delay");
1241 sv_pitch_min = cvar("sv_pitch_min");
1242 sv_pitch_max = cvar("sv_pitch_max");
1243 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1245 readplayerstartcvars();
1251 string precache_sound (string s) = #19;
1252 float precache_sound_index (string s) = #19;
1254 #define SND_VOLUME 1
1255 #define SND_ATTENUATION 2
1256 #define SND_LARGEENTITY 8
1257 #define SND_LARGESOUND 16
1259 float sound_allowed(float dest, entity e)
1261 // sounds from world may always pass
1264 if (e.classname == "body")
1266 else if (e.realowner && e.realowner != e)
1268 else if (e.owner && e.owner != e)
1273 // sounds to self may always pass
1274 if (dest == MSG_ONE)
1275 if (e == msg_entity)
1277 // sounds by players can be removed
1278 if (autocvar_bot_sound_monopoly)
1279 if (clienttype(e) == CLIENTTYPE_REAL)
1281 // anything else may pass
1285 #ifdef COMPAT_XON010_CHANNELS
1286 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1287 void sound(entity e, float chan, string samp, float vol, float atten)
1289 if (!sound_allowed(MSG_BROADCAST, e))
1291 builtin_sound(e, chan, samp, vol, atten);
1295 void sound(entity e, float chan, string samp, float vol, float atten)
1297 if (!sound_allowed(MSG_BROADCAST, e))
1299 sound7(e, chan, samp, vol, atten, 0, 0);
1303 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1307 if (!sound_allowed(dest, e))
1310 entno = num_for_edict(e);
1311 idx = precache_sound_index(samp);
1316 atten = floor(atten * 64);
1317 vol = floor(vol * 255);
1320 sflags |= SND_VOLUME;
1322 sflags |= SND_ATTENUATION;
1323 if (entno >= 8192 || chan < 0 || chan > 7)
1324 sflags |= SND_LARGEENTITY;
1326 sflags |= SND_LARGESOUND;
1328 WriteByte(dest, SVC_SOUND);
1329 WriteByte(dest, sflags);
1330 if (sflags & SND_VOLUME)
1331 WriteByte(dest, vol);
1332 if (sflags & SND_ATTENUATION)
1333 WriteByte(dest, atten);
1334 if (sflags & SND_LARGEENTITY)
1336 WriteShort(dest, entno);
1337 WriteByte(dest, chan);
1341 WriteShort(dest, entno * 8 + chan);
1343 if (sflags & SND_LARGESOUND)
1344 WriteShort(dest, idx);
1346 WriteByte(dest, idx);
1348 WriteCoord(dest, o_x);
1349 WriteCoord(dest, o_y);
1350 WriteCoord(dest, o_z);
1352 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1356 if (!sound_allowed(dest, e))
1359 o = e.origin + 0.5 * (e.mins + e.maxs);
1360 soundtoat(dest, e, o, chan, samp, vol, atten);
1362 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1364 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1366 void stopsoundto(float dest, entity e, float chan)
1370 if (!sound_allowed(dest, e))
1373 entno = num_for_edict(e);
1375 if (entno >= 8192 || chan < 0 || chan > 7)
1378 idx = precache_sound_index("misc/null.wav");
1379 sflags = SND_LARGEENTITY;
1381 sflags |= SND_LARGESOUND;
1382 WriteByte(dest, SVC_SOUND);
1383 WriteByte(dest, sflags);
1384 WriteShort(dest, entno);
1385 WriteByte(dest, chan);
1386 if (sflags & SND_LARGESOUND)
1387 WriteShort(dest, idx);
1389 WriteByte(dest, idx);
1390 WriteCoord(dest, e.origin_x);
1391 WriteCoord(dest, e.origin_y);
1392 WriteCoord(dest, e.origin_z);
1396 WriteByte(dest, SVC_STOPSOUND);
1397 WriteShort(dest, entno * 8 + chan);
1400 void stopsound(entity e, float chan)
1402 if (!sound_allowed(MSG_BROADCAST, e))
1405 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1406 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1409 void play2(entity e, string filename)
1411 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1413 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1416 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1418 float spamsound(entity e, float chan, string samp, float vol, float atten)
1420 if (!sound_allowed(MSG_BROADCAST, e))
1423 if (time > e.spamtime)
1426 sound(e, chan, samp, vol, atten);
1432 void play2team(float t, string filename)
1436 if (autocvar_bot_sound_monopoly)
1439 FOR_EACH_REALPLAYER(head)
1442 play2(head, filename);
1446 void play2all(string samp)
1448 if (autocvar_bot_sound_monopoly)
1451 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1454 void PrecachePlayerSounds(string f);
1455 void precache_playermodel(string m)
1457 float globhandle, i, n;
1460 if(substring(m, -9,5) == "_lod1")
1462 if(substring(m, -9,5) == "_lod2")
1467 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1470 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1475 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1478 n = search_getsize(globhandle);
1479 for (i = 0; i < n; ++i)
1481 //print(search_getfilename(globhandle, i), "\n");
1482 f = search_getfilename(globhandle, i);
1483 PrecachePlayerSounds(f);
1485 search_end(globhandle);
1487 void precache_all_playermodels(string pattern)
1489 float globhandle, i, n;
1492 globhandle = search_begin(pattern, TRUE, FALSE);
1495 n = search_getsize(globhandle);
1496 for (i = 0; i < n; ++i)
1498 //print(search_getfilename(globhandle, i), "\n");
1499 f = search_getfilename(globhandle, i);
1500 precache_playermodel(f);
1502 search_end(globhandle);
1507 // gamemode related things
1508 precache_model ("models/misc/chatbubble.spr");
1511 precache_model ("models/runematch/curse.mdl");
1512 precache_model ("models/runematch/rune.mdl");
1515 #ifdef TTURRETS_ENABLED
1516 if (autocvar_g_turrets)
1520 // Precache all player models if desired
1521 if (autocvar_sv_precacheplayermodels)
1523 PrecachePlayerSounds("sound/player/default.sounds");
1524 precache_all_playermodels("models/player/*.zym");
1525 precache_all_playermodels("models/player/*.dpm");
1526 precache_all_playermodels("models/player/*.md3");
1527 precache_all_playermodels("models/player/*.psk");
1528 precache_all_playermodels("models/player/*.iqm");
1531 if (autocvar_sv_defaultcharacter)
1534 s = autocvar_sv_defaultplayermodel_red;
1536 precache_playermodel(s);
1537 s = autocvar_sv_defaultplayermodel_blue;
1539 precache_playermodel(s);
1540 s = autocvar_sv_defaultplayermodel_yellow;
1542 precache_playermodel(s);
1543 s = autocvar_sv_defaultplayermodel_pink;
1545 precache_playermodel(s);
1546 s = autocvar_sv_defaultplayermodel;
1548 precache_playermodel(s);
1553 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1554 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1557 // gore and miscellaneous sounds
1558 //precache_sound ("misc/h2ohit.wav");
1559 precache_model ("models/hook.md3");
1560 precache_sound ("misc/armorimpact.wav");
1561 precache_sound ("misc/bodyimpact1.wav");
1562 precache_sound ("misc/bodyimpact2.wav");
1563 precache_sound ("misc/gib.wav");
1564 precache_sound ("misc/gib_splat01.wav");
1565 precache_sound ("misc/gib_splat02.wav");
1566 precache_sound ("misc/gib_splat03.wav");
1567 precache_sound ("misc/gib_splat04.wav");
1568 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1569 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1570 precache_sound ("misc/null.wav");
1571 precache_sound ("misc/spawn.wav");
1572 precache_sound ("misc/talk.wav");
1573 precache_sound ("misc/teleport.wav");
1574 precache_sound ("misc/poweroff.wav");
1575 precache_sound ("player/lava.wav");
1576 precache_sound ("player/slime.wav");
1579 precache_sound ("misc/jetpack_fly.wav");
1581 precache_model ("models/sprites/0.spr32");
1582 precache_model ("models/sprites/1.spr32");
1583 precache_model ("models/sprites/2.spr32");
1584 precache_model ("models/sprites/3.spr32");
1585 precache_model ("models/sprites/4.spr32");
1586 precache_model ("models/sprites/5.spr32");
1587 precache_model ("models/sprites/6.spr32");
1588 precache_model ("models/sprites/7.spr32");
1589 precache_model ("models/sprites/8.spr32");
1590 precache_model ("models/sprites/9.spr32");
1591 precache_model ("models/sprites/10.spr32");
1593 // common weapon precaches
1594 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1595 precache_sound ("weapons/weapon_switch.wav");
1596 precache_sound ("weapons/weaponpickup.wav");
1597 precache_sound ("weapons/unavailable.wav");
1598 precache_sound ("weapons/dryfire.wav");
1599 if (g_grappling_hook)
1601 precache_sound ("weapons/hook_fire.wav"); // hook
1602 precache_sound ("weapons/hook_impact.wav"); // hook
1605 if(autocvar_sv_precacheweapons)
1607 //precache weapon models/sounds
1610 while (wep <= WEP_LAST)
1612 weapon_action(wep, WR_PRECACHE);
1617 precache_model("models/elaser.mdl");
1618 precache_model("models/laser.mdl");
1619 precache_model("models/ebomb.mdl");
1622 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1624 if (!self.noise && self.music) // quake 3 uses the music field
1625 self.noise = self.music;
1627 // plays music for the level if there is any
1630 precache_sound (self.noise);
1631 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1636 // sorry, but using \ in macros breaks line numbers
1637 #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
1638 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1639 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1642 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1644 if (clienttype(e) == CLIENTTYPE_REAL)
1647 WRITESPECTATABLE_MSG_ONE({
1648 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1649 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1650 WriteByte(MSG_ONE, id);
1651 WriteString(MSG_ONE, s);
1652 if (id != 0 && s != "")
1654 WriteByte(MSG_ONE, duration);
1655 WriteByte(MSG_ONE, countdown_num);
1660 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1662 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1664 // WARNING: this kills the trace globals
1665 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1666 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1668 #define INITPRIO_FIRST 0
1669 #define INITPRIO_GAMETYPE 0
1670 #define INITPRIO_GAMETYPE_FALLBACK 1
1671 #define INITPRIO_FINDTARGET 10
1672 #define INITPRIO_DROPTOFLOOR 20
1673 #define INITPRIO_SETLOCATION 90
1674 #define INITPRIO_LINKDOORS 91
1675 #define INITPRIO_LAST 99
1677 .void(void) initialize_entity;
1678 .float initialize_entity_order;
1679 .entity initialize_entity_next;
1680 entity initialize_entity_first;
1682 void make_safe_for_remove(entity e)
1684 if (e.initialize_entity)
1687 for (ent = initialize_entity_first; ent; )
1689 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1691 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1692 // skip it in linked list
1695 prev.initialize_entity_next = ent.initialize_entity_next;
1696 ent = prev.initialize_entity_next;
1700 initialize_entity_first = ent.initialize_entity_next;
1701 ent = initialize_entity_first;
1707 ent = ent.initialize_entity_next;
1713 void objerror(string s)
1715 make_safe_for_remove(self);
1716 builtin_objerror(s);
1719 .float remove_except_protected_forbidden;
1720 void remove_except_protected(entity e)
1722 if(e.remove_except_protected_forbidden)
1723 error("not allowed to remove this at this point");
1727 void remove_unsafely(entity e)
1729 if(e.classname == "spike")
1730 error("Removing spikes is forbidden (crylink bug), please report");
1734 void remove_safely(entity e)
1736 make_safe_for_remove(e);
1740 void InitializeEntity(entity e, void(void) func, float order)
1744 if (!e || e.initialize_entity)
1746 // make a proxy initializer entity
1750 e.classname = "initialize_entity";
1754 e.initialize_entity = func;
1755 e.initialize_entity_order = order;
1757 cur = initialize_entity_first;
1760 if (!cur || cur.initialize_entity_order > order)
1762 // insert between prev and cur
1764 prev.initialize_entity_next = e;
1766 initialize_entity_first = e;
1767 e.initialize_entity_next = cur;
1771 cur = cur.initialize_entity_next;
1774 void InitializeEntitiesRun()
1777 startoflist = initialize_entity_first;
1778 initialize_entity_first = world;
1779 remove = remove_except_protected;
1780 for (self = startoflist; self; self = self.initialize_entity_next)
1782 self.remove_except_protected_forbidden = 1;
1784 for (self = startoflist; self; )
1787 var void(void) func;
1788 e = self.initialize_entity_next;
1789 func = self.initialize_entity;
1790 self.initialize_entity_order = 0;
1791 self.initialize_entity = func_null;
1792 self.initialize_entity_next = world;
1793 self.remove_except_protected_forbidden = 0;
1794 if (self.classname == "initialize_entity")
1798 builtin_remove(self);
1801 //dprint("Delayed initialization: ", self.classname, "\n");
1802 if(func != func_null)
1807 backtrace(strcat("Null function in: ", self.classname, "\n"));
1811 remove = remove_unsafely;
1814 .float uncustomizeentityforclient_set;
1815 .void(void) uncustomizeentityforclient;
1816 void(void) SUB_Nullpointer = #0;
1817 void UncustomizeEntitiesRun()
1821 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1822 self.uncustomizeentityforclient();
1825 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1827 e.customizeentityforclient = customizer;
1828 e.uncustomizeentityforclient = uncustomizer;
1829 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1833 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1836 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1840 if (e.classname == "")
1841 e.classname = "net_linked";
1843 if (e.model == "" || self.modelindex == 0)
1847 setmodel(e, "null");
1851 e.SendEntity = sendfunc;
1852 e.SendFlags = 0xFFFFFF;
1855 e.effects |= EF_NODEPTHTEST;
1859 e.nextthink = time + dt;
1860 e.think = SUB_Remove;
1864 void adaptor_think2touch()
1873 void adaptor_think2use()
1885 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1887 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
1888 self.projectiledeathtype |= HITTYPE_SPLASH;
1889 adaptor_think2use();
1892 // deferred dropping
1893 void DropToFloor_Handler()
1895 builtin_droptofloor();
1896 self.dropped_origin = self.origin;
1901 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1906 float trace_hits_box_a0, trace_hits_box_a1;
1908 float trace_hits_box_1d(float end, float thmi, float thma)
1912 // just check if x is in range
1920 // do the trace with respect to x
1921 // 0 -> end has to stay in thmi -> thma
1922 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1923 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1924 if (trace_hits_box_a0 > trace_hits_box_a1)
1930 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1935 // now it is a trace from 0 to end
1937 trace_hits_box_a0 = 0;
1938 trace_hits_box_a1 = 1;
1940 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1942 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1944 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1950 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1952 return trace_hits_box(start, end, thmi - ma, thma - mi);
1955 float SUB_NoImpactCheck()
1957 // zero hitcontents = this is not the real impact, but either the
1958 // mirror-impact of something hitting the projectile instead of the
1959 // projectile hitting the something, or a touchareagrid one. Neither of
1960 // these stop the projectile from moving, so...
1961 if(trace_dphitcontents == 0)
1963 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1964 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)));
1967 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1969 if (other == world && self.size != '0 0 0')
1972 tic = self.velocity * sys_frametime;
1973 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1974 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1975 if (trace_fraction >= 1)
1977 dprint("Odd... did not hit...?\n");
1979 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1981 dprint("Detected and prevented the sky-grapple bug.\n");
1989 #define SUB_OwnerCheck() (other && (other == self.owner))
1991 void RemoveGrapplingHook(entity pl);
1992 void W_Crylink_Dequeue(entity e);
1993 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1995 if(SUB_OwnerCheck())
1997 if(SUB_NoImpactCheck())
1999 if(self.classname == "grapplinghook")
2000 RemoveGrapplingHook(self.realowner);
2001 else if(self.classname == "spike")
2003 W_Crylink_Dequeue(self);
2010 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2011 UpdateCSQCProjectile(self);
2014 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2016 float MAX_IPBAN_URIS = 16;
2018 float URI_GET_DISCARD = 0;
2019 float URI_GET_IPBAN = 1;
2020 float URI_GET_IPBAN_END = 16;
2022 void URI_Get_Callback(float id, float status, string data)
2024 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2026 dprint("\nEnd of data.\n");
2028 if(url_URI_Get_Callback(id, status, data))
2032 else if (id == URI_GET_DISCARD)
2036 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2039 OnlineBanList_URI_Get_Callback(id, status, data);
2043 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2047 void print_to(entity e, string s)
2050 sprint(e, strcat(s, "\n"));
2055 string uid2name(string myuid) {
2057 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
2059 // FIXME remove this later after 0.6 release
2060 // convert old style broken records to correct style
2063 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2066 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
2067 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
2072 s = "^1Unregistered Player";
2076 float race_readTime(string map, float pos)
2084 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2087 string race_readUID(string map, float pos)
2095 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2098 float race_readPos(string map, float t) {
2100 for (i = 1; i <= RANKINGS_CNT; ++i)
2101 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2104 return 0; // pos is zero if unranked
2107 void race_writeTime(string map, float t, string myuid)
2116 newpos = race_readPos(map, t);
2119 for(i = 1; i <= RANKINGS_CNT; ++i)
2121 if(race_readUID(map, i) == myuid)
2124 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2125 for (i = prevpos; i > newpos; --i) {
2126 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2127 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2129 } else { // player has no ranked record yet
2130 for (i = RANKINGS_CNT; i > newpos; --i) {
2131 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2132 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2136 // store new time itself
2137 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2138 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2141 string race_readName(string map, float pos)
2149 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2152 string race_placeName(float pos) {
2153 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2155 if(mod(pos, 10) == 1)
2156 return strcat(ftos(pos), "st");
2157 else if(mod(pos, 10) == 2)
2158 return strcat(ftos(pos), "nd");
2159 else if(mod(pos, 10) == 3)
2160 return strcat(ftos(pos), "rd");
2162 return strcat(ftos(pos), "th");
2165 return strcat(ftos(pos), "th");
2167 string getrecords(float page) // 50 records per page
2181 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2183 if (MapInfo_Get_ByID(i))
2185 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2189 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2190 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2198 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2200 if (MapInfo_Get_ByID(i))
2202 r = race_readTime(MapInfo_Map_bspname, 1);
2205 h = race_readName(MapInfo_Map_bspname, 1);
2206 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2214 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2216 if (MapInfo_Get_ByID(i))
2218 r = race_readTime(MapInfo_Map_bspname, 1);
2221 h = race_readName(MapInfo_Map_bspname, 1);
2222 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2228 MapInfo_ClearTemps();
2230 if (s == "" && page == 0)
2231 return "No records are available on this server.\n";
2236 string getrankings()
2249 for (i = 1; i <= RANKINGS_CNT; ++i)
2251 t = race_readTime(map, i);
2254 n = race_readName(map, i);
2255 p = race_placeName(i);
2256 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2259 MapInfo_ClearTemps();
2262 return strcat("No records are available for the map: ", map, "\n");
2264 return strcat("Records for ", map, ":\n", s);
2267 #define LADDER_FIRSTPOINT 100
2268 #define LADDER_CNT 10
2269 // position X still gives LADDER_FIRSTPOINT/X points
2270 #define LADDER_SIZE 30
2271 // ladder shows the top X players
2272 string top_uids[LADDER_SIZE];
2273 float top_scores[LADDER_SIZE];
2276 float i, j, k, uidcnt;
2290 for (k = 0; k < MapInfo_count; ++k)
2292 if (MapInfo_Get_ByID(k))
2294 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2295 if(i == 0) // speed award
2297 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2300 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2302 else // normal record, if it exists (else break)
2304 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2307 myuid = race_readUID(MapInfo_Map_bspname, i);
2310 // string s contains:
2311 // arg 0 = # of speed recs
2312 // arg 1 = # of 1st place recs
2313 // arg 2 = # of 2nd place recs
2315 // LADDER_CNT+1 = total points
2317 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2320 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2322 for (j = 0; j <= LADDER_CNT + 1; ++j)
2324 if(j != LADDER_CNT + 1)
2325 temp_s = strcat(temp_s, "0 ");
2327 temp_s = strcat(temp_s, "0");
2331 tokenize_console(temp_s);
2334 if(i == 0) // speed award
2335 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2337 if(j == 0) // speed award
2338 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2340 s = strcat(s, " ", argv(j)); // just copy over everything else
2343 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2346 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2347 else if(j == i) // wanted rec!
2348 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2350 s = strcat(s, " ", argv(j)); // just copy over everything else
2353 // total points are (by default) calculated like this:
2354 // speedrec = floor(100 / 10) = 10 points
2355 // 1st place = floor(100 / 1) = 100 points
2356 // 2nd place = floor(100 / 2) = 50 points
2357 // 3rd place = floor(100 / 3) = 33 points
2358 // 4th place = floor(100 / 4) = 25 points
2359 // 5th place = floor(100 / 5) = 20 points
2363 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2365 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2367 db_put(TemporaryDB, strcat("ladder", myuid), s);
2374 for (i = 0; i <= uidcnt; ++i) // for each known uid
2376 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2377 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2378 tokenize_console(temp_s);
2379 thiscnt = stof(argv(LADDER_CNT+1));
2381 if(thiscnt > top_scores[LADDER_SIZE-1])
2382 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2384 if(thiscnt > top_scores[j])
2386 for (k = LADDER_SIZE-1; k >= j; --k)
2388 top_uids[k] = top_uids[k-1];
2389 top_scores[k] = top_scores[k-1];
2391 top_uids[j] = thisuid;
2392 top_scores[j] = thiscnt;
2398 s = "^3-----------------------\n\n";
2400 s = strcat(s, "Pos ^3|");
2401 s = strcat(s, " ^7Total ^3|");
2402 for (i = 1; i <= LADDER_CNT; ++i)
2404 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2406 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2408 s = strcat(s, "\n^3----+--------");
2409 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2411 s = strcat(s, "+-----");
2414 for (i = 1; i <= LADDER_CNT - 9; ++i)
2416 s = strcat(s, "+------");
2420 s = strcat(s, "+--------------+--------------------\n");
2422 for (i = 0; i < LADDER_SIZE; ++i)
2424 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2425 tokenize_console(temp_s);
2426 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2428 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2429 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2430 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2432 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2435 for (j = 10; j <= LADDER_CNT; ++j)
2437 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2441 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2442 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2445 MapInfo_ClearTemps();
2448 return "No ladder on this server!\n";
2450 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2454 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2457 vector start, org, delta, end, enddown, mstart;
2460 m = e.dphitcontentsmask;
2461 e.dphitcontentsmask = goodcontents | badcontents;
2464 delta = world.maxs - world.mins;
2466 for (i = 0; i < attempts; ++i)
2468 start_x = org_x + random() * delta_x;
2469 start_y = org_y + random() * delta_y;
2470 start_z = org_z + random() * delta_z;
2472 // rule 1: start inside world bounds, and outside
2473 // solid, and don't start from somewhere where you can
2474 // fall down to evil
2475 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2476 if (trace_fraction >= 1)
2478 if (trace_startsolid)
2480 if (trace_dphitcontents & badcontents)
2482 if (trace_dphitq3surfaceflags & badsurfaceflags)
2485 // rule 2: if we are too high, lower the point
2486 if (trace_fraction * delta_z > maxaboveground)
2487 start = trace_endpos + '0 0 1' * maxaboveground;
2488 enddown = trace_endpos;
2490 // rule 3: make sure we aren't outside the map. This only works
2491 // for somewhat well formed maps. A good rule of thumb is that
2492 // the map should have a convex outside hull.
2493 // these can be traceLINES as we already verified the starting box
2494 mstart = start + 0.5 * (e.mins + e.maxs);
2495 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2496 if (trace_fraction >= 1)
2498 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2499 if (trace_fraction >= 1)
2501 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2502 if (trace_fraction >= 1)
2504 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2505 if (trace_fraction >= 1)
2507 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2508 if (trace_fraction >= 1)
2511 // rule 4: we must "see" some spawnpoint
2512 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2513 if(checkpvs(mstart, sp))
2517 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2518 if(checkpvs(mstart, sp))
2524 // find a random vector to "look at"
2525 end_x = org_x + random() * delta_x;
2526 end_y = org_y + random() * delta_y;
2527 end_z = org_z + random() * delta_z;
2528 end = start + normalize(end - start) * vlen(delta);
2530 // rule 4: start TO end must not be too short
2531 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2532 if (trace_startsolid)
2534 if (trace_fraction < minviewdistance / vlen(delta))
2537 // rule 5: don't want to look at sky
2538 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2541 // rule 6: we must not end up in trigger_hurt
2542 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2548 e.dphitcontentsmask = m;
2552 setorigin(e, start);
2553 e.angles = vectoangles(end - start);
2554 dprint("Needed ", ftos(i + 1), " attempts\n");
2561 float zcurveparticles_effectno;
2562 vector zcurveparticles_start;
2563 float zcurveparticles_spd;
2565 void endzcurveparticles()
2567 if(zcurveparticles_effectno)
2570 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2572 zcurveparticles_effectno = 0;
2575 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2577 spd = bound(0, floor(spd / 16), 32767);
2578 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2580 endzcurveparticles();
2581 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2582 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2583 WriteShort(MSG_BROADCAST, effectno);
2584 WriteCoord(MSG_BROADCAST, start_x);
2585 WriteCoord(MSG_BROADCAST, start_y);
2586 WriteCoord(MSG_BROADCAST, start_z);
2587 zcurveparticles_effectno = effectno;
2588 zcurveparticles_start = start;
2591 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2592 WriteCoord(MSG_BROADCAST, end_x);
2593 WriteCoord(MSG_BROADCAST, end_y);
2594 WriteCoord(MSG_BROADCAST, end_z);
2595 WriteCoord(MSG_BROADCAST, end_dz);
2596 zcurveparticles_spd = spd;
2599 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2602 vector vecxy, velxy;
2604 vecxy = end - start;
2609 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2611 endzcurveparticles();
2612 trailparticles(world, effectno, start, end);
2616 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2617 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2620 void write_recordmarker(entity pl, float tstart, float dt)
2622 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2624 // also write a marker into demo files for demotc-race-record-extractor to find
2627 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2628 " ", ftos(tstart), " ", ftos(dt), "\n"));
2631 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2644 if(allowcenter) // 2: allow center handedness
2657 if(allowcenter) // 2: allow center handedness
2673 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2678 if (autocvar_g_shootfromeye)
2691 else if (autocvar_g_shootfromcenter)
2696 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2706 else if (autocvar_g_shootfromclient)
2708 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2713 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2715 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2719 void attach_sameorigin(entity e, entity to, string tag)
2721 vector org, t_forward, t_left, t_up, e_forward, e_up;
2728 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2729 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2730 t_forward = v_forward * tagscale;
2731 t_left = v_right * -tagscale;
2732 t_up = v_up * tagscale;
2734 e.origin_x = org * t_forward;
2735 e.origin_y = org * t_left;
2736 e.origin_z = org * t_up;
2738 // current forward and up directions
2739 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2740 e.angles = AnglesTransform_FromVAngles(e.angles);
2742 e.angles = AnglesTransform_FromAngles(e.angles);
2743 fixedmakevectors(e.angles);
2745 // untransform forward, up!
2746 e_forward_x = v_forward * t_forward;
2747 e_forward_y = v_forward * t_left;
2748 e_forward_z = v_forward * t_up;
2749 e_up_x = v_up * t_forward;
2750 e_up_y = v_up * t_left;
2751 e_up_z = v_up * t_up;
2753 e.angles = fixedvectoangles2(e_forward, e_up);
2754 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2755 e.angles = AnglesTransform_ToVAngles(e.angles);
2757 e.angles = AnglesTransform_ToAngles(e.angles);
2759 setattachment(e, to, tag);
2760 setorigin(e, e.origin);
2763 void detach_sameorigin(entity e)
2766 org = gettaginfo(e, 0);
2767 e.angles = fixedvectoangles2(v_forward, v_up);
2768 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2769 e.angles = AnglesTransform_ToVAngles(e.angles);
2771 e.angles = AnglesTransform_ToAngles(e.angles);
2773 setattachment(e, world, "");
2774 setorigin(e, e.origin);
2777 void follow_sameorigin(entity e, entity to)
2779 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2780 e.aiment = to; // make the hole follow bmodel
2781 e.punchangle = to.angles; // the original angles of bmodel
2782 e.view_ofs = e.origin - to.origin; // relative origin
2783 e.v_angle = e.angles - to.angles; // relative angles
2786 void unfollow_sameorigin(entity e)
2788 e.movetype = MOVETYPE_NONE;
2791 entity gettaginfo_relative_ent;
2792 vector gettaginfo_relative(entity e, float tag)
2794 if (!gettaginfo_relative_ent)
2796 gettaginfo_relative_ent = spawn();
2797 gettaginfo_relative_ent.effects = EF_NODRAW;
2799 gettaginfo_relative_ent.model = e.model;
2800 gettaginfo_relative_ent.modelindex = e.modelindex;
2801 gettaginfo_relative_ent.frame = e.frame;
2802 return gettaginfo(gettaginfo_relative_ent, tag);
2805 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2809 if (pl.soundentity.cnt & p)
2811 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2812 pl.soundentity.cnt |= p;
2815 void SoundEntity_StopSound(entity pl, float chan)
2819 if (pl.soundentity.cnt & p)
2821 stopsoundto(MSG_ALL, pl.soundentity, chan);
2822 pl.soundentity.cnt &~= p;
2826 void SoundEntity_Attach(entity pl)
2828 pl.soundentity = spawn();
2829 pl.soundentity.classname = "soundentity";
2830 pl.soundentity.owner = pl;
2831 setattachment(pl.soundentity, pl, "");
2832 setmodel(pl.soundentity, "null");
2835 void SoundEntity_Detach(entity pl)
2838 for (i = 0; i <= 7; ++i)
2839 SoundEntity_StopSound(pl, i);
2843 float ParseCommandPlayerSlotTarget_firsttoken;
2844 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2852 ParseCommandPlayerSlotTarget_firsttoken = -1;
2856 if (substring(argv(idx), 0, 1) == "#")
2858 s = substring(argv(idx), 1, -1);
2860 if (s == "") if (tokens > idx)
2865 ParseCommandPlayerSlotTarget_firsttoken = idx;
2867 if (s == ftos(n) && n > 0 && n <= maxclients)
2870 if (e.flags & FL_CLIENT)
2876 // it must be a nick name
2879 ParseCommandPlayerSlotTarget_firsttoken = idx;
2882 FOR_EACH_CLIENT(head)
2883 if (head.netname == s)
2891 s = strdecolorize(s);
2893 FOR_EACH_CLIENT(head)
2894 if (strdecolorize(head.netname) == s)
2909 float modeleffect_SendEntity(entity to, float sf)
2912 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2915 if(self.velocity != '0 0 0')
2917 if(self.angles != '0 0 0')
2919 if(self.avelocity != '0 0 0')
2922 WriteByte(MSG_ENTITY, f);
2923 WriteShort(MSG_ENTITY, self.modelindex);
2924 WriteByte(MSG_ENTITY, self.skin);
2925 WriteByte(MSG_ENTITY, self.frame);
2926 WriteCoord(MSG_ENTITY, self.origin_x);
2927 WriteCoord(MSG_ENTITY, self.origin_y);
2928 WriteCoord(MSG_ENTITY, self.origin_z);
2931 WriteCoord(MSG_ENTITY, self.velocity_x);
2932 WriteCoord(MSG_ENTITY, self.velocity_y);
2933 WriteCoord(MSG_ENTITY, self.velocity_z);
2937 WriteCoord(MSG_ENTITY, self.angles_x);
2938 WriteCoord(MSG_ENTITY, self.angles_y);
2939 WriteCoord(MSG_ENTITY, self.angles_z);
2943 WriteCoord(MSG_ENTITY, self.avelocity_x);
2944 WriteCoord(MSG_ENTITY, self.avelocity_y);
2945 WriteCoord(MSG_ENTITY, self.avelocity_z);
2947 WriteShort(MSG_ENTITY, self.scale * 256.0);
2948 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2949 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2950 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2951 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2956 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)
2961 e.classname = "modeleffect";
2969 e.teleport_time = t1;
2973 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2977 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2980 sz = max(e.scale, e.scale2);
2981 setsize(e, e.mins * sz, e.maxs * sz);
2982 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2985 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2987 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2990 float randombit(float bits)
2992 if not(bits & (bits-1)) // this ONLY holds for powers of two!
3001 for(f = 1; f <= bits; f *= 2)
3010 r = (r - 1) / (n - 1);
3017 float randombits(float bits, float k, float error_return)
3021 while(k > 0 && bits != r)
3023 r += randombit(bits - r);
3032 void randombit_test(float bits, float iter)
3036 print(ftos(randombit(bits)), "\n");
3041 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3043 if(halflifedist > 0)
3044 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3045 else if(halflifedist < 0)
3046 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3055 #define cvar_string_normal builtin_cvar_string
3056 #define cvar_normal builtin_cvar
3058 string cvar_string_normal(string n)
3060 if not(cvar_type(n) & 1)
3061 backtrace(strcat("Attempt to access undefined cvar: ", n));
3062 return builtin_cvar_string(n);
3065 float cvar_normal(string n)
3067 return stof(cvar_string_normal(n));
3070 #define cvar_set_normal builtin_cvar_set
3078 oself.think = SUB_Remove;
3079 oself.nextthink = time;
3085 Execute func() after time + fdelay.
3086 self when func is executed = self when defer is called
3088 void defer(float fdelay, void() func)
3095 e.think = defer_think;
3096 e.nextthink = time + fdelay;
3099 .string aiment_classname;
3100 .float aiment_deadflag;
3101 void SetMovetypeFollow(entity ent, entity e)
3103 // FIXME this may not be warpzone aware
3104 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3105 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.
3106 ent.aiment = e; // make the hole follow bmodel
3107 ent.punchangle = e.angles; // the original angles of bmodel
3108 ent.view_ofs = ent.origin - e.origin; // relative origin
3109 ent.v_angle = ent.angles - e.angles; // relative angles
3110 ent.aiment_classname = strzone(e.classname);
3111 ent.aiment_deadflag = e.deadflag;
3113 void UnsetMovetypeFollow(entity ent)
3115 ent.movetype = MOVETYPE_FLY;
3116 PROJECTILE_MAKETRIGGER(ent);
3119 float LostMovetypeFollow(entity ent)
3122 if(ent.movetype != MOVETYPE_FOLLOW)
3128 if(ent.aiment.classname != ent.aiment_classname)
3130 if(ent.aiment.deadflag != ent.aiment_deadflag)
3136 float isPushable(entity e)
3143 case "droppedweapon":
3144 case "keepawayball":
3145 case "nexball_basketball":
3146 case "nexball_football":
3148 case "bullet": // antilagged bullets can't hit this either
3151 if (e.projectiledeathtype)