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 makevectors(pl.v_angle);
10 traceline_antilag(pl, pl.origin + pl.view_ofs, pl.origin + pl.view_ofs + v_forward * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
13 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
14 void() spawnpoint_use;
15 float race_GetTime(float pos);
16 string race_GetName(float pos);
17 string race_PlaceName(float pos);
19 string ColoredTeamName(float t);
21 string admin_name(void)
23 if(cvar_string("sv_adminnick") != "")
24 return cvar_string("sv_adminnick");
26 return "SERVER ADMIN";
29 float DistributeEvenly_amount;
30 float DistributeEvenly_totalweight;
31 void DistributeEvenly_Init(float amount, float totalweight)
33 if (DistributeEvenly_amount)
35 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
36 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
39 DistributeEvenly_amount = 0;
41 DistributeEvenly_amount = amount;
42 DistributeEvenly_totalweight = totalweight;
44 float DistributeEvenly_Get(float weight)
49 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
50 DistributeEvenly_totalweight -= weight;
51 DistributeEvenly_amount -= f;
55 void move_out_of_solid_expand(entity e, vector by)
58 tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
61 if (trace_fraction < 1)
64 // adjust origin in the other direction...
65 setorigin(e,e.origin - by * (1 - trace_fraction));
69 float move_out_of_solid(entity e)
74 traceline(o, o, MOVE_WORLDONLY, e);
78 tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
79 if (!trace_startsolid)
86 move_out_of_solid_expand(e, '1 0 0' * m0_x);
88 move_out_of_solid_expand(e, '1 0 0' * m1_x);
90 move_out_of_solid_expand(e, '0 1 0' * m0_y);
92 move_out_of_solid_expand(e, '0 1 0' * m1_y);
94 move_out_of_solid_expand(e, '0 0 1' * m0_z);
96 move_out_of_solid_expand(e, '0 0 1' * m1_z);
98 setorigin(e, e.origin);
100 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
101 if (trace_startsolid)
110 string STR_PLAYER = "player";
111 string STR_SPECTATOR = "spectator";
112 string STR_OBSERVER = "observer";
115 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
116 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
117 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
118 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
120 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
121 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
122 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
123 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
124 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
127 // copies a string to a tempstring (so one can strunzone it)
128 string strcat1(string s) = #115; // FRIK_FILE
133 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
135 local float nPlayerHealth = rint(enPlayer.health);
136 local float nPlayerArmor = rint(enPlayer.armorvalue);
137 local float nPlayerHandicap = enPlayer.cvar_cl_handicap;
138 local float nPlayerPing = rint(enPlayer.ping);
139 local string strPlayerPingColor;
140 local string strMessage;
141 if(nPlayerPing >= 150)
142 strPlayerPingColor = "^1";
144 strPlayerPingColor = "^2";
146 if((cvar("sv_fragmessage_information_stats")) && (enPlayer.health >= 1))
147 strMessage = strcat(strMessage, "\n^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
149 if(cvar("sv_fragmessage_information_ping")) {
150 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
151 strMessage = strcat(strMessage, "\n^7(^2Bot");
153 strMessage = strcat(strMessage, "\n^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
154 if(cvar("sv_fragmessage_information_handicap"))
155 if(cvar("sv_fragmessage_information_handicap") == 2)
156 if(nPlayerHandicap <= 1)
157 strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
159 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
160 else if not(nPlayerHandicap <= 1)
161 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
163 strMessage = strcat(strMessage, "^7)");
164 } else if(cvar("sv_fragmessage_information_handicap")) {
165 if(cvar("sv_fragmessage_information_handicap") == 2)
166 if(nPlayerHandicap <= 1)
167 strMessage = strcat(strMessage, "\n^7(Handicap ^2Off^7)");
169 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
170 else if(nPlayerHandicap > 1)
171 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
175 void bcenterprint(string s)
177 // TODO replace by MSG_ALL (would show it to spectators too, though)?
179 FOR_EACH_PLAYER(head)
180 if (clienttype(head) == CLIENTTYPE_REAL)
181 centerprint(head, s);
184 void GameLogEcho(string s)
189 if (cvar("sv_eventlog_files"))
194 matches = cvar("sv_eventlog_files_counter") + 1;
195 cvar_set("sv_eventlog_files_counter", ftos(matches));
198 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
199 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
200 logfile = fopen(fn, FILE_APPEND);
201 fputs(logfile, ":logversion:3\n");
205 if (cvar("sv_eventlog_files_timestamps"))
206 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
208 fputs(logfile, strcat(s, "\n"));
211 if (cvar("sv_eventlog_console"))
220 // will be opened later
225 if (logfile_open && logfile >= 0)
235 vector PL_CROUCH_VIEW_OFS;
236 vector PL_CROUCH_MIN;
237 vector PL_CROUCH_MAX;
239 float spawnpoint_nag;
240 void relocate_spawnpoint()
242 PL_VIEW_OFS = stov(cvar_string("sv_player_viewoffset"));
243 PL_MIN = stov(cvar_string("sv_player_mins"));
244 PL_MAX = stov(cvar_string("sv_player_maxs"));
245 PL_CROUCH_VIEW_OFS = stov(cvar_string("sv_player_crouch_viewoffset"));
246 PL_CROUCH_MIN = stov(cvar_string("sv_player_crouch_mins"));
247 PL_CROUCH_MAX = stov(cvar_string("sv_player_crouch_maxs"));
249 // nudge off the floor
250 setorigin(self, self.origin + '0 0 1');
252 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
253 if (trace_startsolid)
259 if (!move_out_of_solid(self))
260 objerror("could not get out of solid at all!");
261 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
262 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
263 print(" ", ftos(self.origin_y - o_y));
264 print(" ", ftos(self.origin_z - o_z), "'\n");
265 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
268 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
274 self.mins = self.maxs = '0 0 0';
275 objerror("player spawn point in solid, mapper sucks!\n");
280 if (cvar("g_spawnpoints_autodrop"))
282 setsize(self, PL_MIN, PL_MAX);
286 self.use = spawnpoint_use;
287 self.team_saved = self.team;
291 if (have_team_spawns != 0)
293 have_team_spawns = 1;
295 if (cvar("r_showbboxes"))
297 // show where spawnpoints point at too
298 makevectors(self.angles);
301 e.classname = "info_player_foo";
302 setorigin(e, self.origin + v_forward * 24);
303 setsize(e, '-8 -8 -8', '8 8 8');
304 e.solid = SOLID_TRIGGER;
308 #define strstr strstrofs
310 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
311 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
312 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
313 // BE CONSTANT OR strzoneD!
314 float strstr(string haystack, string needle, float offset)
318 len = strlen(needle);
319 endpos = strlen(haystack) - len;
320 while(offset <= endpos)
322 found = substring(haystack, offset, len);
331 float NUM_NEAREST_ENTITIES = 4;
332 entity nearest_entity[NUM_NEAREST_ENTITIES];
333 float nearest_length[NUM_NEAREST_ENTITIES];
334 entity findnearest(vector point, .string field, string value, vector axismod)
345 localhead = find(world, field, value);
348 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
349 dist = localhead.oldorigin;
351 dist = localhead.origin;
353 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
356 for (i = 0; i < num_nearest; ++i)
358 if (len < nearest_length[i])
362 // now i tells us where to insert at
363 // INSERTION SORT! YOU'VE SEEN IT! RUN!
364 if (i < NUM_NEAREST_ENTITIES)
366 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
368 nearest_length[j + 1] = nearest_length[j];
369 nearest_entity[j + 1] = nearest_entity[j];
371 nearest_length[i] = len;
372 nearest_entity[i] = localhead;
373 if (num_nearest < NUM_NEAREST_ENTITIES)
374 num_nearest = num_nearest + 1;
377 localhead = find(localhead, field, value);
380 // now use the first one from our list that we can see
381 for (i = 0; i < num_nearest; ++i)
383 traceline(point, nearest_entity[i].origin, TRUE, world);
384 if (trace_fraction == 1)
388 dprint("Nearest point (");
389 dprint(nearest_entity[0].netname);
390 dprint(") is not visible, using a visible one.\n");
392 return nearest_entity[i];
396 if (num_nearest == 0)
399 dprint("Not seeing any location point, using nearest as fallback.\n");
401 dprint("Candidates were: ");
402 for(j = 0; j < num_nearest; ++j)
406 dprint(nearest_entity[j].netname);
411 return nearest_entity[0];
414 void spawnfunc_target_location()
416 self.classname = "target_location";
417 // location name in netname
418 // eventually support: count, teamgame selectors, line of sight?
421 void spawnfunc_info_location()
423 self.classname = "target_location";
424 self.message = self.netname;
427 string NearestLocation(vector p)
432 loc = findnearest(p, classname, "target_location", '1 1 1');
439 loc = findnearest(p, target, "###item###", '1 1 4');
446 string formatmessage(string msg)
457 crosshair_trace(self);
458 cursor = trace_endpos;
459 cursor_ent = trace_ent;
463 break; // too many replacements
466 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
467 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
480 replacement = substring(msg, p, 2);
481 escape = substring(msg, p + 1, 1);
485 else if (escape == "\\")
487 else if (escape == "n")
489 else if (escape == "a")
490 replacement = ftos(floor(self.armorvalue));
491 else if (escape == "h")
492 replacement = ftos(floor(self.health));
493 else if (escape == "l")
494 replacement = NearestLocation(self.origin);
495 else if (escape == "y")
496 replacement = NearestLocation(cursor);
497 else if (escape == "d")
498 replacement = NearestLocation(self.death_origin);
499 else if (escape == "w") {
503 wep = self.switchweapon;
506 replacement = W_Name(wep);
507 } else if (escape == "W") {
508 if (self.items & IT_SHELLS) replacement = "shells";
509 else if (self.items & IT_NAILS) replacement = "bullets";
510 else if (self.items & IT_ROCKETS) replacement = "rockets";
511 else if (self.items & IT_CELLS) replacement = "cells";
512 else replacement = "batteries"; // ;)
513 } else if (escape == "x") {
514 replacement = cursor_ent.netname;
515 if (!replacement || !cursor_ent)
516 replacement = "nothing";
517 } else if (escape == "p") {
518 if (self.last_selected_player)
519 replacement = self.last_selected_player.netname;
521 replacement = "(nobody)";
522 } else if (escape == "s")
523 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
524 else if (escape == "S")
525 replacement = ftos(vlen(self.velocity));
526 else if (escape == "v") {
530 if(self.classname == "spectator")
535 weapon_number = stats.weapon;
538 weapon_number = stats.switchweapon;
541 weapon_number = stats.cnt;
543 if(stats.cvar_cl_accuracy_data_share && stats.stats_fired[weapon_number - 1])
544 replacement = ftos(bound(0, floor(100 * stats.stats_hit[weapon_number - 1] / stats.stats_fired[weapon_number - 1]), 100));
546 replacement = "~"; // or something to indicate NULL, not available
549 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
550 p = p + strlen(replacement);
555 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
556 return (value == 0) ? FALSE : TRUE;
565 >0: receives a cvar from name=argv(f) value=argv(f+1)
567 void GetCvars_handleString(string thisname, float f, .string field, string name)
572 strunzone(self.field);
573 self.field = string_null;
577 if (thisname == name)
580 strunzone(self.field);
581 self.field = strzone(argv(f + 1));
585 stuffcmd(self, strcat("sendcvar ", name, "\n"));
587 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
589 GetCvars_handleString(thisname, f, field, name);
590 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
591 if (thisname == name)
594 s = func(strcat1(self.field));
597 strunzone(self.field);
598 self.field = strzone(s);
602 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
609 if (thisname == name)
610 self.field = stof(argv(f + 1));
613 stuffcmd(self, strcat("sendcvar ", name, "\n"));
615 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
622 if (thisname == name)
626 self.field = stof(argv(f + 1));
635 stuffcmd(self, strcat("sendcvar ", name, "\n"));
638 string W_FixWeaponOrder_ForceComplete(string s);
639 string W_FixWeaponOrder_AllowIncomplete(string s);
640 float w_getbestweapon(entity e);
641 void GetCvars(float f)
645 s = strcat1(argv(f));
646 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
647 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
648 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
649 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
650 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
651 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
652 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
653 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
654 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
655 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
656 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
657 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
658 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
659 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
660 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
661 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
662 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
663 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
664 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
665 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
666 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
667 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
668 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
669 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
671 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
672 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
674 #ifdef ALLOW_FORCEMODELS
675 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
676 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
678 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
680 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
683 if (s == "cl_weaponpriority")
684 self.switchweapon = w_getbestweapon(self);
688 float fexists(string f)
691 fh = fopen(f, FILE_READ);
698 void backtrace(string msg)
701 dev = cvar("developer");
702 war = cvar("prvm_backtraceforwarnings");
703 cvar_set("developer", "1");
704 cvar_set("prvm_backtraceforwarnings", "1");
706 print("--- CUT HERE ---\nWARNING: ");
709 remove(world); // isn't there any better way to cause a backtrace?
710 print("\n--- CUT UNTIL HERE ---\n");
711 cvar_set("developer", ftos(dev));
712 cvar_set("prvm_backtraceforwarnings", ftos(war));
715 string Team_ColorCode(float teamid)
717 if (teamid == COLOR_TEAM1)
719 else if (teamid == COLOR_TEAM2)
721 else if (teamid == COLOR_TEAM3)
723 else if (teamid == COLOR_TEAM4)
729 string Team_ColorName(float t)
731 // fixme: Search for team entities and get their .netname's!
732 if (t == COLOR_TEAM1)
734 if (t == COLOR_TEAM2)
736 if (t == COLOR_TEAM3)
738 if (t == COLOR_TEAM4)
743 string Team_ColorNameLowerCase(float t)
745 // fixme: Search for team entities and get their .netname's!
746 if (t == COLOR_TEAM1)
748 if (t == COLOR_TEAM2)
750 if (t == COLOR_TEAM3)
752 if (t == COLOR_TEAM4)
757 float ColourToNumber(string team_colour)
759 if (team_colour == "red")
762 if (team_colour == "blue")
765 if (team_colour == "yellow")
768 if (team_colour == "pink")
771 if (team_colour == "auto")
777 float NumberToTeamNumber(float number)
794 #define CENTERPRIO_POINT 1
795 #define CENTERPRIO_SPAM 2
796 #define CENTERPRIO_VOTE 4
797 #define CENTERPRIO_NORMAL 5
798 #define CENTERPRIO_SHIELDING 7
799 #define CENTERPRIO_MAPVOTE 9
800 #define CENTERPRIO_IDLEKICK 50
801 #define CENTERPRIO_ADMIN 99
802 .float centerprint_priority;
803 .float centerprint_expires;
804 void centerprint_atprio(entity e, float prio, string s)
806 if (intermission_running)
807 if (prio < CENTERPRIO_MAPVOTE)
809 if (time > e.centerprint_expires)
810 e.centerprint_priority = 0;
811 if (prio >= e.centerprint_priority)
813 e.centerprint_priority = prio;
814 if (timeoutStatus == 2)
815 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
817 e.centerprint_expires = time + e.cvar_scr_centertime;
818 centerprint_builtin(e, s);
821 void centerprint_expire(entity e, float prio)
823 if (prio == e.centerprint_priority)
825 e.centerprint_priority = 0;
826 centerprint_builtin(e, "");
829 void centerprint(entity e, string s)
831 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
834 // decolorizes and team colors the player name when needed
835 string playername(entity p)
838 if (teams_matter && !intermission_running && p.classname == "player")
840 t = Team_ColorCode(p.team);
841 return strcat(t, strdecolorize(p.netname));
847 vector randompos(vector m1, vector m2)
851 v_x = m2_x * random() + m1_x;
852 v_y = m2_y * random() + m1_y;
853 v_z = m2_z * random() + m1_z;
857 float g_pickup_shells;
858 float g_pickup_shells_max;
859 float g_pickup_nails;
860 float g_pickup_nails_max;
861 float g_pickup_rockets;
862 float g_pickup_rockets_max;
863 float g_pickup_cells;
864 float g_pickup_cells_max;
866 float g_pickup_fuel_jetpack;
867 float g_pickup_fuel_max;
868 float g_pickup_armorsmall;
869 float g_pickup_armorsmall_max;
870 float g_pickup_armormedium;
871 float g_pickup_armormedium_max;
872 float g_pickup_armorbig;
873 float g_pickup_armorbig_max;
874 float g_pickup_armorlarge;
875 float g_pickup_armorlarge_max;
876 float g_pickup_healthsmall;
877 float g_pickup_healthsmall_max;
878 float g_pickup_healthmedium;
879 float g_pickup_healthmedium_max;
880 float g_pickup_healthlarge;
881 float g_pickup_healthlarge_max;
882 float g_pickup_healthmega;
883 float g_pickup_healthmega_max;
885 float g_weaponarena_random;
886 string g_weaponarena_list;
887 float g_weaponspeedfactor;
888 float g_weaponratefactor;
889 float g_weapondamagefactor;
890 float g_weaponforcefactor;
891 float g_weaponspreadfactor;
895 float start_ammo_shells;
896 float start_ammo_nails;
897 float start_ammo_rockets;
898 float start_ammo_cells;
899 float start_ammo_fuel;
901 float start_armorvalue;
902 float warmup_start_weapons;
903 float warmup_start_ammo_shells;
904 float warmup_start_ammo_nails;
905 float warmup_start_ammo_rockets;
906 float warmup_start_ammo_cells;
907 float warmup_start_ammo_fuel;
908 float warmup_start_health;
909 float warmup_start_armorvalue;
913 entity get_weaponinfo(float w);
915 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
917 var float i = weaponinfo.weapon;
922 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
924 if (t < 0) // "default" weapon selection
926 if (g_lms || g_ca || allguns)
927 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
930 else if (g_race || g_cts)
931 t = (i == WEP_LASER);
933 t = 0; // weapon is set a few lines later
935 t = (i == WEP_LASER || i == WEP_SHOTGUN);
936 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
937 t |= (i == WEP_HOOK);
940 // we cannot disable porto in Nexball, we must force it
941 if(g_nexball && i == WEP_PORTO)
947 float NixNex_CanChooseWeapon(float wpn);
948 void readplayerstartcvars()
954 // initialize starting values for players
957 start_ammo_shells = 0;
958 start_ammo_nails = 0;
959 start_ammo_rockets = 0;
960 start_ammo_cells = 0;
961 start_health = cvar("g_balance_health_start");
962 start_armorvalue = cvar("g_balance_armor_start");
965 s = cvar_string("g_weaponarena");
971 g_weaponarena_list = "All Weapons";
972 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
974 e = get_weaponinfo(j);
975 g_weaponarena |= e.weapons;
976 weapon_action(e.weapon, WR_PRECACHE);
979 else if (s == "most")
981 g_weaponarena_list = "Most Weapons";
982 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
984 e = get_weaponinfo(j);
985 if (e.spawnflags & WEP_FLAG_NORMAL)
987 g_weaponarena |= e.weapons;
988 weapon_action(e.weapon, WR_PRECACHE);
992 else if (s == "none")
994 g_weaponarena_list = "No Weapons";
995 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
999 t = tokenize_console(s);
1000 g_weaponarena_list = "";
1001 for (i = 0; i < t; ++i)
1004 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
1006 e = get_weaponinfo(j);
1009 g_weaponarena |= e.weapons;
1010 weapon_action(e.weapon, WR_PRECACHE);
1011 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
1017 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
1020 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
1024 g_weaponarena_random = cvar("g_weaponarena_random");
1026 g_weaponarena_random = 0;
1031 // will be done later
1032 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1033 if (NixNex_CanChooseWeapon(i))
1034 weapon_action(i, WR_PRECACHE);
1035 if(!cvar("g_use_ammunition"))
1036 start_items |= IT_UNLIMITED_AMMO;
1038 else if (g_weaponarena)
1040 start_weapons = g_weaponarena;
1041 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
1042 start_ammo_rockets = 999;
1043 if (g_weaponarena & WEPBIT_SHOTGUN)
1044 start_ammo_shells = 999;
1045 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
1046 start_ammo_cells = 999;
1047 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
1048 start_ammo_nails = 999;
1049 if (g_weaponarena & WEPBIT_HOOK)
1050 start_ammo_fuel = 999;
1051 start_items |= IT_UNLIMITED_AMMO;
1053 else if (g_minstagib)
1056 start_armorvalue = 0;
1057 start_weapons = WEPBIT_MINSTANEX;
1058 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
1059 start_ammo_cells = cvar("g_minstagib_ammo_start");
1060 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
1061 start_ammo_fuel = cvar("g_start_ammo_fuel");
1063 if (g_minstagib_invis_alpha <= 0)
1064 g_minstagib_invis_alpha = -1;
1070 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1071 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1072 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1073 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1074 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1075 start_health = cvar("g_lms_start_health");
1076 start_armorvalue = cvar("g_lms_start_armor");
1078 else if (cvar("g_use_ammunition"))
1080 start_ammo_shells = cvar("g_start_ammo_shells");
1081 start_ammo_nails = cvar("g_start_ammo_nails");
1082 start_ammo_rockets = cvar("g_start_ammo_rockets");
1083 start_ammo_cells = cvar("g_start_ammo_cells");
1084 start_ammo_fuel = cvar("g_start_ammo_fuel");
1088 start_ammo_shells = cvar("g_pickup_shells_max");
1089 start_ammo_nails = cvar("g_pickup_nails_max");
1090 start_ammo_rockets = cvar("g_pickup_rockets_max");
1091 start_ammo_cells = cvar("g_pickup_cells_max");
1092 start_ammo_fuel = cvar("g_pickup_fuel_max");
1093 start_items |= IT_UNLIMITED_AMMO;
1096 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1098 e = get_weaponinfo(i);
1099 if(want_weapon("g_start_weapon_", e, FALSE))
1101 start_weapons |= e.weapons;
1102 weapon_action(e.weapon, WR_PRECACHE);
1109 warmup_start_ammo_shells = start_ammo_shells;
1110 warmup_start_ammo_nails = start_ammo_nails;
1111 warmup_start_ammo_rockets = start_ammo_rockets;
1112 warmup_start_ammo_cells = start_ammo_cells;
1113 warmup_start_ammo_fuel = start_ammo_fuel;
1114 warmup_start_health = start_health;
1115 warmup_start_armorvalue = start_armorvalue;
1116 warmup_start_weapons = start_weapons;
1118 if (!g_weaponarena && !g_nixnex && !g_minstagib && !g_ca)
1120 if (cvar("g_use_ammunition"))
1122 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1123 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1124 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1125 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1126 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1128 warmup_start_health = cvar("g_warmup_start_health");
1129 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1130 warmup_start_weapons = 0;
1131 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1133 e = get_weaponinfo(i);
1134 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1136 warmup_start_weapons |= e.weapons;
1137 weapon_action(e.weapon, WR_PRECACHE);
1143 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1145 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1146 start_items |= IT_FUEL_REGEN;
1147 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1148 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1152 start_items |= IT_JETPACK;
1154 if (g_weapon_stay == 2)
1156 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1157 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1158 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1159 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1160 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1161 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1162 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1163 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1164 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1165 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1168 start_ammo_shells = max(0, start_ammo_shells);
1169 start_ammo_nails = max(0, start_ammo_nails);
1170 start_ammo_cells = max(0, start_ammo_cells);
1171 start_ammo_rockets = max(0, start_ammo_rockets);
1172 start_ammo_fuel = max(0, start_ammo_fuel);
1174 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1175 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1176 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1177 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1178 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1182 float g_bugrigs_planar_movement;
1183 float g_bugrigs_planar_movement_car_jumping;
1184 float g_bugrigs_reverse_spinning;
1185 float g_bugrigs_reverse_speeding;
1186 float g_bugrigs_reverse_stopping;
1187 float g_bugrigs_air_steering;
1188 float g_bugrigs_angle_smoothing;
1189 float g_bugrigs_friction_floor;
1190 float g_bugrigs_friction_brake;
1191 float g_bugrigs_friction_air;
1192 float g_bugrigs_accel;
1193 float g_bugrigs_speed_ref;
1194 float g_bugrigs_speed_pow;
1195 float g_bugrigs_steer;
1197 float g_touchexplode;
1198 float g_touchexplode_radius;
1199 float g_touchexplode_damage;
1200 float g_touchexplode_edgedamage;
1201 float g_touchexplode_force;
1208 float sv_pitch_fixyaw;
1210 float sv_accuracy_data_share;
1212 void readlevelcvars(void)
1214 g_bugrigs = cvar("g_bugrigs");
1215 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1216 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1217 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1218 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1219 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1220 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1221 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1222 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1223 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1224 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1225 g_bugrigs_accel = cvar("g_bugrigs_accel");
1226 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1227 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1228 g_bugrigs_steer = cvar("g_bugrigs_steer");
1230 g_touchexplode = cvar("g_touchexplode");
1231 g_touchexplode_radius = cvar("g_touchexplode_radius");
1232 g_touchexplode_damage = cvar("g_touchexplode_damage");
1233 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1234 g_touchexplode_force = cvar("g_touchexplode_force");
1236 #ifdef ALLOW_FORCEMODELS
1237 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1239 sv_loddistance1 = cvar("sv_loddistance1");
1240 sv_loddistance2 = cvar("sv_loddistance2");
1242 if(sv_loddistance2 <= sv_loddistance1)
1243 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1245 sv_clones = cvar("sv_clones");
1246 sv_gentle = cvar("sv_gentle");
1247 sv_foginterval = cvar("sv_foginterval");
1248 g_cloaked = cvar("g_cloaked");
1249 g_jump_grunt = cvar("g_jump_grunt");
1250 g_footsteps = cvar("g_footsteps");
1251 g_grappling_hook = cvar("g_grappling_hook");
1252 g_jetpack = cvar("g_jetpack");
1253 g_laserguided_missile = cvar("g_laserguided_missile");
1254 g_midair = cvar("g_midair");
1255 g_minstagib = cvar("g_minstagib");
1256 g_nixnex = cvar("g_nixnex");
1257 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1258 g_norecoil = cvar("g_norecoil");
1259 g_vampire = cvar("g_vampire");
1260 g_bloodloss = cvar("g_bloodloss");
1261 sv_maxidle = cvar("sv_maxidle");
1262 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1263 sv_pogostick = cvar("sv_pogostick");
1264 sv_doublejump = cvar("sv_doublejump");
1265 g_ctf_reverse = cvar("g_ctf_reverse");
1266 sv_autotaunt = cvar("sv_autotaunt");
1267 sv_taunt = cvar("sv_taunt");
1269 inWarmupStage = cvar("g_warmup");
1270 g_warmup_limit = cvar("g_warmup_limit");
1271 g_warmup_allguns = cvar("g_warmup_allguns");
1272 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1274 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1275 inWarmupStage = 0; // these modes cannot work together, sorry
1277 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1278 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1279 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1280 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1281 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1282 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1283 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1284 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1285 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1286 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1287 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1288 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1290 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1291 if (g_nixnex) g_weaponarena = 0;
1294 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1295 g_weaponratefactor = cvar("g_weaponratefactor");
1296 g_weapondamagefactor = cvar("g_weapondamagefactor");
1297 g_weaponforcefactor = cvar("g_weaponforcefactor");
1298 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1300 g_pickup_shells = cvar("g_pickup_shells");
1301 g_pickup_shells_max = cvar("g_pickup_shells_max");
1302 g_pickup_nails = cvar("g_pickup_nails");
1303 g_pickup_nails_max = cvar("g_pickup_nails_max");
1304 g_pickup_rockets = cvar("g_pickup_rockets");
1305 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1306 g_pickup_cells = cvar("g_pickup_cells");
1307 g_pickup_cells_max = cvar("g_pickup_cells_max");
1308 g_pickup_fuel = cvar("g_pickup_fuel");
1309 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1310 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1311 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1312 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1313 g_pickup_armormedium = cvar("g_pickup_armormedium");
1314 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1315 g_pickup_armorbig = cvar("g_pickup_armorbig");
1316 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1317 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1318 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1319 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1320 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1321 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1322 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1323 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1324 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1325 g_pickup_healthmega = cvar("g_pickup_healthmega");
1326 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1328 g_pinata = cvar("g_pinata");
1330 g_weapon_stay = cvar("g_weapon_stay");
1332 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1335 g_ghost_items = cvar("g_ghost_items");
1337 if(g_ghost_items >= 1)
1338 g_ghost_items = 0.25; // default alpha value
1340 if not(inWarmupStage && !g_ca)
1341 game_starttime = cvar("g_start_delay");
1343 sv_pitch_min = cvar("sv_pitch_min");
1344 sv_pitch_max = cvar("sv_pitch_max");
1345 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1347 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1349 readplayerstartcvars();
1353 // TODO sound pack system
1356 string precache_sound_builtin (string s) = #19;
1357 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1358 string precache_sound(string s)
1360 return precache_sound_builtin(strcat(soundpack, s));
1362 void play2(entity e, string filename)
1364 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1366 void sound(entity e, float chan, string samp, float vol, float atten)
1368 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1373 string precache_sound (string s) = #19;
1374 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1375 float precache_sound_index (string s) = #19;
1377 #define SND_VOLUME 1
1378 #define SND_ATTENUATION 2
1379 #define SND_LARGEENTITY 8
1380 #define SND_LARGESOUND 16
1382 float sound_allowed(float dest, entity e)
1384 // sounds from world may always pass
1387 if (e.classname == "body")
1389 if (e.owner && e.owner != e)
1394 // sounds to self may always pass
1395 if (dest == MSG_ONE)
1396 if (e == msg_entity)
1398 // sounds by players can be removed
1399 if (cvar("bot_sound_monopoly"))
1400 if (clienttype(e) == CLIENTTYPE_REAL)
1402 // anything else may pass
1406 void sound(entity e, float chan, string samp, float vol, float atten)
1408 if (!sound_allowed(MSG_BROADCAST, e))
1410 sound_builtin(e, chan, samp, vol, atten);
1412 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1416 if (!sound_allowed(dest, e))
1419 entno = num_for_edict(e);
1420 idx = precache_sound_index(samp);
1425 atten = floor(atten * 64);
1426 vol = floor(vol * 255);
1429 sflags |= SND_VOLUME;
1431 sflags |= SND_ATTENUATION;
1433 sflags |= SND_LARGEENTITY;
1435 sflags |= SND_LARGESOUND;
1437 WriteByte(dest, SVC_SOUND);
1438 WriteByte(dest, sflags);
1439 if (sflags & SND_VOLUME)
1440 WriteByte(dest, vol);
1441 if (sflags & SND_ATTENUATION)
1442 WriteByte(dest, atten);
1443 if (sflags & SND_LARGEENTITY)
1445 WriteShort(dest, entno);
1446 WriteByte(dest, chan);
1450 WriteShort(dest, entno * 8 + chan);
1452 if (sflags & SND_LARGESOUND)
1453 WriteShort(dest, idx);
1455 WriteByte(dest, idx);
1457 WriteCoord(dest, o_x);
1458 WriteCoord(dest, o_y);
1459 WriteCoord(dest, o_z);
1461 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1465 if (!sound_allowed(dest, e))
1468 o = e.origin + 0.5 * (e.mins + e.maxs);
1469 soundtoat(dest, e, o, chan, samp, vol, atten);
1471 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1473 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1475 void stopsoundto(float dest, entity e, float chan)
1479 if (!sound_allowed(dest, e))
1482 entno = num_for_edict(e);
1487 idx = precache_sound_index("misc/null.wav");
1488 sflags = SND_LARGEENTITY;
1490 sflags |= SND_LARGESOUND;
1491 WriteByte(dest, SVC_SOUND);
1492 WriteByte(dest, sflags);
1493 WriteShort(dest, entno);
1494 WriteByte(dest, chan);
1495 if (sflags & SND_LARGESOUND)
1496 WriteShort(dest, idx);
1498 WriteByte(dest, idx);
1499 WriteCoord(dest, e.origin_x);
1500 WriteCoord(dest, e.origin_y);
1501 WriteCoord(dest, e.origin_z);
1505 WriteByte(dest, SVC_STOPSOUND);
1506 WriteShort(dest, entno * 8 + chan);
1509 void stopsound(entity e, float chan)
1511 if (!sound_allowed(MSG_BROADCAST, e))
1514 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1515 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1518 void play2(entity e, string filename)
1520 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1522 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1525 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1527 float spamsound(entity e, float chan, string samp, float vol, float atten)
1529 if (!sound_allowed(MSG_BROADCAST, e))
1532 if (time > e.spamtime)
1535 sound(e, chan, samp, vol, atten);
1541 void play2team(float t, string filename)
1545 if (cvar("bot_sound_monopoly"))
1548 FOR_EACH_REALPLAYER(head)
1551 play2(head, filename);
1555 void play2all(string samp)
1557 if (cvar("bot_sound_monopoly"))
1560 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1563 void PrecachePlayerSounds(string f);
1564 void precache_all_models(string pattern)
1566 float globhandle, i, n;
1569 globhandle = search_begin(pattern, TRUE, FALSE);
1572 n = search_getsize(globhandle);
1573 for (i = 0; i < n; ++i)
1575 //print(search_getfilename(globhandle, i), "\n");
1576 f = search_getfilename(globhandle, i);
1579 if(substring(f, -9,5) == "_lod1")
1581 if(substring(f, -9,5) == "_lod2")
1583 if(!sv_loddistance1)
1585 PrecachePlayerSounds(strcat(f, ".sounds"));
1587 search_end(globhandle);
1592 // gamemode related things
1593 precache_model ("models/misc/chatbubble.spr");
1594 precache_model ("models/misc/teambubble.spr");
1597 precache_model ("models/runematch/curse.mdl");
1598 precache_model ("models/runematch/rune.mdl");
1601 #ifdef TTURRETS_ENABLED
1602 if (cvar("g_turrets"))
1606 // Precache all player models if desired
1607 if (cvar("sv_precacheplayermodels"))
1609 PrecachePlayerSounds("sound/player/default.sounds");
1610 precache_all_models("models/player/*.zym");
1611 precache_all_models("models/player/*.dpm");
1612 precache_all_models("models/player/*.md3");
1613 precache_all_models("models/player/*.psk");
1614 //precache_model("models/player/carni.zym");
1615 //precache_model("models/player/crash.zym");
1616 //precache_model("models/player/grunt.zym");
1617 //precache_model("models/player/headhunter.zym");
1618 //precache_model("models/player/insurrectionist.zym");
1619 //precache_model("models/player/jeandarc.zym");
1620 //precache_model("models/player/lurk.zym");
1621 //precache_model("models/player/lycanthrope.zym");
1622 //precache_model("models/player/marine.zym");
1623 //precache_model("models/player/nexus.zym");
1624 //precache_model("models/player/pyria.zym");
1625 //precache_model("models/player/shock.zym");
1626 //precache_model("models/player/skadi.zym");
1627 //precache_model("models/player/specop.zym");
1628 //precache_model("models/player/visitant.zym");
1631 if (cvar("sv_defaultcharacter"))
1634 s = cvar_string("sv_defaultplayermodel_red");
1638 PrecachePlayerSounds(strcat(s, ".sounds"));
1640 s = cvar_string("sv_defaultplayermodel_blue");
1644 PrecachePlayerSounds(strcat(s, ".sounds"));
1646 s = cvar_string("sv_defaultplayermodel_yellow");
1650 PrecachePlayerSounds(strcat(s, ".sounds"));
1652 s = cvar_string("sv_defaultplayermodel_pink");
1656 PrecachePlayerSounds(strcat(s, ".sounds"));
1658 s = cvar_string("sv_defaultplayermodel");
1662 PrecachePlayerSounds(strcat(s, ".sounds"));
1668 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1669 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1672 // gore and miscellaneous sounds
1673 //precache_sound ("misc/h2ohit.wav");
1674 precache_model ("models/hook.md3");
1675 precache_sound ("misc/armorimpact.wav");
1676 precache_sound ("misc/bodyimpact1.wav");
1677 precache_sound ("misc/bodyimpact2.wav");
1678 precache_sound ("misc/gib.wav");
1679 precache_sound ("misc/gib_splat01.wav");
1680 precache_sound ("misc/gib_splat02.wav");
1681 precache_sound ("misc/gib_splat03.wav");
1682 precache_sound ("misc/gib_splat04.wav");
1683 precache_sound ("misc/hit.wav");
1684 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1685 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1686 precache_sound ("misc/null.wav");
1687 precache_sound ("misc/spawn.wav");
1688 precache_sound ("misc/talk.wav");
1689 precache_sound ("misc/teleport.wav");
1690 precache_sound ("misc/poweroff.wav");
1691 precache_sound ("player/lava.wav");
1692 precache_sound ("player/slime.wav");
1695 precache_sound ("misc/jetpack_fly.wav");
1697 precache_model ("models/sprites/0.spr32");
1698 precache_model ("models/sprites/1.spr32");
1699 precache_model ("models/sprites/2.spr32");
1700 precache_model ("models/sprites/3.spr32");
1701 precache_model ("models/sprites/4.spr32");
1702 precache_model ("models/sprites/5.spr32");
1703 precache_model ("models/sprites/6.spr32");
1704 precache_model ("models/sprites/7.spr32");
1705 precache_model ("models/sprites/8.spr32");
1706 precache_model ("models/sprites/9.spr32");
1707 precache_model ("models/sprites/10.spr32");
1709 // common weapon precaches
1710 precache_sound ("weapons/weapon_switch.wav");
1711 precache_sound ("weapons/weaponpickup.wav");
1712 precache_sound ("weapons/unavailable.wav");
1713 if (g_grappling_hook)
1715 precache_sound ("weapons/hook_fire.wav"); // hook
1716 precache_sound ("weapons/hook_impact.wav"); // hook
1719 if (cvar("sv_precacheweapons") || g_nixnex)
1721 //precache weapon models/sounds
1724 while (wep <= WEP_LAST)
1726 weapon_action(wep, WR_PRECACHE);
1731 precache_model("models/elaser.mdl");
1732 precache_model("models/laser.mdl");
1733 precache_model("models/ebomb.mdl");
1736 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1738 if (!self.noise && self.music) // quake 3 uses the music field
1739 self.noise = self.music;
1741 // plays music for the level if there is any
1744 precache_sound (self.noise);
1745 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1750 // sorry, but using \ in macros breaks line numbers
1751 #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
1752 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1753 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1755 vector ExactTriggerHit_mins;
1756 vector ExactTriggerHit_maxs;
1757 float ExactTriggerHit_Recurse()
1763 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1766 if (trace_ent == self)
1771 se.solid = SOLID_NOT;
1772 f = ExactTriggerHit_Recurse();
1778 float ExactTriggerHit()
1782 if not(self.modelindex)
1786 self.solid = SOLID_BSP;
1787 ExactTriggerHit_mins = other.absmin;
1788 ExactTriggerHit_maxs = other.absmax;
1789 f = ExactTriggerHit_Recurse();
1795 // WARNING: this kills the trace globals
1796 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1797 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1799 #define INITPRIO_FIRST 0
1800 #define INITPRIO_GAMETYPE 0
1801 #define INITPRIO_GAMETYPE_FALLBACK 1
1802 #define INITPRIO_CVARS 5
1803 #define INITPRIO_FINDTARGET 10
1804 #define INITPRIO_DROPTOFLOOR 20
1805 #define INITPRIO_SETLOCATION 90
1806 #define INITPRIO_LINKDOORS 91
1807 #define INITPRIO_LAST 99
1809 .void(void) initialize_entity;
1810 .float initialize_entity_order;
1811 .entity initialize_entity_next;
1812 entity initialize_entity_first;
1814 void make_safe_for_remove(entity e)
1816 if (e.initialize_entity)
1819 for (ent = initialize_entity_first; ent; )
1821 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1823 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1824 // skip it in linked list
1827 prev.initialize_entity_next = ent.initialize_entity_next;
1828 ent = prev.initialize_entity_next;
1832 initialize_entity_first = ent.initialize_entity_next;
1833 ent = initialize_entity_first;
1839 ent = ent.initialize_entity_next;
1845 void objerror(string s)
1847 make_safe_for_remove(self);
1848 objerror_builtin(s);
1851 void remove_unsafely(entity e)
1856 void remove_safely(entity e)
1858 make_safe_for_remove(e);
1862 void InitializeEntity(entity e, void(void) func, float order)
1866 if (!e || e.initialize_entity)
1868 // make a proxy initializer entity
1872 e.classname = "initialize_entity";
1876 e.initialize_entity = func;
1877 e.initialize_entity_order = order;
1879 cur = initialize_entity_first;
1882 if (!cur || cur.initialize_entity_order > order)
1884 // insert between prev and cur
1886 prev.initialize_entity_next = e;
1888 initialize_entity_first = e;
1889 e.initialize_entity_next = cur;
1893 cur = cur.initialize_entity_next;
1896 void InitializeEntitiesRun()
1899 startoflist = initialize_entity_first;
1900 initialize_entity_first = world;
1901 for (self = startoflist; self; )
1904 var void(void) func;
1905 e = self.initialize_entity_next;
1906 func = self.initialize_entity;
1907 self.initialize_entity_order = 0;
1908 self.initialize_entity = func_null;
1909 self.initialize_entity_next = world;
1910 if (self.classname == "initialize_entity")
1914 remove_builtin(self);
1917 //dprint("Delayed initialization: ", self.classname, "\n");
1923 .float uncustomizeentityforclient_set;
1924 .void(void) uncustomizeentityforclient;
1925 void(void) SUB_Nullpointer = #0;
1926 void UncustomizeEntitiesRun()
1930 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1931 self.uncustomizeentityforclient();
1934 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1936 e.customizeentityforclient = customizer;
1937 e.uncustomizeentityforclient = uncustomizer;
1938 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1942 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1945 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1949 if (e.classname == "")
1950 e.classname = "net_linked";
1952 if (e.model == "" || self.modelindex == 0)
1956 setmodel(e, "null");
1960 e.SendEntity = sendfunc;
1961 e.SendFlags = 0xFFFFFF;
1964 e.effects |= EF_NODEPTHTEST;
1968 e.nextthink = time + dt;
1969 e.think = SUB_Remove;
1973 void adaptor_think2touch()
1982 void adaptor_think2use()
1994 // deferred dropping
1995 void DropToFloor_Handler()
1997 droptofloor_builtin();
1998 self.dropped_origin = self.origin;
2003 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
2008 float trace_hits_box_a0, trace_hits_box_a1;
2010 float trace_hits_box_1d(float end, float thmi, float thma)
2014 // just check if x is in range
2022 // do the trace with respect to x
2023 // 0 -> end has to stay in thmi -> thma
2024 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
2025 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
2026 if (trace_hits_box_a0 > trace_hits_box_a1)
2032 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
2037 // now it is a trace from 0 to end
2039 trace_hits_box_a0 = 0;
2040 trace_hits_box_a1 = 1;
2042 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
2044 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
2046 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
2052 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
2054 return trace_hits_box(start, end, thmi - ma, thma - mi);
2057 float SUB_NoImpactCheck()
2059 // zero hitcontents = this is not the real impact, but either the
2060 // mirror-impact of something hitting the projectile instead of the
2061 // projectile hitting the something, or a touchareagrid one. Neither of
2062 // these stop the projectile from moving, so...
2063 if(trace_dphitcontents == 0)
2065 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
2068 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2070 if (other == world && self.size != '0 0 0')
2073 tic = self.velocity * sys_frametime;
2074 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2075 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2076 if (trace_fraction >= 1)
2078 dprint("Odd... did not hit...?\n");
2080 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2082 dprint("Detected and prevented the sky-grapple bug.\n");
2090 #define SUB_OwnerCheck() (other && (other == self.owner))
2092 #define PROJECTILE_TOUCH do { if(SUB_OwnerCheck()) return; if(SUB_NoImpactCheck()) { remove(self); return; } if(trace_ent && trace_ent.solid > SOLID_TRIGGER) UpdateCSQCProjectileNextFrame(self); } while(0)
2094 float MAX_IPBAN_URIS = 16;
2096 float URI_GET_DISCARD = 0;
2097 float URI_GET_IPBAN = 1;
2098 float URI_GET_IPBAN_END = 16;
2100 void URI_Get_Callback(float id, float status, string data)
2102 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2104 dprint("\nEnd of data.\n");
2106 if (id == URI_GET_DISCARD)
2110 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2113 OnlineBanList_URI_Get_Callback(id, status, data);
2117 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2121 void print_to(entity e, string s)
2124 sprint(e, strcat(s, "\n"));
2129 string getrecords(float page) // 50 records per page
2143 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2145 if (MapInfo_Get_ByID(i))
2147 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2150 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2151 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2159 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2161 if (MapInfo_Get_ByID(i))
2163 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2166 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2167 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2175 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2177 if (MapInfo_Get_ByID(i))
2179 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2182 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2183 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2189 MapInfo_ClearTemps();
2191 if (s == "" && page == 0)
2192 return "No records are available on this server.\n";
2197 string getrankings()
2210 for (i = 1; i <= RANKINGS_CNT; ++i)
2212 t = race_GetTime(i);
2215 n = race_GetName(i);
2216 p = race_PlaceName(i);
2217 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2220 MapInfo_ClearTemps();
2223 return strcat("No records are available for the map: ", map, "\n");
2225 return strcat("Records for ", map, ":\n", s);
2228 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2231 vector start, org, delta, end, enddown, mstart;
2233 m = e.dphitcontentsmask;
2234 e.dphitcontentsmask = goodcontents | badcontents;
2237 delta = world.maxs - world.mins;
2239 for (i = 0; i < attempts; ++i)
2241 start_x = org_x + random() * delta_x;
2242 start_y = org_y + random() * delta_y;
2243 start_z = org_z + random() * delta_z;
2245 // rule 1: start inside world bounds, and outside
2246 // solid, and don't start from somewhere where you can
2247 // fall down to evil
2248 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2249 if (trace_fraction >= 1)
2251 if (trace_startsolid)
2253 if (trace_dphitcontents & badcontents)
2255 if (trace_dphitq3surfaceflags & badsurfaceflags)
2258 // rule 2: if we are too high, lower the point
2259 if (trace_fraction * delta_z > maxaboveground)
2260 start = trace_endpos + '0 0 1' * maxaboveground;
2261 enddown = trace_endpos;
2263 // rule 3: make sure we aren't outside the map. This only works
2264 // for somewhat well formed maps. A good rule of thumb is that
2265 // the map should have a convex outside hull.
2266 // these can be traceLINES as we already verified the starting box
2267 mstart = start + 0.5 * (e.mins + e.maxs);
2268 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2269 if (trace_fraction >= 1)
2271 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2272 if (trace_fraction >= 1)
2274 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2275 if (trace_fraction >= 1)
2277 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2278 if (trace_fraction >= 1)
2280 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2281 if (trace_fraction >= 1)
2284 // find a random vector to "look at"
2285 end_x = org_x + random() * delta_x;
2286 end_y = org_y + random() * delta_y;
2287 end_z = org_z + random() * delta_z;
2288 end = start + normalize(end - start) * vlen(delta);
2290 // rule 4: start TO end must not be too short
2291 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2292 if (trace_startsolid)
2294 if (trace_fraction < minviewdistance / vlen(delta))
2297 // rule 5: don't want to look at sky
2298 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2301 // rule 6: we must not end up in trigger_hurt
2302 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2304 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2311 e.dphitcontentsmask = m;
2315 setorigin(e, start);
2316 e.angles = vectoangles(end - start);
2317 dprint("Needed ", ftos(i + 1), " attempts\n");
2324 float zcurveparticles_effectno;
2325 vector zcurveparticles_start;
2326 float zcurveparticles_spd;
2328 void endzcurveparticles()
2330 if(zcurveparticles_effectno)
2333 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2335 zcurveparticles_effectno = 0;
2338 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2340 spd = bound(0, floor(spd / 16), 32767);
2341 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2343 endzcurveparticles();
2344 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2345 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2346 WriteShort(MSG_BROADCAST, effectno);
2347 WriteCoord(MSG_BROADCAST, start_x);
2348 WriteCoord(MSG_BROADCAST, start_y);
2349 WriteCoord(MSG_BROADCAST, start_z);
2350 zcurveparticles_effectno = effectno;
2351 zcurveparticles_start = start;
2354 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2355 WriteCoord(MSG_BROADCAST, end_x);
2356 WriteCoord(MSG_BROADCAST, end_y);
2357 WriteCoord(MSG_BROADCAST, end_z);
2358 WriteCoord(MSG_BROADCAST, end_dz);
2359 zcurveparticles_spd = spd;
2362 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2365 vector vecxy, velxy;
2367 vecxy = end - start;
2372 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2374 endzcurveparticles();
2375 trailparticles(world, effectno, start, end);
2379 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2380 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2383 string GetGametype(); // g_world.qc
2384 void write_recordmarker(entity pl, float tstart, float dt)
2386 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2388 // also write a marker into demo files for demotc-race-record-extractor to find
2391 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2392 " ", ftos(tstart), " ", ftos(dt), "\n"));
2395 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2397 switch(self.owner.cvar_cl_gunalign)
2408 if(allowcenter) // 2: allow center handedness
2421 if(allowcenter) // 2: allow center handedness
2437 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2442 if (cvar("g_shootfromeye"))
2446 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2454 else if (cvar("g_shootfromcenter"))
2458 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2466 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2476 else if (cvar("g_shootfromclient"))
2478 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2485 void attach_sameorigin(entity e, entity to, string tag)
2487 vector org, t_forward, t_left, t_up, e_forward, e_up;
2494 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2495 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2496 t_forward = v_forward * tagscale;
2497 t_left = v_right * -tagscale;
2498 t_up = v_up * tagscale;
2500 e.origin_x = org * t_forward;
2501 e.origin_y = org * t_left;
2502 e.origin_z = org * t_up;
2504 // current forward and up directions
2505 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2506 e.angles_x = -e.angles_x;
2507 fixedmakevectors(e.angles);
2509 // untransform forward, up!
2510 e_forward_x = v_forward * t_forward;
2511 e_forward_y = v_forward * t_left;
2512 e_forward_z = v_forward * t_up;
2513 e_up_x = v_up * t_forward;
2514 e_up_y = v_up * t_left;
2515 e_up_z = v_up * t_up;
2517 e.angles = fixedvectoangles2(e_forward, e_up);
2518 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2519 e.angles_x = -e.angles_x;
2521 setattachment(e, to, tag);
2522 setorigin(e, e.origin);
2525 void detach_sameorigin(entity e)
2528 org = gettaginfo(e, 0);
2529 e.angles = fixedvectoangles2(v_forward, v_up);
2530 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2531 e.angles_x = -e.angles_x;
2533 setattachment(e, world, "");
2534 setorigin(e, e.origin);
2537 void follow_sameorigin(entity e, entity to)
2539 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2540 e.aiment = to; // make the hole follow bmodel
2541 e.punchangle = to.angles; // the original angles of bmodel
2542 e.view_ofs = e.origin - to.origin; // relative origin
2543 e.v_angle = e.angles - to.angles; // relative angles
2546 void unfollow_sameorigin(entity e)
2548 e.movetype = MOVETYPE_NONE;
2551 entity gettaginfo_relative_ent;
2552 vector gettaginfo_relative(entity e, float tag)
2554 if (!gettaginfo_relative_ent)
2556 gettaginfo_relative_ent = spawn();
2557 gettaginfo_relative_ent.effects = EF_NODRAW;
2559 gettaginfo_relative_ent.model = e.model;
2560 gettaginfo_relative_ent.modelindex = e.modelindex;
2561 gettaginfo_relative_ent.frame = e.frame;
2562 return gettaginfo(gettaginfo_relative_ent, tag);
2565 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2569 if (pl.soundentity.cnt & p)
2571 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2572 pl.soundentity.cnt |= p;
2575 void SoundEntity_StopSound(entity pl, float chan)
2579 if (pl.soundentity.cnt & p)
2581 stopsoundto(MSG_ALL, pl.soundentity, chan);
2582 pl.soundentity.cnt &~= p;
2586 void SoundEntity_Attach(entity pl)
2588 pl.soundentity = spawn();
2589 pl.soundentity.classname = "soundentity";
2590 pl.soundentity.owner = pl;
2591 setattachment(pl.soundentity, pl, "");
2592 setmodel(pl.soundentity, "null");
2595 void SoundEntity_Detach(entity pl)
2598 for (i = 0; i <= 7; ++i)
2599 SoundEntity_StopSound(pl, i);
2603 float ParseCommandPlayerSlotTarget_firsttoken;
2604 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2612 ParseCommandPlayerSlotTarget_firsttoken = -1;
2616 if (substring(argv(idx), 0, 1) == "#")
2618 s = substring(argv(idx), 1, -1);
2626 ParseCommandPlayerSlotTarget_firsttoken = idx;
2627 if (s == ftos(stof(s)))
2629 e = edict_num(stof(s));
2630 if (e.flags & FL_CLIENT)
2636 // it must be a nick name
2639 ParseCommandPlayerSlotTarget_firsttoken = idx;
2642 FOR_EACH_CLIENT(head)
2643 if (head.netname == s)
2651 s = strdecolorize(s);
2653 FOR_EACH_CLIENT(head)
2654 if (strdecolorize(head.netname) == s)
2669 float modeleffect_SendEntity(entity to, float sf)
2672 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2675 if(self.velocity != '0 0 0')
2677 if(self.angles != '0 0 0')
2679 if(self.avelocity != '0 0 0')
2682 WriteByte(MSG_ENTITY, f);
2683 WriteShort(MSG_ENTITY, self.modelindex);
2684 WriteByte(MSG_ENTITY, self.skin);
2685 WriteByte(MSG_ENTITY, self.frame);
2686 WriteCoord(MSG_ENTITY, self.origin_x);
2687 WriteCoord(MSG_ENTITY, self.origin_y);
2688 WriteCoord(MSG_ENTITY, self.origin_z);
2691 WriteCoord(MSG_ENTITY, self.velocity_x);
2692 WriteCoord(MSG_ENTITY, self.velocity_y);
2693 WriteCoord(MSG_ENTITY, self.velocity_z);
2697 WriteCoord(MSG_ENTITY, self.angles_x);
2698 WriteCoord(MSG_ENTITY, self.angles_y);
2699 WriteCoord(MSG_ENTITY, self.angles_z);
2703 WriteCoord(MSG_ENTITY, self.avelocity_x);
2704 WriteCoord(MSG_ENTITY, self.avelocity_y);
2705 WriteCoord(MSG_ENTITY, self.avelocity_z);
2707 WriteShort(MSG_ENTITY, self.scale * 256.0);
2708 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2709 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2710 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2711 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2716 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)
2721 e.classname = "modeleffect";
2729 e.teleport_time = t1;
2733 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2737 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2740 sz = max(e.scale, e.scale2);
2741 setsize(e, e.mins * sz, e.maxs * sz);
2742 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2745 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2747 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2750 float randombit(float bits)
2752 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2761 for(f = 1; f <= bits; f *= 2)
2770 r = (r - 1) / (n - 1);
2777 float randombits(float bits, float k, float error_return)
2781 while(k > 0 && bits != r)
2783 r += randombit(bits - r);
2792 void randombit_test(float bits, float iter)
2796 print(ftos(randombit(bits)), "\n");
2801 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2803 if(halflifedist > 0)
2804 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2805 else if(halflifedist < 0)
2806 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2815 #define cvar_string_normal cvar_string_builtin
2816 #define cvar_normal cvar_builtin
2818 string cvar_string_normal(string n)
2820 if not(cvar_type(n) & 1)
2821 backtrace(strcat("Attempt to access undefined cvar: ", n));
2822 return cvar_string_builtin(n);
2825 float cvar_normal(string n)
2827 return stof(cvar_string_normal(n));
2830 #define cvar_set_normal cvar_set_builtin
2838 oself.think = SUB_Remove;
2839 oself.nextthink = time;
2845 Execute func() after time + fdelay.
2846 self when func is executed = self when defer is called
2848 void defer(float fdelay, void() func)
2855 e.think = defer_think;
2856 e.nextthink = time + fdelay;