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;
20 string ColoredTeamName(float t);
22 string admin_name(void)
24 if(autocvar_sv_adminnick != "")
25 return autocvar_sv_adminnick;
27 return "SERVER ADMIN";
30 float DistributeEvenly_amount;
31 float DistributeEvenly_totalweight;
32 void DistributeEvenly_Init(float amount, float totalweight)
34 if (DistributeEvenly_amount)
36 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
37 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
40 DistributeEvenly_amount = 0;
42 DistributeEvenly_amount = amount;
43 DistributeEvenly_totalweight = totalweight;
45 float DistributeEvenly_Get(float weight)
50 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
51 DistributeEvenly_totalweight -= weight;
52 DistributeEvenly_amount -= f;
56 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
59 string STR_PLAYER = "player";
60 string STR_SPECTATOR = "spectator";
61 string STR_OBSERVER = "observer";
64 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
65 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
66 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
67 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
69 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
70 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
71 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
72 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
73 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
76 // copies a string to a tempstring (so one can strunzone it)
77 string strcat1(string s) = #115; // FRIK_FILE
82 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
84 local float nPlayerHealth = rint(enPlayer.health);
85 local float nPlayerArmor = rint(enPlayer.armorvalue);
86 local float nPlayerHandicap = enPlayer.cvar_cl_handicap;
87 local float nPlayerPing = rint(enPlayer.ping);
88 local string strPlayerPingColor;
89 local string strMessage;
90 if(nPlayerPing >= 150)
91 strPlayerPingColor = "^1";
93 strPlayerPingColor = "^2";
95 if((autocvar_sv_fragmessage_information_stats) && (enPlayer.health >= 1))
96 strMessage = strcat(strMessage, "\n^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
98 if(autocvar_sv_fragmessage_information_ping) {
99 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
100 strMessage = strcat(strMessage, " ^7(^2Bot");
102 strMessage = strcat(strMessage, " ^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
103 if(autocvar_sv_fragmessage_information_handicap)
104 if(autocvar_sv_fragmessage_information_handicap == 2)
105 if(nPlayerHandicap <= 1)
106 strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
108 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
109 else if not(nPlayerHandicap <= 1)
110 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
112 strMessage = strcat(strMessage, "^7)");
113 } else if(autocvar_sv_fragmessage_information_handicap) {
114 if(autocvar_sv_fragmessage_information_handicap == 2)
115 if(nPlayerHandicap <= 1)
116 strMessage = strcat(strMessage, "\n^7(Handicap ^2Off^7)");
118 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
119 else if(nPlayerHandicap > 1)
120 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
124 void bcenterprint(string s)
126 // TODO replace by MSG_ALL (would show it to spectators too, though)?
128 FOR_EACH_PLAYER(head)
129 if (clienttype(head) == CLIENTTYPE_REAL)
130 centerprint(head, s);
133 void GameLogEcho(string s)
138 if (autocvar_sv_eventlog_files)
143 matches = autocvar_sv_eventlog_files_counter + 1;
144 cvar_set("sv_eventlog_files_counter", ftos(matches));
147 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
148 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
149 logfile = fopen(fn, FILE_APPEND);
150 fputs(logfile, ":logversion:3\n");
154 if (autocvar_sv_eventlog_files_timestamps)
155 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
157 fputs(logfile, strcat(s, "\n"));
160 if (autocvar_sv_eventlog_console)
169 // will be opened later
174 if (logfile_open && logfile >= 0)
185 vector PL_CROUCH_VIEW_OFS;
186 vector PL_CROUCH_MIN;
187 vector PL_CROUCH_MAX;
189 float spawnpoint_nag;
190 void relocate_spawnpoint()
192 PL_VIEW_OFS = stov(autocvar_sv_player_viewoffset);
193 PL_MIN = stov(autocvar_sv_player_mins);
194 PL_MAX = stov(autocvar_sv_player_maxs);
195 PL_HEAD = stov(autocvar_sv_player_headsize);
196 PL_CROUCH_VIEW_OFS = stov(autocvar_sv_player_crouch_viewoffset);
197 PL_CROUCH_MIN = stov(autocvar_sv_player_crouch_mins);
198 PL_CROUCH_MAX = stov(autocvar_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 (autocvar_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;
239 have_team_spawns_forteam[self.team] = 1;
241 if (autocvar_r_showbboxes)
243 // show where spawnpoints point at too
244 makevectors(self.angles);
247 e.classname = "info_player_foo";
248 setorigin(e, self.origin + v_forward * 24);
249 setsize(e, '-8 -8 -8', '8 8 8');
250 e.solid = SOLID_TRIGGER;
254 #define strstr strstrofs
256 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
257 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
258 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
259 // BE CONSTANT OR strzoneD!
260 float strstr(string haystack, string needle, float offset)
264 len = strlen(needle);
265 endpos = strlen(haystack) - len;
266 while(offset <= endpos)
268 found = substring(haystack, offset, len);
277 float NUM_NEAREST_ENTITIES = 4;
278 entity nearest_entity[NUM_NEAREST_ENTITIES];
279 float nearest_length[NUM_NEAREST_ENTITIES];
280 entity findnearest(vector point, .string field, string value, vector axismod)
291 localhead = find(world, field, value);
294 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
295 dist = localhead.oldorigin;
297 dist = localhead.origin;
299 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
302 for (i = 0; i < num_nearest; ++i)
304 if (len < nearest_length[i])
308 // now i tells us where to insert at
309 // INSERTION SORT! YOU'VE SEEN IT! RUN!
310 if (i < NUM_NEAREST_ENTITIES)
312 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
314 nearest_length[j + 1] = nearest_length[j];
315 nearest_entity[j + 1] = nearest_entity[j];
317 nearest_length[i] = len;
318 nearest_entity[i] = localhead;
319 if (num_nearest < NUM_NEAREST_ENTITIES)
320 num_nearest = num_nearest + 1;
323 localhead = find(localhead, field, value);
326 // now use the first one from our list that we can see
327 for (i = 0; i < num_nearest; ++i)
329 traceline(point, nearest_entity[i].origin, TRUE, world);
330 if (trace_fraction == 1)
334 dprint("Nearest point (");
335 dprint(nearest_entity[0].netname);
336 dprint(") is not visible, using a visible one.\n");
338 return nearest_entity[i];
342 if (num_nearest == 0)
345 dprint("Not seeing any location point, using nearest as fallback.\n");
347 dprint("Candidates were: ");
348 for(j = 0; j < num_nearest; ++j)
352 dprint(nearest_entity[j].netname);
357 return nearest_entity[0];
360 void spawnfunc_target_location()
362 self.classname = "target_location";
363 // location name in netname
364 // eventually support: count, teamgame selectors, line of sight?
367 void spawnfunc_info_location()
369 self.classname = "target_location";
370 self.message = self.netname;
373 string NearestLocation(vector p)
378 loc = findnearest(p, classname, "target_location", '1 1 1');
385 loc = findnearest(p, target, "###item###", '1 1 4');
392 string formatmessage(string msg)
403 WarpZone_crosshair_trace(self);
404 cursor = trace_endpos;
405 cursor_ent = trace_ent;
409 break; // too many replacements
412 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
413 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
426 replacement = substring(msg, p, 2);
427 escape = substring(msg, p + 1, 1);
431 else if (escape == "\\")
433 else if (escape == "n")
435 else if (escape == "a")
436 replacement = ftos(floor(self.armorvalue));
437 else if (escape == "h")
438 replacement = ftos(floor(self.health));
439 else if (escape == "l")
440 replacement = NearestLocation(self.origin);
441 else if (escape == "y")
442 replacement = NearestLocation(cursor);
443 else if (escape == "d")
444 replacement = NearestLocation(self.death_origin);
445 else if (escape == "w") {
449 wep = self.switchweapon;
452 replacement = W_Name(wep);
453 } else if (escape == "W") {
454 if (self.items & IT_SHELLS) replacement = "shells";
455 else if (self.items & IT_NAILS) replacement = "bullets";
456 else if (self.items & IT_ROCKETS) replacement = "rockets";
457 else if (self.items & IT_CELLS) replacement = "cells";
458 else replacement = "batteries"; // ;)
459 } else if (escape == "x") {
460 replacement = cursor_ent.netname;
461 if (!replacement || !cursor_ent)
462 replacement = "nothing";
463 } else if (escape == "s")
464 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
465 else if (escape == "S")
466 replacement = ftos(vlen(self.velocity));
468 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
469 p = p + strlen(replacement);
474 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
475 return (value == 0) ? FALSE : TRUE;
484 >0: receives a cvar from name=argv(f) value=argv(f+1)
486 void GetCvars_handleString(string thisname, float f, .string field, string name)
491 strunzone(self.field);
492 self.field = string_null;
496 if (thisname == name)
499 strunzone(self.field);
500 self.field = strzone(argv(f + 1));
504 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
506 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
508 GetCvars_handleString(thisname, f, field, name);
509 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
510 if (thisname == name)
513 s = func(strcat1(self.field));
516 strunzone(self.field);
517 self.field = strzone(s);
521 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
528 if (thisname == name)
529 self.field = stof(argv(f + 1));
532 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
534 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
541 if (thisname == name)
545 self.field = stof(argv(f + 1));
554 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
557 float w_getbestweapon(entity e);
558 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
561 o = W_FixWeaponOrder_ForceComplete(wo);
562 if(self.weaponorder_byimpulse)
564 strunzone(self.weaponorder_byimpulse);
565 self.weaponorder_byimpulse = string_null;
567 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
570 void GetCvars(float f)
575 s = strcat1(argv(f));
579 MUTATOR_CALLHOOK(GetCvars);
580 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
581 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
582 GetCvars_handleFloat(s, f, cvar_hud_panel_centerprint_time, "hud_panel_centerprint_time");
583 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
584 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
585 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
586 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
587 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
588 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
589 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
590 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
591 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
592 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
593 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
594 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
595 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
596 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
597 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
598 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
599 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
600 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
601 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
602 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
604 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
605 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
607 #ifdef ALLOW_FORCEMODELS
608 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
609 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
611 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
612 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
613 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
615 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
618 if (s == "cl_weaponpriority")
619 self.switchweapon = w_getbestweapon(self);
620 if (s == "cl_allow_uidtracking")
621 PlayerStats_AddPlayer(self);
625 void backtrace(string msg)
628 dev = autocvar_developer;
629 war = autocvar_prvm_backtraceforwarnings;
630 cvar_set("developer", "1");
631 cvar_set("prvm_backtraceforwarnings", "1");
633 print("--- CUT HERE ---\nWARNING: ");
636 remove(world); // isn't there any better way to cause a backtrace?
637 print("\n--- CUT UNTIL HERE ---\n");
638 cvar_set("developer", ftos(dev));
639 cvar_set("prvm_backtraceforwarnings", ftos(war));
642 string Team_ColorCode(float teamid)
644 if (teamid == COLOR_TEAM1)
646 else if (teamid == COLOR_TEAM2)
648 else if (teamid == COLOR_TEAM3)
650 else if (teamid == COLOR_TEAM4)
656 string Team_ColorName(float t)
658 // fixme: Search for team entities and get their .netname's!
659 if (t == COLOR_TEAM1)
661 if (t == COLOR_TEAM2)
663 if (t == COLOR_TEAM3)
665 if (t == COLOR_TEAM4)
670 string Team_ColorNameLowerCase(float t)
672 // fixme: Search for team entities and get their .netname's!
673 if (t == COLOR_TEAM1)
675 if (t == COLOR_TEAM2)
677 if (t == COLOR_TEAM3)
679 if (t == COLOR_TEAM4)
684 float ColourToNumber(string team_colour)
686 if (team_colour == "red")
689 if (team_colour == "blue")
692 if (team_colour == "yellow")
695 if (team_colour == "pink")
698 if (team_colour == "auto")
704 float NumberToTeamNumber(float number)
721 void Send_CSQC_Centerprint_Generic(entity e, float id, string s1, float duration, float countdown_num);
722 void centerprint(entity e, string s)
724 Send_CSQC_Centerprint_Generic(e, 0, s, 0, 0);
727 // decolorizes and team colors the player name when needed
728 string playername(entity p)
731 if (teamplay && !intermission_running && p.classname == "player")
733 t = Team_ColorCode(p.team);
734 return strcat(t, strdecolorize(p.netname));
740 vector randompos(vector m1, vector m2)
744 v_x = m2_x * random() + m1_x;
745 v_y = m2_y * random() + m1_y;
746 v_z = m2_z * random() + m1_z;
750 //#NO AUTOCVARS START
752 float g_pickup_shells;
753 float g_pickup_shells_max;
754 float g_pickup_nails;
755 float g_pickup_nails_max;
756 float g_pickup_rockets;
757 float g_pickup_rockets_max;
758 float g_pickup_cells;
759 float g_pickup_cells_max;
761 float g_pickup_fuel_jetpack;
762 float g_pickup_fuel_max;
763 float g_pickup_armorsmall;
764 float g_pickup_armorsmall_max;
765 float g_pickup_armorsmall_anyway;
766 float g_pickup_armormedium;
767 float g_pickup_armormedium_max;
768 float g_pickup_armormedium_anyway;
769 float g_pickup_armorbig;
770 float g_pickup_armorbig_max;
771 float g_pickup_armorbig_anyway;
772 float g_pickup_armorlarge;
773 float g_pickup_armorlarge_max;
774 float g_pickup_armorlarge_anyway;
775 float g_pickup_healthsmall;
776 float g_pickup_healthsmall_max;
777 float g_pickup_healthsmall_anyway;
778 float g_pickup_healthmedium;
779 float g_pickup_healthmedium_max;
780 float g_pickup_healthmedium_anyway;
781 float g_pickup_healthlarge;
782 float g_pickup_healthlarge_max;
783 float g_pickup_healthlarge_anyway;
784 float g_pickup_healthmega;
785 float g_pickup_healthmega_max;
786 float g_pickup_healthmega_anyway;
787 float g_pickup_ammo_anyway;
788 float g_pickup_weapons_anyway;
790 float g_weaponarena_random;
791 float g_weaponarena_random_with_laser;
792 string g_weaponarena_list;
793 float g_weaponspeedfactor;
794 float g_weaponratefactor;
795 float g_weapondamagefactor;
796 float g_weaponforcefactor;
797 float g_weaponspreadfactor;
801 float start_ammo_shells;
802 float start_ammo_nails;
803 float start_ammo_rockets;
804 float start_ammo_cells;
805 float start_ammo_fuel;
807 float start_armorvalue;
808 float warmup_start_weapons;
809 float warmup_start_ammo_shells;
810 float warmup_start_ammo_nails;
811 float warmup_start_ammo_rockets;
812 float warmup_start_ammo_cells;
813 float warmup_start_ammo_fuel;
814 float warmup_start_health;
815 float warmup_start_armorvalue;
819 entity get_weaponinfo(float w);
821 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
823 var float i = weaponinfo.weapon;
828 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
830 if (t < 0) // "default" weapon selection
832 if (g_lms || g_ca || allguns)
833 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
837 t = (i == WEP_SHOTGUN);
839 t = 0; // weapon is set a few lines later
841 t = (i == WEP_LASER || i == WEP_SHOTGUN);
842 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
843 t |= (i == WEP_HOOK);
846 // we cannot disable porto in Nexball, we must force it
847 if(g_nexball && i == WEP_PORTO)
853 void readplayerstartcvars()
859 // initialize starting values for players
862 start_ammo_shells = 0;
863 start_ammo_nails = 0;
864 start_ammo_rockets = 0;
865 start_ammo_cells = 0;
866 start_health = cvar("g_balance_health_start");
867 start_armorvalue = cvar("g_balance_armor_start");
870 s = cvar_string("g_weaponarena");
871 if (s == "0" || s == "")
877 if (s == "0" || s == "")
883 // forcibly turn off weaponarena
887 g_weaponarena_list = "All Weapons";
888 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
890 e = get_weaponinfo(j);
891 g_weaponarena |= e.weapons;
892 weapon_action(e.weapon, WR_PRECACHE);
895 else if (s == "most")
897 g_weaponarena_list = "Most Weapons";
898 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
900 e = get_weaponinfo(j);
901 if (e.spawnflags & WEP_FLAG_NORMAL)
903 g_weaponarena |= e.weapons;
904 weapon_action(e.weapon, WR_PRECACHE);
908 else if (s == "none")
910 g_weaponarena_list = "No Weapons";
911 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
915 t = tokenize_console(s);
916 g_weaponarena_list = "";
917 for (i = 0; i < t; ++i)
920 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
922 e = get_weaponinfo(j);
925 g_weaponarena |= e.weapons;
926 weapon_action(e.weapon, WR_PRECACHE);
927 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
933 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
936 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
940 g_weaponarena_random = cvar("g_weaponarena_random");
942 g_weaponarena_random = 0;
943 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
947 start_weapons = g_weaponarena;
949 start_items |= IT_UNLIMITED_AMMO;
951 else if (g_minstagib)
954 start_armorvalue = 0;
955 start_weapons = WEPBIT_MINSTANEX;
956 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
957 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
959 if (g_minstagib_invis_alpha <= 0)
960 g_minstagib_invis_alpha = -1;
964 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
966 e = get_weaponinfo(i);
967 if(want_weapon("g_start_weapon_", e, FALSE))
968 start_weapons |= e.weapons;
972 if(!cvar("g_use_ammunition"))
973 start_items |= IT_UNLIMITED_AMMO;
977 start_ammo_cells = cvar("g_minstagib_ammo_start");
978 start_ammo_fuel = cvar("g_start_ammo_fuel");
980 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
982 start_ammo_rockets = 999;
983 start_ammo_shells = 999;
984 start_ammo_cells = 999;
985 start_ammo_nails = 999;
986 start_ammo_fuel = 999;
992 start_ammo_shells = cvar("g_lms_start_ammo_shells");
993 start_ammo_nails = cvar("g_lms_start_ammo_nails");
994 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
995 start_ammo_cells = cvar("g_lms_start_ammo_cells");
996 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1000 start_ammo_shells = cvar("g_start_ammo_shells");
1001 start_ammo_nails = cvar("g_start_ammo_nails");
1002 start_ammo_rockets = cvar("g_start_ammo_rockets");
1003 start_ammo_cells = cvar("g_start_ammo_cells");
1004 start_ammo_fuel = cvar("g_start_ammo_fuel");
1010 start_health = cvar("g_lms_start_health");
1011 start_armorvalue = cvar("g_lms_start_armor");
1016 warmup_start_ammo_shells = start_ammo_shells;
1017 warmup_start_ammo_nails = start_ammo_nails;
1018 warmup_start_ammo_rockets = start_ammo_rockets;
1019 warmup_start_ammo_cells = start_ammo_cells;
1020 warmup_start_ammo_fuel = start_ammo_fuel;
1021 warmup_start_health = start_health;
1022 warmup_start_armorvalue = start_armorvalue;
1023 warmup_start_weapons = start_weapons;
1025 if (!g_weaponarena && !g_minstagib && !g_ca)
1027 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1028 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1029 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1030 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1031 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1032 warmup_start_health = cvar("g_warmup_start_health");
1033 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1034 warmup_start_weapons = 0;
1035 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1037 e = get_weaponinfo(i);
1038 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1039 warmup_start_weapons |= e.weapons;
1044 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1046 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1047 start_items |= IT_FUEL_REGEN;
1048 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1049 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1053 start_items |= IT_JETPACK;
1055 if (g_weapon_stay == 2)
1057 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1058 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1059 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1060 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1061 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1062 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1063 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1064 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1065 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1066 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1069 MUTATOR_CALLHOOK(SetStartItems);
1071 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1073 e = get_weaponinfo(i);
1074 if(e.weapons & (start_weapons | warmup_start_weapons))
1075 weapon_action(e.weapon, WR_PRECACHE);
1078 start_ammo_shells = max(0, start_ammo_shells);
1079 start_ammo_nails = max(0, start_ammo_nails);
1080 start_ammo_cells = max(0, start_ammo_cells);
1081 start_ammo_rockets = max(0, start_ammo_rockets);
1082 start_ammo_fuel = max(0, start_ammo_fuel);
1084 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1085 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1086 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1087 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1088 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1092 float g_bugrigs_planar_movement;
1093 float g_bugrigs_planar_movement_car_jumping;
1094 float g_bugrigs_reverse_spinning;
1095 float g_bugrigs_reverse_speeding;
1096 float g_bugrigs_reverse_stopping;
1097 float g_bugrigs_air_steering;
1098 float g_bugrigs_angle_smoothing;
1099 float g_bugrigs_friction_floor;
1100 float g_bugrigs_friction_brake;
1101 float g_bugrigs_friction_air;
1102 float g_bugrigs_accel;
1103 float g_bugrigs_speed_ref;
1104 float g_bugrigs_speed_pow;
1105 float g_bugrigs_steer;
1107 float g_touchexplode;
1108 float g_touchexplode_radius;
1109 float g_touchexplode_damage;
1110 float g_touchexplode_edgedamage;
1111 float g_touchexplode_force;
1118 float sv_pitch_fixyaw;
1120 string GetGametype(); // g_world.qc
1121 void readlevelcvars(void)
1123 // first load all the mutators
1125 MUTATOR_ADD(mutator_nix);
1126 if(cvar("g_dodging"))
1127 MUTATOR_ADD(mutator_dodging);
1128 if(cvar("g_rocket_flying"))
1129 MUTATOR_ADD(mutator_rocketflying);
1130 if(cvar("g_vampire"))
1131 MUTATOR_ADD(mutator_vampire);
1133 if(cvar("sv_allow_fullbright"))
1134 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1136 g_bugrigs = cvar("g_bugrigs");
1137 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1138 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1139 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1140 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1141 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1142 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1143 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1144 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1145 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1146 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1147 g_bugrigs_accel = cvar("g_bugrigs_accel");
1148 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1149 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1150 g_bugrigs_steer = cvar("g_bugrigs_steer");
1152 g_touchexplode = cvar("g_touchexplode");
1153 g_touchexplode_radius = cvar("g_touchexplode_radius");
1154 g_touchexplode_damage = cvar("g_touchexplode_damage");
1155 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1156 g_touchexplode_force = cvar("g_touchexplode_force");
1158 #ifdef ALLOW_FORCEMODELS
1159 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1161 sv_loddistance1 = cvar("sv_loddistance1");
1162 sv_loddistance2 = cvar("sv_loddistance2");
1164 if(sv_loddistance2 <= sv_loddistance1)
1165 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1167 sv_clones = cvar("sv_clones");
1168 sv_gentle = cvar("sv_gentle");
1169 sv_foginterval = cvar("sv_foginterval");
1170 g_cloaked = cvar("g_cloaked");
1172 g_cloaked = 1; // always enable cloak in CTS
1173 g_jump_grunt = cvar("g_jump_grunt");
1174 g_footsteps = cvar("g_footsteps");
1175 g_grappling_hook = cvar("g_grappling_hook");
1176 g_jetpack = cvar("g_jetpack");
1177 g_midair = cvar("g_midair");
1178 g_minstagib = cvar("g_minstagib");
1179 g_norecoil = cvar("g_norecoil");
1180 g_bloodloss = cvar("g_bloodloss");
1181 sv_maxidle = cvar("sv_maxidle");
1182 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1183 sv_pogostick = cvar("sv_pogostick");
1184 g_ctf_reverse = cvar("g_ctf_reverse");
1185 sv_autotaunt = cvar("sv_autotaunt");
1186 sv_taunt = cvar("sv_taunt");
1188 inWarmupStage = cvar("g_warmup");
1189 g_warmup_limit = cvar("g_warmup_limit");
1190 g_warmup_allguns = cvar("g_warmup_allguns");
1191 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1193 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1194 inWarmupStage = 0; // these modes cannot work together, sorry
1196 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1197 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1198 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1199 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1200 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1201 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1202 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1203 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1204 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1205 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1206 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1207 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1209 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1210 g_weaponratefactor = cvar("g_weaponratefactor");
1211 g_weapondamagefactor = cvar("g_weapondamagefactor");
1212 g_weaponforcefactor = cvar("g_weaponforcefactor");
1213 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1215 g_pickup_shells = cvar("g_pickup_shells");
1216 g_pickup_shells_max = cvar("g_pickup_shells_max");
1217 g_pickup_nails = cvar("g_pickup_nails");
1218 g_pickup_nails_max = cvar("g_pickup_nails_max");
1219 g_pickup_rockets = cvar("g_pickup_rockets");
1220 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1221 g_pickup_cells = cvar("g_pickup_cells");
1222 g_pickup_cells_max = cvar("g_pickup_cells_max");
1223 g_pickup_fuel = cvar("g_pickup_fuel");
1224 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1225 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1226 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1227 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1228 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1229 g_pickup_armormedium = cvar("g_pickup_armormedium");
1230 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1231 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1232 g_pickup_armorbig = cvar("g_pickup_armorbig");
1233 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1234 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1235 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1236 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1237 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1238 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1239 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1240 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1241 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1242 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1243 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1244 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1245 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1246 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1247 g_pickup_healthmega = cvar("g_pickup_healthmega");
1248 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1249 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1251 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1252 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1254 g_pinata = cvar("g_pinata");
1256 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1258 g_weapon_stay = cvar("g_weapon_stay");
1260 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1263 g_ghost_items = cvar("g_ghost_items");
1265 if(g_ghost_items >= 1)
1266 g_ghost_items = 0.25; // default alpha value
1268 if not(inWarmupStage && !g_ca)
1269 game_starttime = cvar("g_start_delay");
1271 sv_pitch_min = cvar("sv_pitch_min");
1272 sv_pitch_max = cvar("sv_pitch_max");
1273 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1275 readplayerstartcvars();
1281 string precache_sound (string s) = #19;
1282 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1283 float precache_sound_index (string s) = #19;
1285 #define SND_VOLUME 1
1286 #define SND_ATTENUATION 2
1287 #define SND_LARGEENTITY 8
1288 #define SND_LARGESOUND 16
1290 float sound_allowed(float dest, entity e)
1292 // sounds from world may always pass
1295 if (e.classname == "body")
1297 if (e.owner && e.owner != e)
1302 // sounds to self may always pass
1303 if (dest == MSG_ONE)
1304 if (e == msg_entity)
1306 // sounds by players can be removed
1307 if (autocvar_bot_sound_monopoly)
1308 if (clienttype(e) == CLIENTTYPE_REAL)
1310 // anything else may pass
1314 void sound(entity e, float chan, string samp, float vol, float atten)
1316 if (!sound_allowed(MSG_BROADCAST, e))
1318 sound_builtin(e, chan, samp, vol, atten);
1320 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1324 if (!sound_allowed(dest, e))
1327 entno = num_for_edict(e);
1328 idx = precache_sound_index(samp);
1333 atten = floor(atten * 64);
1334 vol = floor(vol * 255);
1337 sflags |= SND_VOLUME;
1339 sflags |= SND_ATTENUATION;
1341 sflags |= SND_LARGEENTITY;
1343 sflags |= SND_LARGESOUND;
1345 WriteByte(dest, SVC_SOUND);
1346 WriteByte(dest, sflags);
1347 if (sflags & SND_VOLUME)
1348 WriteByte(dest, vol);
1349 if (sflags & SND_ATTENUATION)
1350 WriteByte(dest, atten);
1351 if (sflags & SND_LARGEENTITY)
1353 WriteShort(dest, entno);
1354 WriteByte(dest, chan);
1358 WriteShort(dest, entno * 8 + chan);
1360 if (sflags & SND_LARGESOUND)
1361 WriteShort(dest, idx);
1363 WriteByte(dest, idx);
1365 WriteCoord(dest, o_x);
1366 WriteCoord(dest, o_y);
1367 WriteCoord(dest, o_z);
1369 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1373 if (!sound_allowed(dest, e))
1376 o = e.origin + 0.5 * (e.mins + e.maxs);
1377 soundtoat(dest, e, o, chan, samp, vol, atten);
1379 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1381 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1383 void stopsoundto(float dest, entity e, float chan)
1387 if (!sound_allowed(dest, e))
1390 entno = num_for_edict(e);
1395 idx = precache_sound_index("misc/null.wav");
1396 sflags = SND_LARGEENTITY;
1398 sflags |= SND_LARGESOUND;
1399 WriteByte(dest, SVC_SOUND);
1400 WriteByte(dest, sflags);
1401 WriteShort(dest, entno);
1402 WriteByte(dest, chan);
1403 if (sflags & SND_LARGESOUND)
1404 WriteShort(dest, idx);
1406 WriteByte(dest, idx);
1407 WriteCoord(dest, e.origin_x);
1408 WriteCoord(dest, e.origin_y);
1409 WriteCoord(dest, e.origin_z);
1413 WriteByte(dest, SVC_STOPSOUND);
1414 WriteShort(dest, entno * 8 + chan);
1417 void stopsound(entity e, float chan)
1419 if (!sound_allowed(MSG_BROADCAST, e))
1422 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1423 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1426 void play2(entity e, string filename)
1428 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1430 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1433 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1435 float spamsound(entity e, float chan, string samp, float vol, float atten)
1437 if (!sound_allowed(MSG_BROADCAST, e))
1440 if (time > e.spamtime)
1443 sound(e, chan, samp, vol, atten);
1449 void play2team(float t, string filename)
1453 if (autocvar_bot_sound_monopoly)
1456 FOR_EACH_REALPLAYER(head)
1459 play2(head, filename);
1463 void play2all(string samp)
1465 if (autocvar_bot_sound_monopoly)
1468 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1471 void PrecachePlayerSounds(string f);
1472 void precache_playermodel(string m)
1474 float globhandle, i, n;
1477 if(substring(m, -9,5) == "_lod1")
1479 if(substring(m, -9,5) == "_lod2")
1484 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1487 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1492 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1495 n = search_getsize(globhandle);
1496 for (i = 0; i < n; ++i)
1498 //print(search_getfilename(globhandle, i), "\n");
1499 f = search_getfilename(globhandle, i);
1500 PrecachePlayerSounds(f);
1502 search_end(globhandle);
1504 void precache_all_playermodels(string pattern)
1506 float globhandle, i, n;
1509 globhandle = search_begin(pattern, TRUE, FALSE);
1512 n = search_getsize(globhandle);
1513 for (i = 0; i < n; ++i)
1515 //print(search_getfilename(globhandle, i), "\n");
1516 f = search_getfilename(globhandle, i);
1517 precache_playermodel(f);
1519 search_end(globhandle);
1524 // gamemode related things
1525 precache_model ("models/misc/chatbubble.spr");
1528 precache_model ("models/runematch/curse.mdl");
1529 precache_model ("models/runematch/rune.mdl");
1532 #ifdef TTURRETS_ENABLED
1533 if (autocvar_g_turrets)
1537 // Precache all player models if desired
1538 if (autocvar_sv_precacheplayermodels)
1540 PrecachePlayerSounds("sound/player/default.sounds");
1541 precache_all_playermodels("models/player/*.zym");
1542 precache_all_playermodels("models/player/*.dpm");
1543 precache_all_playermodels("models/player/*.md3");
1544 precache_all_playermodels("models/player/*.psk");
1545 precache_all_playermodels("models/player/*.iqm");
1548 if (autocvar_sv_defaultcharacter)
1551 s = autocvar_sv_defaultplayermodel_red;
1553 precache_playermodel(s);
1554 s = autocvar_sv_defaultplayermodel_blue;
1556 precache_playermodel(s);
1557 s = autocvar_sv_defaultplayermodel_yellow;
1559 precache_playermodel(s);
1560 s = autocvar_sv_defaultplayermodel_pink;
1562 precache_playermodel(s);
1563 s = autocvar_sv_defaultplayermodel;
1565 precache_playermodel(s);
1570 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1571 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1574 // gore and miscellaneous sounds
1575 //precache_sound ("misc/h2ohit.wav");
1576 precache_model ("models/hook.md3");
1577 precache_sound ("misc/armorimpact.wav");
1578 precache_sound ("misc/bodyimpact1.wav");
1579 precache_sound ("misc/bodyimpact2.wav");
1580 precache_sound ("misc/gib.wav");
1581 precache_sound ("misc/gib_splat01.wav");
1582 precache_sound ("misc/gib_splat02.wav");
1583 precache_sound ("misc/gib_splat03.wav");
1584 precache_sound ("misc/gib_splat04.wav");
1585 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1586 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1587 precache_sound ("misc/null.wav");
1588 precache_sound ("misc/spawn.wav");
1589 precache_sound ("misc/talk.wav");
1590 precache_sound ("misc/teleport.wav");
1591 precache_sound ("misc/poweroff.wav");
1592 precache_sound ("player/lava.wav");
1593 precache_sound ("player/slime.wav");
1596 precache_sound ("misc/jetpack_fly.wav");
1598 precache_model ("models/sprites/0.spr32");
1599 precache_model ("models/sprites/1.spr32");
1600 precache_model ("models/sprites/2.spr32");
1601 precache_model ("models/sprites/3.spr32");
1602 precache_model ("models/sprites/4.spr32");
1603 precache_model ("models/sprites/5.spr32");
1604 precache_model ("models/sprites/6.spr32");
1605 precache_model ("models/sprites/7.spr32");
1606 precache_model ("models/sprites/8.spr32");
1607 precache_model ("models/sprites/9.spr32");
1608 precache_model ("models/sprites/10.spr32");
1610 // common weapon precaches
1611 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1612 precache_sound ("weapons/weapon_switch.wav");
1613 precache_sound ("weapons/weaponpickup.wav");
1614 precache_sound ("weapons/unavailable.wav");
1615 precache_sound ("weapons/dryfire.wav");
1616 if (g_grappling_hook)
1618 precache_sound ("weapons/hook_fire.wav"); // hook
1619 precache_sound ("weapons/hook_impact.wav"); // hook
1622 if(autocvar_sv_precacheweapons)
1624 //precache weapon models/sounds
1627 while (wep <= WEP_LAST)
1629 weapon_action(wep, WR_PRECACHE);
1634 precache_model("models/elaser.mdl");
1635 precache_model("models/laser.mdl");
1636 precache_model("models/ebomb.mdl");
1639 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1641 if (!self.noise && self.music) // quake 3 uses the music field
1642 self.noise = self.music;
1644 // plays music for the level if there is any
1647 precache_sound (self.noise);
1648 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1653 // sorry, but using \ in macros breaks line numbers
1654 #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
1655 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1656 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1659 void Send_CSQC_Centerprint_Generic(entity e, float id, string s1, float duration, float countdown_num)
1661 if (clienttype(e) == CLIENTTYPE_REAL)
1664 WRITESPECTATABLE_MSG_ONE({
1665 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1666 WriteByte(MSG_ONE, TE_CSQC_NOTIFY);
1667 WriteByte(MSG_ONE, CSQC_CENTERPRINT_GENERIC);
1668 WriteByte(MSG_ONE, id);
1669 WriteString(MSG_ONE, s1);
1672 WriteByte(MSG_ONE, duration);
1673 WriteByte(MSG_ONE, countdown_num);
1678 // WARNING: this kills the trace globals
1679 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1680 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1682 #define INITPRIO_FIRST 0
1683 #define INITPRIO_GAMETYPE 0
1684 #define INITPRIO_GAMETYPE_FALLBACK 1
1685 #define INITPRIO_FINDTARGET 10
1686 #define INITPRIO_DROPTOFLOOR 20
1687 #define INITPRIO_SETLOCATION 90
1688 #define INITPRIO_LINKDOORS 91
1689 #define INITPRIO_LAST 99
1691 .void(void) initialize_entity;
1692 .float initialize_entity_order;
1693 .entity initialize_entity_next;
1694 entity initialize_entity_first;
1696 void make_safe_for_remove(entity e)
1698 if (e.initialize_entity)
1701 for (ent = initialize_entity_first; ent; )
1703 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1705 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1706 // skip it in linked list
1709 prev.initialize_entity_next = ent.initialize_entity_next;
1710 ent = prev.initialize_entity_next;
1714 initialize_entity_first = ent.initialize_entity_next;
1715 ent = initialize_entity_first;
1721 ent = ent.initialize_entity_next;
1727 void objerror(string s)
1729 make_safe_for_remove(self);
1730 objerror_builtin(s);
1733 .float remove_except_protected_forbidden;
1734 void remove_except_protected(entity e)
1736 if(e.remove_except_protected_forbidden)
1737 error("not allowed to remove this at this point");
1741 void remove_unsafely(entity e)
1746 void remove_safely(entity e)
1748 make_safe_for_remove(e);
1752 void InitializeEntity(entity e, void(void) func, float order)
1756 if (!e || e.initialize_entity)
1758 // make a proxy initializer entity
1762 e.classname = "initialize_entity";
1766 e.initialize_entity = func;
1767 e.initialize_entity_order = order;
1769 cur = initialize_entity_first;
1772 if (!cur || cur.initialize_entity_order > order)
1774 // insert between prev and cur
1776 prev.initialize_entity_next = e;
1778 initialize_entity_first = e;
1779 e.initialize_entity_next = cur;
1783 cur = cur.initialize_entity_next;
1786 void InitializeEntitiesRun()
1789 startoflist = initialize_entity_first;
1790 initialize_entity_first = world;
1791 remove = remove_except_protected;
1792 for (self = startoflist; self; self = self.initialize_entity_next)
1794 self.remove_except_protected_forbidden = 1;
1796 for (self = startoflist; self; )
1799 var void(void) func;
1800 e = self.initialize_entity_next;
1801 func = self.initialize_entity;
1802 self.initialize_entity_order = 0;
1803 self.initialize_entity = func_null;
1804 self.initialize_entity_next = world;
1805 self.remove_except_protected_forbidden = 0;
1806 if (self.classname == "initialize_entity")
1810 remove_builtin(self);
1813 //dprint("Delayed initialization: ", self.classname, "\n");
1814 if(func != func_null)
1819 backtrace(strcat("Null function in: ", self.classname, "\n"));
1823 remove = remove_unsafely;
1826 .float uncustomizeentityforclient_set;
1827 .void(void) uncustomizeentityforclient;
1828 void(void) SUB_Nullpointer = #0;
1829 void UncustomizeEntitiesRun()
1833 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1834 self.uncustomizeentityforclient();
1837 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1839 e.customizeentityforclient = customizer;
1840 e.uncustomizeentityforclient = uncustomizer;
1841 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1845 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1848 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1852 if (e.classname == "")
1853 e.classname = "net_linked";
1855 if (e.model == "" || self.modelindex == 0)
1859 setmodel(e, "null");
1863 e.SendEntity = sendfunc;
1864 e.SendFlags = 0xFFFFFF;
1867 e.effects |= EF_NODEPTHTEST;
1871 e.nextthink = time + dt;
1872 e.think = SUB_Remove;
1876 void adaptor_think2touch()
1885 void adaptor_think2use()
1897 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1899 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
1900 self.projectiledeathtype |= HITTYPE_SPLASH;
1901 adaptor_think2use();
1904 // deferred dropping
1905 void DropToFloor_Handler()
1907 droptofloor_builtin();
1908 self.dropped_origin = self.origin;
1913 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1918 float trace_hits_box_a0, trace_hits_box_a1;
1920 float trace_hits_box_1d(float end, float thmi, float thma)
1924 // just check if x is in range
1932 // do the trace with respect to x
1933 // 0 -> end has to stay in thmi -> thma
1934 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1935 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1936 if (trace_hits_box_a0 > trace_hits_box_a1)
1942 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1947 // now it is a trace from 0 to end
1949 trace_hits_box_a0 = 0;
1950 trace_hits_box_a1 = 1;
1952 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1954 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1956 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1962 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1964 return trace_hits_box(start, end, thmi - ma, thma - mi);
1967 float SUB_NoImpactCheck()
1969 // zero hitcontents = this is not the real impact, but either the
1970 // mirror-impact of something hitting the projectile instead of the
1971 // projectile hitting the something, or a touchareagrid one. Neither of
1972 // these stop the projectile from moving, so...
1973 if(trace_dphitcontents == 0)
1975 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1976 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)));
1979 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1981 if (other == world && self.size != '0 0 0')
1984 tic = self.velocity * sys_frametime;
1985 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1986 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1987 if (trace_fraction >= 1)
1989 dprint("Odd... did not hit...?\n");
1991 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1993 dprint("Detected and prevented the sky-grapple bug.\n");
2001 #define SUB_OwnerCheck() (other && (other == self.owner))
2003 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
2005 if(SUB_OwnerCheck())
2007 if(SUB_NoImpactCheck())
2012 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2013 UpdateCSQCProjectileNextFrame(self);
2016 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2018 float MAX_IPBAN_URIS = 16;
2020 float URI_GET_DISCARD = 0;
2021 float URI_GET_IPBAN = 1;
2022 float URI_GET_IPBAN_END = 16;
2023 float URI_GET_PLAYERSTATS_SENT = 17;
2025 void URI_Get_Callback(float id, float status, string data)
2027 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2029 dprint("\nEnd of data.\n");
2031 if (id == URI_GET_DISCARD)
2035 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2038 OnlineBanList_URI_Get_Callback(id, status, data);
2040 else if (id == URI_GET_PLAYERSTATS_SENT)
2042 PlayerStats_Sent_URI_Get_Callback(id, status, data);
2046 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2050 void print_to(entity e, string s)
2053 sprint(e, strcat(s, "\n"));
2058 string uid2name(string myuid) {
2060 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2063 s = "^1Unregistered Player";
2067 float race_readTime(string map, float pos)
2075 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2078 string race_readUID(string map, float pos)
2086 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2089 float race_readPos(string map, float t) {
2091 for (i = 1; i <= RANKINGS_CNT; ++i)
2092 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2095 return 0; // pos is zero if unranked
2098 void race_writeTime(string map, float t, string myuid)
2107 newpos = race_readPos(map, t);
2110 for(i = 1; i <= RANKINGS_CNT; ++i)
2112 if(race_readUID(map, i) == myuid)
2115 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2116 for (i = prevpos; i > newpos; --i) {
2117 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2118 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2120 } else { // player has no ranked record yet
2121 for (i = RANKINGS_CNT; i > newpos; --i) {
2122 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2123 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2127 // store new time itself
2128 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2129 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), 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((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2146 if(mod(pos, 10) == 1)
2147 return strcat(ftos(pos), "st");
2148 else if(mod(pos, 10) == 2)
2149 return strcat(ftos(pos), "nd");
2150 else if(mod(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 #define LADDER_FIRSTPOINT 100
2259 #define LADDER_CNT 10
2260 // position X still gives LADDER_FIRSTPOINT/X points
2261 #define LADDER_SIZE 30
2262 // ladder shows the top X players
2263 string top_uids[LADDER_SIZE];
2264 float top_scores[LADDER_SIZE];
2267 float i, j, k, uidcnt;
2281 for (k = 0; k < MapInfo_count; ++k)
2283 if (MapInfo_Get_ByID(k))
2285 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2286 if(i == 0) // speed award
2288 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2291 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2293 else // normal record, if it exists (else break)
2295 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2298 myuid = race_readUID(MapInfo_Map_bspname, i);
2301 // string s contains:
2302 // arg 0 = # of speed recs
2303 // arg 1 = # of 1st place recs
2304 // arg 2 = # of 2nd place recs
2306 // LADDER_CNT+1 = total points
2308 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2311 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2313 for (j = 0; j <= LADDER_CNT + 1; ++j)
2315 if(j != LADDER_CNT + 1)
2316 temp_s = strcat(temp_s, "0 ");
2318 temp_s = strcat(temp_s, "0");
2322 tokenize_console(temp_s);
2325 if(i == 0) // speed award
2326 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2328 if(j == 0) // speed award
2329 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2331 s = strcat(s, " ", argv(j)); // just copy over everything else
2334 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2337 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2338 else if(j == i) // wanted rec!
2339 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2341 s = strcat(s, " ", argv(j)); // just copy over everything else
2344 // total points are (by default) calculated like this:
2345 // speedrec = floor(100 / 10) = 10 points
2346 // 1st place = floor(100 / 1) = 100 points
2347 // 2nd place = floor(100 / 2) = 50 points
2348 // 3rd place = floor(100 / 3) = 33 points
2349 // 4th place = floor(100 / 4) = 25 points
2350 // 5th place = floor(100 / 5) = 20 points
2354 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2356 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2358 db_put(TemporaryDB, strcat("ladder", myuid), s);
2365 for (i = 0; i <= uidcnt; ++i) // for each known uid
2367 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2368 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2369 tokenize_console(temp_s);
2370 thiscnt = stof(argv(LADDER_CNT+1));
2372 if(thiscnt > top_scores[LADDER_SIZE-1])
2373 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2375 if(thiscnt > top_scores[j])
2377 for (k = LADDER_SIZE-1; k >= j; --k)
2379 top_uids[k] = top_uids[k-1];
2380 top_scores[k] = top_scores[k-1];
2382 top_uids[j] = thisuid;
2383 top_scores[j] = thiscnt;
2389 s = "^3-----------------------\n\n";
2391 s = strcat(s, "Pos ^3|");
2392 s = strcat(s, " ^7Total ^3|");
2393 for (i = 1; i <= LADDER_CNT; ++i)
2395 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2397 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2399 s = strcat(s, "\n^3----+--------");
2400 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2402 s = strcat(s, "+-----");
2405 for (i = 1; i <= LADDER_CNT - 9; ++i)
2407 s = strcat(s, "+------");
2411 s = strcat(s, "+--------------+--------------------\n");
2413 for (i = 0; i < LADDER_SIZE; ++i)
2415 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2416 tokenize_console(temp_s);
2417 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2419 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2420 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2421 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2423 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2426 for (j = 10; j <= LADDER_CNT; ++j)
2428 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2432 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2433 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2436 MapInfo_ClearTemps();
2439 return "No ladder on this server!\n";
2441 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2445 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2448 vector start, org, delta, end, enddown, mstart;
2450 m = e.dphitcontentsmask;
2451 e.dphitcontentsmask = goodcontents | badcontents;
2454 delta = world.maxs - world.mins;
2456 for (i = 0; i < attempts; ++i)
2458 start_x = org_x + random() * delta_x;
2459 start_y = org_y + random() * delta_y;
2460 start_z = org_z + random() * delta_z;
2462 // rule 1: start inside world bounds, and outside
2463 // solid, and don't start from somewhere where you can
2464 // fall down to evil
2465 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2466 if (trace_fraction >= 1)
2468 if (trace_startsolid)
2470 if (trace_dphitcontents & badcontents)
2472 if (trace_dphitq3surfaceflags & badsurfaceflags)
2475 // rule 2: if we are too high, lower the point
2476 if (trace_fraction * delta_z > maxaboveground)
2477 start = trace_endpos + '0 0 1' * maxaboveground;
2478 enddown = trace_endpos;
2480 // rule 3: make sure we aren't outside the map. This only works
2481 // for somewhat well formed maps. A good rule of thumb is that
2482 // the map should have a convex outside hull.
2483 // these can be traceLINES as we already verified the starting box
2484 mstart = start + 0.5 * (e.mins + e.maxs);
2485 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2486 if (trace_fraction >= 1)
2488 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2489 if (trace_fraction >= 1)
2491 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2492 if (trace_fraction >= 1)
2494 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2495 if (trace_fraction >= 1)
2497 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2498 if (trace_fraction >= 1)
2501 // find a random vector to "look at"
2502 end_x = org_x + random() * delta_x;
2503 end_y = org_y + random() * delta_y;
2504 end_z = org_z + random() * delta_z;
2505 end = start + normalize(end - start) * vlen(delta);
2507 // rule 4: start TO end must not be too short
2508 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2509 if (trace_startsolid)
2511 if (trace_fraction < minviewdistance / vlen(delta))
2514 // rule 5: don't want to look at sky
2515 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2518 // rule 6: we must not end up in trigger_hurt
2519 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2525 e.dphitcontentsmask = m;
2529 setorigin(e, start);
2530 e.angles = vectoangles(end - start);
2531 dprint("Needed ", ftos(i + 1), " attempts\n");
2538 float zcurveparticles_effectno;
2539 vector zcurveparticles_start;
2540 float zcurveparticles_spd;
2542 void endzcurveparticles()
2544 if(zcurveparticles_effectno)
2547 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2549 zcurveparticles_effectno = 0;
2552 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2554 spd = bound(0, floor(spd / 16), 32767);
2555 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2557 endzcurveparticles();
2558 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2559 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2560 WriteShort(MSG_BROADCAST, effectno);
2561 WriteCoord(MSG_BROADCAST, start_x);
2562 WriteCoord(MSG_BROADCAST, start_y);
2563 WriteCoord(MSG_BROADCAST, start_z);
2564 zcurveparticles_effectno = effectno;
2565 zcurveparticles_start = start;
2568 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2569 WriteCoord(MSG_BROADCAST, end_x);
2570 WriteCoord(MSG_BROADCAST, end_y);
2571 WriteCoord(MSG_BROADCAST, end_z);
2572 WriteCoord(MSG_BROADCAST, end_dz);
2573 zcurveparticles_spd = spd;
2576 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2579 vector vecxy, velxy;
2581 vecxy = end - start;
2586 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2588 endzcurveparticles();
2589 trailparticles(world, effectno, start, end);
2593 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2594 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2597 void write_recordmarker(entity pl, float tstart, float dt)
2599 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2601 // also write a marker into demo files for demotc-race-record-extractor to find
2604 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2605 " ", ftos(tstart), " ", ftos(dt), "\n"));
2608 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2621 if(allowcenter) // 2: allow center handedness
2634 if(allowcenter) // 2: allow center handedness
2650 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2655 if (autocvar_g_shootfromeye)
2668 else if (autocvar_g_shootfromcenter)
2673 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2683 else if (autocvar_g_shootfromclient)
2685 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2690 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2692 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2696 void attach_sameorigin(entity e, entity to, string tag)
2698 vector org, t_forward, t_left, t_up, e_forward, e_up;
2705 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2706 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2707 t_forward = v_forward * tagscale;
2708 t_left = v_right * -tagscale;
2709 t_up = v_up * tagscale;
2711 e.origin_x = org * t_forward;
2712 e.origin_y = org * t_left;
2713 e.origin_z = org * t_up;
2715 // current forward and up directions
2716 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2717 e.angles = AnglesTransform_FromVAngles(e.angles);
2719 e.angles = AnglesTransform_FromAngles(e.angles);
2720 fixedmakevectors(e.angles);
2722 // untransform forward, up!
2723 e_forward_x = v_forward * t_forward;
2724 e_forward_y = v_forward * t_left;
2725 e_forward_z = v_forward * t_up;
2726 e_up_x = v_up * t_forward;
2727 e_up_y = v_up * t_left;
2728 e_up_z = v_up * t_up;
2730 e.angles = fixedvectoangles2(e_forward, e_up);
2731 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2732 e.angles = AnglesTransform_ToVAngles(e.angles);
2734 e.angles = AnglesTransform_ToAngles(e.angles);
2736 setattachment(e, to, tag);
2737 setorigin(e, e.origin);
2740 void detach_sameorigin(entity e)
2743 org = gettaginfo(e, 0);
2744 e.angles = fixedvectoangles2(v_forward, v_up);
2745 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2746 e.angles = AnglesTransform_ToVAngles(e.angles);
2748 e.angles = AnglesTransform_ToAngles(e.angles);
2750 setattachment(e, world, "");
2751 setorigin(e, e.origin);
2754 void follow_sameorigin(entity e, entity to)
2756 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2757 e.aiment = to; // make the hole follow bmodel
2758 e.punchangle = to.angles; // the original angles of bmodel
2759 e.view_ofs = e.origin - to.origin; // relative origin
2760 e.v_angle = e.angles - to.angles; // relative angles
2763 void unfollow_sameorigin(entity e)
2765 e.movetype = MOVETYPE_NONE;
2768 entity gettaginfo_relative_ent;
2769 vector gettaginfo_relative(entity e, float tag)
2771 if (!gettaginfo_relative_ent)
2773 gettaginfo_relative_ent = spawn();
2774 gettaginfo_relative_ent.effects = EF_NODRAW;
2776 gettaginfo_relative_ent.model = e.model;
2777 gettaginfo_relative_ent.modelindex = e.modelindex;
2778 gettaginfo_relative_ent.frame = e.frame;
2779 return gettaginfo(gettaginfo_relative_ent, tag);
2782 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2786 if (pl.soundentity.cnt & p)
2788 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2789 pl.soundentity.cnt |= p;
2792 void SoundEntity_StopSound(entity pl, float chan)
2796 if (pl.soundentity.cnt & p)
2798 stopsoundto(MSG_ALL, pl.soundentity, chan);
2799 pl.soundentity.cnt &~= p;
2803 void SoundEntity_Attach(entity pl)
2805 pl.soundentity = spawn();
2806 pl.soundentity.classname = "soundentity";
2807 pl.soundentity.owner = pl;
2808 setattachment(pl.soundentity, pl, "");
2809 setmodel(pl.soundentity, "null");
2812 void SoundEntity_Detach(entity pl)
2815 for (i = 0; i <= 7; ++i)
2816 SoundEntity_StopSound(pl, i);
2820 float ParseCommandPlayerSlotTarget_firsttoken;
2821 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2829 ParseCommandPlayerSlotTarget_firsttoken = -1;
2833 if (substring(argv(idx), 0, 1) == "#")
2835 s = substring(argv(idx), 1, -1);
2837 if (s == "") if (tokens > idx)
2842 ParseCommandPlayerSlotTarget_firsttoken = idx;
2844 if (s == ftos(n) && n > 0 && n <= maxclients)
2847 if (e.flags & FL_CLIENT)
2853 // it must be a nick name
2856 ParseCommandPlayerSlotTarget_firsttoken = idx;
2859 FOR_EACH_CLIENT(head)
2860 if (head.netname == s)
2868 s = strdecolorize(s);
2870 FOR_EACH_CLIENT(head)
2871 if (strdecolorize(head.netname) == s)
2886 float modeleffect_SendEntity(entity to, float sf)
2889 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2892 if(self.velocity != '0 0 0')
2894 if(self.angles != '0 0 0')
2896 if(self.avelocity != '0 0 0')
2899 WriteByte(MSG_ENTITY, f);
2900 WriteShort(MSG_ENTITY, self.modelindex);
2901 WriteByte(MSG_ENTITY, self.skin);
2902 WriteByte(MSG_ENTITY, self.frame);
2903 WriteCoord(MSG_ENTITY, self.origin_x);
2904 WriteCoord(MSG_ENTITY, self.origin_y);
2905 WriteCoord(MSG_ENTITY, self.origin_z);
2908 WriteCoord(MSG_ENTITY, self.velocity_x);
2909 WriteCoord(MSG_ENTITY, self.velocity_y);
2910 WriteCoord(MSG_ENTITY, self.velocity_z);
2914 WriteCoord(MSG_ENTITY, self.angles_x);
2915 WriteCoord(MSG_ENTITY, self.angles_y);
2916 WriteCoord(MSG_ENTITY, self.angles_z);
2920 WriteCoord(MSG_ENTITY, self.avelocity_x);
2921 WriteCoord(MSG_ENTITY, self.avelocity_y);
2922 WriteCoord(MSG_ENTITY, self.avelocity_z);
2924 WriteShort(MSG_ENTITY, self.scale * 256.0);
2925 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2926 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2927 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2928 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2933 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)
2938 e.classname = "modeleffect";
2946 e.teleport_time = t1;
2950 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2954 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2957 sz = max(e.scale, e.scale2);
2958 setsize(e, e.mins * sz, e.maxs * sz);
2959 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2962 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2964 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2967 float randombit(float bits)
2969 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2978 for(f = 1; f <= bits; f *= 2)
2987 r = (r - 1) / (n - 1);
2994 float randombits(float bits, float k, float error_return)
2998 while(k > 0 && bits != r)
3000 r += randombit(bits - r);
3009 void randombit_test(float bits, float iter)
3013 print(ftos(randombit(bits)), "\n");
3018 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3020 if(halflifedist > 0)
3021 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3022 else if(halflifedist < 0)
3023 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3032 #define cvar_string_normal cvar_string_builtin
3033 #define cvar_normal cvar_builtin
3035 string cvar_string_normal(string n)
3037 if not(cvar_type(n) & 1)
3038 backtrace(strcat("Attempt to access undefined cvar: ", n));
3039 return cvar_string_builtin(n);
3042 float cvar_normal(string n)
3044 return stof(cvar_string_normal(n));
3047 #define cvar_set_normal cvar_set_builtin
3055 oself.think = SUB_Remove;
3056 oself.nextthink = time;
3062 Execute func() after time + fdelay.
3063 self when func is executed = self when defer is called
3065 void defer(float fdelay, void() func)
3072 e.think = defer_think;
3073 e.nextthink = time + fdelay;