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 void bcenterprint(string s)
84 // TODO replace by MSG_ALL (would show it to spectators too, though)?
87 if (clienttype(head) == CLIENTTYPE_REAL)
91 void GameLogEcho(string s)
96 if (autocvar_sv_eventlog_files)
101 matches = autocvar_sv_eventlog_files_counter + 1;
102 cvar_set("sv_eventlog_files_counter", ftos(matches));
105 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
106 fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
107 logfile = fopen(fn, FILE_APPEND);
108 fputs(logfile, ":logversion:3\n");
112 if (autocvar_sv_eventlog_files_timestamps)
113 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
115 fputs(logfile, strcat(s, "\n"));
118 if (autocvar_sv_eventlog_console)
127 // will be opened later
132 if (logfile_open && logfile >= 0)
139 float spawnpoint_nag;
140 void relocate_spawnpoint()
142 // nudge off the floor
143 setorigin(self, self.origin + '0 0 1');
145 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
146 if (trace_startsolid)
152 if (!move_out_of_solid(self))
153 objerror("could not get out of solid at all!");
154 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
155 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
156 print(" ", ftos(self.origin_y - o_y));
157 print(" ", ftos(self.origin_z - o_z), "'\n");
158 if (autocvar_g_spawnpoints_auto_move_out_of_solid)
161 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
167 self.mins = self.maxs = '0 0 0';
168 objerror("player spawn point in solid, mapper sucks!\n");
173 self.use = spawnpoint_use;
174 self.team_saved = self.team;
178 if (have_team_spawns != 0)
180 have_team_spawns = 1;
181 have_team_spawns_forteam[self.team] = 1;
183 if (autocvar_r_showbboxes)
185 // show where spawnpoints point at too
186 makevectors(self.angles);
189 e.classname = "info_player_foo";
190 setorigin(e, self.origin + v_forward * 24);
191 setsize(e, '-8 -8 -8', '8 8 8');
192 e.solid = SOLID_TRIGGER;
196 #define strstr strstrofs
198 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
199 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
200 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
201 // BE CONSTANT OR strzoneD!
202 float strstr(string haystack, string needle, float offset)
206 len = strlen(needle);
207 endpos = strlen(haystack) - len;
208 while(offset <= endpos)
210 found = substring(haystack, offset, len);
219 float NUM_NEAREST_ENTITIES = 4;
220 entity nearest_entity[NUM_NEAREST_ENTITIES];
221 float nearest_length[NUM_NEAREST_ENTITIES];
222 entity findnearest(vector point, .string field, string value, vector axismod)
233 localhead = find(world, field, value);
236 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
237 dist = localhead.oldorigin;
239 dist = localhead.origin;
241 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
244 for (i = 0; i < num_nearest; ++i)
246 if (len < nearest_length[i])
250 // now i tells us where to insert at
251 // INSERTION SORT! YOU'VE SEEN IT! RUN!
252 if (i < NUM_NEAREST_ENTITIES)
254 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
256 nearest_length[j + 1] = nearest_length[j];
257 nearest_entity[j + 1] = nearest_entity[j];
259 nearest_length[i] = len;
260 nearest_entity[i] = localhead;
261 if (num_nearest < NUM_NEAREST_ENTITIES)
262 num_nearest = num_nearest + 1;
265 localhead = find(localhead, field, value);
268 // now use the first one from our list that we can see
269 for (i = 0; i < num_nearest; ++i)
271 traceline(point, nearest_entity[i].origin, TRUE, world);
272 if (trace_fraction == 1)
276 dprint("Nearest point (");
277 dprint(nearest_entity[0].netname);
278 dprint(") is not visible, using a visible one.\n");
280 return nearest_entity[i];
284 if (num_nearest == 0)
287 dprint("Not seeing any location point, using nearest as fallback.\n");
289 dprint("Candidates were: ");
290 for(j = 0; j < num_nearest; ++j)
294 dprint(nearest_entity[j].netname);
299 return nearest_entity[0];
302 void spawnfunc_target_location()
304 self.classname = "target_location";
305 // location name in netname
306 // eventually support: count, teamgame selectors, line of sight?
309 void spawnfunc_info_location()
311 self.classname = "target_location";
312 self.message = self.netname;
315 string NearestLocation(vector p)
320 loc = findnearest(p, classname, "target_location", '1 1 1');
327 loc = findnearest(p, target, "###item###", '1 1 4');
334 string formatmessage(string msg)
345 WarpZone_crosshair_trace(self);
346 cursor = trace_endpos;
347 cursor_ent = trace_ent;
351 break; // too many replacements
354 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
355 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
368 replacement = substring(msg, p, 2);
369 escape = substring(msg, p + 1, 1);
373 else if (escape == "\\")
375 else if (escape == "n")
377 else if (escape == "a")
378 replacement = ftos(floor(self.armorvalue));
379 else if (escape == "h")
380 replacement = ftos(floor(self.health));
381 else if (escape == "l")
382 replacement = NearestLocation(self.origin);
383 else if (escape == "y")
384 replacement = NearestLocation(cursor);
385 else if (escape == "d")
386 replacement = NearestLocation(self.death_origin);
387 else if (escape == "w") {
391 wep = self.switchweapon;
394 replacement = W_Name(wep);
395 } else if (escape == "W") {
396 if (self.items & IT_SHELLS) replacement = "shells";
397 else if (self.items & IT_NAILS) replacement = "bullets";
398 else if (self.items & IT_ROCKETS) replacement = "rockets";
399 else if (self.items & IT_CELLS) replacement = "cells";
400 else replacement = "batteries"; // ;)
401 } else if (escape == "x") {
402 replacement = cursor_ent.netname;
403 if (!replacement || !cursor_ent)
404 replacement = "nothing";
405 } else if (escape == "s")
406 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
407 else if (escape == "S")
408 replacement = ftos(vlen(self.velocity));
410 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
411 p = p + strlen(replacement);
416 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
417 return (value == 0) ? FALSE : TRUE;
426 >0: receives a cvar from name=argv(f) value=argv(f+1)
428 void GetCvars_handleString(string thisname, float f, .string field, string name)
433 strunzone(self.field);
434 self.field = string_null;
438 if (thisname == name)
441 strunzone(self.field);
442 self.field = strzone(argv(f + 1));
446 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
448 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
450 GetCvars_handleString(thisname, f, field, name);
451 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
452 if (thisname == name)
455 s = func(strcat1(self.field));
458 strunzone(self.field);
459 self.field = strzone(s);
463 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
470 if (thisname == name)
471 self.field = stof(argv(f + 1));
474 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
476 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
483 if (thisname == name)
487 self.field = stof(argv(f + 1));
496 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
499 float w_getbestweapon(entity e);
500 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
503 o = W_FixWeaponOrder_ForceComplete(wo);
504 if(self.weaponorder_byimpulse)
506 strunzone(self.weaponorder_byimpulse);
507 self.weaponorder_byimpulse = string_null;
509 self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
512 void GetCvars(float f)
517 s = strcat1(argv(f));
521 MUTATOR_CALLHOOK(GetCvars);
522 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
523 GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
524 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
525 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
526 GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
527 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
528 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
529 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
530 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
531 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
532 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
533 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
534 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
535 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
536 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
537 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
538 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
539 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
540 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
541 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
542 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
543 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
544 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
546 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
547 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
549 #ifdef ALLOW_FORCEMODELS
550 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
551 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
553 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
554 GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
555 GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
556 GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
557 GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
559 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
562 if (s == "cl_weaponpriority")
563 self.switchweapon = w_getbestweapon(self);
564 if (s == "cl_allow_uidtracking")
565 PlayerStats_AddPlayer(self);
569 void backtrace(string msg)
572 dev = autocvar_developer;
573 war = autocvar_prvm_backtraceforwarnings;
574 cvar_set("developer", "1");
575 cvar_set("prvm_backtraceforwarnings", "1");
577 print("--- CUT HERE ---\nWARNING: ");
580 remove(world); // isn't there any better way to cause a backtrace?
581 print("\n--- CUT UNTIL HERE ---\n");
582 cvar_set("developer", ftos(dev));
583 cvar_set("prvm_backtraceforwarnings", ftos(war));
586 string Team_ColorCode(float teamid)
588 if (teamid == COLOR_TEAM1)
590 else if (teamid == COLOR_TEAM2)
592 else if (teamid == COLOR_TEAM3)
594 else if (teamid == COLOR_TEAM4)
600 string Team_ColorName(float t)
602 // fixme: Search for team entities and get their .netname's!
603 if (t == COLOR_TEAM1)
605 if (t == COLOR_TEAM2)
607 if (t == COLOR_TEAM3)
609 if (t == COLOR_TEAM4)
614 string Team_ColorNameLowerCase(float t)
616 // fixme: Search for team entities and get their .netname's!
617 if (t == COLOR_TEAM1)
619 if (t == COLOR_TEAM2)
621 if (t == COLOR_TEAM3)
623 if (t == COLOR_TEAM4)
628 float ColourToNumber(string team_colour)
630 if (team_colour == "red")
633 if (team_colour == "blue")
636 if (team_colour == "yellow")
639 if (team_colour == "pink")
642 if (team_colour == "auto")
648 float NumberToTeamNumber(float number)
665 // decolorizes and team colors the player name when needed
666 string playername(entity p)
669 if (teamplay && !intermission_running && p.classname == "player")
671 t = Team_ColorCode(p.team);
672 return strcat(t, strdecolorize(p.netname));
678 vector randompos(vector m1, vector m2)
682 v_x = m2_x * random() + m1_x;
683 v_y = m2_y * random() + m1_y;
684 v_z = m2_z * random() + m1_z;
688 //#NO AUTOCVARS START
690 float g_pickup_shells;
691 float g_pickup_shells_max;
692 float g_pickup_nails;
693 float g_pickup_nails_max;
694 float g_pickup_rockets;
695 float g_pickup_rockets_max;
696 float g_pickup_cells;
697 float g_pickup_cells_max;
699 float g_pickup_fuel_jetpack;
700 float g_pickup_fuel_max;
701 float g_pickup_armorsmall;
702 float g_pickup_armorsmall_max;
703 float g_pickup_armorsmall_anyway;
704 float g_pickup_armormedium;
705 float g_pickup_armormedium_max;
706 float g_pickup_armormedium_anyway;
707 float g_pickup_armorbig;
708 float g_pickup_armorbig_max;
709 float g_pickup_armorbig_anyway;
710 float g_pickup_armorlarge;
711 float g_pickup_armorlarge_max;
712 float g_pickup_armorlarge_anyway;
713 float g_pickup_healthsmall;
714 float g_pickup_healthsmall_max;
715 float g_pickup_healthsmall_anyway;
716 float g_pickup_healthmedium;
717 float g_pickup_healthmedium_max;
718 float g_pickup_healthmedium_anyway;
719 float g_pickup_healthlarge;
720 float g_pickup_healthlarge_max;
721 float g_pickup_healthlarge_anyway;
722 float g_pickup_healthmega;
723 float g_pickup_healthmega_max;
724 float g_pickup_healthmega_anyway;
725 float g_pickup_ammo_anyway;
726 float g_pickup_weapons_anyway;
728 float g_weaponarena_random;
729 float g_weaponarena_random_with_laser;
730 string g_weaponarena_list;
731 float g_weaponspeedfactor;
732 float g_weaponratefactor;
733 float g_weapondamagefactor;
734 float g_weaponforcefactor;
735 float g_weaponspreadfactor;
739 float start_ammo_shells;
740 float start_ammo_nails;
741 float start_ammo_rockets;
742 float start_ammo_cells;
743 float start_ammo_fuel;
745 float start_armorvalue;
746 float warmup_start_weapons;
747 float warmup_start_ammo_shells;
748 float warmup_start_ammo_nails;
749 float warmup_start_ammo_rockets;
750 float warmup_start_ammo_cells;
751 float warmup_start_ammo_fuel;
752 float warmup_start_health;
753 float warmup_start_armorvalue;
757 entity get_weaponinfo(float w);
759 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
761 var float i = weaponinfo.weapon;
766 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
768 if (t < 0) // "default" weapon selection
770 if (g_lms || g_ca || allguns)
771 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
775 t = (i == WEP_SHOTGUN);
777 t = 0; // weapon is set a few lines later
779 t = (i == WEP_LASER || i == WEP_SHOTGUN);
780 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
781 t |= (i == WEP_HOOK);
784 // we cannot disable porto in Nexball, we must force it
785 if(g_nexball && i == WEP_PORTO)
791 void readplayerstartcvars()
797 // initialize starting values for players
800 start_ammo_shells = 0;
801 start_ammo_nails = 0;
802 start_ammo_rockets = 0;
803 start_ammo_cells = 0;
804 start_health = cvar("g_balance_health_start");
805 start_armorvalue = cvar("g_balance_armor_start");
808 s = cvar_string("g_weaponarena");
809 if (s == "0" || s == "")
815 if (s == "0" || s == "")
821 // forcibly turn off weaponarena
825 g_weaponarena_list = "All Weapons";
826 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
828 e = get_weaponinfo(j);
829 g_weaponarena |= e.weapons;
830 weapon_action(e.weapon, WR_PRECACHE);
833 else if (s == "most")
835 g_weaponarena_list = "Most Weapons";
836 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
838 e = get_weaponinfo(j);
839 if (e.spawnflags & WEP_FLAG_NORMAL)
841 g_weaponarena |= e.weapons;
842 weapon_action(e.weapon, WR_PRECACHE);
846 else if (s == "none")
848 g_weaponarena_list = "No Weapons";
849 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
853 t = tokenize_console(s);
854 g_weaponarena_list = "";
855 for (i = 0; i < t; ++i)
858 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
860 e = get_weaponinfo(j);
863 g_weaponarena |= e.weapons;
864 weapon_action(e.weapon, WR_PRECACHE);
865 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
871 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
874 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
878 g_weaponarena_random = cvar("g_weaponarena_random");
880 g_weaponarena_random = 0;
881 g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
885 start_weapons = g_weaponarena;
887 start_items |= IT_UNLIMITED_AMMO;
889 else if (g_minstagib)
892 start_armorvalue = 0;
893 start_weapons = WEPBIT_MINSTANEX;
894 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
895 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
897 if (g_minstagib_invis_alpha <= 0)
898 g_minstagib_invis_alpha = -1;
902 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
904 e = get_weaponinfo(i);
905 if(want_weapon("g_start_weapon_", e, FALSE))
906 start_weapons |= e.weapons;
910 if(!cvar("g_use_ammunition"))
911 start_items |= IT_UNLIMITED_AMMO;
915 start_ammo_cells = cvar("g_minstagib_ammo_start");
916 start_ammo_fuel = cvar("g_start_ammo_fuel");
918 else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
920 start_ammo_rockets = 999;
921 start_ammo_shells = 999;
922 start_ammo_cells = 999;
923 start_ammo_nails = 999;
924 start_ammo_fuel = 999;
930 start_ammo_shells = cvar("g_lms_start_ammo_shells");
931 start_ammo_nails = cvar("g_lms_start_ammo_nails");
932 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
933 start_ammo_cells = cvar("g_lms_start_ammo_cells");
934 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
938 start_ammo_shells = cvar("g_start_ammo_shells");
939 start_ammo_nails = cvar("g_start_ammo_nails");
940 start_ammo_rockets = cvar("g_start_ammo_rockets");
941 start_ammo_cells = cvar("g_start_ammo_cells");
942 start_ammo_fuel = cvar("g_start_ammo_fuel");
948 start_health = cvar("g_lms_start_health");
949 start_armorvalue = cvar("g_lms_start_armor");
954 warmup_start_ammo_shells = start_ammo_shells;
955 warmup_start_ammo_nails = start_ammo_nails;
956 warmup_start_ammo_rockets = start_ammo_rockets;
957 warmup_start_ammo_cells = start_ammo_cells;
958 warmup_start_ammo_fuel = start_ammo_fuel;
959 warmup_start_health = start_health;
960 warmup_start_armorvalue = start_armorvalue;
961 warmup_start_weapons = start_weapons;
963 if (!g_weaponarena && !g_minstagib && !g_ca)
965 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
966 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
967 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
968 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
969 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
970 warmup_start_health = cvar("g_warmup_start_health");
971 warmup_start_armorvalue = cvar("g_warmup_start_armor");
972 warmup_start_weapons = 0;
973 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
975 e = get_weaponinfo(i);
976 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
977 warmup_start_weapons |= e.weapons;
982 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
984 g_grappling_hook = 0; // these two can't coexist, as they use the same button
985 start_items |= IT_FUEL_REGEN;
986 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
987 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
991 start_items |= IT_JETPACK;
993 if (g_weapon_stay == 2)
995 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
996 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
997 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
998 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
999 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1000 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1001 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1002 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1003 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1004 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1007 MUTATOR_CALLHOOK(SetStartItems);
1009 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1011 e = get_weaponinfo(i);
1012 if(e.weapons & (start_weapons | warmup_start_weapons))
1013 weapon_action(e.weapon, WR_PRECACHE);
1016 start_ammo_shells = max(0, start_ammo_shells);
1017 start_ammo_nails = max(0, start_ammo_nails);
1018 start_ammo_cells = max(0, start_ammo_cells);
1019 start_ammo_rockets = max(0, start_ammo_rockets);
1020 start_ammo_fuel = max(0, start_ammo_fuel);
1022 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1023 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1024 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1025 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1026 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1030 float g_bugrigs_planar_movement;
1031 float g_bugrigs_planar_movement_car_jumping;
1032 float g_bugrigs_reverse_spinning;
1033 float g_bugrigs_reverse_speeding;
1034 float g_bugrigs_reverse_stopping;
1035 float g_bugrigs_air_steering;
1036 float g_bugrigs_angle_smoothing;
1037 float g_bugrigs_friction_floor;
1038 float g_bugrigs_friction_brake;
1039 float g_bugrigs_friction_air;
1040 float g_bugrigs_accel;
1041 float g_bugrigs_speed_ref;
1042 float g_bugrigs_speed_pow;
1043 float g_bugrigs_steer;
1045 float g_touchexplode;
1046 float g_touchexplode_radius;
1047 float g_touchexplode_damage;
1048 float g_touchexplode_edgedamage;
1049 float g_touchexplode_force;
1056 float sv_pitch_fixyaw;
1058 string GetGametype(); // g_world.qc
1059 void readlevelcvars(void)
1061 // first load all the mutators
1062 if(cvar("g_invincible_projectiles"))
1063 MUTATOR_ADD(mutator_invincibleprojectiles);
1065 MUTATOR_ADD(mutator_nix);
1066 if(cvar("g_dodging"))
1067 MUTATOR_ADD(mutator_dodging);
1068 if(cvar("g_rocket_flying"))
1069 MUTATOR_ADD(mutator_rocketflying);
1070 if(cvar("g_vampire"))
1071 MUTATOR_ADD(mutator_vampire);
1073 if(cvar("sv_allow_fullbright"))
1074 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1076 g_bugrigs = cvar("g_bugrigs");
1077 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1078 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1079 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1080 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1081 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1082 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1083 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1084 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1085 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1086 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1087 g_bugrigs_accel = cvar("g_bugrigs_accel");
1088 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1089 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1090 g_bugrigs_steer = cvar("g_bugrigs_steer");
1092 g_touchexplode = cvar("g_touchexplode");
1093 g_touchexplode_radius = cvar("g_touchexplode_radius");
1094 g_touchexplode_damage = cvar("g_touchexplode_damage");
1095 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1096 g_touchexplode_force = cvar("g_touchexplode_force");
1098 #ifdef ALLOW_FORCEMODELS
1099 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1102 sv_clones = cvar("sv_clones");
1103 sv_gentle = cvar("sv_gentle");
1104 sv_foginterval = cvar("sv_foginterval");
1105 g_cloaked = cvar("g_cloaked");
1107 g_cloaked = 1; // always enable cloak in CTS
1108 g_jump_grunt = cvar("g_jump_grunt");
1109 g_footsteps = cvar("g_footsteps");
1110 g_grappling_hook = cvar("g_grappling_hook");
1111 g_jetpack = cvar("g_jetpack");
1112 g_midair = cvar("g_midair");
1113 g_minstagib = cvar("g_minstagib");
1114 g_norecoil = cvar("g_norecoil");
1115 g_bloodloss = cvar("g_bloodloss");
1116 sv_maxidle = cvar("sv_maxidle");
1117 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1118 g_ctf_reverse = cvar("g_ctf_reverse");
1119 sv_autotaunt = cvar("sv_autotaunt");
1120 sv_taunt = cvar("sv_taunt");
1122 inWarmupStage = cvar("g_warmup");
1123 g_warmup_limit = cvar("g_warmup_limit");
1124 g_warmup_allguns = cvar("g_warmup_allguns");
1125 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1127 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1128 inWarmupStage = 0; // these modes cannot work together, sorry
1130 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1131 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1132 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1133 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1134 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1135 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1136 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1137 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1138 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1139 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1140 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1141 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1143 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1144 g_weaponratefactor = cvar("g_weaponratefactor");
1145 g_weapondamagefactor = cvar("g_weapondamagefactor");
1146 g_weaponforcefactor = cvar("g_weaponforcefactor");
1147 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1149 g_pickup_shells = cvar("g_pickup_shells");
1150 g_pickup_shells_max = cvar("g_pickup_shells_max");
1151 g_pickup_nails = cvar("g_pickup_nails");
1152 g_pickup_nails_max = cvar("g_pickup_nails_max");
1153 g_pickup_rockets = cvar("g_pickup_rockets");
1154 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1155 g_pickup_cells = cvar("g_pickup_cells");
1156 g_pickup_cells_max = cvar("g_pickup_cells_max");
1157 g_pickup_fuel = cvar("g_pickup_fuel");
1158 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1159 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1160 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1161 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1162 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1163 g_pickup_armormedium = cvar("g_pickup_armormedium");
1164 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1165 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1166 g_pickup_armorbig = cvar("g_pickup_armorbig");
1167 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1168 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1169 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1170 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1171 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1172 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1173 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1174 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1175 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1176 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1177 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1178 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1179 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1180 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1181 g_pickup_healthmega = cvar("g_pickup_healthmega");
1182 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1183 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1185 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1186 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1188 g_pinata = cvar("g_pinata");
1190 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1192 g_weapon_stay = cvar("g_weapon_stay");
1194 g_ghost_items = cvar("g_ghost_items");
1196 if(g_ghost_items >= 1)
1197 g_ghost_items = 0.25; // default alpha value
1199 if not(inWarmupStage && !g_ca)
1200 game_starttime = cvar("g_start_delay");
1202 sv_pitch_min = cvar("sv_pitch_min");
1203 sv_pitch_max = cvar("sv_pitch_max");
1204 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1206 readplayerstartcvars();
1212 string precache_sound (string s) = #19;
1213 float precache_sound_index (string s) = #19;
1215 #define SND_VOLUME 1
1216 #define SND_ATTENUATION 2
1217 #define SND_LARGEENTITY 8
1218 #define SND_LARGESOUND 16
1220 float sound_allowed(float dest, entity e)
1222 // sounds from world may always pass
1225 if (e.classname == "body")
1227 else if (e.realowner && e.realowner != e)
1229 else if (e.owner && e.owner != e)
1234 // sounds to self may always pass
1235 if (dest == MSG_ONE)
1236 if (e == msg_entity)
1238 // sounds by players can be removed
1239 if (autocvar_bot_sound_monopoly)
1240 if (clienttype(e) == CLIENTTYPE_REAL)
1242 // anything else may pass
1246 #ifdef COMPAT_XON010_CHANNELS
1247 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1248 void sound(entity e, float chan, string samp, float vol, float atten)
1250 if (!sound_allowed(MSG_BROADCAST, e))
1252 sound_builtin(e, chan, samp, vol, atten);
1256 void sound(entity e, float chan, string samp, float vol, float atten)
1258 if (!sound_allowed(MSG_BROADCAST, e))
1260 sound7(e, chan, samp, vol, atten, 0, 0);
1264 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1268 if (!sound_allowed(dest, e))
1271 entno = num_for_edict(e);
1272 idx = precache_sound_index(samp);
1277 atten = floor(atten * 64);
1278 vol = floor(vol * 255);
1281 sflags |= SND_VOLUME;
1283 sflags |= SND_ATTENUATION;
1284 if (entno >= 8192 || chan < 0 || chan > 7)
1285 sflags |= SND_LARGEENTITY;
1287 sflags |= SND_LARGESOUND;
1289 WriteByte(dest, SVC_SOUND);
1290 WriteByte(dest, sflags);
1291 if (sflags & SND_VOLUME)
1292 WriteByte(dest, vol);
1293 if (sflags & SND_ATTENUATION)
1294 WriteByte(dest, atten);
1295 if (sflags & SND_LARGEENTITY)
1297 WriteShort(dest, entno);
1298 WriteByte(dest, chan);
1302 WriteShort(dest, entno * 8 + chan);
1304 if (sflags & SND_LARGESOUND)
1305 WriteShort(dest, idx);
1307 WriteByte(dest, idx);
1309 WriteCoord(dest, o_x);
1310 WriteCoord(dest, o_y);
1311 WriteCoord(dest, o_z);
1313 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1317 if (!sound_allowed(dest, e))
1320 o = e.origin + 0.5 * (e.mins + e.maxs);
1321 soundtoat(dest, e, o, chan, samp, vol, atten);
1323 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1325 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1327 void stopsoundto(float dest, entity e, float chan)
1331 if (!sound_allowed(dest, e))
1334 entno = num_for_edict(e);
1336 if (entno >= 8192 || chan < 0 || chan > 7)
1339 idx = precache_sound_index("misc/null.wav");
1340 sflags = SND_LARGEENTITY;
1342 sflags |= SND_LARGESOUND;
1343 WriteByte(dest, SVC_SOUND);
1344 WriteByte(dest, sflags);
1345 WriteShort(dest, entno);
1346 WriteByte(dest, chan);
1347 if (sflags & SND_LARGESOUND)
1348 WriteShort(dest, idx);
1350 WriteByte(dest, idx);
1351 WriteCoord(dest, e.origin_x);
1352 WriteCoord(dest, e.origin_y);
1353 WriteCoord(dest, e.origin_z);
1357 WriteByte(dest, SVC_STOPSOUND);
1358 WriteShort(dest, entno * 8 + chan);
1361 void stopsound(entity e, float chan)
1363 if (!sound_allowed(MSG_BROADCAST, e))
1366 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1367 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1370 void play2(entity e, string filename)
1372 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1374 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1377 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1379 float spamsound(entity e, float chan, string samp, float vol, float atten)
1381 if (!sound_allowed(MSG_BROADCAST, e))
1384 if (time > e.spamtime)
1387 sound(e, chan, samp, vol, atten);
1393 void play2team(float t, string filename)
1397 if (autocvar_bot_sound_monopoly)
1400 FOR_EACH_REALPLAYER(head)
1403 play2(head, filename);
1407 void play2all(string samp)
1409 if (autocvar_bot_sound_monopoly)
1412 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1415 void PrecachePlayerSounds(string f);
1416 void precache_playermodel(string m)
1418 float globhandle, i, n;
1421 if(substring(m, -9,5) == "_lod1")
1423 if(substring(m, -9,5) == "_lod2")
1426 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1429 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1433 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1436 n = search_getsize(globhandle);
1437 for (i = 0; i < n; ++i)
1439 //print(search_getfilename(globhandle, i), "\n");
1440 f = search_getfilename(globhandle, i);
1441 PrecachePlayerSounds(f);
1443 search_end(globhandle);
1445 void precache_all_playermodels(string pattern)
1447 float globhandle, i, n;
1450 globhandle = search_begin(pattern, TRUE, FALSE);
1453 n = search_getsize(globhandle);
1454 for (i = 0; i < n; ++i)
1456 //print(search_getfilename(globhandle, i), "\n");
1457 f = search_getfilename(globhandle, i);
1458 precache_playermodel(f);
1460 search_end(globhandle);
1465 // gamemode related things
1466 precache_model ("models/misc/chatbubble.spr");
1469 precache_model ("models/runematch/curse.mdl");
1470 precache_model ("models/runematch/rune.mdl");
1473 #ifdef TTURRETS_ENABLED
1474 if (autocvar_g_turrets)
1478 // Precache all player models if desired
1479 if (autocvar_sv_precacheplayermodels)
1481 PrecachePlayerSounds("sound/player/default.sounds");
1482 precache_all_playermodels("models/player/*.zym");
1483 precache_all_playermodels("models/player/*.dpm");
1484 precache_all_playermodels("models/player/*.md3");
1485 precache_all_playermodels("models/player/*.psk");
1486 precache_all_playermodels("models/player/*.iqm");
1489 if (autocvar_sv_defaultcharacter)
1492 s = autocvar_sv_defaultplayermodel_red;
1494 precache_playermodel(s);
1495 s = autocvar_sv_defaultplayermodel_blue;
1497 precache_playermodel(s);
1498 s = autocvar_sv_defaultplayermodel_yellow;
1500 precache_playermodel(s);
1501 s = autocvar_sv_defaultplayermodel_pink;
1503 precache_playermodel(s);
1504 s = autocvar_sv_defaultplayermodel;
1506 precache_playermodel(s);
1511 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1512 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1515 // gore and miscellaneous sounds
1516 //precache_sound ("misc/h2ohit.wav");
1517 precache_model ("models/hook.md3");
1518 precache_sound ("misc/armorimpact.wav");
1519 precache_sound ("misc/bodyimpact1.wav");
1520 precache_sound ("misc/bodyimpact2.wav");
1521 precache_sound ("misc/gib.wav");
1522 precache_sound ("misc/gib_splat01.wav");
1523 precache_sound ("misc/gib_splat02.wav");
1524 precache_sound ("misc/gib_splat03.wav");
1525 precache_sound ("misc/gib_splat04.wav");
1526 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1527 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1528 precache_sound ("misc/null.wav");
1529 precache_sound ("misc/spawn.wav");
1530 precache_sound ("misc/talk.wav");
1531 precache_sound ("misc/teleport.wav");
1532 precache_sound ("misc/poweroff.wav");
1533 precache_sound ("player/lava.wav");
1534 precache_sound ("player/slime.wav");
1537 precache_sound ("misc/jetpack_fly.wav");
1539 precache_model ("models/sprites/0.spr32");
1540 precache_model ("models/sprites/1.spr32");
1541 precache_model ("models/sprites/2.spr32");
1542 precache_model ("models/sprites/3.spr32");
1543 precache_model ("models/sprites/4.spr32");
1544 precache_model ("models/sprites/5.spr32");
1545 precache_model ("models/sprites/6.spr32");
1546 precache_model ("models/sprites/7.spr32");
1547 precache_model ("models/sprites/8.spr32");
1548 precache_model ("models/sprites/9.spr32");
1549 precache_model ("models/sprites/10.spr32");
1551 // common weapon precaches
1552 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1553 precache_sound ("weapons/weapon_switch.wav");
1554 precache_sound ("weapons/weaponpickup.wav");
1555 precache_sound ("weapons/unavailable.wav");
1556 precache_sound ("weapons/dryfire.wav");
1557 if (g_grappling_hook)
1559 precache_sound ("weapons/hook_fire.wav"); // hook
1560 precache_sound ("weapons/hook_impact.wav"); // hook
1563 if(autocvar_sv_precacheweapons)
1565 //precache weapon models/sounds
1568 while (wep <= WEP_LAST)
1570 weapon_action(wep, WR_PRECACHE);
1575 precache_model("models/elaser.mdl");
1576 precache_model("models/laser.mdl");
1577 precache_model("models/ebomb.mdl");
1580 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1582 if (!self.noise && self.music) // quake 3 uses the music field
1583 self.noise = self.music;
1585 // plays music for the level if there is any
1588 precache_sound (self.noise);
1589 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1594 // sorry, but using \ in macros breaks line numbers
1595 #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
1596 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1597 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1600 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1602 if (clienttype(e) == CLIENTTYPE_REAL)
1605 WRITESPECTATABLE_MSG_ONE({
1606 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1607 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1608 WriteByte(MSG_ONE, id);
1609 WriteString(MSG_ONE, s);
1610 if (id != 0 && s != "")
1612 WriteByte(MSG_ONE, duration);
1613 WriteByte(MSG_ONE, countdown_num);
1618 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1620 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1622 // WARNING: this kills the trace globals
1623 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1624 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1626 #define INITPRIO_FIRST 0
1627 #define INITPRIO_GAMETYPE 0
1628 #define INITPRIO_GAMETYPE_FALLBACK 1
1629 #define INITPRIO_FINDTARGET 10
1630 #define INITPRIO_DROPTOFLOOR 20
1631 #define INITPRIO_SETLOCATION 90
1632 #define INITPRIO_LINKDOORS 91
1633 #define INITPRIO_LAST 99
1635 .void(void) initialize_entity;
1636 .float initialize_entity_order;
1637 .entity initialize_entity_next;
1638 entity initialize_entity_first;
1640 void make_safe_for_remove(entity e)
1642 if (e.initialize_entity)
1645 for (ent = initialize_entity_first; ent; )
1647 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1649 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1650 // skip it in linked list
1653 prev.initialize_entity_next = ent.initialize_entity_next;
1654 ent = prev.initialize_entity_next;
1658 initialize_entity_first = ent.initialize_entity_next;
1659 ent = initialize_entity_first;
1665 ent = ent.initialize_entity_next;
1671 void objerror(string s)
1673 make_safe_for_remove(self);
1674 objerror_builtin(s);
1677 .float remove_except_protected_forbidden;
1678 void remove_except_protected(entity e)
1680 if(e.remove_except_protected_forbidden)
1681 error("not allowed to remove this at this point");
1685 void remove_unsafely(entity e)
1687 if(e.classname == "spike")
1688 error("Removing spikes is forbidden (crylink bug), please report");
1692 void remove_safely(entity e)
1694 make_safe_for_remove(e);
1698 void InitializeEntity(entity e, void(void) func, float order)
1702 if (!e || e.initialize_entity)
1704 // make a proxy initializer entity
1708 e.classname = "initialize_entity";
1712 e.initialize_entity = func;
1713 e.initialize_entity_order = order;
1715 cur = initialize_entity_first;
1718 if (!cur || cur.initialize_entity_order > order)
1720 // insert between prev and cur
1722 prev.initialize_entity_next = e;
1724 initialize_entity_first = e;
1725 e.initialize_entity_next = cur;
1729 cur = cur.initialize_entity_next;
1732 void InitializeEntitiesRun()
1735 startoflist = initialize_entity_first;
1736 initialize_entity_first = world;
1737 remove = remove_except_protected;
1738 for (self = startoflist; self; self = self.initialize_entity_next)
1740 self.remove_except_protected_forbidden = 1;
1742 for (self = startoflist; self; )
1745 var void(void) func;
1746 e = self.initialize_entity_next;
1747 func = self.initialize_entity;
1748 self.initialize_entity_order = 0;
1749 self.initialize_entity = func_null;
1750 self.initialize_entity_next = world;
1751 self.remove_except_protected_forbidden = 0;
1752 if (self.classname == "initialize_entity")
1756 remove_builtin(self);
1759 //dprint("Delayed initialization: ", self.classname, "\n");
1760 if(func != func_null)
1765 backtrace(strcat("Null function in: ", self.classname, "\n"));
1769 remove = remove_unsafely;
1772 .float uncustomizeentityforclient_set;
1773 .void(void) uncustomizeentityforclient;
1774 void(void) SUB_Nullpointer = #0;
1775 void UncustomizeEntitiesRun()
1779 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1780 self.uncustomizeentityforclient();
1783 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1785 e.customizeentityforclient = customizer;
1786 e.uncustomizeentityforclient = uncustomizer;
1787 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1791 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1794 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1798 if (e.classname == "")
1799 e.classname = "net_linked";
1801 if (e.model == "" || self.modelindex == 0)
1805 setmodel(e, "null");
1809 e.SendEntity = sendfunc;
1810 e.SendFlags = 0xFFFFFF;
1813 e.effects |= EF_NODEPTHTEST;
1817 e.nextthink = time + dt;
1818 e.think = SUB_Remove;
1822 void adaptor_think2touch()
1831 void adaptor_think2use()
1843 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1845 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
1846 self.projectiledeathtype |= HITTYPE_SPLASH;
1847 adaptor_think2use();
1850 // deferred dropping
1851 void DropToFloor_Handler()
1853 droptofloor_builtin();
1854 self.dropped_origin = self.origin;
1859 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1864 float trace_hits_box_a0, trace_hits_box_a1;
1866 float trace_hits_box_1d(float end, float thmi, float thma)
1870 // just check if x is in range
1878 // do the trace with respect to x
1879 // 0 -> end has to stay in thmi -> thma
1880 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1881 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1882 if (trace_hits_box_a0 > trace_hits_box_a1)
1888 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1893 // now it is a trace from 0 to end
1895 trace_hits_box_a0 = 0;
1896 trace_hits_box_a1 = 1;
1898 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1900 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1902 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1908 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1910 return trace_hits_box(start, end, thmi - ma, thma - mi);
1913 float SUB_NoImpactCheck()
1915 // zero hitcontents = this is not the real impact, but either the
1916 // mirror-impact of something hitting the projectile instead of the
1917 // projectile hitting the something, or a touchareagrid one. Neither of
1918 // these stop the projectile from moving, so...
1919 if(trace_dphitcontents == 0)
1921 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1922 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)));
1925 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1927 if (other == world && self.size != '0 0 0')
1930 tic = self.velocity * sys_frametime;
1931 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1932 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1933 if (trace_fraction >= 1)
1935 dprint("Odd... did not hit...?\n");
1937 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1939 dprint("Detected and prevented the sky-grapple bug.\n");
1947 #define SUB_OwnerCheck() (other && (other == self.owner))
1949 void RemoveGrapplingHook(entity pl);
1950 void W_Crylink_Dequeue(entity e);
1951 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1953 if(SUB_OwnerCheck())
1955 if(SUB_NoImpactCheck())
1957 if(self.classname == "grapplinghook")
1958 RemoveGrapplingHook(self.realowner);
1959 else if(self.classname == "spike")
1961 W_Crylink_Dequeue(self);
1968 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1969 UpdateCSQCProjectile(self);
1972 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1974 float MAX_IPBAN_URIS = 16;
1976 float URI_GET_DISCARD = 0;
1977 float URI_GET_IPBAN = 1;
1978 float URI_GET_IPBAN_END = 16;
1980 void URI_Get_Callback(float id, float status, string data)
1982 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1984 dprint("\nEnd of data.\n");
1986 if(url_URI_Get_Callback(id, status, data))
1990 else if (id == URI_GET_DISCARD)
1994 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1997 OnlineBanList_URI_Get_Callback(id, status, data);
2001 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2005 void print_to(entity e, string s)
2008 sprint(e, strcat(s, "\n"));
2013 string uid2name(string myuid) {
2015 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
2017 // FIXME remove this later after 0.6 release
2018 // convert old style broken records to correct style
2021 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2024 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
2025 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
2030 s = "^1Unregistered Player";
2034 float race_readTime(string map, float pos)
2042 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2045 string race_readUID(string map, float pos)
2053 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2056 float race_readPos(string map, float t) {
2058 for (i = 1; i <= RANKINGS_CNT; ++i)
2059 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2062 return 0; // pos is zero if unranked
2065 void race_writeTime(string map, float t, string myuid)
2074 newpos = race_readPos(map, t);
2077 for(i = 1; i <= RANKINGS_CNT; ++i)
2079 if(race_readUID(map, i) == myuid)
2082 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2083 for (i = prevpos; i > newpos; --i) {
2084 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2085 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2087 } else { // player has no ranked record yet
2088 for (i = RANKINGS_CNT; i > newpos; --i) {
2089 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2090 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2094 // store new time itself
2095 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2096 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2099 string race_readName(string map, float pos)
2107 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2110 string race_placeName(float pos) {
2111 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2113 if(mod(pos, 10) == 1)
2114 return strcat(ftos(pos), "st");
2115 else if(mod(pos, 10) == 2)
2116 return strcat(ftos(pos), "nd");
2117 else if(mod(pos, 10) == 3)
2118 return strcat(ftos(pos), "rd");
2120 return strcat(ftos(pos), "th");
2123 return strcat(ftos(pos), "th");
2125 string getrecords(float page) // 50 records per page
2139 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2141 if (MapInfo_Get_ByID(i))
2143 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2147 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2148 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2156 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2158 if (MapInfo_Get_ByID(i))
2160 r = race_readTime(MapInfo_Map_bspname, 1);
2163 h = race_readName(MapInfo_Map_bspname, 1);
2164 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2172 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2174 if (MapInfo_Get_ByID(i))
2176 r = race_readTime(MapInfo_Map_bspname, 1);
2179 h = race_readName(MapInfo_Map_bspname, 1);
2180 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2186 MapInfo_ClearTemps();
2188 if (s == "" && page == 0)
2189 return "No records are available on this server.\n";
2194 string getrankings()
2207 for (i = 1; i <= RANKINGS_CNT; ++i)
2209 t = race_readTime(map, i);
2212 n = race_readName(map, i);
2213 p = race_placeName(i);
2214 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2217 MapInfo_ClearTemps();
2220 return strcat("No records are available for the map: ", map, "\n");
2222 return strcat("Records for ", map, ":\n", s);
2225 #define LADDER_FIRSTPOINT 100
2226 #define LADDER_CNT 10
2227 // position X still gives LADDER_FIRSTPOINT/X points
2228 #define LADDER_SIZE 30
2229 // ladder shows the top X players
2230 string top_uids[LADDER_SIZE];
2231 float top_scores[LADDER_SIZE];
2234 float i, j, k, uidcnt;
2248 for (k = 0; k < MapInfo_count; ++k)
2250 if (MapInfo_Get_ByID(k))
2252 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2253 if(i == 0) // speed award
2255 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2258 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2260 else // normal record, if it exists (else break)
2262 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2265 myuid = race_readUID(MapInfo_Map_bspname, i);
2268 // string s contains:
2269 // arg 0 = # of speed recs
2270 // arg 1 = # of 1st place recs
2271 // arg 2 = # of 2nd place recs
2273 // LADDER_CNT+1 = total points
2275 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2278 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2280 for (j = 0; j <= LADDER_CNT + 1; ++j)
2282 if(j != LADDER_CNT + 1)
2283 temp_s = strcat(temp_s, "0 ");
2285 temp_s = strcat(temp_s, "0");
2289 tokenize_console(temp_s);
2292 if(i == 0) // speed award
2293 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2295 if(j == 0) // speed award
2296 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2298 s = strcat(s, " ", argv(j)); // just copy over everything else
2301 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2304 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2305 else if(j == i) // wanted rec!
2306 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2308 s = strcat(s, " ", argv(j)); // just copy over everything else
2311 // total points are (by default) calculated like this:
2312 // speedrec = floor(100 / 10) = 10 points
2313 // 1st place = floor(100 / 1) = 100 points
2314 // 2nd place = floor(100 / 2) = 50 points
2315 // 3rd place = floor(100 / 3) = 33 points
2316 // 4th place = floor(100 / 4) = 25 points
2317 // 5th place = floor(100 / 5) = 20 points
2321 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2323 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2325 db_put(TemporaryDB, strcat("ladder", myuid), s);
2332 for (i = 0; i <= uidcnt; ++i) // for each known uid
2334 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2335 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2336 tokenize_console(temp_s);
2337 thiscnt = stof(argv(LADDER_CNT+1));
2339 if(thiscnt > top_scores[LADDER_SIZE-1])
2340 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2342 if(thiscnt > top_scores[j])
2344 for (k = LADDER_SIZE-1; k >= j; --k)
2346 top_uids[k] = top_uids[k-1];
2347 top_scores[k] = top_scores[k-1];
2349 top_uids[j] = thisuid;
2350 top_scores[j] = thiscnt;
2356 s = "^3-----------------------\n\n";
2358 s = strcat(s, "Pos ^3|");
2359 s = strcat(s, " ^7Total ^3|");
2360 for (i = 1; i <= LADDER_CNT; ++i)
2362 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2364 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2366 s = strcat(s, "\n^3----+--------");
2367 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2369 s = strcat(s, "+-----");
2372 for (i = 1; i <= LADDER_CNT - 9; ++i)
2374 s = strcat(s, "+------");
2378 s = strcat(s, "+--------------+--------------------\n");
2380 for (i = 0; i < LADDER_SIZE; ++i)
2382 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2383 tokenize_console(temp_s);
2384 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2386 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2387 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2388 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2390 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2393 for (j = 10; j <= LADDER_CNT; ++j)
2395 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2399 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2400 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2403 MapInfo_ClearTemps();
2406 return "No ladder on this server!\n";
2408 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2412 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2415 vector start, org, delta, end, enddown, mstart;
2418 m = e.dphitcontentsmask;
2419 e.dphitcontentsmask = goodcontents | badcontents;
2422 delta = world.maxs - world.mins;
2424 for (i = 0; i < attempts; ++i)
2426 start_x = org_x + random() * delta_x;
2427 start_y = org_y + random() * delta_y;
2428 start_z = org_z + random() * delta_z;
2430 // rule 1: start inside world bounds, and outside
2431 // solid, and don't start from somewhere where you can
2432 // fall down to evil
2433 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2434 if (trace_fraction >= 1)
2436 if (trace_startsolid)
2438 if (trace_dphitcontents & badcontents)
2440 if (trace_dphitq3surfaceflags & badsurfaceflags)
2443 // rule 2: if we are too high, lower the point
2444 if (trace_fraction * delta_z > maxaboveground)
2445 start = trace_endpos + '0 0 1' * maxaboveground;
2446 enddown = trace_endpos;
2448 // rule 3: make sure we aren't outside the map. This only works
2449 // for somewhat well formed maps. A good rule of thumb is that
2450 // the map should have a convex outside hull.
2451 // these can be traceLINES as we already verified the starting box
2452 mstart = start + 0.5 * (e.mins + e.maxs);
2453 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2454 if (trace_fraction >= 1)
2456 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2457 if (trace_fraction >= 1)
2459 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2460 if (trace_fraction >= 1)
2462 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2463 if (trace_fraction >= 1)
2465 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2466 if (trace_fraction >= 1)
2469 // rule 4: we must "see" some spawnpoint
2470 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2471 if(checkpvs(mstart, sp))
2475 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2476 if(checkpvs(mstart, sp))
2482 // find a random vector to "look at"
2483 end_x = org_x + random() * delta_x;
2484 end_y = org_y + random() * delta_y;
2485 end_z = org_z + random() * delta_z;
2486 end = start + normalize(end - start) * vlen(delta);
2488 // rule 4: start TO end must not be too short
2489 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2490 if (trace_startsolid)
2492 if (trace_fraction < minviewdistance / vlen(delta))
2495 // rule 5: don't want to look at sky
2496 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2499 // rule 6: we must not end up in trigger_hurt
2500 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2506 e.dphitcontentsmask = m;
2510 setorigin(e, start);
2511 e.angles = vectoangles(end - start);
2512 dprint("Needed ", ftos(i + 1), " attempts\n");
2519 float zcurveparticles_effectno;
2520 vector zcurveparticles_start;
2521 float zcurveparticles_spd;
2523 void endzcurveparticles()
2525 if(zcurveparticles_effectno)
2528 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2530 zcurveparticles_effectno = 0;
2533 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2535 spd = bound(0, floor(spd / 16), 32767);
2536 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2538 endzcurveparticles();
2539 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2540 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2541 WriteShort(MSG_BROADCAST, effectno);
2542 WriteCoord(MSG_BROADCAST, start_x);
2543 WriteCoord(MSG_BROADCAST, start_y);
2544 WriteCoord(MSG_BROADCAST, start_z);
2545 zcurveparticles_effectno = effectno;
2546 zcurveparticles_start = start;
2549 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2550 WriteCoord(MSG_BROADCAST, end_x);
2551 WriteCoord(MSG_BROADCAST, end_y);
2552 WriteCoord(MSG_BROADCAST, end_z);
2553 WriteCoord(MSG_BROADCAST, end_dz);
2554 zcurveparticles_spd = spd;
2557 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2560 vector vecxy, velxy;
2562 vecxy = end - start;
2567 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2569 endzcurveparticles();
2570 trailparticles(world, effectno, start, end);
2574 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2575 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2578 void write_recordmarker(entity pl, float tstart, float dt)
2580 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2582 // also write a marker into demo files for demotc-race-record-extractor to find
2585 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2586 " ", ftos(tstart), " ", ftos(dt), "\n"));
2589 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2602 if(allowcenter) // 2: allow center handedness
2615 if(allowcenter) // 2: allow center handedness
2631 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2636 if (autocvar_g_shootfromeye)
2649 else if (autocvar_g_shootfromcenter)
2654 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2664 else if (autocvar_g_shootfromclient)
2666 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2671 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2673 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2677 void attach_sameorigin(entity e, entity to, string tag)
2679 vector org, t_forward, t_left, t_up, e_forward, e_up;
2686 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2687 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2688 t_forward = v_forward * tagscale;
2689 t_left = v_right * -tagscale;
2690 t_up = v_up * tagscale;
2692 e.origin_x = org * t_forward;
2693 e.origin_y = org * t_left;
2694 e.origin_z = org * t_up;
2696 // current forward and up directions
2697 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2698 e.angles = AnglesTransform_FromVAngles(e.angles);
2700 e.angles = AnglesTransform_FromAngles(e.angles);
2701 fixedmakevectors(e.angles);
2703 // untransform forward, up!
2704 e_forward_x = v_forward * t_forward;
2705 e_forward_y = v_forward * t_left;
2706 e_forward_z = v_forward * t_up;
2707 e_up_x = v_up * t_forward;
2708 e_up_y = v_up * t_left;
2709 e_up_z = v_up * t_up;
2711 e.angles = fixedvectoangles2(e_forward, e_up);
2712 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2713 e.angles = AnglesTransform_ToVAngles(e.angles);
2715 e.angles = AnglesTransform_ToAngles(e.angles);
2717 setattachment(e, to, tag);
2718 setorigin(e, e.origin);
2721 void detach_sameorigin(entity e)
2724 org = gettaginfo(e, 0);
2725 e.angles = fixedvectoangles2(v_forward, v_up);
2726 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2727 e.angles = AnglesTransform_ToVAngles(e.angles);
2729 e.angles = AnglesTransform_ToAngles(e.angles);
2731 setattachment(e, world, "");
2732 setorigin(e, e.origin);
2735 void follow_sameorigin(entity e, entity to)
2737 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2738 e.aiment = to; // make the hole follow bmodel
2739 e.punchangle = to.angles; // the original angles of bmodel
2740 e.view_ofs = e.origin - to.origin; // relative origin
2741 e.v_angle = e.angles - to.angles; // relative angles
2744 void unfollow_sameorigin(entity e)
2746 e.movetype = MOVETYPE_NONE;
2749 entity gettaginfo_relative_ent;
2750 vector gettaginfo_relative(entity e, float tag)
2752 if (!gettaginfo_relative_ent)
2754 gettaginfo_relative_ent = spawn();
2755 gettaginfo_relative_ent.effects = EF_NODRAW;
2757 gettaginfo_relative_ent.model = e.model;
2758 gettaginfo_relative_ent.modelindex = e.modelindex;
2759 gettaginfo_relative_ent.frame = e.frame;
2760 return gettaginfo(gettaginfo_relative_ent, tag);
2763 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2767 if (pl.soundentity.cnt & p)
2769 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2770 pl.soundentity.cnt |= p;
2773 void SoundEntity_StopSound(entity pl, float chan)
2777 if (pl.soundentity.cnt & p)
2779 stopsoundto(MSG_ALL, pl.soundentity, chan);
2780 pl.soundentity.cnt &~= p;
2784 void SoundEntity_Attach(entity pl)
2786 pl.soundentity = spawn();
2787 pl.soundentity.classname = "soundentity";
2788 pl.soundentity.owner = pl;
2789 setattachment(pl.soundentity, pl, "");
2790 setmodel(pl.soundentity, "null");
2793 void SoundEntity_Detach(entity pl)
2796 for (i = 0; i <= 7; ++i)
2797 SoundEntity_StopSound(pl, i);
2801 float ParseCommandPlayerSlotTarget_firsttoken;
2802 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2810 ParseCommandPlayerSlotTarget_firsttoken = -1;
2814 if (substring(argv(idx), 0, 1) == "#")
2816 s = substring(argv(idx), 1, -1);
2818 if (s == "") if (tokens > idx)
2823 ParseCommandPlayerSlotTarget_firsttoken = idx;
2825 if (s == ftos(n) && n > 0 && n <= maxclients)
2828 if (e.flags & FL_CLIENT)
2834 // it must be a nick name
2837 ParseCommandPlayerSlotTarget_firsttoken = idx;
2840 FOR_EACH_CLIENT(head)
2841 if (head.netname == s)
2849 s = strdecolorize(s);
2851 FOR_EACH_CLIENT(head)
2852 if (strdecolorize(head.netname) == s)
2867 float modeleffect_SendEntity(entity to, float sf)
2870 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2873 if(self.velocity != '0 0 0')
2875 if(self.angles != '0 0 0')
2877 if(self.avelocity != '0 0 0')
2880 WriteByte(MSG_ENTITY, f);
2881 WriteShort(MSG_ENTITY, self.modelindex);
2882 WriteByte(MSG_ENTITY, self.skin);
2883 WriteByte(MSG_ENTITY, self.frame);
2884 WriteCoord(MSG_ENTITY, self.origin_x);
2885 WriteCoord(MSG_ENTITY, self.origin_y);
2886 WriteCoord(MSG_ENTITY, self.origin_z);
2889 WriteCoord(MSG_ENTITY, self.velocity_x);
2890 WriteCoord(MSG_ENTITY, self.velocity_y);
2891 WriteCoord(MSG_ENTITY, self.velocity_z);
2895 WriteCoord(MSG_ENTITY, self.angles_x);
2896 WriteCoord(MSG_ENTITY, self.angles_y);
2897 WriteCoord(MSG_ENTITY, self.angles_z);
2901 WriteCoord(MSG_ENTITY, self.avelocity_x);
2902 WriteCoord(MSG_ENTITY, self.avelocity_y);
2903 WriteCoord(MSG_ENTITY, self.avelocity_z);
2905 WriteShort(MSG_ENTITY, self.scale * 256.0);
2906 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2907 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2908 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2909 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2914 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)
2919 e.classname = "modeleffect";
2927 e.teleport_time = t1;
2931 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2935 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2938 sz = max(e.scale, e.scale2);
2939 setsize(e, e.mins * sz, e.maxs * sz);
2940 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2943 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2945 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2948 float randombit(float bits)
2950 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2959 for(f = 1; f <= bits; f *= 2)
2968 r = (r - 1) / (n - 1);
2975 float randombits(float bits, float k, float error_return)
2979 while(k > 0 && bits != r)
2981 r += randombit(bits - r);
2990 void randombit_test(float bits, float iter)
2994 print(ftos(randombit(bits)), "\n");
2999 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3001 if(halflifedist > 0)
3002 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3003 else if(halflifedist < 0)
3004 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3013 #define cvar_string_normal cvar_string_builtin
3014 #define cvar_normal cvar_builtin
3016 string cvar_string_normal(string n)
3018 if not(cvar_type(n) & 1)
3019 backtrace(strcat("Attempt to access undefined cvar: ", n));
3020 return cvar_string_builtin(n);
3023 float cvar_normal(string n)
3025 return stof(cvar_string_normal(n));
3028 #define cvar_set_normal cvar_set_builtin
3036 oself.think = SUB_Remove;
3037 oself.nextthink = time;
3043 Execute func() after time + fdelay.
3044 self when func is executed = self when defer is called
3046 void defer(float fdelay, void() func)
3053 e.think = defer_think;
3054 e.nextthink = time + fdelay;
3057 .string aiment_classname;
3058 .float aiment_deadflag;
3059 void SetMovetypeFollow(entity ent, entity e)
3061 // FIXME this may not be warpzone aware
3062 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3063 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.
3064 ent.aiment = e; // make the hole follow bmodel
3065 ent.punchangle = e.angles; // the original angles of bmodel
3066 ent.view_ofs = ent.origin - e.origin; // relative origin
3067 ent.v_angle = ent.angles - e.angles; // relative angles
3068 ent.aiment_classname = strzone(e.classname);
3069 ent.aiment_deadflag = e.deadflag;
3071 void UnsetMovetypeFollow(entity ent)
3073 ent.movetype = MOVETYPE_FLY;
3074 PROJECTILE_MAKETRIGGER(ent);
3077 float LostMovetypeFollow(entity ent)
3080 if(ent.movetype != MOVETYPE_FOLLOW)
3086 if(ent.aiment.classname != ent.aiment_classname)
3088 if(ent.aiment.deadflag != ent.aiment_deadflag)
3094 float isPushable(entity e)
3101 case "droppedweapon":
3102 case "keepawayball":
3103 case "nexball_basketball":
3104 case "nexball_football":
3106 case "bullet": // antilagged bullets can't hit this either
3109 if (e.projectiledeathtype)