1 var void remove(entity e);
2 void objerror(string s);
4 .vector dropped_origin;
6 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
7 void crosshair_trace(entity pl)
9 traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
11 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
12 void WarpZone_crosshair_trace(entity pl)
14 WarpZone_traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
17 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
18 void() spawnpoint_use;
19 string race_readName(float pos);
20 string race_placeName(float pos);
22 string ColoredTeamName(float t);
24 string admin_name(void)
26 if(cvar_string("sv_adminnick") != "")
27 return cvar_string("sv_adminnick");
29 return "SERVER ADMIN";
32 float DistributeEvenly_amount;
33 float DistributeEvenly_totalweight;
34 void DistributeEvenly_Init(float amount, float totalweight)
36 if (DistributeEvenly_amount)
38 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
39 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
42 DistributeEvenly_amount = 0;
44 DistributeEvenly_amount = amount;
45 DistributeEvenly_totalweight = totalweight;
47 float DistributeEvenly_Get(float weight)
52 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
53 DistributeEvenly_totalweight -= weight;
54 DistributeEvenly_amount -= f;
58 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
61 string STR_PLAYER = "player";
62 string STR_SPECTATOR = "spectator";
63 string STR_OBSERVER = "observer";
66 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
67 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
68 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
69 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
71 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
72 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
73 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
74 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
75 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
78 // copies a string to a tempstring (so one can strunzone it)
79 string strcat1(string s) = #115; // FRIK_FILE
84 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
86 local float nPlayerHealth = rint(enPlayer.health);
87 local float nPlayerArmor = rint(enPlayer.armorvalue);
88 local float nPlayerHandicap = enPlayer.cvar_cl_handicap;
89 local float nPlayerPing = rint(enPlayer.ping);
90 local string strPlayerPingColor;
91 local string strMessage;
92 if(nPlayerPing >= 150)
93 strPlayerPingColor = "^1";
95 strPlayerPingColor = "^2";
97 if((cvar("sv_fragmessage_information_stats")) && (enPlayer.health >= 1))
98 strMessage = strcat(strMessage, "\n^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
100 if(cvar("sv_fragmessage_information_ping")) {
101 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
102 strMessage = strcat(strMessage, " ^7(^2Bot");
104 strMessage = strcat(strMessage, " ^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
105 if(cvar("sv_fragmessage_information_handicap"))
106 if(cvar("sv_fragmessage_information_handicap") == 2)
107 if(nPlayerHandicap <= 1)
108 strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
110 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
111 else if not(nPlayerHandicap <= 1)
112 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
114 strMessage = strcat(strMessage, "^7)");
115 } else if(cvar("sv_fragmessage_information_handicap")) {
116 if(cvar("sv_fragmessage_information_handicap") == 2)
117 if(nPlayerHandicap <= 1)
118 strMessage = strcat(strMessage, "\n^7(Handicap ^2Off^7)");
120 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
121 else if(nPlayerHandicap > 1)
122 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
126 void bcenterprint(string s)
128 // TODO replace by MSG_ALL (would show it to spectators too, though)?
130 FOR_EACH_PLAYER(head)
131 if (clienttype(head) == CLIENTTYPE_REAL)
132 centerprint(head, s);
135 void GameLogEcho(string s)
140 if (cvar("sv_eventlog_files"))
145 matches = cvar("sv_eventlog_files_counter") + 1;
146 cvar_set("sv_eventlog_files_counter", ftos(matches));
149 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
150 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
151 logfile = fopen(fn, FILE_APPEND);
152 fputs(logfile, ":logversion:3\n");
156 if (cvar("sv_eventlog_files_timestamps"))
157 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
159 fputs(logfile, strcat(s, "\n"));
162 if (cvar("sv_eventlog_console"))
171 // will be opened later
176 if (logfile_open && logfile >= 0)
186 vector PL_CROUCH_VIEW_OFS;
187 vector PL_CROUCH_MIN;
188 vector PL_CROUCH_MAX;
190 float spawnpoint_nag;
191 void relocate_spawnpoint()
193 PL_VIEW_OFS = stov(cvar_string("sv_player_viewoffset"));
194 PL_MIN = stov(cvar_string("sv_player_mins"));
195 PL_MAX = stov(cvar_string("sv_player_maxs"));
196 PL_CROUCH_VIEW_OFS = stov(cvar_string("sv_player_crouch_viewoffset"));
197 PL_CROUCH_MIN = stov(cvar_string("sv_player_crouch_mins"));
198 PL_CROUCH_MAX = stov(cvar_string("sv_player_crouch_maxs"));
200 // nudge off the floor
201 setorigin(self, self.origin + '0 0 1');
203 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
204 if (trace_startsolid)
210 if (!move_out_of_solid(self))
211 objerror("could not get out of solid at all!");
212 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
213 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
214 print(" ", ftos(self.origin_y - o_y));
215 print(" ", ftos(self.origin_z - o_z), "'\n");
216 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
219 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
225 self.mins = self.maxs = '0 0 0';
226 objerror("player spawn point in solid, mapper sucks!\n");
231 self.use = spawnpoint_use;
232 self.team_saved = self.team;
236 if (have_team_spawns != 0)
238 have_team_spawns = 1;
240 if (cvar("r_showbboxes"))
242 // show where spawnpoints point at too
243 makevectors(self.angles);
246 e.classname = "info_player_foo";
247 setorigin(e, self.origin + v_forward * 24);
248 setsize(e, '-8 -8 -8', '8 8 8');
249 e.solid = SOLID_TRIGGER;
253 #define strstr strstrofs
255 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
256 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
257 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
258 // BE CONSTANT OR strzoneD!
259 float strstr(string haystack, string needle, float offset)
263 len = strlen(needle);
264 endpos = strlen(haystack) - len;
265 while(offset <= endpos)
267 found = substring(haystack, offset, len);
276 float NUM_NEAREST_ENTITIES = 4;
277 entity nearest_entity[NUM_NEAREST_ENTITIES];
278 float nearest_length[NUM_NEAREST_ENTITIES];
279 entity findnearest(vector point, .string field, string value, vector axismod)
290 localhead = find(world, field, value);
293 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
294 dist = localhead.oldorigin;
296 dist = localhead.origin;
298 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
301 for (i = 0; i < num_nearest; ++i)
303 if (len < nearest_length[i])
307 // now i tells us where to insert at
308 // INSERTION SORT! YOU'VE SEEN IT! RUN!
309 if (i < NUM_NEAREST_ENTITIES)
311 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
313 nearest_length[j + 1] = nearest_length[j];
314 nearest_entity[j + 1] = nearest_entity[j];
316 nearest_length[i] = len;
317 nearest_entity[i] = localhead;
318 if (num_nearest < NUM_NEAREST_ENTITIES)
319 num_nearest = num_nearest + 1;
322 localhead = find(localhead, field, value);
325 // now use the first one from our list that we can see
326 for (i = 0; i < num_nearest; ++i)
328 traceline(point, nearest_entity[i].origin, TRUE, world);
329 if (trace_fraction == 1)
333 dprint("Nearest point (");
334 dprint(nearest_entity[0].netname);
335 dprint(") is not visible, using a visible one.\n");
337 return nearest_entity[i];
341 if (num_nearest == 0)
344 dprint("Not seeing any location point, using nearest as fallback.\n");
346 dprint("Candidates were: ");
347 for(j = 0; j < num_nearest; ++j)
351 dprint(nearest_entity[j].netname);
356 return nearest_entity[0];
359 void spawnfunc_target_location()
361 self.classname = "target_location";
362 // location name in netname
363 // eventually support: count, teamgame selectors, line of sight?
366 void spawnfunc_info_location()
368 self.classname = "target_location";
369 self.message = self.netname;
372 string NearestLocation(vector p)
377 loc = findnearest(p, classname, "target_location", '1 1 1');
384 loc = findnearest(p, target, "###item###", '1 1 4');
391 string formatmessage(string msg)
402 WarpZone_crosshair_trace(self);
403 cursor = trace_endpos;
404 cursor_ent = trace_ent;
408 break; // too many replacements
411 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
412 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
425 replacement = substring(msg, p, 2);
426 escape = substring(msg, p + 1, 1);
430 else if (escape == "\\")
432 else if (escape == "n")
434 else if (escape == "a")
435 replacement = ftos(floor(self.armorvalue));
436 else if (escape == "h")
437 replacement = ftos(floor(self.health));
438 else if (escape == "l")
439 replacement = NearestLocation(self.origin);
440 else if (escape == "y")
441 replacement = NearestLocation(cursor);
442 else if (escape == "d")
443 replacement = NearestLocation(self.death_origin);
444 else if (escape == "w") {
448 wep = self.switchweapon;
451 replacement = W_Name(wep);
452 } else if (escape == "W") {
453 if (self.items & IT_SHELLS) replacement = "shells";
454 else if (self.items & IT_NAILS) replacement = "bullets";
455 else if (self.items & IT_ROCKETS) replacement = "rockets";
456 else if (self.items & IT_CELLS) replacement = "cells";
457 else replacement = "batteries"; // ;)
458 } else if (escape == "x") {
459 replacement = cursor_ent.netname;
460 if (!replacement || !cursor_ent)
461 replacement = "nothing";
462 } else if (escape == "p") {
463 if (self.last_selected_player)
464 replacement = self.last_selected_player.netname;
466 replacement = "(nobody)";
467 } else if (escape == "s")
468 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
469 else if (escape == "S")
470 replacement = ftos(vlen(self.velocity));
471 else if (escape == "v") {
475 if(self.classname == "spectator")
480 weapon_number = stats.weapon;
483 weapon_number = stats.switchweapon;
486 weapon_number = stats.cnt;
488 if(stats.cvar_cl_accuracy_data_share && stats.stats_fired[weapon_number - 1])
489 replacement = ftos(bound(0, floor(100 * stats.stats_hit[weapon_number - 1] / stats.stats_fired[weapon_number - 1]), 100));
491 replacement = "~"; // or something to indicate NULL, not available
494 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
495 p = p + strlen(replacement);
500 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
501 return (value == 0) ? FALSE : TRUE;
510 >0: receives a cvar from name=argv(f) value=argv(f+1)
512 void GetCvars_handleString(string thisname, float f, .string field, string name)
517 strunzone(self.field);
518 self.field = string_null;
522 if (thisname == name)
525 strunzone(self.field);
526 self.field = strzone(argv(f + 1));
530 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
532 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
534 GetCvars_handleString(thisname, f, field, name);
535 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
536 if (thisname == name)
539 s = func(strcat1(self.field));
542 strunzone(self.field);
543 self.field = strzone(s);
547 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
554 if (thisname == name)
555 self.field = stof(argv(f + 1));
558 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
560 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
567 if (thisname == name)
571 self.field = stof(argv(f + 1));
580 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
583 string W_FixWeaponOrder_ForceComplete(string s);
584 string W_FixWeaponOrder_AllowIncomplete(string s);
585 float w_getbestweapon(entity e);
586 void GetCvars(float f)
591 s = strcat1(argv(f));
595 MUTATOR_CALLHOOK(GetCvars);
596 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
597 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
598 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
599 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
600 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
601 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
602 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
603 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
604 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
605 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
606 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
607 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
608 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
609 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
610 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
611 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
612 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
613 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
614 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
615 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
616 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
617 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
618 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
619 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
620 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
622 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
623 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
625 #ifdef ALLOW_FORCEMODELS
626 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
627 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
629 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
631 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
634 if (s == "cl_weaponpriority")
635 self.switchweapon = w_getbestweapon(self);
639 float fexists(string f)
642 fh = fopen(f, FILE_READ);
649 void backtrace(string msg)
652 dev = cvar("developer");
653 war = cvar("prvm_backtraceforwarnings");
654 cvar_set("developer", "1");
655 cvar_set("prvm_backtraceforwarnings", "1");
657 print("--- CUT HERE ---\nWARNING: ");
660 remove(world); // isn't there any better way to cause a backtrace?
661 print("\n--- CUT UNTIL HERE ---\n");
662 cvar_set("developer", ftos(dev));
663 cvar_set("prvm_backtraceforwarnings", ftos(war));
666 string Team_ColorCode(float teamid)
668 if (teamid == COLOR_TEAM1)
670 else if (teamid == COLOR_TEAM2)
672 else if (teamid == COLOR_TEAM3)
674 else if (teamid == COLOR_TEAM4)
680 string Team_ColorName(float t)
682 // fixme: Search for team entities and get their .netname's!
683 if (t == COLOR_TEAM1)
685 if (t == COLOR_TEAM2)
687 if (t == COLOR_TEAM3)
689 if (t == COLOR_TEAM4)
694 string Team_ColorNameLowerCase(float t)
696 // fixme: Search for team entities and get their .netname's!
697 if (t == COLOR_TEAM1)
699 if (t == COLOR_TEAM2)
701 if (t == COLOR_TEAM3)
703 if (t == COLOR_TEAM4)
708 float ColourToNumber(string team_colour)
710 if (team_colour == "red")
713 if (team_colour == "blue")
716 if (team_colour == "yellow")
719 if (team_colour == "pink")
722 if (team_colour == "auto")
728 float NumberToTeamNumber(float number)
745 #define CENTERPRIO_POINT 1
746 #define CENTERPRIO_SPAM 2
747 #define CENTERPRIO_VOTE 4
748 #define CENTERPRIO_NORMAL 5
749 #define CENTERPRIO_SHIELDING 7
750 #define CENTERPRIO_MAPVOTE 9
751 #define CENTERPRIO_IDLEKICK 50
752 #define CENTERPRIO_ADMIN 99
753 .float centerprint_priority;
754 .float centerprint_expires;
755 void centerprint_atprio(entity e, float prio, string s)
757 if (intermission_running)
758 if (prio < CENTERPRIO_MAPVOTE)
760 if (time > e.centerprint_expires)
761 e.centerprint_priority = 0;
762 if (prio >= e.centerprint_priority)
764 e.centerprint_priority = prio;
765 if (timeoutStatus == 2)
766 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
768 e.centerprint_expires = time + e.cvar_scr_centertime;
769 centerprint_builtin(e, s);
772 void centerprint_expire(entity e, float prio)
774 if (prio == e.centerprint_priority)
776 e.centerprint_priority = 0;
777 centerprint_builtin(e, "");
780 void centerprint(entity e, string s)
782 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
785 // decolorizes and team colors the player name when needed
786 string playername(entity p)
789 if (teams_matter && !intermission_running && p.classname == "player")
791 t = Team_ColorCode(p.team);
792 return strcat(t, strdecolorize(p.netname));
798 vector randompos(vector m1, vector m2)
802 v_x = m2_x * random() + m1_x;
803 v_y = m2_y * random() + m1_y;
804 v_z = m2_z * random() + m1_z;
808 float g_pickup_shells;
809 float g_pickup_shells_max;
810 float g_pickup_nails;
811 float g_pickup_nails_max;
812 float g_pickup_rockets;
813 float g_pickup_rockets_max;
814 float g_pickup_cells;
815 float g_pickup_cells_max;
817 float g_pickup_fuel_jetpack;
818 float g_pickup_fuel_max;
819 float g_pickup_armorsmall;
820 float g_pickup_armorsmall_max;
821 float g_pickup_armorsmall_anyway;
822 float g_pickup_armormedium;
823 float g_pickup_armormedium_max;
824 float g_pickup_armormedium_anyway;
825 float g_pickup_armorbig;
826 float g_pickup_armorbig_max;
827 float g_pickup_armorbig_anyway;
828 float g_pickup_armorlarge;
829 float g_pickup_armorlarge_max;
830 float g_pickup_armorlarge_anyway;
831 float g_pickup_healthsmall;
832 float g_pickup_healthsmall_max;
833 float g_pickup_healthsmall_anyway;
834 float g_pickup_healthmedium;
835 float g_pickup_healthmedium_max;
836 float g_pickup_healthmedium_anyway;
837 float g_pickup_healthlarge;
838 float g_pickup_healthlarge_max;
839 float g_pickup_healthlarge_anyway;
840 float g_pickup_healthmega;
841 float g_pickup_healthmega_max;
842 float g_pickup_healthmega_anyway;
843 float g_pickup_ammo_anyway;
844 float g_pickup_weapons_anyway;
846 float g_weaponarena_random;
847 string g_weaponarena_list;
848 float g_weaponspeedfactor;
849 float g_weaponratefactor;
850 float g_weapondamagefactor;
851 float g_weaponforcefactor;
852 float g_weaponspreadfactor;
856 float start_ammo_shells;
857 float start_ammo_nails;
858 float start_ammo_rockets;
859 float start_ammo_cells;
860 float start_ammo_fuel;
862 float start_armorvalue;
863 float warmup_start_weapons;
864 float warmup_start_ammo_shells;
865 float warmup_start_ammo_nails;
866 float warmup_start_ammo_rockets;
867 float warmup_start_ammo_cells;
868 float warmup_start_ammo_fuel;
869 float warmup_start_health;
870 float warmup_start_armorvalue;
874 entity get_weaponinfo(float w);
876 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
878 var float i = weaponinfo.weapon;
883 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
885 if (t < 0) // "default" weapon selection
887 if (g_lms || g_ca || allguns)
888 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
891 else if (g_race || g_cts)
892 t = (i == WEP_LASER);
894 t = 0; // weapon is set a few lines later
896 t = (i == WEP_LASER || i == WEP_SHOTGUN);
897 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
898 t |= (i == WEP_HOOK);
901 // we cannot disable porto in Nexball, we must force it
902 if(g_nexball && i == WEP_PORTO)
908 void readplayerstartcvars()
914 // initialize starting values for players
917 start_ammo_shells = 0;
918 start_ammo_nails = 0;
919 start_ammo_rockets = 0;
920 start_ammo_cells = 0;
921 start_health = cvar("g_balance_health_start");
922 start_armorvalue = cvar("g_balance_armor_start");
925 s = cvar_string("g_weaponarena");
931 g_weaponarena_list = "All Weapons";
932 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
934 e = get_weaponinfo(j);
935 g_weaponarena |= e.weapons;
936 weapon_action(e.weapon, WR_PRECACHE);
939 else if (s == "most")
941 g_weaponarena_list = "Most Weapons";
942 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
944 e = get_weaponinfo(j);
945 if (e.spawnflags & WEP_FLAG_NORMAL)
947 g_weaponarena |= e.weapons;
948 weapon_action(e.weapon, WR_PRECACHE);
952 else if (s == "none")
954 g_weaponarena_list = "No Weapons";
955 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
959 t = tokenize_console(s);
960 g_weaponarena_list = "";
961 for (i = 0; i < t; ++i)
964 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
966 e = get_weaponinfo(j);
969 g_weaponarena |= e.weapons;
970 weapon_action(e.weapon, WR_PRECACHE);
971 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
977 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
980 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
984 g_weaponarena_random = cvar("g_weaponarena_random");
986 g_weaponarena_random = 0;
990 start_weapons = g_weaponarena;
991 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_MINE_LAYER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
992 start_ammo_rockets = 999;
993 if (g_weaponarena & WEPBIT_SHOTGUN)
994 start_ammo_shells = 999;
995 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
996 start_ammo_cells = 999;
997 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
998 start_ammo_nails = 999;
999 if (g_weaponarena & WEPBIT_HOOK)
1000 start_ammo_fuel = 999;
1001 start_items |= IT_UNLIMITED_AMMO;
1003 else if (g_minstagib)
1006 start_armorvalue = 0;
1007 start_weapons = WEPBIT_MINSTANEX;
1008 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
1009 start_ammo_cells = cvar("g_minstagib_ammo_start");
1010 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
1011 start_ammo_fuel = cvar("g_start_ammo_fuel");
1013 if (g_minstagib_invis_alpha <= 0)
1014 g_minstagib_invis_alpha = -1;
1020 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1021 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1022 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1023 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1024 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1025 start_health = cvar("g_lms_start_health");
1026 start_armorvalue = cvar("g_lms_start_armor");
1030 start_ammo_shells = cvar("g_start_ammo_shells");
1031 start_ammo_nails = cvar("g_start_ammo_nails");
1032 start_ammo_rockets = cvar("g_start_ammo_rockets");
1033 start_ammo_cells = cvar("g_start_ammo_cells");
1034 start_ammo_fuel = cvar("g_start_ammo_fuel");
1037 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1039 e = get_weaponinfo(i);
1040 if(want_weapon("g_start_weapon_", e, FALSE))
1041 start_weapons |= e.weapons;
1047 warmup_start_ammo_shells = start_ammo_shells;
1048 warmup_start_ammo_nails = start_ammo_nails;
1049 warmup_start_ammo_rockets = start_ammo_rockets;
1050 warmup_start_ammo_cells = start_ammo_cells;
1051 warmup_start_ammo_fuel = start_ammo_fuel;
1052 warmup_start_health = start_health;
1053 warmup_start_armorvalue = start_armorvalue;
1054 warmup_start_weapons = start_weapons;
1056 if (!g_weaponarena && !g_minstagib && !g_ca)
1058 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1059 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1060 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1061 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1062 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1063 warmup_start_health = cvar("g_warmup_start_health");
1064 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1065 warmup_start_weapons = 0;
1066 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1068 e = get_weaponinfo(i);
1069 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1070 warmup_start_weapons |= e.weapons;
1075 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1077 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1078 start_items |= IT_FUEL_REGEN;
1079 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1080 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1083 if(!cvar("g_use_ammunition"))
1085 start_ammo_shells = cvar("g_pickup_shells_max");
1086 start_ammo_nails = cvar("g_pickup_nails_max");
1087 start_ammo_rockets = cvar("g_pickup_rockets_max");
1088 start_ammo_cells = cvar("g_pickup_cells_max");
1089 start_ammo_fuel = cvar("g_pickup_fuel_max");
1090 start_items |= IT_UNLIMITED_AMMO;
1091 warmup_start_ammo_shells = cvar("g_pickup_shells_max");
1092 warmup_start_ammo_nails = cvar("g_pickup_nails_max");
1093 warmup_start_ammo_rockets = cvar("g_pickup_rockets_max");
1094 warmup_start_ammo_cells = cvar("g_pickup_cells_max");
1095 warmup_start_ammo_fuel = cvar("g_pickup_fuel_max");
1096 //warmup_start_items |= IT_UNLIMITED_AMMO;
1100 start_items |= IT_JETPACK;
1102 if (g_weapon_stay == 2)
1104 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1105 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1106 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1107 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1108 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1109 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1110 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1111 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1112 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1113 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1116 MUTATOR_CALLHOOK(SetStartItems);
1118 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1120 e = get_weaponinfo(i);
1121 if(e.weapons & (start_weapons | warmup_start_weapons))
1122 weapon_action(e.weapon, WR_PRECACHE);
1125 start_ammo_shells = max(0, start_ammo_shells);
1126 start_ammo_nails = max(0, start_ammo_nails);
1127 start_ammo_cells = max(0, start_ammo_cells);
1128 start_ammo_rockets = max(0, start_ammo_rockets);
1129 start_ammo_fuel = max(0, start_ammo_fuel);
1131 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1132 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1133 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1134 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1135 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1139 float g_bugrigs_planar_movement;
1140 float g_bugrigs_planar_movement_car_jumping;
1141 float g_bugrigs_reverse_spinning;
1142 float g_bugrigs_reverse_speeding;
1143 float g_bugrigs_reverse_stopping;
1144 float g_bugrigs_air_steering;
1145 float g_bugrigs_angle_smoothing;
1146 float g_bugrigs_friction_floor;
1147 float g_bugrigs_friction_brake;
1148 float g_bugrigs_friction_air;
1149 float g_bugrigs_accel;
1150 float g_bugrigs_speed_ref;
1151 float g_bugrigs_speed_pow;
1152 float g_bugrigs_steer;
1154 float g_touchexplode;
1155 float g_touchexplode_radius;
1156 float g_touchexplode_damage;
1157 float g_touchexplode_edgedamage;
1158 float g_touchexplode_force;
1165 float sv_pitch_fixyaw;
1167 float sv_accuracy_data_share;
1169 void readlevelcvars(void)
1171 // first load all the mutators
1173 MUTATOR_ADD(mutator_nix);
1174 if(cvar("g_dodging"))
1175 MUTATOR_ADD(mutator_dodging);
1176 if(cvar("g_rocket_flying") || (cvar("g_balance_rocketlauncher_detonatedelay") == 0 && stof(cvar_defstring("g_balance_rocketlauncher_detonatedelay")) != 0))
1177 MUTATOR_ADD(mutator_rocketflying);
1179 g_bugrigs = cvar("g_bugrigs");
1180 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1181 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1182 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1183 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1184 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1185 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1186 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1187 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1188 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1189 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1190 g_bugrigs_accel = cvar("g_bugrigs_accel");
1191 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1192 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1193 g_bugrigs_steer = cvar("g_bugrigs_steer");
1195 g_touchexplode = cvar("g_touchexplode");
1196 g_touchexplode_radius = cvar("g_touchexplode_radius");
1197 g_touchexplode_damage = cvar("g_touchexplode_damage");
1198 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1199 g_touchexplode_force = cvar("g_touchexplode_force");
1201 #ifdef ALLOW_FORCEMODELS
1202 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1204 sv_loddistance1 = cvar("sv_loddistance1");
1205 sv_loddistance2 = cvar("sv_loddistance2");
1207 if(sv_loddistance2 <= sv_loddistance1)
1208 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1210 sv_clones = cvar("sv_clones");
1211 sv_gentle = cvar("sv_gentle");
1212 sv_foginterval = cvar("sv_foginterval");
1213 g_cloaked = cvar("g_cloaked");
1214 g_jump_grunt = cvar("g_jump_grunt");
1215 g_footsteps = cvar("g_footsteps");
1216 g_grappling_hook = cvar("g_grappling_hook");
1217 g_jetpack = cvar("g_jetpack");
1218 g_laserguided_missile = cvar("g_laserguided_missile");
1219 g_midair = cvar("g_midair");
1220 g_minstagib = cvar("g_minstagib");
1221 g_norecoil = cvar("g_norecoil");
1222 g_vampire = cvar("g_vampire");
1223 g_bloodloss = cvar("g_bloodloss");
1224 sv_maxidle = cvar("sv_maxidle");
1225 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1226 sv_pogostick = cvar("sv_pogostick");
1227 g_ctf_reverse = cvar("g_ctf_reverse");
1228 sv_autotaunt = cvar("sv_autotaunt");
1229 sv_taunt = cvar("sv_taunt");
1231 inWarmupStage = cvar("g_warmup");
1232 g_warmup_limit = cvar("g_warmup_limit");
1233 g_warmup_allguns = cvar("g_warmup_allguns");
1234 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1236 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1237 inWarmupStage = 0; // these modes cannot work together, sorry
1239 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1240 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1241 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1242 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1243 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1244 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1245 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1246 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1247 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1248 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1249 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1250 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1252 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1253 g_weaponratefactor = cvar("g_weaponratefactor");
1254 g_weapondamagefactor = cvar("g_weapondamagefactor");
1255 g_weaponforcefactor = cvar("g_weaponforcefactor");
1256 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1258 g_pickup_shells = cvar("g_pickup_shells");
1259 g_pickup_shells_max = cvar("g_pickup_shells_max");
1260 g_pickup_nails = cvar("g_pickup_nails");
1261 g_pickup_nails_max = cvar("g_pickup_nails_max");
1262 g_pickup_rockets = cvar("g_pickup_rockets");
1263 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1264 g_pickup_cells = cvar("g_pickup_cells");
1265 g_pickup_cells_max = cvar("g_pickup_cells_max");
1266 g_pickup_fuel = cvar("g_pickup_fuel");
1267 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1268 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1269 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1270 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1271 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1272 g_pickup_armormedium = cvar("g_pickup_armormedium");
1273 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1274 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1275 g_pickup_armorbig = cvar("g_pickup_armorbig");
1276 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1277 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1278 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1279 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1280 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1281 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1282 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1283 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1284 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1285 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1286 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1287 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1288 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1289 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1290 g_pickup_healthmega = cvar("g_pickup_healthmega");
1291 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1292 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1294 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1295 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1297 g_pinata = cvar("g_pinata");
1299 g_weapon_stay = cvar("g_weapon_stay");
1301 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1304 g_ghost_items = cvar("g_ghost_items");
1306 if(g_ghost_items >= 1)
1307 g_ghost_items = 0.25; // default alpha value
1309 if not(inWarmupStage && !g_ca)
1310 game_starttime = cvar("g_start_delay");
1312 sv_pitch_min = cvar("sv_pitch_min");
1313 sv_pitch_max = cvar("sv_pitch_max");
1314 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1316 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1318 readplayerstartcvars();
1322 // TODO sound pack system
1325 string precache_sound_builtin (string s) = #19;
1326 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1327 string precache_sound(string s)
1329 return precache_sound_builtin(strcat(soundpack, s));
1331 void play2(entity e, string filename)
1333 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1335 void sound(entity e, float chan, string samp, float vol, float atten)
1337 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1342 string precache_sound (string s) = #19;
1343 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1344 float precache_sound_index (string s) = #19;
1346 #define SND_VOLUME 1
1347 #define SND_ATTENUATION 2
1348 #define SND_LARGEENTITY 8
1349 #define SND_LARGESOUND 16
1351 float sound_allowed(float dest, entity e)
1353 // sounds from world may always pass
1356 if (e.classname == "body")
1358 if (e.owner && e.owner != e)
1363 // sounds to self may always pass
1364 if (dest == MSG_ONE)
1365 if (e == msg_entity)
1367 // sounds by players can be removed
1368 if (cvar("bot_sound_monopoly"))
1369 if (clienttype(e) == CLIENTTYPE_REAL)
1371 // anything else may pass
1375 void sound(entity e, float chan, string samp, float vol, float atten)
1377 if (!sound_allowed(MSG_BROADCAST, e))
1379 sound_builtin(e, chan, samp, vol, atten);
1381 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1385 if (!sound_allowed(dest, e))
1388 entno = num_for_edict(e);
1389 idx = precache_sound_index(samp);
1394 atten = floor(atten * 64);
1395 vol = floor(vol * 255);
1398 sflags |= SND_VOLUME;
1400 sflags |= SND_ATTENUATION;
1402 sflags |= SND_LARGEENTITY;
1404 sflags |= SND_LARGESOUND;
1406 WriteByte(dest, SVC_SOUND);
1407 WriteByte(dest, sflags);
1408 if (sflags & SND_VOLUME)
1409 WriteByte(dest, vol);
1410 if (sflags & SND_ATTENUATION)
1411 WriteByte(dest, atten);
1412 if (sflags & SND_LARGEENTITY)
1414 WriteShort(dest, entno);
1415 WriteByte(dest, chan);
1419 WriteShort(dest, entno * 8 + chan);
1421 if (sflags & SND_LARGESOUND)
1422 WriteShort(dest, idx);
1424 WriteByte(dest, idx);
1426 WriteCoord(dest, o_x);
1427 WriteCoord(dest, o_y);
1428 WriteCoord(dest, o_z);
1430 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1434 if (!sound_allowed(dest, e))
1437 o = e.origin + 0.5 * (e.mins + e.maxs);
1438 soundtoat(dest, e, o, chan, samp, vol, atten);
1440 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1442 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1444 void stopsoundto(float dest, entity e, float chan)
1448 if (!sound_allowed(dest, e))
1451 entno = num_for_edict(e);
1456 idx = precache_sound_index("misc/null.wav");
1457 sflags = SND_LARGEENTITY;
1459 sflags |= SND_LARGESOUND;
1460 WriteByte(dest, SVC_SOUND);
1461 WriteByte(dest, sflags);
1462 WriteShort(dest, entno);
1463 WriteByte(dest, chan);
1464 if (sflags & SND_LARGESOUND)
1465 WriteShort(dest, idx);
1467 WriteByte(dest, idx);
1468 WriteCoord(dest, e.origin_x);
1469 WriteCoord(dest, e.origin_y);
1470 WriteCoord(dest, e.origin_z);
1474 WriteByte(dest, SVC_STOPSOUND);
1475 WriteShort(dest, entno * 8 + chan);
1478 void stopsound(entity e, float chan)
1480 if (!sound_allowed(MSG_BROADCAST, e))
1483 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1484 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1487 void play2(entity e, string filename)
1489 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1491 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1494 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1496 float spamsound(entity e, float chan, string samp, float vol, float atten)
1498 if (!sound_allowed(MSG_BROADCAST, e))
1501 if (time > e.spamtime)
1504 sound(e, chan, samp, vol, atten);
1510 void play2team(float t, string filename)
1514 if (cvar("bot_sound_monopoly"))
1517 FOR_EACH_REALPLAYER(head)
1520 play2(head, filename);
1524 void play2all(string samp)
1526 if (cvar("bot_sound_monopoly"))
1529 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1532 void PrecachePlayerSounds(string f);
1533 void precache_playermodel(string m)
1535 float globhandle, i, n;
1538 if(substring(m, -9,5) == "_lod1")
1540 if(substring(m, -9,5) == "_lod2")
1545 precache_model(strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1)));
1546 precache_model(strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1)));
1549 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1552 n = search_getsize(globhandle);
1553 for (i = 0; i < n; ++i)
1555 //print(search_getfilename(globhandle, i), "\n");
1556 f = search_getfilename(globhandle, i);
1557 PrecachePlayerSounds(f);
1559 search_end(globhandle);
1561 void precache_all_playermodels(string pattern)
1563 float globhandle, i, n;
1566 globhandle = search_begin(pattern, TRUE, FALSE);
1569 n = search_getsize(globhandle);
1570 for (i = 0; i < n; ++i)
1572 //print(search_getfilename(globhandle, i), "\n");
1573 f = search_getfilename(globhandle, i);
1574 precache_playermodel(f);
1576 search_end(globhandle);
1581 // gamemode related things
1582 precache_model ("models/misc/chatbubble.spr");
1583 precache_model ("models/misc/teambubble.spr");
1586 precache_model ("models/runematch/curse.mdl");
1587 precache_model ("models/runematch/rune.mdl");
1590 #ifdef TTURRETS_ENABLED
1591 if (cvar("g_turrets"))
1595 // Precache all player models if desired
1596 if (cvar("sv_precacheplayermodels"))
1598 PrecachePlayerSounds("sound/player/default.sounds");
1599 precache_all_playermodels("models/player/*.zym");
1600 precache_all_playermodels("models/player/*.dpm");
1601 precache_all_playermodels("models/player/*.md3");
1602 precache_all_playermodels("models/player/*.psk");
1603 precache_all_playermodels("models/player/*.iqm");
1606 if (cvar("sv_defaultcharacter"))
1609 s = cvar_string("sv_defaultplayermodel_red");
1611 precache_playermodel(s);
1612 s = cvar_string("sv_defaultplayermodel_blue");
1614 precache_playermodel(s);
1615 s = cvar_string("sv_defaultplayermodel_yellow");
1617 precache_playermodel(s);
1618 s = cvar_string("sv_defaultplayermodel_pink");
1620 precache_playermodel(s);
1621 s = cvar_string("sv_defaultplayermodel");
1623 precache_playermodel(s);
1628 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1629 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1632 // gore and miscellaneous sounds
1633 //precache_sound ("misc/h2ohit.wav");
1634 precache_model ("models/hook.md3");
1635 precache_sound ("misc/armorimpact.wav");
1636 precache_sound ("misc/bodyimpact1.wav");
1637 precache_sound ("misc/bodyimpact2.wav");
1638 precache_sound ("misc/gib.wav");
1639 precache_sound ("misc/gib_splat01.wav");
1640 precache_sound ("misc/gib_splat02.wav");
1641 precache_sound ("misc/gib_splat03.wav");
1642 precache_sound ("misc/gib_splat04.wav");
1643 precache_sound ("misc/hit.wav");
1644 precache_sound ("misc/typehit.wav");
1645 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1646 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1647 precache_sound ("misc/null.wav");
1648 precache_sound ("misc/spawn.wav");
1649 precache_sound ("misc/talk.wav");
1650 precache_sound ("misc/teleport.wav");
1651 precache_sound ("misc/poweroff.wav");
1652 precache_sound ("player/lava.wav");
1653 precache_sound ("player/slime.wav");
1656 precache_sound ("misc/jetpack_fly.wav");
1658 precache_model ("models/sprites/0.spr32");
1659 precache_model ("models/sprites/1.spr32");
1660 precache_model ("models/sprites/2.spr32");
1661 precache_model ("models/sprites/3.spr32");
1662 precache_model ("models/sprites/4.spr32");
1663 precache_model ("models/sprites/5.spr32");
1664 precache_model ("models/sprites/6.spr32");
1665 precache_model ("models/sprites/7.spr32");
1666 precache_model ("models/sprites/8.spr32");
1667 precache_model ("models/sprites/9.spr32");
1668 precache_model ("models/sprites/10.spr32");
1670 // common weapon precaches
1671 precache_sound ("weapons/weapon_switch.wav");
1672 precache_sound ("weapons/weaponpickup.wav");
1673 precache_sound ("weapons/unavailable.wav");
1674 precache_sound ("weapons/dryfire.wav");
1675 if (g_grappling_hook)
1677 precache_sound ("weapons/hook_fire.wav"); // hook
1678 precache_sound ("weapons/hook_impact.wav"); // hook
1681 if(cvar("sv_precacheweapons"))
1683 //precache weapon models/sounds
1686 while (wep <= WEP_LAST)
1688 weapon_action(wep, WR_PRECACHE);
1693 precache_model("models/elaser.mdl");
1694 precache_model("models/laser.mdl");
1695 precache_model("models/ebomb.mdl");
1698 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1700 if (!self.noise && self.music) // quake 3 uses the music field
1701 self.noise = self.music;
1703 // plays music for the level if there is any
1706 precache_sound (self.noise);
1707 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1712 // sorry, but using \ in macros breaks line numbers
1713 #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
1714 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1715 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1717 // WARNING: this kills the trace globals
1718 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1719 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1721 #define INITPRIO_FIRST 0
1722 #define INITPRIO_GAMETYPE 0
1723 #define INITPRIO_GAMETYPE_FALLBACK 1
1724 #define INITPRIO_CVARS 5
1725 #define INITPRIO_FINDTARGET 10
1726 #define INITPRIO_DROPTOFLOOR 20
1727 #define INITPRIO_SETLOCATION 90
1728 #define INITPRIO_LINKDOORS 91
1729 #define INITPRIO_LAST 99
1731 .void(void) initialize_entity;
1732 .float initialize_entity_order;
1733 .entity initialize_entity_next;
1734 entity initialize_entity_first;
1736 void make_safe_for_remove(entity e)
1738 if (e.initialize_entity)
1741 for (ent = initialize_entity_first; ent; )
1743 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1745 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1746 // skip it in linked list
1749 prev.initialize_entity_next = ent.initialize_entity_next;
1750 ent = prev.initialize_entity_next;
1754 initialize_entity_first = ent.initialize_entity_next;
1755 ent = initialize_entity_first;
1761 ent = ent.initialize_entity_next;
1767 void objerror(string s)
1769 make_safe_for_remove(self);
1770 objerror_builtin(s);
1773 void remove_unsafely(entity e)
1778 void remove_safely(entity e)
1780 make_safe_for_remove(e);
1784 void InitializeEntity(entity e, void(void) func, float order)
1788 if (!e || e.initialize_entity)
1790 // make a proxy initializer entity
1794 e.classname = "initialize_entity";
1798 e.initialize_entity = func;
1799 e.initialize_entity_order = order;
1801 cur = initialize_entity_first;
1804 if (!cur || cur.initialize_entity_order > order)
1806 // insert between prev and cur
1808 prev.initialize_entity_next = e;
1810 initialize_entity_first = e;
1811 e.initialize_entity_next = cur;
1815 cur = cur.initialize_entity_next;
1818 void InitializeEntitiesRun()
1821 startoflist = initialize_entity_first;
1822 initialize_entity_first = world;
1823 for (self = startoflist; self; )
1826 var void(void) func;
1827 e = self.initialize_entity_next;
1828 func = self.initialize_entity;
1829 self.initialize_entity_order = 0;
1830 self.initialize_entity = func_null;
1831 self.initialize_entity_next = world;
1832 if (self.classname == "initialize_entity")
1836 remove_builtin(self);
1839 //dprint("Delayed initialization: ", self.classname, "\n");
1845 .float uncustomizeentityforclient_set;
1846 .void(void) uncustomizeentityforclient;
1847 void(void) SUB_Nullpointer = #0;
1848 void UncustomizeEntitiesRun()
1852 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1853 self.uncustomizeentityforclient();
1856 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1858 e.customizeentityforclient = customizer;
1859 e.uncustomizeentityforclient = uncustomizer;
1860 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1864 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1867 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1871 if (e.classname == "")
1872 e.classname = "net_linked";
1874 if (e.model == "" || self.modelindex == 0)
1878 setmodel(e, "null");
1882 e.SendEntity = sendfunc;
1883 e.SendFlags = 0xFFFFFF;
1886 e.effects |= EF_NODEPTHTEST;
1890 e.nextthink = time + dt;
1891 e.think = SUB_Remove;
1895 void adaptor_think2touch()
1904 void adaptor_think2use()
1916 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1918 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
1919 self.projectiledeathtype |= HITTYPE_SPLASH;
1920 adaptor_think2use();
1923 // deferred dropping
1924 void DropToFloor_Handler()
1926 droptofloor_builtin();
1927 self.dropped_origin = self.origin;
1932 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1937 float trace_hits_box_a0, trace_hits_box_a1;
1939 float trace_hits_box_1d(float end, float thmi, float thma)
1943 // just check if x is in range
1951 // do the trace with respect to x
1952 // 0 -> end has to stay in thmi -> thma
1953 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1954 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1955 if (trace_hits_box_a0 > trace_hits_box_a1)
1961 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1966 // now it is a trace from 0 to end
1968 trace_hits_box_a0 = 0;
1969 trace_hits_box_a1 = 1;
1971 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1973 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1975 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1981 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1983 return trace_hits_box(start, end, thmi - ma, thma - mi);
1986 float SUB_NoImpactCheck()
1988 // zero hitcontents = this is not the real impact, but either the
1989 // mirror-impact of something hitting the projectile instead of the
1990 // projectile hitting the something, or a touchareagrid one. Neither of
1991 // these stop the projectile from moving, so...
1992 if(trace_dphitcontents == 0)
1994 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1997 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1999 if (other == world && self.size != '0 0 0')
2002 tic = self.velocity * sys_frametime;
2003 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2004 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2005 if (trace_fraction >= 1)
2007 dprint("Odd... did not hit...?\n");
2009 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2011 dprint("Detected and prevented the sky-grapple bug.\n");
2019 #define SUB_OwnerCheck() (other && (other == self.owner))
2021 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
2023 if(SUB_OwnerCheck())
2025 if(SUB_NoImpactCheck())
2030 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2031 UpdateCSQCProjectileNextFrame(self);
2034 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2036 float MAX_IPBAN_URIS = 16;
2038 float URI_GET_DISCARD = 0;
2039 float URI_GET_IPBAN = 1;
2040 float URI_GET_IPBAN_END = 16;
2042 void URI_Get_Callback(float id, float status, string data)
2044 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2046 dprint("\nEnd of data.\n");
2048 if (id == URI_GET_DISCARD)
2052 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2055 OnlineBanList_URI_Get_Callback(id, status, data);
2059 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2063 void print_to(entity e, string s)
2066 sprint(e, strcat(s, "\n"));
2071 string uid2name(string myuid) {
2073 // store uid in separate uid2name db IF CLIENT ALLOWS THIS
2074 // - make it global for all mods
2075 // perhaps show 10 first digits here if missing name?
2077 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2080 s = "^1Unregistered Player";
2084 float race_readTime(string map, float pos)
2092 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2095 string race_readUID(string map, float pos)
2103 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2106 float race_readPos(string map, float t) {
2108 for (i = 1; i <= RANKINGS_CNT; ++i)
2109 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2112 return 0; // pos is zero if unranked
2115 float race_writeTime(string map, float t, string myuid)
2124 pos = race_readPos(map, t);
2126 if(pos) { // don't even ALLOW writing unranked times into the db, less stuff to worry about in other code then :-)
2127 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(pos)), ftos(t));
2128 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)), myuid);
2132 string race_readName(string map, float pos)
2140 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2143 string race_placeName(float pos) {
2144 if(floor((pos % 100)/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2147 return strcat(ftos(pos), "st");
2148 else if(pos % 10 == 2)
2149 return strcat(ftos(pos), "nd");
2150 else if(pos % 10 == 3)
2151 return strcat(ftos(pos), "rd");
2153 return strcat(ftos(pos), "th");
2156 return strcat(ftos(pos), "th");
2158 string getrecords(float page) // 50 records per page
2172 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2174 if (MapInfo_Get_ByID(i))
2176 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2180 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2181 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2189 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2191 if (MapInfo_Get_ByID(i))
2193 r = race_readTime(MapInfo_Map_bspname, 1);
2196 h = race_readName(MapInfo_Map_bspname, 1)
2197 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2205 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2207 if (MapInfo_Get_ByID(i))
2209 r = race_readTime(MapInfo_Map_bspname, 1);
2212 h = race_readName(MapInfo_Map_bspname, 1)
2213 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2219 MapInfo_ClearTemps();
2221 if (s == "" && page == 0)
2222 return "No records are available on this server.\n";
2227 string getrankings()
2240 for (i = 1; i <= RANKINGS_CNT; ++i)
2242 t = race_readTime(map, i);
2245 n = race_readName(map, i);
2246 p = race_placeName(i);
2247 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2250 MapInfo_ClearTemps();
2253 return strcat("No records are available for the map: ", map, "\n");
2255 return strcat("Records for ", map, ":\n", s);
2258 const float LADDER_FIRSTPOINT = 100;
2259 const float LADDER_CNT = 10; // position X still gives LADDER_FIRSTPOINT/X points
2260 const float LADDER_SIZE = 30; // ladder shows the top X players
2261 string top_uids[LADDER_SIZE];
2262 float top_scores[LADDER_SIZE];
2267 string s, len, temp_s;
2274 for (k = 0; k < MapInfo_count; ++k)
2276 if (MapInfo_Get_ByID(k))
2278 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2279 if(race_readTime(GetMapname(), i) == 0)
2282 myuid = race_readUID(GetMapname(), i);
2284 // string s contains:
2285 // arg 0 = # of speed recs
2286 // arg 1 = # of 1st place recs
2287 // arg 2 = # of 2nd place recs
2289 // LADDER_CNT+1 = total points
2291 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2294 db_put(TemporaryDB, strcat("uid", ftos(UIDCNT)), myuid);
2296 for (j = 0; j <= LADDER_CNT + 1; ++j)
2298 if(j != LADDER_CNT + 1)
2299 temp_s = strcat(temp_s, "0 ");
2301 temp_s = strcat(temp_s, "0");
2305 len = tokenize_console(temp_s);
2308 if(i == 0) // speed award
2309 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2311 if(j == 0) // speed award
2312 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2314 s = strcat(s, " ", argv(j)); // just copy over everything else
2317 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2320 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2321 else if(j == i) // wanted rec!
2322 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2324 s = strcat(s, " ", argv(j)); // just copy over everything else
2327 // total points are (by default) calculated like this:
2328 // speedrec = floor(100 / 10) = 10 points
2329 // 1st place = floor(100 / 1) = 100 points
2330 // 2nd place = floor(100 / 2) = 50 points
2331 // 3rd place = floor(100 / 3) = 33 points
2332 // 4th place = floor(100 / 4) = 25 points
2333 // 5th place = floor(100 / 5) = 20 points
2337 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2339 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2341 db_put(TemporaryDB, strcat("ladder", myuid), s);
2349 for (i = 0; i <= UIDCNT; ++i) // for each known uid
2351 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2352 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2353 len = tokenize_console(temp_s);
2354 thiscnt = stof(argv(LADDER_CNT+1));
2356 if(thiscnt > top_scores[LADDER_SIZE-1])
2357 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2359 if(thiscnt > top_scores[j])
2361 for (k = LADDER_SIZE-1; k >= j; --k)
2363 top_uids[k] = top_uids[k-1];
2364 top_scores[k] = top_scores[k-1];
2366 top_uids[j] = thisuid;
2367 top_scores[j] = thiscnt;
2373 s = "^3-----------------------\n\n";
2375 s = strcat(s, "Pos ^3|");
2376 s = strcat(s, " ^7Total ^3|");
2377 for (i = 1; i <= LADDER_CNT; ++i)
2379 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2381 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2383 s = strcat(s, "\n^3----+--------");
2384 for (i = 1; i <= LADDER_CNT; ++i)
2386 s = strcat(s, "+-----");
2388 s = strcat(s, "+--------------+--------------------\n");
2390 for (i = 0; i < LADDER_SIZE; ++i)
2392 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2393 len = tokenize_console(temp_s);
2394 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2396 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2397 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2398 for (j = 1; j <= LADDER_CNT; ++j)
2400 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2402 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2403 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2406 MapInfo_ClearTemps();
2409 return "No ladder on this server!\n";
2411 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2415 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2418 vector start, org, delta, end, enddown, mstart;
2420 m = e.dphitcontentsmask;
2421 e.dphitcontentsmask = goodcontents | badcontents;
2424 delta = world.maxs - world.mins;
2426 for (i = 0; i < attempts; ++i)
2428 start_x = org_x + random() * delta_x;
2429 start_y = org_y + random() * delta_y;
2430 start_z = org_z + random() * delta_z;
2432 // rule 1: start inside world bounds, and outside
2433 // solid, and don't start from somewhere where you can
2434 // fall down to evil
2435 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2436 if (trace_fraction >= 1)
2438 if (trace_startsolid)
2440 if (trace_dphitcontents & badcontents)
2442 if (trace_dphitq3surfaceflags & badsurfaceflags)
2445 // rule 2: if we are too high, lower the point
2446 if (trace_fraction * delta_z > maxaboveground)
2447 start = trace_endpos + '0 0 1' * maxaboveground;
2448 enddown = trace_endpos;
2450 // rule 3: make sure we aren't outside the map. This only works
2451 // for somewhat well formed maps. A good rule of thumb is that
2452 // the map should have a convex outside hull.
2453 // these can be traceLINES as we already verified the starting box
2454 mstart = start + 0.5 * (e.mins + e.maxs);
2455 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2456 if (trace_fraction >= 1)
2458 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2459 if (trace_fraction >= 1)
2461 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2462 if (trace_fraction >= 1)
2464 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2465 if (trace_fraction >= 1)
2467 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2468 if (trace_fraction >= 1)
2471 // find a random vector to "look at"
2472 end_x = org_x + random() * delta_x;
2473 end_y = org_y + random() * delta_y;
2474 end_z = org_z + random() * delta_z;
2475 end = start + normalize(end - start) * vlen(delta);
2477 // rule 4: start TO end must not be too short
2478 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2479 if (trace_startsolid)
2481 if (trace_fraction < minviewdistance / vlen(delta))
2484 // rule 5: don't want to look at sky
2485 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2488 // rule 6: we must not end up in trigger_hurt
2489 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2491 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2498 e.dphitcontentsmask = m;
2502 setorigin(e, start);
2503 e.angles = vectoangles(end - start);
2504 dprint("Needed ", ftos(i + 1), " attempts\n");
2511 float zcurveparticles_effectno;
2512 vector zcurveparticles_start;
2513 float zcurveparticles_spd;
2515 void endzcurveparticles()
2517 if(zcurveparticles_effectno)
2520 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2522 zcurveparticles_effectno = 0;
2525 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2527 spd = bound(0, floor(spd / 16), 32767);
2528 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2530 endzcurveparticles();
2531 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2532 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2533 WriteShort(MSG_BROADCAST, effectno);
2534 WriteCoord(MSG_BROADCAST, start_x);
2535 WriteCoord(MSG_BROADCAST, start_y);
2536 WriteCoord(MSG_BROADCAST, start_z);
2537 zcurveparticles_effectno = effectno;
2538 zcurveparticles_start = start;
2541 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2542 WriteCoord(MSG_BROADCAST, end_x);
2543 WriteCoord(MSG_BROADCAST, end_y);
2544 WriteCoord(MSG_BROADCAST, end_z);
2545 WriteCoord(MSG_BROADCAST, end_dz);
2546 zcurveparticles_spd = spd;
2549 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2552 vector vecxy, velxy;
2554 vecxy = end - start;
2559 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2561 endzcurveparticles();
2562 trailparticles(world, effectno, start, end);
2566 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2567 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2570 string GetGametype(); // g_world.qc
2571 void write_recordmarker(entity pl, float tstart, float dt)
2573 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2575 // also write a marker into demo files for demotc-race-record-extractor to find
2578 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2579 " ", ftos(tstart), " ", ftos(dt), "\n"));
2582 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2595 if(allowcenter) // 2: allow center handedness
2608 if(allowcenter) // 2: allow center handedness
2624 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2629 if (cvar("g_shootfromeye"))
2633 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE, algn);
2641 else if (cvar("g_shootfromcenter"))
2645 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE, algn);
2653 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2663 else if (cvar("g_shootfromclient"))
2665 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2), algn);
2670 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2672 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2676 void attach_sameorigin(entity e, entity to, string tag)
2678 vector org, t_forward, t_left, t_up, e_forward, e_up;
2685 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2686 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2687 t_forward = v_forward * tagscale;
2688 t_left = v_right * -tagscale;
2689 t_up = v_up * tagscale;
2691 e.origin_x = org * t_forward;
2692 e.origin_y = org * t_left;
2693 e.origin_z = org * t_up;
2695 // current forward and up directions
2696 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2697 e.angles = AnglesTransform_FromVAngles(e.angles);
2699 e.angles = AnglesTransform_FromAngles(e.angles);
2700 fixedmakevectors(e.angles);
2702 // untransform forward, up!
2703 e_forward_x = v_forward * t_forward;
2704 e_forward_y = v_forward * t_left;
2705 e_forward_z = v_forward * t_up;
2706 e_up_x = v_up * t_forward;
2707 e_up_y = v_up * t_left;
2708 e_up_z = v_up * t_up;
2710 e.angles = fixedvectoangles2(e_forward, e_up);
2711 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2712 e.angles = AnglesTransform_ToVAngles(e.angles);
2714 e.angles = AnglesTransform_ToAngles(e.angles);
2716 setattachment(e, to, tag);
2717 setorigin(e, e.origin);
2720 void detach_sameorigin(entity e)
2723 org = gettaginfo(e, 0);
2724 e.angles = fixedvectoangles2(v_forward, v_up);
2725 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2726 e.angles = AnglesTransform_ToVAngles(e.angles);
2728 e.angles = AnglesTransform_ToAngles(e.angles);
2730 setattachment(e, world, "");
2731 setorigin(e, e.origin);
2734 void follow_sameorigin(entity e, entity to)
2736 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2737 e.aiment = to; // make the hole follow bmodel
2738 e.punchangle = to.angles; // the original angles of bmodel
2739 e.view_ofs = e.origin - to.origin; // relative origin
2740 e.v_angle = e.angles - to.angles; // relative angles
2743 void unfollow_sameorigin(entity e)
2745 e.movetype = MOVETYPE_NONE;
2748 entity gettaginfo_relative_ent;
2749 vector gettaginfo_relative(entity e, float tag)
2751 if (!gettaginfo_relative_ent)
2753 gettaginfo_relative_ent = spawn();
2754 gettaginfo_relative_ent.effects = EF_NODRAW;
2756 gettaginfo_relative_ent.model = e.model;
2757 gettaginfo_relative_ent.modelindex = e.modelindex;
2758 gettaginfo_relative_ent.frame = e.frame;
2759 return gettaginfo(gettaginfo_relative_ent, tag);
2762 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2766 if (pl.soundentity.cnt & p)
2768 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2769 pl.soundentity.cnt |= p;
2772 void SoundEntity_StopSound(entity pl, float chan)
2776 if (pl.soundentity.cnt & p)
2778 stopsoundto(MSG_ALL, pl.soundentity, chan);
2779 pl.soundentity.cnt &~= p;
2783 void SoundEntity_Attach(entity pl)
2785 pl.soundentity = spawn();
2786 pl.soundentity.classname = "soundentity";
2787 pl.soundentity.owner = pl;
2788 setattachment(pl.soundentity, pl, "");
2789 setmodel(pl.soundentity, "null");
2792 void SoundEntity_Detach(entity pl)
2795 for (i = 0; i <= 7; ++i)
2796 SoundEntity_StopSound(pl, i);
2800 float ParseCommandPlayerSlotTarget_firsttoken;
2801 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2809 ParseCommandPlayerSlotTarget_firsttoken = -1;
2813 if (substring(argv(idx), 0, 1) == "#")
2815 s = substring(argv(idx), 1, -1);
2823 ParseCommandPlayerSlotTarget_firsttoken = idx;
2824 if (s == ftos(stof(s)))
2826 e = edict_num(stof(s));
2827 if (e.flags & FL_CLIENT)
2833 // it must be a nick name
2836 ParseCommandPlayerSlotTarget_firsttoken = idx;
2839 FOR_EACH_CLIENT(head)
2840 if (head.netname == s)
2848 s = strdecolorize(s);
2850 FOR_EACH_CLIENT(head)
2851 if (strdecolorize(head.netname) == s)
2866 float modeleffect_SendEntity(entity to, float sf)
2869 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2872 if(self.velocity != '0 0 0')
2874 if(self.angles != '0 0 0')
2876 if(self.avelocity != '0 0 0')
2879 WriteByte(MSG_ENTITY, f);
2880 WriteShort(MSG_ENTITY, self.modelindex);
2881 WriteByte(MSG_ENTITY, self.skin);
2882 WriteByte(MSG_ENTITY, self.frame);
2883 WriteCoord(MSG_ENTITY, self.origin_x);
2884 WriteCoord(MSG_ENTITY, self.origin_y);
2885 WriteCoord(MSG_ENTITY, self.origin_z);
2888 WriteCoord(MSG_ENTITY, self.velocity_x);
2889 WriteCoord(MSG_ENTITY, self.velocity_y);
2890 WriteCoord(MSG_ENTITY, self.velocity_z);
2894 WriteCoord(MSG_ENTITY, self.angles_x);
2895 WriteCoord(MSG_ENTITY, self.angles_y);
2896 WriteCoord(MSG_ENTITY, self.angles_z);
2900 WriteCoord(MSG_ENTITY, self.avelocity_x);
2901 WriteCoord(MSG_ENTITY, self.avelocity_y);
2902 WriteCoord(MSG_ENTITY, self.avelocity_z);
2904 WriteShort(MSG_ENTITY, self.scale * 256.0);
2905 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2906 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2907 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2908 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2913 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)
2918 e.classname = "modeleffect";
2926 e.teleport_time = t1;
2930 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2934 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2937 sz = max(e.scale, e.scale2);
2938 setsize(e, e.mins * sz, e.maxs * sz);
2939 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2942 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2944 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2947 float randombit(float bits)
2949 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2958 for(f = 1; f <= bits; f *= 2)
2967 r = (r - 1) / (n - 1);
2974 float randombits(float bits, float k, float error_return)
2978 while(k > 0 && bits != r)
2980 r += randombit(bits - r);
2989 void randombit_test(float bits, float iter)
2993 print(ftos(randombit(bits)), "\n");
2998 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3000 if(halflifedist > 0)
3001 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3002 else if(halflifedist < 0)
3003 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3012 #define cvar_string_normal cvar_string_builtin
3013 #define cvar_normal cvar_builtin
3015 string cvar_string_normal(string n)
3017 if not(cvar_type(n) & 1)
3018 backtrace(strcat("Attempt to access undefined cvar: ", n));
3019 return cvar_string_builtin(n);
3022 float cvar_normal(string n)
3024 return stof(cvar_string_normal(n));
3027 #define cvar_set_normal cvar_set_builtin
3035 oself.think = SUB_Remove;
3036 oself.nextthink = time;
3042 Execute func() after time + fdelay.
3043 self when func is executed = self when defer is called
3045 void defer(float fdelay, void() func)
3052 e.think = defer_think;
3053 e.nextthink = time + fdelay;