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 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
99 float nPlayerHealth = rint(enPlayer.health);
100 float nPlayerArmor = rint(enPlayer.armorvalue);
101 float nPlayerHandicap = enPlayer.cvar_cl_handicap;
102 float nPlayerPing = rint(enPlayer.ping);
103 string strPlayerPingColor;
106 if(nPlayerPing >= 150)
107 strPlayerPingColor = "^1";
109 strPlayerPingColor = "^2";
111 if((autocvar_sv_fragmessage_information_stats) && (enPlayer.health >= 1))
112 strMessage = strcat(strMessage, "^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
114 if(autocvar_sv_fragmessage_information_ping) {
115 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
116 strMessage = strcat(strMessage, " ^7(^2Bot");
118 strMessage = strcat(strMessage, " ^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
119 if(autocvar_sv_fragmessage_information_handicap)
120 if(autocvar_sv_fragmessage_information_handicap == 2)
121 if(nPlayerHandicap <= 1)
122 strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
124 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
125 else if not(nPlayerHandicap <= 1)
126 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
128 strMessage = strcat(strMessage, "^7)");
129 } else if(autocvar_sv_fragmessage_information_handicap) {
130 if(autocvar_sv_fragmessage_information_handicap == 2)
131 if(nPlayerHandicap <= 1)
132 strMessage = strcat(strMessage, "^7(Handicap ^2Off^7)");
134 strMessage = strcat(strMessage, "^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
135 else if(nPlayerHandicap > 1)
136 strMessage = strcat(strMessage, "^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
139 if(strMessage) // add new line to the beginning if there is a message
140 strMessage = strcat("\n", strMessage);
144 void bcenterprint(string s)
146 // TODO replace by MSG_ALL (would show it to spectators too, though)?
148 FOR_EACH_PLAYER(head)
149 if (clienttype(head) == CLIENTTYPE_REAL)
150 centerprint(head, s);
153 void GameLogEcho(string s)
158 if (autocvar_sv_eventlog_files)
163 matches = autocvar_sv_eventlog_files_counter + 1;
164 cvar_set("sv_eventlog_files_counter", ftos(matches));
167 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
168 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
169 logfile = fopen(fn, FILE_APPEND);
170 fputs(logfile, ":logversion:3\n");
174 if (autocvar_sv_eventlog_files_timestamps)
175 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
177 fputs(logfile, strcat(s, "\n"));
180 if (autocvar_sv_eventlog_console)
189 // will be opened later
194 if (logfile_open && logfile >= 0)
205 vector PL_CROUCH_VIEW_OFS;
206 vector PL_CROUCH_MIN;
207 vector PL_CROUCH_MAX;
209 float spawnpoint_nag;
210 void relocate_spawnpoint()
212 PL_VIEW_OFS = stov(autocvar_sv_player_viewoffset);
213 PL_MIN = stov(autocvar_sv_player_mins);
214 PL_MAX = stov(autocvar_sv_player_maxs);
215 PL_HEAD = stov(autocvar_sv_player_headsize);
216 PL_CROUCH_VIEW_OFS = stov(autocvar_sv_player_crouch_viewoffset);
217 PL_CROUCH_MIN = stov(autocvar_sv_player_crouch_mins);
218 PL_CROUCH_MAX = stov(autocvar_sv_player_crouch_maxs);
220 // nudge off the floor
221 setorigin(self, self.origin + '0 0 1');
223 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
224 if (trace_startsolid)
230 if (!move_out_of_solid(self))
231 objerror("could not get out of solid at all!");
232 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
233 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
234 print(" ", ftos(self.origin_y - o_y));
235 print(" ", ftos(self.origin_z - o_z), "'\n");
236 if (autocvar_g_spawnpoints_auto_move_out_of_solid)
239 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
245 self.mins = self.maxs = '0 0 0';
246 objerror("player spawn point in solid, mapper sucks!\n");
251 self.use = spawnpoint_use;
252 self.team_saved = self.team;
256 if (have_team_spawns != 0)
258 have_team_spawns = 1;
259 have_team_spawns_forteam[self.team] = 1;
261 if (autocvar_r_showbboxes)
263 // show where spawnpoints point at too
264 makevectors(self.angles);
267 e.classname = "info_player_foo";
268 setorigin(e, self.origin + v_forward * 24);
269 setsize(e, '-8 -8 -8', '8 8 8');
270 e.solid = SOLID_TRIGGER;
274 #define strstr strstrofs
276 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
277 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
278 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
279 // BE CONSTANT OR strzoneD!
280 float strstr(string haystack, string needle, float offset)
284 len = strlen(needle);
285 endpos = strlen(haystack) - len;
286 while(offset <= endpos)
288 found = substring(haystack, offset, len);
297 float NUM_NEAREST_ENTITIES = 4;
298 entity nearest_entity[NUM_NEAREST_ENTITIES];
299 float nearest_length[NUM_NEAREST_ENTITIES];
300 entity findnearest(vector point, .string field, string value, vector axismod)
311 localhead = find(world, field, value);
314 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
315 dist = localhead.oldorigin;
317 dist = localhead.origin;
319 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
322 for (i = 0; i < num_nearest; ++i)
324 if (len < nearest_length[i])
328 // now i tells us where to insert at
329 // INSERTION SORT! YOU'VE SEEN IT! RUN!
330 if (i < NUM_NEAREST_ENTITIES)
332 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
334 nearest_length[j + 1] = nearest_length[j];
335 nearest_entity[j + 1] = nearest_entity[j];
337 nearest_length[i] = len;
338 nearest_entity[i] = localhead;
339 if (num_nearest < NUM_NEAREST_ENTITIES)
340 num_nearest = num_nearest + 1;
343 localhead = find(localhead, field, value);
346 // now use the first one from our list that we can see
347 for (i = 0; i < num_nearest; ++i)
349 traceline(point, nearest_entity[i].origin, TRUE, world);
350 if (trace_fraction == 1)
354 dprint("Nearest point (");
355 dprint(nearest_entity[0].netname);
356 dprint(") is not visible, using a visible one.\n");
358 return nearest_entity[i];
362 if (num_nearest == 0)
365 dprint("Not seeing any location point, using nearest as fallback.\n");
367 dprint("Candidates were: ");
368 for(j = 0; j < num_nearest; ++j)
372 dprint(nearest_entity[j].netname);
377 return nearest_entity[0];
380 void spawnfunc_target_location()
382 self.classname = "target_location";
383 // location name in netname
384 // eventually support: count, teamgame selectors, line of sight?
387 void spawnfunc_info_location()
389 self.classname = "target_location";
390 self.message = self.netname;
393 string NearestLocation(vector p)
398 loc = findnearest(p, classname, "target_location", '1 1 1');
405 loc = findnearest(p, target, "###item###", '1 1 4');
412 string formatmessage(string msg)
423 WarpZone_crosshair_trace(self);
424 cursor = trace_endpos;
425 cursor_ent = trace_ent;
429 break; // too many replacements
432 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
433 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
446 replacement = substring(msg, p, 2);
447 escape = substring(msg, p + 1, 1);
451 else if (escape == "\\")
453 else if (escape == "n")
455 else if (escape == "a")
456 replacement = ftos(floor(self.armorvalue));
457 else if (escape == "h")
458 replacement = ftos(floor(self.health));
459 else if (escape == "l")
460 replacement = NearestLocation(self.origin);
461 else if (escape == "y")
462 replacement = NearestLocation(cursor);
463 else if (escape == "d")
464 replacement = NearestLocation(self.death_origin);
465 else if (escape == "w") {
469 wep = self.switchweapon;
472 replacement = W_Name(wep);
473 } else if (escape == "W") {
474 if (self.items & IT_SHELLS) replacement = "shells";
475 else if (self.items & IT_NAILS) replacement = "bullets";
476 else if (self.items & IT_ROCKETS) replacement = "rockets";
477 else if (self.items & IT_CELLS) replacement = "cells";
478 else replacement = "batteries"; // ;)
479 } else if (escape == "x") {
480 replacement = cursor_ent.netname;
481 if (!replacement || !cursor_ent)
482 replacement = "nothing";
483 } else if (escape == "s")
484 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
485 else if (escape == "S")
486 replacement = ftos(vlen(self.velocity));
488 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
489 p = p + strlen(replacement);
494 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
495 return (value == 0) ? FALSE : TRUE;
504 >0: receives a cvar from name=argv(f) value=argv(f+1)
506 void GetCvars_handleString(string thisname, float f, .string field, string name)
511 strunzone(self.field);
512 self.field = string_null;
516 if (thisname == name)
519 strunzone(self.field);
520 self.field = strzone(argv(f + 1));
524 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
526 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
528 GetCvars_handleString(thisname, f, field, name);
529 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
530 if (thisname == name)
533 s = func(strcat1(self.field));
536 strunzone(self.field);
537 self.field = strzone(s);
541 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
548 if (thisname == name)
549 self.field = stof(argv(f + 1));
552 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
554 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
561 if (thisname == name)
565 self.field = stof(argv(f + 1));
574 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
577 float w_getbestweapon(entity e);
578 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
581 o = W_FixWeaponOrder_ForceComplete(wo);
582 if(self.weaponorder_byimpulse)
584 strunzone(self.weaponorder_byimpulse);
585 self.weaponorder_byimpulse = string_null;
587 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
590 void GetCvars(float f)
595 s = strcat1(argv(f));
599 MUTATOR_CALLHOOK(GetCvars);
600 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
601 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
602 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
603 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
604 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
605 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
606 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
607 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
608 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
609 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
610 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
611 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
612 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
613 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
614 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
615 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
616 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
617 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
618 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
619 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
620 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
621 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
622 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
623 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
625 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
626 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
628 #ifdef ALLOW_FORCEMODELS
629 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
630 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
632 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
633 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
634 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
635 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
636 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
638 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
641 if (s == "cl_weaponpriority")
642 self.switchweapon = w_getbestweapon(self);
643 if (s == "cl_allow_uidtracking")
644 PlayerStats_AddPlayer(self);
648 void backtrace(string msg)
651 dev = autocvar_developer;
652 war = autocvar_prvm_backtraceforwarnings;
653 cvar_set("developer", "1");
654 cvar_set("prvm_backtraceforwarnings", "1");
656 print("--- CUT HERE ---\nWARNING: ");
659 remove(world); // isn't there any better way to cause a backtrace?
660 print("\n--- CUT UNTIL HERE ---\n");
661 cvar_set("developer", ftos(dev));
662 cvar_set("prvm_backtraceforwarnings", ftos(war));
665 string Team_ColorCode(float teamid)
667 if (teamid == COLOR_TEAM1)
669 else if (teamid == COLOR_TEAM2)
671 else if (teamid == COLOR_TEAM3)
673 else if (teamid == COLOR_TEAM4)
679 string Team_ColorName(float t)
681 // fixme: Search for team entities and get their .netname's!
682 if (t == COLOR_TEAM1)
684 if (t == COLOR_TEAM2)
686 if (t == COLOR_TEAM3)
688 if (t == COLOR_TEAM4)
693 string Team_ColorNameLowerCase(float t)
695 // fixme: Search for team entities and get their .netname's!
696 if (t == COLOR_TEAM1)
698 if (t == COLOR_TEAM2)
700 if (t == COLOR_TEAM3)
702 if (t == COLOR_TEAM4)
707 float ColourToNumber(string team_colour)
709 if (team_colour == "red")
712 if (team_colour == "blue")
715 if (team_colour == "yellow")
718 if (team_colour == "pink")
721 if (team_colour == "auto")
727 float NumberToTeamNumber(float number)
744 // decolorizes and team colors the player name when needed
745 string playername(entity p)
748 if (teamplay && !intermission_running && p.classname == "player")
750 t = Team_ColorCode(p.team);
751 return strcat(t, strdecolorize(p.netname));
757 vector randompos(vector m1, vector m2)
761 v_x = m2_x * random() + m1_x;
762 v_y = m2_y * random() + m1_y;
763 v_z = m2_z * random() + m1_z;
767 //#NO AUTOCVARS START
769 float g_pickup_shells;
770 float g_pickup_shells_max;
771 float g_pickup_nails;
772 float g_pickup_nails_max;
773 float g_pickup_rockets;
774 float g_pickup_rockets_max;
775 float g_pickup_cells;
776 float g_pickup_cells_max;
778 float g_pickup_fuel_jetpack;
779 float g_pickup_fuel_max;
780 float g_pickup_armorsmall;
781 float g_pickup_armorsmall_max;
782 float g_pickup_armorsmall_anyway;
783 float g_pickup_armormedium;
784 float g_pickup_armormedium_max;
785 float g_pickup_armormedium_anyway;
786 float g_pickup_armorbig;
787 float g_pickup_armorbig_max;
788 float g_pickup_armorbig_anyway;
789 float g_pickup_armorlarge;
790 float g_pickup_armorlarge_max;
791 float g_pickup_armorlarge_anyway;
792 float g_pickup_healthsmall;
793 float g_pickup_healthsmall_max;
794 float g_pickup_healthsmall_anyway;
795 float g_pickup_healthmedium;
796 float g_pickup_healthmedium_max;
797 float g_pickup_healthmedium_anyway;
798 float g_pickup_healthlarge;
799 float g_pickup_healthlarge_max;
800 float g_pickup_healthlarge_anyway;
801 float g_pickup_healthmega;
802 float g_pickup_healthmega_max;
803 float g_pickup_healthmega_anyway;
804 float g_pickup_ammo_anyway;
805 float g_pickup_weapons_anyway;
807 float g_weaponarena_random;
808 float g_weaponarena_random_with_laser;
809 string g_weaponarena_list;
810 float g_weaponspeedfactor;
811 float g_weaponratefactor;
812 float g_weapondamagefactor;
813 float g_weaponforcefactor;
814 float g_weaponspreadfactor;
818 float start_ammo_shells;
819 float start_ammo_nails;
820 float start_ammo_rockets;
821 float start_ammo_cells;
822 float start_ammo_fuel;
824 float start_armorvalue;
825 float warmup_start_weapons;
826 float warmup_start_ammo_shells;
827 float warmup_start_ammo_nails;
828 float warmup_start_ammo_rockets;
829 float warmup_start_ammo_cells;
830 float warmup_start_ammo_fuel;
831 float warmup_start_health;
832 float warmup_start_armorvalue;
836 entity get_weaponinfo(float w);
838 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
840 var float i = weaponinfo.weapon;
845 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
847 if (t < 0) // "default" weapon selection
849 if (g_lms || g_ca || allguns)
850 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
854 t = (i == WEP_SHOTGUN);
856 t = 0; // weapon is set a few lines later
858 t = (i == WEP_LASER || i == WEP_SHOTGUN);
859 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
860 t |= (i == WEP_HOOK);
863 // we cannot disable porto in Nexball, we must force it
864 if(g_nexball && i == WEP_PORTO)
870 void readplayerstartcvars()
876 // initialize starting values for players
879 start_ammo_shells = 0;
880 start_ammo_nails = 0;
881 start_ammo_rockets = 0;
882 start_ammo_cells = 0;
883 start_health = cvar("g_balance_health_start");
884 start_armorvalue = cvar("g_balance_armor_start");
887 s = cvar_string("g_weaponarena");
888 if (s == "0" || s == "")
894 if (s == "0" || s == "")
900 // forcibly turn off weaponarena
904 g_weaponarena_list = "All Weapons";
905 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
907 e = get_weaponinfo(j);
908 g_weaponarena |= e.weapons;
909 weapon_action(e.weapon, WR_PRECACHE);
912 else if (s == "most")
914 g_weaponarena_list = "Most Weapons";
915 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
917 e = get_weaponinfo(j);
918 if (e.spawnflags & WEP_FLAG_NORMAL)
920 g_weaponarena |= e.weapons;
921 weapon_action(e.weapon, WR_PRECACHE);
925 else if (s == "none")
927 g_weaponarena_list = "No Weapons";
928 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
932 t = tokenize_console(s);
933 g_weaponarena_list = "";
934 for (i = 0; i < t; ++i)
937 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
939 e = get_weaponinfo(j);
942 g_weaponarena |= e.weapons;
943 weapon_action(e.weapon, WR_PRECACHE);
944 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
950 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
953 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
957 g_weaponarena_random = cvar("g_weaponarena_random");
959 g_weaponarena_random = 0;
960 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
964 start_weapons = g_weaponarena;
966 start_items |= IT_UNLIMITED_AMMO;
968 else if (g_minstagib)
971 start_armorvalue = 0;
972 start_weapons = WEPBIT_MINSTANEX;
973 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
974 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
976 if (g_minstagib_invis_alpha <= 0)
977 g_minstagib_invis_alpha = -1;
981 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
983 e = get_weaponinfo(i);
984 if(want_weapon("g_start_weapon_", e, FALSE))
985 start_weapons |= e.weapons;
989 if(!cvar("g_use_ammunition"))
990 start_items |= IT_UNLIMITED_AMMO;
994 start_ammo_cells = cvar("g_minstagib_ammo_start");
995 start_ammo_fuel = cvar("g_start_ammo_fuel");
997 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
999 start_ammo_rockets = 999;
1000 start_ammo_shells = 999;
1001 start_ammo_cells = 999;
1002 start_ammo_nails = 999;
1003 start_ammo_fuel = 999;
1009 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1010 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1011 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1012 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1013 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1017 start_ammo_shells = cvar("g_start_ammo_shells");
1018 start_ammo_nails = cvar("g_start_ammo_nails");
1019 start_ammo_rockets = cvar("g_start_ammo_rockets");
1020 start_ammo_cells = cvar("g_start_ammo_cells");
1021 start_ammo_fuel = cvar("g_start_ammo_fuel");
1027 start_health = cvar("g_lms_start_health");
1028 start_armorvalue = cvar("g_lms_start_armor");
1033 warmup_start_ammo_shells = start_ammo_shells;
1034 warmup_start_ammo_nails = start_ammo_nails;
1035 warmup_start_ammo_rockets = start_ammo_rockets;
1036 warmup_start_ammo_cells = start_ammo_cells;
1037 warmup_start_ammo_fuel = start_ammo_fuel;
1038 warmup_start_health = start_health;
1039 warmup_start_armorvalue = start_armorvalue;
1040 warmup_start_weapons = start_weapons;
1042 if (!g_weaponarena && !g_minstagib && !g_ca)
1044 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1045 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1046 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1047 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1048 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1049 warmup_start_health = cvar("g_warmup_start_health");
1050 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1051 warmup_start_weapons = 0;
1052 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1054 e = get_weaponinfo(i);
1055 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1056 warmup_start_weapons |= e.weapons;
1061 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1063 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1064 start_items |= IT_FUEL_REGEN;
1065 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1066 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1070 start_items |= IT_JETPACK;
1072 if (g_weapon_stay == 2)
1074 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1075 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1076 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1077 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1078 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1079 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1080 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1081 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1082 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1083 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1086 MUTATOR_CALLHOOK(SetStartItems);
1088 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1090 e = get_weaponinfo(i);
1091 if(e.weapons & (start_weapons | warmup_start_weapons))
1092 weapon_action(e.weapon, WR_PRECACHE);
1095 start_ammo_shells = max(0, start_ammo_shells);
1096 start_ammo_nails = max(0, start_ammo_nails);
1097 start_ammo_cells = max(0, start_ammo_cells);
1098 start_ammo_rockets = max(0, start_ammo_rockets);
1099 start_ammo_fuel = max(0, start_ammo_fuel);
1101 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1102 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1103 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1104 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1105 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1109 float g_bugrigs_planar_movement;
1110 float g_bugrigs_planar_movement_car_jumping;
1111 float g_bugrigs_reverse_spinning;
1112 float g_bugrigs_reverse_speeding;
1113 float g_bugrigs_reverse_stopping;
1114 float g_bugrigs_air_steering;
1115 float g_bugrigs_angle_smoothing;
1116 float g_bugrigs_friction_floor;
1117 float g_bugrigs_friction_brake;
1118 float g_bugrigs_friction_air;
1119 float g_bugrigs_accel;
1120 float g_bugrigs_speed_ref;
1121 float g_bugrigs_speed_pow;
1122 float g_bugrigs_steer;
1124 float g_touchexplode;
1125 float g_touchexplode_radius;
1126 float g_touchexplode_damage;
1127 float g_touchexplode_edgedamage;
1128 float g_touchexplode_force;
1135 float sv_pitch_fixyaw;
1137 string GetGametype(); // g_world.qc
1138 void readlevelcvars(void)
1140 // first load all the mutators
1141 if(cvar("g_invincible_projectiles"))
1142 MUTATOR_ADD(mutator_invincibleprojectiles);
1144 MUTATOR_ADD(mutator_nix);
1145 if(cvar("g_dodging"))
1146 MUTATOR_ADD(mutator_dodging);
1147 if(cvar("g_rocket_flying"))
1148 MUTATOR_ADD(mutator_rocketflying);
1149 if(cvar("g_vampire"))
1150 MUTATOR_ADD(mutator_vampire);
1151 if(cvar("g_sandbox"))
1152 MUTATOR_ADD(sandbox);
1154 if(cvar("sv_allow_fullbright"))
1155 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1157 g_bugrigs = cvar("g_bugrigs");
1158 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1159 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1160 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1161 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1162 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1163 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1164 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1165 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1166 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1167 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1168 g_bugrigs_accel = cvar("g_bugrigs_accel");
1169 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1170 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1171 g_bugrigs_steer = cvar("g_bugrigs_steer");
1173 g_touchexplode = cvar("g_touchexplode");
1174 g_touchexplode_radius = cvar("g_touchexplode_radius");
1175 g_touchexplode_damage = cvar("g_touchexplode_damage");
1176 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1177 g_touchexplode_force = cvar("g_touchexplode_force");
1179 #ifdef ALLOW_FORCEMODELS
1180 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1182 sv_loddistance1 = cvar("sv_loddistance1");
1183 sv_loddistance2 = cvar("sv_loddistance2");
1185 if(sv_loddistance2 <= sv_loddistance1)
1186 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1188 sv_clones = cvar("sv_clones");
1189 sv_gentle = cvar("sv_gentle");
1190 sv_foginterval = cvar("sv_foginterval");
1191 g_cloaked = cvar("g_cloaked");
1193 g_cloaked = 1; // always enable cloak in CTS
1194 g_jump_grunt = cvar("g_jump_grunt");
1195 g_footsteps = cvar("g_footsteps");
1196 g_grappling_hook = cvar("g_grappling_hook");
1197 g_jetpack = cvar("g_jetpack");
1198 g_midair = cvar("g_midair");
1199 g_minstagib = cvar("g_minstagib");
1200 g_norecoil = cvar("g_norecoil");
1201 g_bloodloss = cvar("g_bloodloss");
1202 sv_maxidle = cvar("sv_maxidle");
1203 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1204 g_ctf_reverse = cvar("g_ctf_reverse");
1205 sv_autotaunt = cvar("sv_autotaunt");
1206 sv_taunt = cvar("sv_taunt");
1208 inWarmupStage = cvar("g_warmup");
1209 g_warmup_limit = cvar("g_warmup_limit");
1210 g_warmup_allguns = cvar("g_warmup_allguns");
1211 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1213 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1214 inWarmupStage = 0; // these modes cannot work together, sorry
1216 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1217 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1218 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1219 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1220 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1221 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1222 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1223 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1224 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1225 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1226 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1227 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1229 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1230 g_weaponratefactor = cvar("g_weaponratefactor");
1231 g_weapondamagefactor = cvar("g_weapondamagefactor");
1232 g_weaponforcefactor = cvar("g_weaponforcefactor");
1233 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1235 g_pickup_shells = cvar("g_pickup_shells");
1236 g_pickup_shells_max = cvar("g_pickup_shells_max");
1237 g_pickup_nails = cvar("g_pickup_nails");
1238 g_pickup_nails_max = cvar("g_pickup_nails_max");
1239 g_pickup_rockets = cvar("g_pickup_rockets");
1240 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1241 g_pickup_cells = cvar("g_pickup_cells");
1242 g_pickup_cells_max = cvar("g_pickup_cells_max");
1243 g_pickup_fuel = cvar("g_pickup_fuel");
1244 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1245 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1246 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1247 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1248 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1249 g_pickup_armormedium = cvar("g_pickup_armormedium");
1250 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1251 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1252 g_pickup_armorbig = cvar("g_pickup_armorbig");
1253 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1254 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1255 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1256 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1257 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1258 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1259 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1260 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1261 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1262 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1263 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1264 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1265 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1266 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1267 g_pickup_healthmega = cvar("g_pickup_healthmega");
1268 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1269 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1271 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1272 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1274 g_pinata = cvar("g_pinata");
1276 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1278 g_weapon_stay = cvar("g_weapon_stay");
1280 g_ghost_items = cvar("g_ghost_items");
1282 if(g_ghost_items >= 1)
1283 g_ghost_items = 0.25; // default alpha value
1285 if not(inWarmupStage && !g_ca)
1286 game_starttime = cvar("g_start_delay");
1288 sv_pitch_min = cvar("sv_pitch_min");
1289 sv_pitch_max = cvar("sv_pitch_max");
1290 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1292 readplayerstartcvars();
1298 string precache_sound (string s) = #19;
1299 float precache_sound_index (string s) = #19;
1301 #define SND_VOLUME 1
1302 #define SND_ATTENUATION 2
1303 #define SND_LARGEENTITY 8
1304 #define SND_LARGESOUND 16
1306 float sound_allowed(float dest, entity e)
1308 // sounds from world may always pass
1311 if (e.classname == "body")
1313 else if (e.realowner && e.realowner != e)
1315 else if (e.owner && e.owner != e)
1320 // sounds to self may always pass
1321 if (dest == MSG_ONE)
1322 if (e == msg_entity)
1324 // sounds by players can be removed
1325 if (autocvar_bot_sound_monopoly)
1326 if (clienttype(e) == CLIENTTYPE_REAL)
1328 // anything else may pass
1332 #ifdef COMPAT_XON010_CHANNELS
1333 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1334 void sound(entity e, float chan, string samp, float vol, float atten)
1336 if (!sound_allowed(MSG_BROADCAST, e))
1338 sound_builtin(e, chan, samp, vol, atten);
1342 void sound(entity e, float chan, string samp, float vol, float atten)
1344 if (!sound_allowed(MSG_BROADCAST, e))
1346 sound7(e, chan, samp, vol, atten, 0, 0);
1350 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1354 if (!sound_allowed(dest, e))
1357 entno = num_for_edict(e);
1358 idx = precache_sound_index(samp);
1363 atten = floor(atten * 64);
1364 vol = floor(vol * 255);
1367 sflags |= SND_VOLUME;
1369 sflags |= SND_ATTENUATION;
1370 if (entno >= 8192 || chan < 0 || chan > 7)
1371 sflags |= SND_LARGEENTITY;
1373 sflags |= SND_LARGESOUND;
1375 WriteByte(dest, SVC_SOUND);
1376 WriteByte(dest, sflags);
1377 if (sflags & SND_VOLUME)
1378 WriteByte(dest, vol);
1379 if (sflags & SND_ATTENUATION)
1380 WriteByte(dest, atten);
1381 if (sflags & SND_LARGEENTITY)
1383 WriteShort(dest, entno);
1384 WriteByte(dest, chan);
1388 WriteShort(dest, entno * 8 + chan);
1390 if (sflags & SND_LARGESOUND)
1391 WriteShort(dest, idx);
1393 WriteByte(dest, idx);
1395 WriteCoord(dest, o_x);
1396 WriteCoord(dest, o_y);
1397 WriteCoord(dest, o_z);
1399 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1403 if (!sound_allowed(dest, e))
1406 o = e.origin + 0.5 * (e.mins + e.maxs);
1407 soundtoat(dest, e, o, chan, samp, vol, atten);
1409 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1411 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1413 void stopsoundto(float dest, entity e, float chan)
1417 if (!sound_allowed(dest, e))
1420 entno = num_for_edict(e);
1422 if (entno >= 8192 || chan < 0 || chan > 7)
1425 idx = precache_sound_index("misc/null.wav");
1426 sflags = SND_LARGEENTITY;
1428 sflags |= SND_LARGESOUND;
1429 WriteByte(dest, SVC_SOUND);
1430 WriteByte(dest, sflags);
1431 WriteShort(dest, entno);
1432 WriteByte(dest, chan);
1433 if (sflags & SND_LARGESOUND)
1434 WriteShort(dest, idx);
1436 WriteByte(dest, idx);
1437 WriteCoord(dest, e.origin_x);
1438 WriteCoord(dest, e.origin_y);
1439 WriteCoord(dest, e.origin_z);
1443 WriteByte(dest, SVC_STOPSOUND);
1444 WriteShort(dest, entno * 8 + chan);
1447 void stopsound(entity e, float chan)
1449 if (!sound_allowed(MSG_BROADCAST, e))
1452 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1453 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1456 void play2(entity e, string filename)
1458 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1460 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1463 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1465 float spamsound(entity e, float chan, string samp, float vol, float atten)
1467 if (!sound_allowed(MSG_BROADCAST, e))
1470 if (time > e.spamtime)
1473 sound(e, chan, samp, vol, atten);
1479 void play2team(float t, string filename)
1483 if (autocvar_bot_sound_monopoly)
1486 FOR_EACH_REALPLAYER(head)
1489 play2(head, filename);
1493 void play2all(string samp)
1495 if (autocvar_bot_sound_monopoly)
1498 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1501 void PrecachePlayerSounds(string f);
1502 void precache_playermodel(string m)
1504 float globhandle, i, n;
1507 if(substring(m, -9,5) == "_lod1")
1509 if(substring(m, -9,5) == "_lod2")
1514 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1517 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1522 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1525 n = search_getsize(globhandle);
1526 for (i = 0; i < n; ++i)
1528 //print(search_getfilename(globhandle, i), "\n");
1529 f = search_getfilename(globhandle, i);
1530 PrecachePlayerSounds(f);
1532 search_end(globhandle);
1534 void precache_all_playermodels(string pattern)
1536 float globhandle, i, n;
1539 globhandle = search_begin(pattern, TRUE, FALSE);
1542 n = search_getsize(globhandle);
1543 for (i = 0; i < n; ++i)
1545 //print(search_getfilename(globhandle, i), "\n");
1546 f = search_getfilename(globhandle, i);
1547 precache_playermodel(f);
1549 search_end(globhandle);
1554 // gamemode related things
1555 precache_model ("models/misc/chatbubble.spr");
1558 precache_model ("models/runematch/curse.mdl");
1559 precache_model ("models/runematch/rune.mdl");
1562 #ifdef TTURRETS_ENABLED
1563 if (autocvar_g_turrets)
1567 // Precache all player models if desired
1568 if (autocvar_sv_precacheplayermodels)
1570 PrecachePlayerSounds("sound/player/default.sounds");
1571 precache_all_playermodels("models/player/*.zym");
1572 precache_all_playermodels("models/player/*.dpm");
1573 precache_all_playermodels("models/player/*.md3");
1574 precache_all_playermodels("models/player/*.psk");
1575 precache_all_playermodels("models/player/*.iqm");
1578 if (autocvar_sv_defaultcharacter)
1581 s = autocvar_sv_defaultplayermodel_red;
1583 precache_playermodel(s);
1584 s = autocvar_sv_defaultplayermodel_blue;
1586 precache_playermodel(s);
1587 s = autocvar_sv_defaultplayermodel_yellow;
1589 precache_playermodel(s);
1590 s = autocvar_sv_defaultplayermodel_pink;
1592 precache_playermodel(s);
1593 s = autocvar_sv_defaultplayermodel;
1595 precache_playermodel(s);
1600 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1601 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1604 // gore and miscellaneous sounds
1605 //precache_sound ("misc/h2ohit.wav");
1606 precache_model ("models/hook.md3");
1607 precache_sound ("misc/armorimpact.wav");
1608 precache_sound ("misc/bodyimpact1.wav");
1609 precache_sound ("misc/bodyimpact2.wav");
1610 precache_sound ("misc/gib.wav");
1611 precache_sound ("misc/gib_splat01.wav");
1612 precache_sound ("misc/gib_splat02.wav");
1613 precache_sound ("misc/gib_splat03.wav");
1614 precache_sound ("misc/gib_splat04.wav");
1615 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1616 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1617 precache_sound ("misc/null.wav");
1618 precache_sound ("misc/spawn.wav");
1619 precache_sound ("misc/talk.wav");
1620 precache_sound ("misc/teleport.wav");
1621 precache_sound ("misc/poweroff.wav");
1622 precache_sound ("player/lava.wav");
1623 precache_sound ("player/slime.wav");
1626 precache_sound ("misc/jetpack_fly.wav");
1628 precache_model ("models/sprites/0.spr32");
1629 precache_model ("models/sprites/1.spr32");
1630 precache_model ("models/sprites/2.spr32");
1631 precache_model ("models/sprites/3.spr32");
1632 precache_model ("models/sprites/4.spr32");
1633 precache_model ("models/sprites/5.spr32");
1634 precache_model ("models/sprites/6.spr32");
1635 precache_model ("models/sprites/7.spr32");
1636 precache_model ("models/sprites/8.spr32");
1637 precache_model ("models/sprites/9.spr32");
1638 precache_model ("models/sprites/10.spr32");
1640 // common weapon precaches
1641 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1642 precache_sound ("weapons/weapon_switch.wav");
1643 precache_sound ("weapons/weaponpickup.wav");
1644 precache_sound ("weapons/unavailable.wav");
1645 precache_sound ("weapons/dryfire.wav");
1646 if (g_grappling_hook)
1648 precache_sound ("weapons/hook_fire.wav"); // hook
1649 precache_sound ("weapons/hook_impact.wav"); // hook
1652 if(autocvar_sv_precacheweapons)
1654 //precache weapon models/sounds
1657 while (wep <= WEP_LAST)
1659 weapon_action(wep, WR_PRECACHE);
1664 precache_model("models/elaser.mdl");
1665 precache_model("models/laser.mdl");
1666 precache_model("models/ebomb.mdl");
1669 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1671 if (!self.noise && self.music) // quake 3 uses the music field
1672 self.noise = self.music;
1674 // plays music for the level if there is any
1677 precache_sound (self.noise);
1678 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1683 // sorry, but using \ in macros breaks line numbers
1684 #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
1685 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1686 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1689 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1691 if (clienttype(e) == CLIENTTYPE_REAL)
1694 WRITESPECTATABLE_MSG_ONE({
1695 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1696 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1697 WriteByte(MSG_ONE, id);
1698 WriteString(MSG_ONE, s);
1699 if (id != 0 && s != "")
1701 WriteByte(MSG_ONE, duration);
1702 WriteByte(MSG_ONE, countdown_num);
1707 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1709 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1711 // WARNING: this kills the trace globals
1712 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1713 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1715 #define INITPRIO_FIRST 0
1716 #define INITPRIO_GAMETYPE 0
1717 #define INITPRIO_GAMETYPE_FALLBACK 1
1718 #define INITPRIO_FINDTARGET 10
1719 #define INITPRIO_DROPTOFLOOR 20
1720 #define INITPRIO_SETLOCATION 90
1721 #define INITPRIO_LINKDOORS 91
1722 #define INITPRIO_LAST 99
1724 .void(void) initialize_entity;
1725 .float initialize_entity_order;
1726 .entity initialize_entity_next;
1727 entity initialize_entity_first;
1729 void make_safe_for_remove(entity e)
1731 if (e.initialize_entity)
1734 for (ent = initialize_entity_first; ent; )
1736 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1738 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1739 // skip it in linked list
1742 prev.initialize_entity_next = ent.initialize_entity_next;
1743 ent = prev.initialize_entity_next;
1747 initialize_entity_first = ent.initialize_entity_next;
1748 ent = initialize_entity_first;
1754 ent = ent.initialize_entity_next;
1760 void objerror(string s)
1762 make_safe_for_remove(self);
1763 objerror_builtin(s);
1766 .float remove_except_protected_forbidden;
1767 void remove_except_protected(entity e)
1769 if(e.remove_except_protected_forbidden)
1770 error("not allowed to remove this at this point");
1774 void remove_unsafely(entity e)
1776 if(e.classname == "spike")
1777 error("Removing spikes is forbidden (crylink bug), please report");
1781 void remove_safely(entity e)
1783 make_safe_for_remove(e);
1787 void InitializeEntity(entity e, void(void) func, float order)
1791 if (!e || e.initialize_entity)
1793 // make a proxy initializer entity
1797 e.classname = "initialize_entity";
1801 e.initialize_entity = func;
1802 e.initialize_entity_order = order;
1804 cur = initialize_entity_first;
1807 if (!cur || cur.initialize_entity_order > order)
1809 // insert between prev and cur
1811 prev.initialize_entity_next = e;
1813 initialize_entity_first = e;
1814 e.initialize_entity_next = cur;
1818 cur = cur.initialize_entity_next;
1821 void InitializeEntitiesRun()
1824 startoflist = initialize_entity_first;
1825 initialize_entity_first = world;
1826 remove = remove_except_protected;
1827 for (self = startoflist; self; self = self.initialize_entity_next)
1829 self.remove_except_protected_forbidden = 1;
1831 for (self = startoflist; self; )
1834 var void(void) func;
1835 e = self.initialize_entity_next;
1836 func = self.initialize_entity;
1837 self.initialize_entity_order = 0;
1838 self.initialize_entity = func_null;
1839 self.initialize_entity_next = world;
1840 self.remove_except_protected_forbidden = 0;
1841 if (self.classname == "initialize_entity")
1845 remove_builtin(self);
1848 //dprint("Delayed initialization: ", self.classname, "\n");
1849 if(func != func_null)
1854 backtrace(strcat("Null function in: ", self.classname, "\n"));
1858 remove = remove_unsafely;
1861 .float uncustomizeentityforclient_set;
1862 .void(void) uncustomizeentityforclient;
1863 void(void) SUB_Nullpointer = #0;
1864 void UncustomizeEntitiesRun()
1868 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1869 self.uncustomizeentityforclient();
1872 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1874 e.customizeentityforclient = customizer;
1875 e.uncustomizeentityforclient = uncustomizer;
1876 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1880 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1883 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1887 if (e.classname == "")
1888 e.classname = "net_linked";
1890 if (e.model == "" || self.modelindex == 0)
1894 setmodel(e, "null");
1898 e.SendEntity = sendfunc;
1899 e.SendFlags = 0xFFFFFF;
1902 e.effects |= EF_NODEPTHTEST;
1906 e.nextthink = time + dt;
1907 e.think = SUB_Remove;
1911 void adaptor_think2touch()
1920 void adaptor_think2use()
1932 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1934 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
1935 self.projectiledeathtype |= HITTYPE_SPLASH;
1936 adaptor_think2use();
1939 // deferred dropping
1940 void DropToFloor_Handler()
1942 droptofloor_builtin();
1943 self.dropped_origin = self.origin;
1948 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1953 float trace_hits_box_a0, trace_hits_box_a1;
1955 float trace_hits_box_1d(float end, float thmi, float thma)
1959 // just check if x is in range
1967 // do the trace with respect to x
1968 // 0 -> end has to stay in thmi -> thma
1969 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1970 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1971 if (trace_hits_box_a0 > trace_hits_box_a1)
1977 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1982 // now it is a trace from 0 to end
1984 trace_hits_box_a0 = 0;
1985 trace_hits_box_a1 = 1;
1987 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1989 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1991 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1997 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1999 return trace_hits_box(start, end, thmi - ma, thma - mi);
2002 float SUB_NoImpactCheck()
2004 // zero hitcontents = this is not the real impact, but either the
2005 // mirror-impact of something hitting the projectile instead of the
2006 // projectile hitting the something, or a touchareagrid one. Neither of
2007 // these stop the projectile from moving, so...
2008 if(trace_dphitcontents == 0)
2010 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
2011 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)));
2014 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2016 if (other == world && self.size != '0 0 0')
2019 tic = self.velocity * sys_frametime;
2020 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2021 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2022 if (trace_fraction >= 1)
2024 dprint("Odd... did not hit...?\n");
2026 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2028 dprint("Detected and prevented the sky-grapple bug.\n");
2036 #define SUB_OwnerCheck() (other && (other == self.owner))
2038 void RemoveGrapplingHook(entity pl);
2039 void W_Crylink_Dequeue(entity e);
2040 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
2042 if(SUB_OwnerCheck())
2044 if(SUB_NoImpactCheck())
2046 if(self.classname == "grapplinghook")
2047 RemoveGrapplingHook(self.realowner);
2048 else if(self.classname == "spike")
2050 W_Crylink_Dequeue(self);
2057 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2058 UpdateCSQCProjectile(self);
2061 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2063 float MAX_IPBAN_URIS = 16;
2065 float URI_GET_DISCARD = 0;
2066 float URI_GET_IPBAN = 1;
2067 float URI_GET_IPBAN_END = 16;
2069 void URI_Get_Callback(float id, float status, string data)
2071 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2073 dprint("\nEnd of data.\n");
2075 if(url_URI_Get_Callback(id, status, data))
2079 else if (id == URI_GET_DISCARD)
2083 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2086 OnlineBanList_URI_Get_Callback(id, status, data);
2090 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2094 void print_to(entity e, string s)
2097 sprint(e, strcat(s, "\n"));
2102 string uid2name(string myuid) {
2104 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2107 s = "^1Unregistered Player";
2111 float race_readTime(string map, float pos)
2119 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2122 string race_readUID(string map, float pos)
2130 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2133 float race_readPos(string map, float t) {
2135 for (i = 1; i <= RANKINGS_CNT; ++i)
2136 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2139 return 0; // pos is zero if unranked
2142 void race_writeTime(string map, float t, string myuid)
2151 newpos = race_readPos(map, t);
2154 for(i = 1; i <= RANKINGS_CNT; ++i)
2156 if(race_readUID(map, i) == myuid)
2159 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2160 for (i = prevpos; i > newpos; --i) {
2161 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2162 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2164 } else { // player has no ranked record yet
2165 for (i = RANKINGS_CNT; i > newpos; --i) {
2166 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2167 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2171 // store new time itself
2172 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2173 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2176 string race_readName(string map, float pos)
2184 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2187 string race_placeName(float pos) {
2188 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2190 if(mod(pos, 10) == 1)
2191 return strcat(ftos(pos), "st");
2192 else if(mod(pos, 10) == 2)
2193 return strcat(ftos(pos), "nd");
2194 else if(mod(pos, 10) == 3)
2195 return strcat(ftos(pos), "rd");
2197 return strcat(ftos(pos), "th");
2200 return strcat(ftos(pos), "th");
2202 string getrecords(float page) // 50 records per page
2216 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2218 if (MapInfo_Get_ByID(i))
2220 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2224 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2225 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2233 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2235 if (MapInfo_Get_ByID(i))
2237 r = race_readTime(MapInfo_Map_bspname, 1);
2240 h = race_readName(MapInfo_Map_bspname, 1);
2241 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2249 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2251 if (MapInfo_Get_ByID(i))
2253 r = race_readTime(MapInfo_Map_bspname, 1);
2256 h = race_readName(MapInfo_Map_bspname, 1);
2257 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2263 MapInfo_ClearTemps();
2265 if (s == "" && page == 0)
2266 return "No records are available on this server.\n";
2271 string getrankings()
2284 for (i = 1; i <= RANKINGS_CNT; ++i)
2286 t = race_readTime(map, i);
2289 n = race_readName(map, i);
2290 p = race_placeName(i);
2291 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2294 MapInfo_ClearTemps();
2297 return strcat("No records are available for the map: ", map, "\n");
2299 return strcat("Records for ", map, ":\n", s);
2302 #define LADDER_FIRSTPOINT 100
2303 #define LADDER_CNT 10
2304 // position X still gives LADDER_FIRSTPOINT/X points
2305 #define LADDER_SIZE 30
2306 // ladder shows the top X players
2307 string top_uids[LADDER_SIZE];
2308 float top_scores[LADDER_SIZE];
2311 float i, j, k, uidcnt;
2325 for (k = 0; k < MapInfo_count; ++k)
2327 if (MapInfo_Get_ByID(k))
2329 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2330 if(i == 0) // speed award
2332 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2335 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2337 else // normal record, if it exists (else break)
2339 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2342 myuid = race_readUID(MapInfo_Map_bspname, i);
2345 // string s contains:
2346 // arg 0 = # of speed recs
2347 // arg 1 = # of 1st place recs
2348 // arg 2 = # of 2nd place recs
2350 // LADDER_CNT+1 = total points
2352 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2355 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2357 for (j = 0; j <= LADDER_CNT + 1; ++j)
2359 if(j != LADDER_CNT + 1)
2360 temp_s = strcat(temp_s, "0 ");
2362 temp_s = strcat(temp_s, "0");
2366 tokenize_console(temp_s);
2369 if(i == 0) // speed award
2370 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2372 if(j == 0) // speed award
2373 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2375 s = strcat(s, " ", argv(j)); // just copy over everything else
2378 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2381 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2382 else if(j == i) // wanted rec!
2383 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2385 s = strcat(s, " ", argv(j)); // just copy over everything else
2388 // total points are (by default) calculated like this:
2389 // speedrec = floor(100 / 10) = 10 points
2390 // 1st place = floor(100 / 1) = 100 points
2391 // 2nd place = floor(100 / 2) = 50 points
2392 // 3rd place = floor(100 / 3) = 33 points
2393 // 4th place = floor(100 / 4) = 25 points
2394 // 5th place = floor(100 / 5) = 20 points
2398 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2400 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2402 db_put(TemporaryDB, strcat("ladder", myuid), s);
2409 for (i = 0; i <= uidcnt; ++i) // for each known uid
2411 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2412 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2413 tokenize_console(temp_s);
2414 thiscnt = stof(argv(LADDER_CNT+1));
2416 if(thiscnt > top_scores[LADDER_SIZE-1])
2417 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2419 if(thiscnt > top_scores[j])
2421 for (k = LADDER_SIZE-1; k >= j; --k)
2423 top_uids[k] = top_uids[k-1];
2424 top_scores[k] = top_scores[k-1];
2426 top_uids[j] = thisuid;
2427 top_scores[j] = thiscnt;
2433 s = "^3-----------------------\n\n";
2435 s = strcat(s, "Pos ^3|");
2436 s = strcat(s, " ^7Total ^3|");
2437 for (i = 1; i <= LADDER_CNT; ++i)
2439 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2441 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2443 s = strcat(s, "\n^3----+--------");
2444 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2446 s = strcat(s, "+-----");
2449 for (i = 1; i <= LADDER_CNT - 9; ++i)
2451 s = strcat(s, "+------");
2455 s = strcat(s, "+--------------+--------------------\n");
2457 for (i = 0; i < LADDER_SIZE; ++i)
2459 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2460 tokenize_console(temp_s);
2461 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2463 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2464 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2465 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2467 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2470 for (j = 10; j <= LADDER_CNT; ++j)
2472 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2476 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2477 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2480 MapInfo_ClearTemps();
2483 return "No ladder on this server!\n";
2485 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2489 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2492 vector start, org, delta, end, enddown, mstart;
2495 m = e.dphitcontentsmask;
2496 e.dphitcontentsmask = goodcontents | badcontents;
2499 delta = world.maxs - world.mins;
2501 for (i = 0; i < attempts; ++i)
2503 start_x = org_x + random() * delta_x;
2504 start_y = org_y + random() * delta_y;
2505 start_z = org_z + random() * delta_z;
2507 // rule 1: start inside world bounds, and outside
2508 // solid, and don't start from somewhere where you can
2509 // fall down to evil
2510 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2511 if (trace_fraction >= 1)
2513 if (trace_startsolid)
2515 if (trace_dphitcontents & badcontents)
2517 if (trace_dphitq3surfaceflags & badsurfaceflags)
2520 // rule 2: if we are too high, lower the point
2521 if (trace_fraction * delta_z > maxaboveground)
2522 start = trace_endpos + '0 0 1' * maxaboveground;
2523 enddown = trace_endpos;
2525 // rule 3: make sure we aren't outside the map. This only works
2526 // for somewhat well formed maps. A good rule of thumb is that
2527 // the map should have a convex outside hull.
2528 // these can be traceLINES as we already verified the starting box
2529 mstart = start + 0.5 * (e.mins + e.maxs);
2530 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2531 if (trace_fraction >= 1)
2533 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2534 if (trace_fraction >= 1)
2536 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2537 if (trace_fraction >= 1)
2539 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2540 if (trace_fraction >= 1)
2542 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2543 if (trace_fraction >= 1)
2546 // rule 4: we must "see" some spawnpoint
2547 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2548 if(checkpvs(mstart, sp))
2552 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2553 if(checkpvs(mstart, sp))
2559 // find a random vector to "look at"
2560 end_x = org_x + random() * delta_x;
2561 end_y = org_y + random() * delta_y;
2562 end_z = org_z + random() * delta_z;
2563 end = start + normalize(end - start) * vlen(delta);
2565 // rule 4: start TO end must not be too short
2566 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2567 if (trace_startsolid)
2569 if (trace_fraction < minviewdistance / vlen(delta))
2572 // rule 5: don't want to look at sky
2573 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2576 // rule 6: we must not end up in trigger_hurt
2577 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2583 e.dphitcontentsmask = m;
2587 setorigin(e, start);
2588 e.angles = vectoangles(end - start);
2589 dprint("Needed ", ftos(i + 1), " attempts\n");
2596 float zcurveparticles_effectno;
2597 vector zcurveparticles_start;
2598 float zcurveparticles_spd;
2600 void endzcurveparticles()
2602 if(zcurveparticles_effectno)
2605 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2607 zcurveparticles_effectno = 0;
2610 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2612 spd = bound(0, floor(spd / 16), 32767);
2613 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2615 endzcurveparticles();
2616 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2617 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2618 WriteShort(MSG_BROADCAST, effectno);
2619 WriteCoord(MSG_BROADCAST, start_x);
2620 WriteCoord(MSG_BROADCAST, start_y);
2621 WriteCoord(MSG_BROADCAST, start_z);
2622 zcurveparticles_effectno = effectno;
2623 zcurveparticles_start = start;
2626 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2627 WriteCoord(MSG_BROADCAST, end_x);
2628 WriteCoord(MSG_BROADCAST, end_y);
2629 WriteCoord(MSG_BROADCAST, end_z);
2630 WriteCoord(MSG_BROADCAST, end_dz);
2631 zcurveparticles_spd = spd;
2634 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2637 vector vecxy, velxy;
2639 vecxy = end - start;
2644 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2646 endzcurveparticles();
2647 trailparticles(world, effectno, start, end);
2651 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2652 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2655 void write_recordmarker(entity pl, float tstart, float dt)
2657 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2659 // also write a marker into demo files for demotc-race-record-extractor to find
2662 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2663 " ", ftos(tstart), " ", ftos(dt), "\n"));
2666 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2679 if(allowcenter) // 2: allow center handedness
2692 if(allowcenter) // 2: allow center handedness
2708 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2713 if (autocvar_g_shootfromeye)
2726 else if (autocvar_g_shootfromcenter)
2731 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2741 else if (autocvar_g_shootfromclient)
2743 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2748 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2750 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2754 void attach_sameorigin(entity e, entity to, string tag)
2756 vector org, t_forward, t_left, t_up, e_forward, e_up;
2763 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2764 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2765 t_forward = v_forward * tagscale;
2766 t_left = v_right * -tagscale;
2767 t_up = v_up * tagscale;
2769 e.origin_x = org * t_forward;
2770 e.origin_y = org * t_left;
2771 e.origin_z = org * t_up;
2773 // current forward and up directions
2774 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2775 e.angles = AnglesTransform_FromVAngles(e.angles);
2777 e.angles = AnglesTransform_FromAngles(e.angles);
2778 fixedmakevectors(e.angles);
2780 // untransform forward, up!
2781 e_forward_x = v_forward * t_forward;
2782 e_forward_y = v_forward * t_left;
2783 e_forward_z = v_forward * t_up;
2784 e_up_x = v_up * t_forward;
2785 e_up_y = v_up * t_left;
2786 e_up_z = v_up * t_up;
2788 e.angles = fixedvectoangles2(e_forward, e_up);
2789 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2790 e.angles = AnglesTransform_ToVAngles(e.angles);
2792 e.angles = AnglesTransform_ToAngles(e.angles);
2794 setattachment(e, to, tag);
2795 setorigin(e, e.origin);
2798 void detach_sameorigin(entity e)
2801 org = gettaginfo(e, 0);
2802 e.angles = fixedvectoangles2(v_forward, v_up);
2803 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2804 e.angles = AnglesTransform_ToVAngles(e.angles);
2806 e.angles = AnglesTransform_ToAngles(e.angles);
2808 setattachment(e, world, "");
2809 setorigin(e, e.origin);
2812 void follow_sameorigin(entity e, entity to)
2814 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2815 e.aiment = to; // make the hole follow bmodel
2816 e.punchangle = to.angles; // the original angles of bmodel
2817 e.view_ofs = e.origin - to.origin; // relative origin
2818 e.v_angle = e.angles - to.angles; // relative angles
2821 void unfollow_sameorigin(entity e)
2823 e.movetype = MOVETYPE_NONE;
2826 entity gettaginfo_relative_ent;
2827 vector gettaginfo_relative(entity e, float tag)
2829 if (!gettaginfo_relative_ent)
2831 gettaginfo_relative_ent = spawn();
2832 gettaginfo_relative_ent.effects = EF_NODRAW;
2834 gettaginfo_relative_ent.model = e.model;
2835 gettaginfo_relative_ent.modelindex = e.modelindex;
2836 gettaginfo_relative_ent.frame = e.frame;
2837 return gettaginfo(gettaginfo_relative_ent, tag);
2840 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2844 if (pl.soundentity.cnt & p)
2846 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2847 pl.soundentity.cnt |= p;
2850 void SoundEntity_StopSound(entity pl, float chan)
2854 if (pl.soundentity.cnt & p)
2856 stopsoundto(MSG_ALL, pl.soundentity, chan);
2857 pl.soundentity.cnt &~= p;
2861 void SoundEntity_Attach(entity pl)
2863 pl.soundentity = spawn();
2864 pl.soundentity.classname = "soundentity";
2865 pl.soundentity.owner = pl;
2866 setattachment(pl.soundentity, pl, "");
2867 setmodel(pl.soundentity, "null");
2870 void SoundEntity_Detach(entity pl)
2873 for (i = 0; i <= 7; ++i)
2874 SoundEntity_StopSound(pl, i);
2878 float ParseCommandPlayerSlotTarget_firsttoken;
2879 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2887 ParseCommandPlayerSlotTarget_firsttoken = -1;
2891 if (substring(argv(idx), 0, 1) == "#")
2893 s = substring(argv(idx), 1, -1);
2895 if (s == "") if (tokens > idx)
2900 ParseCommandPlayerSlotTarget_firsttoken = idx;
2902 if (s == ftos(n) && n > 0 && n <= maxclients)
2905 if (e.flags & FL_CLIENT)
2911 // it must be a nick name
2914 ParseCommandPlayerSlotTarget_firsttoken = idx;
2917 FOR_EACH_CLIENT(head)
2918 if (head.netname == s)
2926 s = strdecolorize(s);
2928 FOR_EACH_CLIENT(head)
2929 if (strdecolorize(head.netname) == s)
2944 float modeleffect_SendEntity(entity to, float sf)
2947 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2950 if(self.velocity != '0 0 0')
2952 if(self.angles != '0 0 0')
2954 if(self.avelocity != '0 0 0')
2957 WriteByte(MSG_ENTITY, f);
2958 WriteShort(MSG_ENTITY, self.modelindex);
2959 WriteByte(MSG_ENTITY, self.skin);
2960 WriteByte(MSG_ENTITY, self.frame);
2961 WriteCoord(MSG_ENTITY, self.origin_x);
2962 WriteCoord(MSG_ENTITY, self.origin_y);
2963 WriteCoord(MSG_ENTITY, self.origin_z);
2966 WriteCoord(MSG_ENTITY, self.velocity_x);
2967 WriteCoord(MSG_ENTITY, self.velocity_y);
2968 WriteCoord(MSG_ENTITY, self.velocity_z);
2972 WriteCoord(MSG_ENTITY, self.angles_x);
2973 WriteCoord(MSG_ENTITY, self.angles_y);
2974 WriteCoord(MSG_ENTITY, self.angles_z);
2978 WriteCoord(MSG_ENTITY, self.avelocity_x);
2979 WriteCoord(MSG_ENTITY, self.avelocity_y);
2980 WriteCoord(MSG_ENTITY, self.avelocity_z);
2982 WriteShort(MSG_ENTITY, self.scale * 256.0);
2983 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2984 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2985 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2986 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2991 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)
2996 e.classname = "modeleffect";
3004 e.teleport_time = t1;
3008 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
3012 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
3015 sz = max(e.scale, e.scale2);
3016 setsize(e, e.mins * sz, e.maxs * sz);
3017 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
3020 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
3022 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
3025 float randombit(float bits)
3027 if not(bits & (bits-1)) // this ONLY holds for powers of two!
3036 for(f = 1; f <= bits; f *= 2)
3045 r = (r - 1) / (n - 1);
3052 float randombits(float bits, float k, float error_return)
3056 while(k > 0 && bits != r)
3058 r += randombit(bits - r);
3067 void randombit_test(float bits, float iter)
3071 print(ftos(randombit(bits)), "\n");
3076 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3078 if(halflifedist > 0)
3079 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3080 else if(halflifedist < 0)
3081 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3090 #define cvar_string_normal cvar_string_builtin
3091 #define cvar_normal cvar_builtin
3093 string cvar_string_normal(string n)
3095 if not(cvar_type(n) & 1)
3096 backtrace(strcat("Attempt to access undefined cvar: ", n));
3097 return cvar_string_builtin(n);
3100 float cvar_normal(string n)
3102 return stof(cvar_string_normal(n));
3105 #define cvar_set_normal cvar_set_builtin
3113 oself.think = SUB_Remove;
3114 oself.nextthink = time;
3120 Execute func() after time + fdelay.
3121 self when func is executed = self when defer is called
3123 void defer(float fdelay, void() func)
3130 e.think = defer_think;
3131 e.nextthink = time + fdelay;
3134 .string aiment_classname;
3135 .float aiment_deadflag;
3136 void SetMovetypeFollow(entity ent, entity e)
3138 // FIXME this may not be warpzone aware
3139 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3140 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.
3141 ent.aiment = e; // make the hole follow bmodel
3142 ent.punchangle = e.angles; // the original angles of bmodel
3143 ent.view_ofs = ent.origin - e.origin; // relative origin
3144 ent.v_angle = ent.angles - e.angles; // relative angles
3145 ent.aiment_classname = strzone(e.classname);
3146 ent.aiment_deadflag = e.deadflag;
3148 void UnsetMovetypeFollow(entity ent)
3150 ent.movetype = MOVETYPE_FLY;
3151 PROJECTILE_MAKETRIGGER(ent);
3154 float LostMovetypeFollow(entity ent)
3157 if(ent.movetype != MOVETYPE_FOLLOW)
3163 if(ent.aiment.classname != ent.aiment_classname)
3165 if(ent.aiment.deadflag != ent.aiment_deadflag)
3171 float isPushable(entity e)
3178 case "droppedweapon":
3179 case "keepawayball":
3180 case "nexball_basketball":
3181 case "nexball_football":
3183 case "bullet": // antilagged bullets can't hit this either
3186 if (e.projectiledeathtype)