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);
1072 if(cvar("g_spawn_near_teammate"))
1073 MUTATOR_ADD(mutator_spawn_near_teammate);
1075 if(cvar("sv_allow_fullbright"))
1076 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1078 g_bugrigs = cvar("g_bugrigs");
1079 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1080 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1081 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1082 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1083 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1084 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1085 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1086 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1087 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1088 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1089 g_bugrigs_accel = cvar("g_bugrigs_accel");
1090 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1091 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1092 g_bugrigs_steer = cvar("g_bugrigs_steer");
1094 g_touchexplode = cvar("g_touchexplode");
1095 g_touchexplode_radius = cvar("g_touchexplode_radius");
1096 g_touchexplode_damage = cvar("g_touchexplode_damage");
1097 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1098 g_touchexplode_force = cvar("g_touchexplode_force");
1100 #ifdef ALLOW_FORCEMODELS
1101 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1104 sv_clones = cvar("sv_clones");
1105 sv_gentle = cvar("sv_gentle");
1106 sv_foginterval = cvar("sv_foginterval");
1107 g_cloaked = cvar("g_cloaked");
1109 g_cloaked = 1; // always enable cloak in CTS
1110 g_jump_grunt = cvar("g_jump_grunt");
1111 g_footsteps = cvar("g_footsteps");
1112 g_grappling_hook = cvar("g_grappling_hook");
1113 g_jetpack = cvar("g_jetpack");
1114 g_midair = cvar("g_midair");
1115 g_minstagib = cvar("g_minstagib");
1116 g_norecoil = cvar("g_norecoil");
1117 g_bloodloss = cvar("g_bloodloss");
1118 sv_maxidle = cvar("sv_maxidle");
1119 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1120 g_ctf_reverse = cvar("g_ctf_reverse");
1121 sv_autotaunt = cvar("sv_autotaunt");
1122 sv_taunt = cvar("sv_taunt");
1124 inWarmupStage = cvar("g_warmup");
1125 g_warmup_limit = cvar("g_warmup_limit");
1126 g_warmup_allguns = cvar("g_warmup_allguns");
1127 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1129 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1130 inWarmupStage = 0; // these modes cannot work together, sorry
1132 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1133 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1134 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1135 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1136 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1137 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1138 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1139 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1140 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1141 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1142 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1143 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1145 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1146 g_weaponratefactor = cvar("g_weaponratefactor");
1147 g_weapondamagefactor = cvar("g_weapondamagefactor");
1148 g_weaponforcefactor = cvar("g_weaponforcefactor");
1149 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1151 g_pickup_shells = cvar("g_pickup_shells");
1152 g_pickup_shells_max = cvar("g_pickup_shells_max");
1153 g_pickup_nails = cvar("g_pickup_nails");
1154 g_pickup_nails_max = cvar("g_pickup_nails_max");
1155 g_pickup_rockets = cvar("g_pickup_rockets");
1156 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1157 g_pickup_cells = cvar("g_pickup_cells");
1158 g_pickup_cells_max = cvar("g_pickup_cells_max");
1159 g_pickup_fuel = cvar("g_pickup_fuel");
1160 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1161 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1162 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1163 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1164 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1165 g_pickup_armormedium = cvar("g_pickup_armormedium");
1166 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1167 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1168 g_pickup_armorbig = cvar("g_pickup_armorbig");
1169 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1170 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1171 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1172 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1173 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1174 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1175 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1176 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1177 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1178 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1179 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1180 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1181 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1182 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1183 g_pickup_healthmega = cvar("g_pickup_healthmega");
1184 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1185 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1187 g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1188 g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1190 g_pinata = cvar("g_pinata");
1192 g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1194 g_weapon_stay = cvar("g_weapon_stay");
1196 g_ghost_items = cvar("g_ghost_items");
1198 if(g_ghost_items >= 1)
1199 g_ghost_items = 0.25; // default alpha value
1201 if not(inWarmupStage && !g_ca)
1202 game_starttime = cvar("g_start_delay");
1204 sv_pitch_min = cvar("sv_pitch_min");
1205 sv_pitch_max = cvar("sv_pitch_max");
1206 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1208 readplayerstartcvars();
1214 string precache_sound (string s) = #19;
1215 float precache_sound_index (string s) = #19;
1217 #define SND_VOLUME 1
1218 #define SND_ATTENUATION 2
1219 #define SND_LARGEENTITY 8
1220 #define SND_LARGESOUND 16
1222 float sound_allowed(float dest, entity e)
1224 // sounds from world may always pass
1227 if (e.classname == "body")
1229 else if (e.realowner && e.realowner != e)
1231 else if (e.owner && e.owner != e)
1236 // sounds to self may always pass
1237 if (dest == MSG_ONE)
1238 if (e == msg_entity)
1240 // sounds by players can be removed
1241 if (autocvar_bot_sound_monopoly)
1242 if (clienttype(e) == CLIENTTYPE_REAL)
1244 // anything else may pass
1248 #ifdef COMPAT_XON010_CHANNELS
1249 void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
1250 void sound(entity e, float chan, string samp, float vol, float atten)
1252 if (!sound_allowed(MSG_BROADCAST, e))
1254 builtin_sound(e, chan, samp, vol, atten);
1258 void sound(entity e, float chan, string samp, float vol, float atten)
1260 if (!sound_allowed(MSG_BROADCAST, e))
1262 sound7(e, chan, samp, vol, atten, 0, 0);
1266 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1270 if (!sound_allowed(dest, e))
1273 entno = num_for_edict(e);
1274 idx = precache_sound_index(samp);
1279 atten = floor(atten * 64);
1280 vol = floor(vol * 255);
1283 sflags |= SND_VOLUME;
1285 sflags |= SND_ATTENUATION;
1286 if (entno >= 8192 || chan < 0 || chan > 7)
1287 sflags |= SND_LARGEENTITY;
1289 sflags |= SND_LARGESOUND;
1291 WriteByte(dest, SVC_SOUND);
1292 WriteByte(dest, sflags);
1293 if (sflags & SND_VOLUME)
1294 WriteByte(dest, vol);
1295 if (sflags & SND_ATTENUATION)
1296 WriteByte(dest, atten);
1297 if (sflags & SND_LARGEENTITY)
1299 WriteShort(dest, entno);
1300 WriteByte(dest, chan);
1304 WriteShort(dest, entno * 8 + chan);
1306 if (sflags & SND_LARGESOUND)
1307 WriteShort(dest, idx);
1309 WriteByte(dest, idx);
1311 WriteCoord(dest, o_x);
1312 WriteCoord(dest, o_y);
1313 WriteCoord(dest, o_z);
1315 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1319 if (!sound_allowed(dest, e))
1322 o = e.origin + 0.5 * (e.mins + e.maxs);
1323 soundtoat(dest, e, o, chan, samp, vol, atten);
1325 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1327 soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, atten);
1329 void stopsoundto(float dest, entity e, float chan)
1333 if (!sound_allowed(dest, e))
1336 entno = num_for_edict(e);
1338 if (entno >= 8192 || chan < 0 || chan > 7)
1341 idx = precache_sound_index("misc/null.wav");
1342 sflags = SND_LARGEENTITY;
1344 sflags |= SND_LARGESOUND;
1345 WriteByte(dest, SVC_SOUND);
1346 WriteByte(dest, sflags);
1347 WriteShort(dest, entno);
1348 WriteByte(dest, chan);
1349 if (sflags & SND_LARGESOUND)
1350 WriteShort(dest, idx);
1352 WriteByte(dest, idx);
1353 WriteCoord(dest, e.origin_x);
1354 WriteCoord(dest, e.origin_y);
1355 WriteCoord(dest, e.origin_z);
1359 WriteByte(dest, SVC_STOPSOUND);
1360 WriteShort(dest, entno * 8 + chan);
1363 void stopsound(entity e, float chan)
1365 if (!sound_allowed(MSG_BROADCAST, e))
1368 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1369 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1372 void play2(entity e, string filename)
1374 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1376 soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTN_NONE);
1379 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1381 float spamsound(entity e, float chan, string samp, float vol, float atten)
1383 if (!sound_allowed(MSG_BROADCAST, e))
1386 if (time > e.spamtime)
1389 sound(e, chan, samp, vol, atten);
1395 void play2team(float t, string filename)
1399 if (autocvar_bot_sound_monopoly)
1402 FOR_EACH_REALPLAYER(head)
1405 play2(head, filename);
1409 void play2all(string samp)
1411 if (autocvar_bot_sound_monopoly)
1414 sound(world, CH_INFO, samp, VOL_BASE, ATTN_NONE);
1417 void PrecachePlayerSounds(string f);
1418 void precache_playermodel(string m)
1420 float globhandle, i, n;
1423 if(substring(m, -9,5) == "_lod1")
1425 if(substring(m, -9,5) == "_lod2")
1428 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1431 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1435 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1438 n = search_getsize(globhandle);
1439 for (i = 0; i < n; ++i)
1441 //print(search_getfilename(globhandle, i), "\n");
1442 f = search_getfilename(globhandle, i);
1443 PrecachePlayerSounds(f);
1445 search_end(globhandle);
1447 void precache_all_playermodels(string pattern)
1449 float globhandle, i, n;
1452 globhandle = search_begin(pattern, TRUE, FALSE);
1455 n = search_getsize(globhandle);
1456 for (i = 0; i < n; ++i)
1458 //print(search_getfilename(globhandle, i), "\n");
1459 f = search_getfilename(globhandle, i);
1460 precache_playermodel(f);
1462 search_end(globhandle);
1467 // gamemode related things
1468 precache_model ("models/misc/chatbubble.spr");
1471 precache_model ("models/runematch/curse.mdl");
1472 precache_model ("models/runematch/rune.mdl");
1475 #ifdef TTURRETS_ENABLED
1476 if (autocvar_g_turrets)
1480 // Precache all player models if desired
1481 if (autocvar_sv_precacheplayermodels)
1483 PrecachePlayerSounds("sound/player/default.sounds");
1484 precache_all_playermodels("models/player/*.zym");
1485 precache_all_playermodels("models/player/*.dpm");
1486 precache_all_playermodels("models/player/*.md3");
1487 precache_all_playermodels("models/player/*.psk");
1488 precache_all_playermodels("models/player/*.iqm");
1491 if (autocvar_sv_defaultcharacter)
1494 s = autocvar_sv_defaultplayermodel_red;
1496 precache_playermodel(s);
1497 s = autocvar_sv_defaultplayermodel_blue;
1499 precache_playermodel(s);
1500 s = autocvar_sv_defaultplayermodel_yellow;
1502 precache_playermodel(s);
1503 s = autocvar_sv_defaultplayermodel_pink;
1505 precache_playermodel(s);
1506 s = autocvar_sv_defaultplayermodel;
1508 precache_playermodel(s);
1513 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1514 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1517 // gore and miscellaneous sounds
1518 //precache_sound ("misc/h2ohit.wav");
1519 precache_model ("models/hook.md3");
1520 precache_sound ("misc/armorimpact.wav");
1521 precache_sound ("misc/bodyimpact1.wav");
1522 precache_sound ("misc/bodyimpact2.wav");
1523 precache_sound ("misc/gib.wav");
1524 precache_sound ("misc/gib_splat01.wav");
1525 precache_sound ("misc/gib_splat02.wav");
1526 precache_sound ("misc/gib_splat03.wav");
1527 precache_sound ("misc/gib_splat04.wav");
1528 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1529 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1530 precache_sound ("misc/null.wav");
1531 precache_sound ("misc/spawn.wav");
1532 precache_sound ("misc/talk.wav");
1533 precache_sound ("misc/teleport.wav");
1534 precache_sound ("misc/poweroff.wav");
1535 precache_sound ("player/lava.wav");
1536 precache_sound ("player/slime.wav");
1539 precache_sound ("misc/jetpack_fly.wav");
1541 precache_model ("models/sprites/0.spr32");
1542 precache_model ("models/sprites/1.spr32");
1543 precache_model ("models/sprites/2.spr32");
1544 precache_model ("models/sprites/3.spr32");
1545 precache_model ("models/sprites/4.spr32");
1546 precache_model ("models/sprites/5.spr32");
1547 precache_model ("models/sprites/6.spr32");
1548 precache_model ("models/sprites/7.spr32");
1549 precache_model ("models/sprites/8.spr32");
1550 precache_model ("models/sprites/9.spr32");
1551 precache_model ("models/sprites/10.spr32");
1553 // common weapon precaches
1554 precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1555 precache_sound ("weapons/weapon_switch.wav");
1556 precache_sound ("weapons/weaponpickup.wav");
1557 precache_sound ("weapons/unavailable.wav");
1558 precache_sound ("weapons/dryfire.wav");
1559 if (g_grappling_hook)
1561 precache_sound ("weapons/hook_fire.wav"); // hook
1562 precache_sound ("weapons/hook_impact.wav"); // hook
1565 if(autocvar_sv_precacheweapons)
1567 //precache weapon models/sounds
1570 while (wep <= WEP_LAST)
1572 weapon_action(wep, WR_PRECACHE);
1577 precache_model("models/elaser.mdl");
1578 precache_model("models/laser.mdl");
1579 precache_model("models/ebomb.mdl");
1582 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1584 if (!self.noise && self.music) // quake 3 uses the music field
1585 self.noise = self.music;
1587 // plays music for the level if there is any
1590 precache_sound (self.noise);
1591 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1596 // sorry, but using \ in macros breaks line numbers
1597 #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
1598 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1599 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1602 void Send_CSQC_Centerprint_Generic(entity e, float id, string s, float duration, float countdown_num)
1604 if (clienttype(e) == CLIENTTYPE_REAL)
1607 WRITESPECTATABLE_MSG_ONE({
1608 WriteByte(MSG_ONE, SVC_TEMPENTITY);
1609 WriteByte(MSG_ONE, TE_CSQC_CENTERPRINT_GENERIC);
1610 WriteByte(MSG_ONE, id);
1611 WriteString(MSG_ONE, s);
1612 if (id != 0 && s != "")
1614 WriteByte(MSG_ONE, duration);
1615 WriteByte(MSG_ONE, countdown_num);
1620 void Send_CSQC_Centerprint_Generic_Expire(entity e, float id)
1622 Send_CSQC_Centerprint_Generic(e, id, "", 1, 0);
1624 // WARNING: this kills the trace globals
1625 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1626 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1628 #define INITPRIO_FIRST 0
1629 #define INITPRIO_GAMETYPE 0
1630 #define INITPRIO_GAMETYPE_FALLBACK 1
1631 #define INITPRIO_FINDTARGET 10
1632 #define INITPRIO_DROPTOFLOOR 20
1633 #define INITPRIO_SETLOCATION 90
1634 #define INITPRIO_LINKDOORS 91
1635 #define INITPRIO_LAST 99
1637 .void(void) initialize_entity;
1638 .float initialize_entity_order;
1639 .entity initialize_entity_next;
1640 entity initialize_entity_first;
1642 void make_safe_for_remove(entity e)
1644 if (e.initialize_entity)
1647 for (ent = initialize_entity_first; ent; )
1649 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1651 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1652 // skip it in linked list
1655 prev.initialize_entity_next = ent.initialize_entity_next;
1656 ent = prev.initialize_entity_next;
1660 initialize_entity_first = ent.initialize_entity_next;
1661 ent = initialize_entity_first;
1667 ent = ent.initialize_entity_next;
1673 void objerror(string s)
1675 make_safe_for_remove(self);
1676 builtin_objerror(s);
1679 .float remove_except_protected_forbidden;
1680 void remove_except_protected(entity e)
1682 if(e.remove_except_protected_forbidden)
1683 error("not allowed to remove this at this point");
1687 void remove_unsafely(entity e)
1689 if(e.classname == "spike")
1690 error("Removing spikes is forbidden (crylink bug), please report");
1694 void remove_safely(entity e)
1696 make_safe_for_remove(e);
1700 void InitializeEntity(entity e, void(void) func, float order)
1704 if (!e || e.initialize_entity)
1706 // make a proxy initializer entity
1710 e.classname = "initialize_entity";
1714 e.initialize_entity = func;
1715 e.initialize_entity_order = order;
1717 cur = initialize_entity_first;
1720 if (!cur || cur.initialize_entity_order > order)
1722 // insert between prev and cur
1724 prev.initialize_entity_next = e;
1726 initialize_entity_first = e;
1727 e.initialize_entity_next = cur;
1731 cur = cur.initialize_entity_next;
1734 void InitializeEntitiesRun()
1737 startoflist = initialize_entity_first;
1738 initialize_entity_first = world;
1739 remove = remove_except_protected;
1740 for (self = startoflist; self; self = self.initialize_entity_next)
1742 self.remove_except_protected_forbidden = 1;
1744 for (self = startoflist; self; )
1747 var void(void) func;
1748 e = self.initialize_entity_next;
1749 func = self.initialize_entity;
1750 self.initialize_entity_order = 0;
1751 self.initialize_entity = func_null;
1752 self.initialize_entity_next = world;
1753 self.remove_except_protected_forbidden = 0;
1754 if (self.classname == "initialize_entity")
1758 builtin_remove(self);
1761 //dprint("Delayed initialization: ", self.classname, "\n");
1762 if(func != func_null)
1767 backtrace(strcat("Null function in: ", self.classname, "\n"));
1771 remove = remove_unsafely;
1774 .float uncustomizeentityforclient_set;
1775 .void(void) uncustomizeentityforclient;
1776 void(void) SUB_Nullpointer = #0;
1777 void UncustomizeEntitiesRun()
1781 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1782 self.uncustomizeentityforclient();
1785 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1787 e.customizeentityforclient = customizer;
1788 e.uncustomizeentityforclient = uncustomizer;
1789 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1793 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1796 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1800 if (e.classname == "")
1801 e.classname = "net_linked";
1803 if (e.model == "" || self.modelindex == 0)
1807 setmodel(e, "null");
1811 e.SendEntity = sendfunc;
1812 e.SendFlags = 0xFFFFFF;
1815 e.effects |= EF_NODEPTHTEST;
1819 e.nextthink = time + dt;
1820 e.think = SUB_Remove;
1824 void adaptor_think2touch()
1833 void adaptor_think2use()
1845 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1847 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
1848 self.projectiledeathtype |= HITTYPE_SPLASH;
1849 adaptor_think2use();
1852 // deferred dropping
1853 void DropToFloor_Handler()
1855 builtin_droptofloor();
1856 self.dropped_origin = self.origin;
1861 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1866 float trace_hits_box_a0, trace_hits_box_a1;
1868 float trace_hits_box_1d(float end, float thmi, float thma)
1872 // just check if x is in range
1880 // do the trace with respect to x
1881 // 0 -> end has to stay in thmi -> thma
1882 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1883 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1884 if (trace_hits_box_a0 > trace_hits_box_a1)
1890 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1895 // now it is a trace from 0 to end
1897 trace_hits_box_a0 = 0;
1898 trace_hits_box_a1 = 1;
1900 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1902 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1904 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1910 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1912 return trace_hits_box(start, end, thmi - ma, thma - mi);
1915 float SUB_NoImpactCheck()
1917 // zero hitcontents = this is not the real impact, but either the
1918 // mirror-impact of something hitting the projectile instead of the
1919 // projectile hitting the something, or a touchareagrid one. Neither of
1920 // these stop the projectile from moving, so...
1921 if(trace_dphitcontents == 0)
1923 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1924 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)));
1927 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1929 if (other == world && self.size != '0 0 0')
1932 tic = self.velocity * sys_frametime;
1933 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1934 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1935 if (trace_fraction >= 1)
1937 dprint("Odd... did not hit...?\n");
1939 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1941 dprint("Detected and prevented the sky-grapple bug.\n");
1949 #define SUB_OwnerCheck() (other && (other == self.owner))
1951 void RemoveGrapplingHook(entity pl);
1952 void W_Crylink_Dequeue(entity e);
1953 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1955 if(SUB_OwnerCheck())
1957 if(SUB_NoImpactCheck())
1959 if(self.classname == "grapplinghook")
1960 RemoveGrapplingHook(self.realowner);
1961 else if(self.classname == "spike")
1963 W_Crylink_Dequeue(self);
1970 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1971 UpdateCSQCProjectile(self);
1974 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
1976 float MAX_IPBAN_URIS = 16;
1978 float URI_GET_DISCARD = 0;
1979 float URI_GET_IPBAN = 1;
1980 float URI_GET_IPBAN_END = 16;
1982 void URI_Get_Callback(float id, float status, string data)
1984 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1986 dprint("\nEnd of data.\n");
1988 if(url_URI_Get_Callback(id, status, data))
1992 else if (id == URI_GET_DISCARD)
1996 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1999 OnlineBanList_URI_Get_Callback(id, status, data);
2003 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2007 void print_to(entity e, string s)
2010 sprint(e, strcat(s, "\n"));
2015 string uid2name(string myuid) {
2017 s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
2019 // FIXME remove this later after 0.6 release
2020 // convert old style broken records to correct style
2023 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2026 db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
2027 db_put(ServerProgsDB, strcat("uid2name", myuid), "");
2032 s = "^1Unregistered Player";
2036 float race_readTime(string map, float pos)
2044 return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2047 string race_readUID(string map, float pos)
2055 return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2058 float race_readPos(string map, float t) {
2060 for (i = 1; i <= RANKINGS_CNT; ++i)
2061 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2064 return 0; // pos is zero if unranked
2067 void race_writeTime(string map, float t, string myuid)
2076 newpos = race_readPos(map, t);
2079 for(i = 1; i <= RANKINGS_CNT; ++i)
2081 if(race_readUID(map, i) == myuid)
2084 if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2085 for (i = prevpos; i > newpos; --i) {
2086 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2087 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2089 } else { // player has no ranked record yet
2090 for (i = RANKINGS_CNT; i > newpos; --i) {
2091 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2092 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2096 // store new time itself
2097 db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2098 db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2101 string race_readName(string map, float pos)
2109 return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2112 string race_placeName(float pos) {
2113 if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2115 if(mod(pos, 10) == 1)
2116 return strcat(ftos(pos), "st");
2117 else if(mod(pos, 10) == 2)
2118 return strcat(ftos(pos), "nd");
2119 else if(mod(pos, 10) == 3)
2120 return strcat(ftos(pos), "rd");
2122 return strcat(ftos(pos), "th");
2125 return strcat(ftos(pos), "th");
2127 string getrecords(float page) // 50 records per page
2141 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2143 if (MapInfo_Get_ByID(i))
2145 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2149 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2150 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2158 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2160 if (MapInfo_Get_ByID(i))
2162 r = race_readTime(MapInfo_Map_bspname, 1);
2165 h = race_readName(MapInfo_Map_bspname, 1);
2166 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2174 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2176 if (MapInfo_Get_ByID(i))
2178 r = race_readTime(MapInfo_Map_bspname, 1);
2181 h = race_readName(MapInfo_Map_bspname, 1);
2182 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2188 MapInfo_ClearTemps();
2190 if (s == "" && page == 0)
2191 return "No records are available on this server.\n";
2196 string getrankings()
2209 for (i = 1; i <= RANKINGS_CNT; ++i)
2211 t = race_readTime(map, i);
2214 n = race_readName(map, i);
2215 p = race_placeName(i);
2216 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2219 MapInfo_ClearTemps();
2222 return strcat("No records are available for the map: ", map, "\n");
2224 return strcat("Records for ", map, ":\n", s);
2227 #define LADDER_FIRSTPOINT 100
2228 #define LADDER_CNT 10
2229 // position X still gives LADDER_FIRSTPOINT/X points
2230 #define LADDER_SIZE 30
2231 // ladder shows the top X players
2232 string top_uids[LADDER_SIZE];
2233 float top_scores[LADDER_SIZE];
2236 float i, j, k, uidcnt;
2250 for (k = 0; k < MapInfo_count; ++k)
2252 if (MapInfo_Get_ByID(k))
2254 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2255 if(i == 0) // speed award
2257 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2260 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2262 else // normal record, if it exists (else break)
2264 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2267 myuid = race_readUID(MapInfo_Map_bspname, i);
2270 // string s contains:
2271 // arg 0 = # of speed recs
2272 // arg 1 = # of 1st place recs
2273 // arg 2 = # of 2nd place recs
2275 // LADDER_CNT+1 = total points
2277 temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2280 db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2282 for (j = 0; j <= LADDER_CNT + 1; ++j)
2284 if(j != LADDER_CNT + 1)
2285 temp_s = strcat(temp_s, "0 ");
2287 temp_s = strcat(temp_s, "0");
2291 tokenize_console(temp_s);
2294 if(i == 0) // speed award
2295 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2297 if(j == 0) // speed award
2298 s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2300 s = strcat(s, " ", argv(j)); // just copy over everything else
2303 for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2306 s = strcat(s, argv(j)); // speed award, dont prefix with " "
2307 else if(j == i) // wanted rec!
2308 s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2310 s = strcat(s, " ", argv(j)); // just copy over everything else
2313 // total points are (by default) calculated like this:
2314 // speedrec = floor(100 / 10) = 10 points
2315 // 1st place = floor(100 / 1) = 100 points
2316 // 2nd place = floor(100 / 2) = 50 points
2317 // 3rd place = floor(100 / 3) = 33 points
2318 // 4th place = floor(100 / 4) = 25 points
2319 // 5th place = floor(100 / 5) = 20 points
2323 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2325 s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2327 db_put(TemporaryDB, strcat("ladder", myuid), s);
2334 for (i = 0; i <= uidcnt; ++i) // for each known uid
2336 thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2337 temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2338 tokenize_console(temp_s);
2339 thiscnt = stof(argv(LADDER_CNT+1));
2341 if(thiscnt > top_scores[LADDER_SIZE-1])
2342 for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2344 if(thiscnt > top_scores[j])
2346 for (k = LADDER_SIZE-1; k >= j; --k)
2348 top_uids[k] = top_uids[k-1];
2349 top_scores[k] = top_scores[k-1];
2351 top_uids[j] = thisuid;
2352 top_scores[j] = thiscnt;
2358 s = "^3-----------------------\n\n";
2360 s = strcat(s, "Pos ^3|");
2361 s = strcat(s, " ^7Total ^3|");
2362 for (i = 1; i <= LADDER_CNT; ++i)
2364 s = strcat(s, " ^7", race_placeName(i), " ^3|");
2366 s = strcat(s, " ^7Speed awards ^3| ^7Name");
2368 s = strcat(s, "\n^3----+--------");
2369 for (i = 1; i <= min(9, LADDER_CNT); ++i)
2371 s = strcat(s, "+-----");
2374 for (i = 1; i <= LADDER_CNT - 9; ++i)
2376 s = strcat(s, "+------");
2380 s = strcat(s, "+--------------+--------------------\n");
2382 for (i = 0; i < LADDER_SIZE; ++i)
2384 temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2385 tokenize_console(temp_s);
2386 if (argv(LADDER_CNT+1) == "") // total is 0, skip
2388 s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2389 s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2390 for (j = 1; j <= min(9, LADDER_CNT); ++j)
2392 s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2395 for (j = 10; j <= LADDER_CNT; ++j)
2397 s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2401 s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2402 s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2405 MapInfo_ClearTemps();
2408 return "No ladder on this server!\n";
2410 return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2414 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2417 vector start, org, delta, end, enddown, mstart;
2420 m = e.dphitcontentsmask;
2421 e.dphitcontentsmask = goodcontents | badcontents;
2424 delta = world.maxs - world.mins;
2426 for (i = 0; i < attempts; ++i)
2428 start_x = org_x + random() * delta_x;
2429 start_y = org_y + random() * delta_y;
2430 start_z = org_z + random() * delta_z;
2432 // rule 1: start inside world bounds, and outside
2433 // solid, and don't start from somewhere where you can
2434 // fall down to evil
2435 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2436 if (trace_fraction >= 1)
2438 if (trace_startsolid)
2440 if (trace_dphitcontents & badcontents)
2442 if (trace_dphitq3surfaceflags & badsurfaceflags)
2445 // rule 2: if we are too high, lower the point
2446 if (trace_fraction * delta_z > maxaboveground)
2447 start = trace_endpos + '0 0 1' * maxaboveground;
2448 enddown = trace_endpos;
2450 // rule 3: make sure we aren't outside the map. This only works
2451 // for somewhat well formed maps. A good rule of thumb is that
2452 // the map should have a convex outside hull.
2453 // these can be traceLINES as we already verified the starting box
2454 mstart = start + 0.5 * (e.mins + e.maxs);
2455 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2456 if (trace_fraction >= 1)
2458 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2459 if (trace_fraction >= 1)
2461 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2462 if (trace_fraction >= 1)
2464 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2465 if (trace_fraction >= 1)
2467 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2468 if (trace_fraction >= 1)
2471 // rule 4: we must "see" some spawnpoint
2472 for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
2473 if(checkpvs(mstart, sp))
2477 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
2478 if(checkpvs(mstart, sp))
2484 // find a random vector to "look at"
2485 end_x = org_x + random() * delta_x;
2486 end_y = org_y + random() * delta_y;
2487 end_z = org_z + random() * delta_z;
2488 end = start + normalize(end - start) * vlen(delta);
2490 // rule 4: start TO end must not be too short
2491 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2492 if (trace_startsolid)
2494 if (trace_fraction < minviewdistance / vlen(delta))
2497 // rule 5: don't want to look at sky
2498 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2501 // rule 6: we must not end up in trigger_hurt
2502 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2508 e.dphitcontentsmask = m;
2512 setorigin(e, start);
2513 e.angles = vectoangles(end - start);
2514 dprint("Needed ", ftos(i + 1), " attempts\n");
2521 float zcurveparticles_effectno;
2522 vector zcurveparticles_start;
2523 float zcurveparticles_spd;
2525 void endzcurveparticles()
2527 if(zcurveparticles_effectno)
2530 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2532 zcurveparticles_effectno = 0;
2535 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2537 spd = bound(0, floor(spd / 16), 32767);
2538 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2540 endzcurveparticles();
2541 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2542 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2543 WriteShort(MSG_BROADCAST, effectno);
2544 WriteCoord(MSG_BROADCAST, start_x);
2545 WriteCoord(MSG_BROADCAST, start_y);
2546 WriteCoord(MSG_BROADCAST, start_z);
2547 zcurveparticles_effectno = effectno;
2548 zcurveparticles_start = start;
2551 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2552 WriteCoord(MSG_BROADCAST, end_x);
2553 WriteCoord(MSG_BROADCAST, end_y);
2554 WriteCoord(MSG_BROADCAST, end_z);
2555 WriteCoord(MSG_BROADCAST, end_dz);
2556 zcurveparticles_spd = spd;
2559 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2562 vector vecxy, velxy;
2564 vecxy = end - start;
2569 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2571 endzcurveparticles();
2572 trailparticles(world, effectno, start, end);
2576 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2577 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2580 void write_recordmarker(entity pl, float tstart, float dt)
2582 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2584 // also write a marker into demo files for demotc-race-record-extractor to find
2587 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2588 " ", ftos(tstart), " ", ftos(dt), "\n"));
2591 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2604 if(allowcenter) // 2: allow center handedness
2617 if(allowcenter) // 2: allow center handedness
2633 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2638 if (autocvar_g_shootfromeye)
2651 else if (autocvar_g_shootfromcenter)
2656 else if ((s = autocvar_g_shootfromfixedorigin) != "")
2666 else if (autocvar_g_shootfromclient)
2668 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2673 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2675 return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2679 void attach_sameorigin(entity e, entity to, string tag)
2681 vector org, t_forward, t_left, t_up, e_forward, e_up;
2688 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2689 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2690 t_forward = v_forward * tagscale;
2691 t_left = v_right * -tagscale;
2692 t_up = v_up * tagscale;
2694 e.origin_x = org * t_forward;
2695 e.origin_y = org * t_left;
2696 e.origin_z = org * t_up;
2698 // current forward and up directions
2699 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2700 e.angles = AnglesTransform_FromVAngles(e.angles);
2702 e.angles = AnglesTransform_FromAngles(e.angles);
2703 fixedmakevectors(e.angles);
2705 // untransform forward, up!
2706 e_forward_x = v_forward * t_forward;
2707 e_forward_y = v_forward * t_left;
2708 e_forward_z = v_forward * t_up;
2709 e_up_x = v_up * t_forward;
2710 e_up_y = v_up * t_left;
2711 e_up_z = v_up * t_up;
2713 e.angles = fixedvectoangles2(e_forward, e_up);
2714 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2715 e.angles = AnglesTransform_ToVAngles(e.angles);
2717 e.angles = AnglesTransform_ToAngles(e.angles);
2719 setattachment(e, to, tag);
2720 setorigin(e, e.origin);
2723 void detach_sameorigin(entity e)
2726 org = gettaginfo(e, 0);
2727 e.angles = fixedvectoangles2(v_forward, v_up);
2728 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2729 e.angles = AnglesTransform_ToVAngles(e.angles);
2731 e.angles = AnglesTransform_ToAngles(e.angles);
2733 setattachment(e, world, "");
2734 setorigin(e, e.origin);
2737 void follow_sameorigin(entity e, entity to)
2739 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2740 e.aiment = to; // make the hole follow bmodel
2741 e.punchangle = to.angles; // the original angles of bmodel
2742 e.view_ofs = e.origin - to.origin; // relative origin
2743 e.v_angle = e.angles - to.angles; // relative angles
2746 void unfollow_sameorigin(entity e)
2748 e.movetype = MOVETYPE_NONE;
2751 entity gettaginfo_relative_ent;
2752 vector gettaginfo_relative(entity e, float tag)
2754 if (!gettaginfo_relative_ent)
2756 gettaginfo_relative_ent = spawn();
2757 gettaginfo_relative_ent.effects = EF_NODRAW;
2759 gettaginfo_relative_ent.model = e.model;
2760 gettaginfo_relative_ent.modelindex = e.modelindex;
2761 gettaginfo_relative_ent.frame = e.frame;
2762 return gettaginfo(gettaginfo_relative_ent, tag);
2765 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2769 if (pl.soundentity.cnt & p)
2771 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2772 pl.soundentity.cnt |= p;
2775 void SoundEntity_StopSound(entity pl, float chan)
2779 if (pl.soundentity.cnt & p)
2781 stopsoundto(MSG_ALL, pl.soundentity, chan);
2782 pl.soundentity.cnt &~= p;
2786 void SoundEntity_Attach(entity pl)
2788 pl.soundentity = spawn();
2789 pl.soundentity.classname = "soundentity";
2790 pl.soundentity.owner = pl;
2791 setattachment(pl.soundentity, pl, "");
2792 setmodel(pl.soundentity, "null");
2795 void SoundEntity_Detach(entity pl)
2798 for (i = 0; i <= 7; ++i)
2799 SoundEntity_StopSound(pl, i);
2803 float ParseCommandPlayerSlotTarget_firsttoken;
2804 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2812 ParseCommandPlayerSlotTarget_firsttoken = -1;
2816 if (substring(argv(idx), 0, 1) == "#")
2818 s = substring(argv(idx), 1, -1);
2820 if (s == "") if (tokens > idx)
2825 ParseCommandPlayerSlotTarget_firsttoken = idx;
2827 if (s == ftos(n) && n > 0 && n <= maxclients)
2830 if (e.flags & FL_CLIENT)
2836 // it must be a nick name
2839 ParseCommandPlayerSlotTarget_firsttoken = idx;
2842 FOR_EACH_CLIENT(head)
2843 if (head.netname == s)
2851 s = strdecolorize(s);
2853 FOR_EACH_CLIENT(head)
2854 if (strdecolorize(head.netname) == s)
2869 float modeleffect_SendEntity(entity to, float sf)
2872 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2875 if(self.velocity != '0 0 0')
2877 if(self.angles != '0 0 0')
2879 if(self.avelocity != '0 0 0')
2882 WriteByte(MSG_ENTITY, f);
2883 WriteShort(MSG_ENTITY, self.modelindex);
2884 WriteByte(MSG_ENTITY, self.skin);
2885 WriteByte(MSG_ENTITY, self.frame);
2886 WriteCoord(MSG_ENTITY, self.origin_x);
2887 WriteCoord(MSG_ENTITY, self.origin_y);
2888 WriteCoord(MSG_ENTITY, self.origin_z);
2891 WriteCoord(MSG_ENTITY, self.velocity_x);
2892 WriteCoord(MSG_ENTITY, self.velocity_y);
2893 WriteCoord(MSG_ENTITY, self.velocity_z);
2897 WriteCoord(MSG_ENTITY, self.angles_x);
2898 WriteCoord(MSG_ENTITY, self.angles_y);
2899 WriteCoord(MSG_ENTITY, self.angles_z);
2903 WriteCoord(MSG_ENTITY, self.avelocity_x);
2904 WriteCoord(MSG_ENTITY, self.avelocity_y);
2905 WriteCoord(MSG_ENTITY, self.avelocity_z);
2907 WriteShort(MSG_ENTITY, self.scale * 256.0);
2908 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2909 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2910 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2911 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2916 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)
2921 e.classname = "modeleffect";
2929 e.teleport_time = t1;
2933 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2937 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2940 sz = max(e.scale, e.scale2);
2941 setsize(e, e.mins * sz, e.maxs * sz);
2942 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2945 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2947 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2950 float randombit(float bits)
2952 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2961 for(f = 1; f <= bits; f *= 2)
2970 r = (r - 1) / (n - 1);
2977 float randombits(float bits, float k, float error_return)
2981 while(k > 0 && bits != r)
2983 r += randombit(bits - r);
2992 void randombit_test(float bits, float iter)
2996 print(ftos(randombit(bits)), "\n");
3001 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3003 if(halflifedist > 0)
3004 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3005 else if(halflifedist < 0)
3006 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3015 #define cvar_string_normal builtin_cvar_string
3016 #define cvar_normal builtin_cvar
3018 string cvar_string_normal(string n)
3020 if not(cvar_type(n) & 1)
3021 backtrace(strcat("Attempt to access undefined cvar: ", n));
3022 return builtin_cvar_string(n);
3025 float cvar_normal(string n)
3027 return stof(cvar_string_normal(n));
3030 #define cvar_set_normal builtin_cvar_set
3038 oself.think = SUB_Remove;
3039 oself.nextthink = time;
3045 Execute func() after time + fdelay.
3046 self when func is executed = self when defer is called
3048 void defer(float fdelay, void() func)
3055 e.think = defer_think;
3056 e.nextthink = time + fdelay;
3059 .string aiment_classname;
3060 .float aiment_deadflag;
3061 void SetMovetypeFollow(entity ent, entity e)
3063 // FIXME this may not be warpzone aware
3064 ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
3065 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.
3066 ent.aiment = e; // make the hole follow bmodel
3067 ent.punchangle = e.angles; // the original angles of bmodel
3068 ent.view_ofs = ent.origin - e.origin; // relative origin
3069 ent.v_angle = ent.angles - e.angles; // relative angles
3070 ent.aiment_classname = strzone(e.classname);
3071 ent.aiment_deadflag = e.deadflag;
3073 void UnsetMovetypeFollow(entity ent)
3075 ent.movetype = MOVETYPE_FLY;
3076 PROJECTILE_MAKETRIGGER(ent);
3079 float LostMovetypeFollow(entity ent)
3082 if(ent.movetype != MOVETYPE_FOLLOW)
3088 if(ent.aiment.classname != ent.aiment_classname)
3090 if(ent.aiment.deadflag != ent.aiment_deadflag)
3096 float isPushable(entity e)
3103 case "droppedweapon":
3104 case "keepawayball":
3105 case "nexball_basketball":
3106 case "nexball_football":
3108 case "bullet": // antilagged bullets can't hit this either
3111 if (e.projectiledeathtype)