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));
12 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
13 void() spawnpoint_use;
14 float race_GetTime(float pos);
15 string race_GetName(float pos);
16 string race_PlaceName(float pos);
18 string ColoredTeamName(float t);
20 string admin_name(void)
22 if(cvar_string("sv_adminnick") != "")
23 return cvar_string("sv_adminnick");
25 return "SERVER ADMIN";
28 float DistributeEvenly_amount;
29 float DistributeEvenly_totalweight;
30 void DistributeEvenly_Init(float amount, float totalweight)
32 if (DistributeEvenly_amount)
34 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
35 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
38 DistributeEvenly_amount = 0;
40 DistributeEvenly_amount = amount;
41 DistributeEvenly_totalweight = totalweight;
43 float DistributeEvenly_Get(float weight)
48 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
49 DistributeEvenly_totalweight -= weight;
50 DistributeEvenly_amount -= f;
54 void move_out_of_solid_expand(entity e, vector by)
57 tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
60 if (trace_fraction < 1)
63 // adjust origin in the other direction...
64 setorigin(e,e.origin - by * (1 - trace_fraction));
68 float move_out_of_solid(entity e)
73 traceline(o, o, MOVE_WORLDONLY, e);
77 tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
78 if (!trace_startsolid)
85 move_out_of_solid_expand(e, '1 0 0' * m0_x);
87 move_out_of_solid_expand(e, '1 0 0' * m1_x);
89 move_out_of_solid_expand(e, '0 1 0' * m0_y);
91 move_out_of_solid_expand(e, '0 1 0' * m1_y);
93 move_out_of_solid_expand(e, '0 0 1' * m0_z);
95 move_out_of_solid_expand(e, '0 0 1' * m1_z);
97 setorigin(e, e.origin);
99 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
100 if (trace_startsolid)
109 string STR_PLAYER = "player";
110 string STR_SPECTATOR = "spectator";
111 string STR_OBSERVER = "observer";
114 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
115 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
116 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
117 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
119 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
120 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
121 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
122 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
123 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
126 // copies a string to a tempstring (so one can strunzone it)
127 string strcat1(string s) = #115; // FRIK_FILE
132 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
134 local float nPlayerHealth = rint(enPlayer.health);
135 local float nPlayerArmor = rint(enPlayer.armorvalue);
136 local float nPlayerHandicap = enPlayer.cvar_cl_handicap;
137 local float nPlayerPing = rint(enPlayer.ping);
138 local string strPlayerPingColor;
139 local string strMessage;
140 if(nPlayerPing >= 150)
141 strPlayerPingColor = "^1";
143 strPlayerPingColor = "^2";
145 if((cvar("sv_fragmessage_information_stats")) && (enPlayer.health >= 1))
146 strMessage = strcat(strMessage, "\n^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
148 if(cvar("sv_fragmessage_information_ping")) {
149 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
150 strMessage = strcat(strMessage, "\n^7(^2Bot");
152 strMessage = strcat(strMessage, "\n^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
153 if(cvar("sv_fragmessage_information_handicap"))
154 if(cvar("sv_fragmessage_information_handicap") == 2)
155 if(nPlayerHandicap <= 1)
156 strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
158 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
159 else if not(nPlayerHandicap <= 1)
160 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
162 strMessage = strcat(strMessage, "^7)");
163 } else if(cvar("sv_fragmessage_information_handicap")) {
164 if(cvar("sv_fragmessage_information_handicap") == 2)
165 if(nPlayerHandicap <= 1)
166 strMessage = strcat(strMessage, "\n^7(Handicap ^2Off^7)");
168 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
169 else if(nPlayerHandicap > 1)
170 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
174 void bcenterprint(string s)
176 // TODO replace by MSG_ALL (would show it to spectators too, though)?
178 FOR_EACH_PLAYER(head)
179 if (clienttype(head) == CLIENTTYPE_REAL)
180 centerprint(head, s);
183 void GameLogEcho(string s)
188 if (cvar("sv_eventlog_files"))
193 matches = cvar("sv_eventlog_files_counter") + 1;
194 cvar_set("sv_eventlog_files_counter", ftos(matches));
197 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
198 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
199 logfile = fopen(fn, FILE_APPEND);
200 fputs(logfile, ":logversion:3\n");
204 if (cvar("sv_eventlog_files_timestamps"))
205 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
207 fputs(logfile, strcat(s, "\n"));
210 if (cvar("sv_eventlog_console"))
219 // will be opened later
224 if (logfile_open && logfile >= 0)
234 vector PL_CROUCH_VIEW_OFS;
235 vector PL_CROUCH_MIN;
236 vector PL_CROUCH_MAX;
238 float spawnpoint_nag;
239 void relocate_spawnpoint()
241 PL_VIEW_OFS = stov(cvar_string("sv_player_viewoffset"));
242 PL_MIN = stov(cvar_string("sv_player_mins"));
243 PL_MAX = stov(cvar_string("sv_player_maxs"));
244 PL_CROUCH_VIEW_OFS = stov(cvar_string("sv_player_crouch_viewoffset"));
245 PL_CROUCH_MIN = stov(cvar_string("sv_player_crouch_mins"));
246 PL_CROUCH_MAX = stov(cvar_string("sv_player_crouch_maxs"));
248 // nudge off the floor
249 setorigin(self, self.origin + '0 0 1');
251 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
252 if (trace_startsolid)
258 if (!move_out_of_solid(self))
259 objerror("could not get out of solid at all!");
260 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
261 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
262 print(" ", ftos(self.origin_y - o_y));
263 print(" ", ftos(self.origin_z - o_z), "'\n");
264 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
267 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
273 self.mins = self.maxs = '0 0 0';
274 objerror("player spawn point in solid, mapper sucks!\n");
279 if (cvar("g_spawnpoints_autodrop"))
281 setsize(self, PL_MIN, PL_MAX);
285 self.use = spawnpoint_use;
286 self.team_saved = self.team;
290 if (have_team_spawns != 0)
292 have_team_spawns = 1;
294 if (cvar("r_showbboxes"))
296 // show where spawnpoints point at too
297 makevectors(self.angles);
300 e.classname = "info_player_foo";
301 setorigin(e, self.origin + v_forward * 24);
302 setsize(e, '-8 -8 -8', '8 8 8');
303 e.solid = SOLID_TRIGGER;
307 #define strstr strstrofs
309 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
310 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
311 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
312 // BE CONSTANT OR strzoneD!
313 float strstr(string haystack, string needle, float offset)
317 len = strlen(needle);
318 endpos = strlen(haystack) - len;
319 while(offset <= endpos)
321 found = substring(haystack, offset, len);
330 float NUM_NEAREST_ENTITIES = 4;
331 entity nearest_entity[NUM_NEAREST_ENTITIES];
332 float nearest_length[NUM_NEAREST_ENTITIES];
333 entity findnearest(vector point, .string field, string value, vector axismod)
344 localhead = find(world, field, value);
347 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
348 dist = localhead.oldorigin;
350 dist = localhead.origin;
352 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
355 for (i = 0; i < num_nearest; ++i)
357 if (len < nearest_length[i])
361 // now i tells us where to insert at
362 // INSERTION SORT! YOU'VE SEEN IT! RUN!
363 if (i < NUM_NEAREST_ENTITIES)
365 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
367 nearest_length[j + 1] = nearest_length[j];
368 nearest_entity[j + 1] = nearest_entity[j];
370 nearest_length[i] = len;
371 nearest_entity[i] = localhead;
372 if (num_nearest < NUM_NEAREST_ENTITIES)
373 num_nearest = num_nearest + 1;
376 localhead = find(localhead, field, value);
379 // now use the first one from our list that we can see
380 for (i = 0; i < num_nearest; ++i)
382 traceline(point, nearest_entity[i].origin, TRUE, world);
383 if (trace_fraction == 1)
387 dprint("Nearest point (");
388 dprint(nearest_entity[0].netname);
389 dprint(") is not visible, using a visible one.\n");
391 return nearest_entity[i];
395 if (num_nearest == 0)
398 dprint("Not seeing any location point, using nearest as fallback.\n");
400 dprint("Candidates were: ");
401 for(j = 0; j < num_nearest; ++j)
405 dprint(nearest_entity[j].netname);
410 return nearest_entity[0];
413 void spawnfunc_target_location()
415 self.classname = "target_location";
416 // location name in netname
417 // eventually support: count, teamgame selectors, line of sight?
420 void spawnfunc_info_location()
422 self.classname = "target_location";
423 self.message = self.netname;
426 string NearestLocation(vector p)
431 loc = findnearest(p, classname, "target_location", '1 1 1');
438 loc = findnearest(p, target, "###item###", '1 1 4');
445 string formatmessage(string msg)
456 crosshair_trace(self);
457 cursor = trace_endpos;
458 cursor_ent = trace_ent;
462 break; // too many replacements
465 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
466 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
479 replacement = substring(msg, p, 2);
480 escape = substring(msg, p + 1, 1);
484 else if (escape == "\\")
486 else if (escape == "n")
488 else if (escape == "a")
489 replacement = ftos(floor(self.armorvalue));
490 else if (escape == "h")
491 replacement = ftos(floor(self.health));
492 else if (escape == "l")
493 replacement = NearestLocation(self.origin);
494 else if (escape == "y")
495 replacement = NearestLocation(cursor);
496 else if (escape == "d")
497 replacement = NearestLocation(self.death_origin);
498 else if (escape == "w") {
502 wep = self.switchweapon;
505 replacement = W_Name(wep);
506 } else if (escape == "W") {
507 if (self.items & IT_SHELLS) replacement = "shells";
508 else if (self.items & IT_NAILS) replacement = "bullets";
509 else if (self.items & IT_ROCKETS) replacement = "rockets";
510 else if (self.items & IT_CELLS) replacement = "cells";
511 else replacement = "batteries"; // ;)
512 } else if (escape == "x") {
513 replacement = cursor_ent.netname;
514 if (!replacement || !cursor_ent)
515 replacement = "nothing";
516 } else if (escape == "p") {
517 if (self.last_selected_player)
518 replacement = self.last_selected_player.netname;
520 replacement = "(nobody)";
521 } else if (escape == "s")
522 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
523 else if (escape == "S")
524 replacement = ftos(vlen(self.velocity));
525 else if (escape == "v") {
529 if(self.classname == "spectator")
534 weapon_number = stats.weapon;
537 weapon_number = stats.switchweapon;
540 weapon_number = stats.cnt;
542 if(stats.cvar_cl_accuracy_data_share && stats.stats_fired[weapon_number - 1])
543 replacement = ftos(bound(0, floor(100 * stats.stats_hit[weapon_number - 1] / stats.stats_fired[weapon_number - 1]), 100));
545 replacement = "~"; // or something to indicate NULL, not available
548 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
549 p = p + strlen(replacement);
554 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
555 return (value == 0) ? FALSE : TRUE;
564 >0: receives a cvar from name=argv(f) value=argv(f+1)
566 void GetCvars_handleString(string thisname, float f, .string field, string name)
571 strunzone(self.field);
572 self.field = string_null;
576 if (thisname == name)
579 strunzone(self.field);
580 self.field = strzone(argv(f + 1));
584 stuffcmd(self, strcat("sendcvar ", name, "\n"));
586 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
588 GetCvars_handleString(thisname, f, field, name);
589 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
590 if (thisname == name)
593 s = func(strcat1(self.field));
596 strunzone(self.field);
597 self.field = strzone(s);
601 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
608 if (thisname == name)
609 self.field = stof(argv(f + 1));
612 stuffcmd(self, strcat("sendcvar ", name, "\n"));
614 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
621 if (thisname == name)
625 self.field = stof(argv(f + 1));
634 stuffcmd(self, strcat("sendcvar ", name, "\n"));
637 string W_FixWeaponOrder_ForceComplete(string s);
638 string W_FixWeaponOrder_AllowIncomplete(string s);
639 float w_getbestweapon(entity e);
640 void GetCvars(float f)
644 s = strcat1(argv(f));
645 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
646 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
647 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
648 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
649 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
650 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
651 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
652 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
653 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
654 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
655 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
656 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
657 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
658 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
659 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
660 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
661 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
662 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
663 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
664 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
665 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
666 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
667 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
668 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
670 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
671 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
673 #ifdef ALLOW_FORCEMODELS
674 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
675 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
677 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
679 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
682 if (s == "cl_weaponpriority")
683 self.switchweapon = w_getbestweapon(self);
687 float fexists(string f)
690 fh = fopen(f, FILE_READ);
697 void backtrace(string msg)
700 dev = cvar("developer");
701 war = cvar("prvm_backtraceforwarnings");
702 cvar_set("developer", "1");
703 cvar_set("prvm_backtraceforwarnings", "1");
705 print("--- CUT HERE ---\nWARNING: ");
708 remove(world); // isn't there any better way to cause a backtrace?
709 print("\n--- CUT UNTIL HERE ---\n");
710 cvar_set("developer", ftos(dev));
711 cvar_set("prvm_backtraceforwarnings", ftos(war));
714 string Team_ColorCode(float teamid)
716 if (teamid == COLOR_TEAM1)
718 else if (teamid == COLOR_TEAM2)
720 else if (teamid == COLOR_TEAM3)
722 else if (teamid == COLOR_TEAM4)
728 string Team_ColorName(float t)
730 // fixme: Search for team entities and get their .netname's!
731 if (t == COLOR_TEAM1)
733 if (t == COLOR_TEAM2)
735 if (t == COLOR_TEAM3)
737 if (t == COLOR_TEAM4)
742 string Team_ColorNameLowerCase(float t)
744 // fixme: Search for team entities and get their .netname's!
745 if (t == COLOR_TEAM1)
747 if (t == COLOR_TEAM2)
749 if (t == COLOR_TEAM3)
751 if (t == COLOR_TEAM4)
756 float ColourToNumber(string team_colour)
758 if (team_colour == "red")
761 if (team_colour == "blue")
764 if (team_colour == "yellow")
767 if (team_colour == "pink")
770 if (team_colour == "auto")
776 float NumberToTeamNumber(float number)
793 #define CENTERPRIO_POINT 1
794 #define CENTERPRIO_SPAM 2
795 #define CENTERPRIO_VOTE 4
796 #define CENTERPRIO_NORMAL 5
797 #define CENTERPRIO_SHIELDING 7
798 #define CENTERPRIO_MAPVOTE 9
799 #define CENTERPRIO_IDLEKICK 50
800 #define CENTERPRIO_ADMIN 99
801 .float centerprint_priority;
802 .float centerprint_expires;
803 void centerprint_atprio(entity e, float prio, string s)
805 if (intermission_running)
806 if (prio < CENTERPRIO_MAPVOTE)
808 if (time > e.centerprint_expires)
809 e.centerprint_priority = 0;
810 if (prio >= e.centerprint_priority)
812 e.centerprint_priority = prio;
813 if (timeoutStatus == 2)
814 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
816 e.centerprint_expires = time + e.cvar_scr_centertime;
817 centerprint_builtin(e, s);
820 void centerprint_expire(entity e, float prio)
822 if (prio == e.centerprint_priority)
824 e.centerprint_priority = 0;
825 centerprint_builtin(e, "");
828 void centerprint(entity e, string s)
830 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
833 // decolorizes and team colors the player name when needed
834 string playername(entity p)
837 if (teams_matter && !intermission_running && p.classname == "player")
839 t = Team_ColorCode(p.team);
840 return strcat(t, strdecolorize(p.netname));
846 vector randompos(vector m1, vector m2)
850 v_x = m2_x * random() + m1_x;
851 v_y = m2_y * random() + m1_y;
852 v_z = m2_z * random() + m1_z;
856 float g_pickup_shells;
857 float g_pickup_shells_max;
858 float g_pickup_nails;
859 float g_pickup_nails_max;
860 float g_pickup_rockets;
861 float g_pickup_rockets_max;
862 float g_pickup_cells;
863 float g_pickup_cells_max;
865 float g_pickup_fuel_jetpack;
866 float g_pickup_fuel_max;
867 float g_pickup_armorsmall;
868 float g_pickup_armorsmall_max;
869 float g_pickup_armormedium;
870 float g_pickup_armormedium_max;
871 float g_pickup_armorbig;
872 float g_pickup_armorbig_max;
873 float g_pickup_armorlarge;
874 float g_pickup_armorlarge_max;
875 float g_pickup_healthsmall;
876 float g_pickup_healthsmall_max;
877 float g_pickup_healthmedium;
878 float g_pickup_healthmedium_max;
879 float g_pickup_healthlarge;
880 float g_pickup_healthlarge_max;
881 float g_pickup_healthmega;
882 float g_pickup_healthmega_max;
884 float g_weaponarena_random;
885 string g_weaponarena_list;
886 float g_weaponspeedfactor;
887 float g_weaponratefactor;
888 float g_weapondamagefactor;
889 float g_weaponforcefactor;
890 float g_weaponspreadfactor;
894 float start_ammo_shells;
895 float start_ammo_nails;
896 float start_ammo_rockets;
897 float start_ammo_cells;
898 float start_ammo_fuel;
900 float start_armorvalue;
901 float warmup_start_weapons;
902 float warmup_start_ammo_shells;
903 float warmup_start_ammo_nails;
904 float warmup_start_ammo_rockets;
905 float warmup_start_ammo_cells;
906 float warmup_start_ammo_fuel;
907 float warmup_start_health;
908 float warmup_start_armorvalue;
912 entity get_weaponinfo(float w);
914 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
916 var float i = weaponinfo.weapon;
921 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
923 if (t < 0) // "default" weapon selection
925 if (g_lms || g_ca || allguns)
926 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
929 else if (g_race || g_cts)
930 t = (i == WEP_LASER);
932 t = 0; // weapon is set a few lines later
934 t = (i == WEP_LASER || i == WEP_SHOTGUN);
935 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
936 t |= (i == WEP_HOOK);
939 // we cannot disable porto in Nexball, we must force it
940 if(g_nexball && i == WEP_PORTO)
946 float NixNex_CanChooseWeapon(float wpn);
947 void readplayerstartcvars()
953 // initialize starting values for players
956 start_ammo_shells = 0;
957 start_ammo_nails = 0;
958 start_ammo_rockets = 0;
959 start_ammo_cells = 0;
960 start_health = cvar("g_balance_health_start");
961 start_armorvalue = cvar("g_balance_armor_start");
964 s = cvar_string("g_weaponarena");
970 g_weaponarena_list = "All Weapons";
971 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
973 e = get_weaponinfo(j);
974 g_weaponarena |= e.weapons;
975 weapon_action(e.weapon, WR_PRECACHE);
978 else if (s == "most")
980 g_weaponarena_list = "Most Weapons";
981 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
983 e = get_weaponinfo(j);
984 if (e.spawnflags & WEP_FLAG_NORMAL)
986 g_weaponarena |= e.weapons;
987 weapon_action(e.weapon, WR_PRECACHE);
991 else if (s == "none")
993 g_weaponarena_list = "No Weapons";
994 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
998 t = tokenize_console(s);
999 g_weaponarena_list = "";
1000 for (i = 0; i < t; ++i)
1003 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
1005 e = get_weaponinfo(j);
1008 g_weaponarena |= e.weapons;
1009 weapon_action(e.weapon, WR_PRECACHE);
1010 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
1016 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
1019 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
1023 g_weaponarena_random = cvar("g_weaponarena_random");
1025 g_weaponarena_random = 0;
1030 // will be done later
1031 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1032 if (NixNex_CanChooseWeapon(i))
1033 weapon_action(i, WR_PRECACHE);
1034 if(!cvar("g_use_ammunition"))
1035 start_items |= IT_UNLIMITED_AMMO;
1037 else if (g_weaponarena)
1039 start_weapons = g_weaponarena;
1040 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
1041 start_ammo_rockets = 999;
1042 if (g_weaponarena & WEPBIT_SHOTGUN)
1043 start_ammo_shells = 999;
1044 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
1045 start_ammo_cells = 999;
1046 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
1047 start_ammo_nails = 999;
1048 if (g_weaponarena & WEPBIT_HOOK)
1049 start_ammo_fuel = 999;
1050 start_items |= IT_UNLIMITED_AMMO;
1052 else if (g_minstagib)
1055 start_armorvalue = 0;
1056 start_weapons = WEPBIT_MINSTANEX;
1057 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
1058 start_ammo_cells = cvar("g_minstagib_ammo_start");
1059 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
1060 start_ammo_fuel = cvar("g_start_ammo_fuel");
1062 if (g_minstagib_invis_alpha <= 0)
1063 g_minstagib_invis_alpha = -1;
1069 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1070 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1071 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1072 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1073 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1074 start_health = cvar("g_lms_start_health");
1075 start_armorvalue = cvar("g_lms_start_armor");
1077 else if (cvar("g_use_ammunition"))
1079 start_ammo_shells = cvar("g_start_ammo_shells");
1080 start_ammo_nails = cvar("g_start_ammo_nails");
1081 start_ammo_rockets = cvar("g_start_ammo_rockets");
1082 start_ammo_cells = cvar("g_start_ammo_cells");
1083 start_ammo_fuel = cvar("g_start_ammo_fuel");
1087 start_ammo_shells = cvar("g_pickup_shells_max");
1088 start_ammo_nails = cvar("g_pickup_nails_max");
1089 start_ammo_rockets = cvar("g_pickup_rockets_max");
1090 start_ammo_cells = cvar("g_pickup_cells_max");
1091 start_ammo_fuel = cvar("g_pickup_fuel_max");
1092 start_items |= IT_UNLIMITED_AMMO;
1095 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1097 e = get_weaponinfo(i);
1098 if(want_weapon("g_start_weapon_", e, FALSE))
1100 start_weapons |= e.weapons;
1101 weapon_action(e.weapon, WR_PRECACHE);
1108 warmup_start_ammo_shells = start_ammo_shells;
1109 warmup_start_ammo_nails = start_ammo_nails;
1110 warmup_start_ammo_rockets = start_ammo_rockets;
1111 warmup_start_ammo_cells = start_ammo_cells;
1112 warmup_start_ammo_fuel = start_ammo_fuel;
1113 warmup_start_health = start_health;
1114 warmup_start_armorvalue = start_armorvalue;
1115 warmup_start_weapons = start_weapons;
1117 if (!g_weaponarena && !g_nixnex && !g_minstagib && !g_ca)
1119 if (cvar("g_use_ammunition"))
1121 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1122 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1123 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1124 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1125 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1127 warmup_start_health = cvar("g_warmup_start_health");
1128 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1129 warmup_start_weapons = 0;
1130 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1132 e = get_weaponinfo(i);
1133 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1135 warmup_start_weapons |= e.weapons;
1136 weapon_action(e.weapon, WR_PRECACHE);
1142 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1144 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1145 start_items |= IT_FUEL_REGEN;
1146 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1147 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1151 start_items |= IT_JETPACK;
1153 if (g_weapon_stay == 2)
1155 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1156 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1157 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1158 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1159 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1160 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1161 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1162 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1163 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1164 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1167 start_ammo_shells = max(0, start_ammo_shells);
1168 start_ammo_nails = max(0, start_ammo_nails);
1169 start_ammo_cells = max(0, start_ammo_cells);
1170 start_ammo_rockets = max(0, start_ammo_rockets);
1171 start_ammo_fuel = max(0, start_ammo_fuel);
1173 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1174 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1175 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1176 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1177 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1181 float g_bugrigs_planar_movement;
1182 float g_bugrigs_planar_movement_car_jumping;
1183 float g_bugrigs_reverse_spinning;
1184 float g_bugrigs_reverse_speeding;
1185 float g_bugrigs_reverse_stopping;
1186 float g_bugrigs_air_steering;
1187 float g_bugrigs_angle_smoothing;
1188 float g_bugrigs_friction_floor;
1189 float g_bugrigs_friction_brake;
1190 float g_bugrigs_friction_air;
1191 float g_bugrigs_accel;
1192 float g_bugrigs_speed_ref;
1193 float g_bugrigs_speed_pow;
1194 float g_bugrigs_steer;
1196 float g_touchexplode;
1197 float g_touchexplode_radius;
1198 float g_touchexplode_damage;
1199 float g_touchexplode_edgedamage;
1200 float g_touchexplode_force;
1207 float sv_pitch_fixyaw;
1209 float sv_accuracy_data_share;
1211 void readlevelcvars(void)
1213 g_bugrigs = cvar("g_bugrigs");
1214 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1215 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1216 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1217 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1218 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1219 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1220 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1221 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1222 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1223 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1224 g_bugrigs_accel = cvar("g_bugrigs_accel");
1225 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1226 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1227 g_bugrigs_steer = cvar("g_bugrigs_steer");
1229 g_touchexplode = cvar("g_touchexplode");
1230 g_touchexplode_radius = cvar("g_touchexplode_radius");
1231 g_touchexplode_damage = cvar("g_touchexplode_damage");
1232 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1233 g_touchexplode_force = cvar("g_touchexplode_force");
1235 #ifdef ALLOW_FORCEMODELS
1236 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1238 sv_loddistance1 = cvar("sv_loddistance1");
1239 sv_loddistance2 = cvar("sv_loddistance2");
1241 if(sv_loddistance2 <= sv_loddistance1)
1242 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1244 sv_clones = cvar("sv_clones");
1245 sv_gentle = cvar("sv_gentle");
1246 sv_foginterval = cvar("sv_foginterval");
1247 g_cloaked = cvar("g_cloaked");
1248 g_jump_grunt = cvar("g_jump_grunt");
1249 g_footsteps = cvar("g_footsteps");
1250 g_grappling_hook = cvar("g_grappling_hook");
1251 g_jetpack = cvar("g_jetpack");
1252 g_laserguided_missile = cvar("g_laserguided_missile");
1253 g_midair = cvar("g_midair");
1254 g_minstagib = cvar("g_minstagib");
1255 g_nixnex = cvar("g_nixnex");
1256 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1257 g_norecoil = cvar("g_norecoil");
1258 g_vampire = cvar("g_vampire");
1259 g_bloodloss = cvar("g_bloodloss");
1260 sv_maxidle = cvar("sv_maxidle");
1261 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1262 sv_pogostick = cvar("sv_pogostick");
1263 sv_doublejump = cvar("sv_doublejump");
1264 g_ctf_reverse = cvar("g_ctf_reverse");
1265 sv_autotaunt = cvar("sv_autotaunt");
1266 sv_taunt = cvar("sv_taunt");
1268 inWarmupStage = cvar("g_warmup");
1269 g_warmup_limit = cvar("g_warmup_limit");
1270 g_warmup_allguns = cvar("g_warmup_allguns");
1271 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1273 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1274 inWarmupStage = 0; // these modes cannot work together, sorry
1276 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1277 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1278 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1279 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1280 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1281 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1282 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1283 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1284 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1285 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1286 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1287 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1289 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1290 if (g_nixnex) g_weaponarena = 0;
1293 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1294 g_weaponratefactor = cvar("g_weaponratefactor");
1295 g_weapondamagefactor = cvar("g_weapondamagefactor");
1296 g_weaponforcefactor = cvar("g_weaponforcefactor");
1297 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1299 g_pickup_shells = cvar("g_pickup_shells");
1300 g_pickup_shells_max = cvar("g_pickup_shells_max");
1301 g_pickup_nails = cvar("g_pickup_nails");
1302 g_pickup_nails_max = cvar("g_pickup_nails_max");
1303 g_pickup_rockets = cvar("g_pickup_rockets");
1304 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1305 g_pickup_cells = cvar("g_pickup_cells");
1306 g_pickup_cells_max = cvar("g_pickup_cells_max");
1307 g_pickup_fuel = cvar("g_pickup_fuel");
1308 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1309 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1310 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1311 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1312 g_pickup_armormedium = cvar("g_pickup_armormedium");
1313 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1314 g_pickup_armorbig = cvar("g_pickup_armorbig");
1315 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1316 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1317 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1318 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1319 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1320 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1321 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1322 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1323 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1324 g_pickup_healthmega = cvar("g_pickup_healthmega");
1325 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1327 g_pinata = cvar("g_pinata");
1329 g_weapon_stay = cvar("g_weapon_stay");
1331 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1334 g_ghost_items = cvar("g_ghost_items");
1336 if(g_ghost_items >= 1)
1337 g_ghost_items = 0.25; // default alpha value
1339 if not(inWarmupStage && !g_ca)
1340 game_starttime = cvar("g_start_delay");
1342 sv_pitch_min = cvar("sv_pitch_min");
1343 sv_pitch_max = cvar("sv_pitch_max");
1344 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1346 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1348 readplayerstartcvars();
1352 // TODO sound pack system
1355 string precache_sound_builtin (string s) = #19;
1356 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1357 string precache_sound(string s)
1359 return precache_sound_builtin(strcat(soundpack, s));
1361 void play2(entity e, string filename)
1363 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1365 void sound(entity e, float chan, string samp, float vol, float atten)
1367 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1372 string precache_sound (string s) = #19;
1373 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1374 float precache_sound_index (string s) = #19;
1376 #define SND_VOLUME 1
1377 #define SND_ATTENUATION 2
1378 #define SND_LARGEENTITY 8
1379 #define SND_LARGESOUND 16
1381 float sound_allowed(float dest, entity e)
1383 // sounds from world may always pass
1386 if (e.classname == "body")
1388 if (e.owner && e.owner != e)
1393 // sounds to self may always pass
1394 if (dest == MSG_ONE)
1395 if (e == msg_entity)
1397 // sounds by players can be removed
1398 if (cvar("bot_sound_monopoly"))
1399 if (clienttype(e) == CLIENTTYPE_REAL)
1401 // anything else may pass
1405 void sound(entity e, float chan, string samp, float vol, float atten)
1407 if (!sound_allowed(MSG_BROADCAST, e))
1409 sound_builtin(e, chan, samp, vol, atten);
1411 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1415 if (!sound_allowed(dest, e))
1418 entno = num_for_edict(e);
1419 idx = precache_sound_index(samp);
1424 atten = floor(atten * 64);
1425 vol = floor(vol * 255);
1428 sflags |= SND_VOLUME;
1430 sflags |= SND_ATTENUATION;
1432 sflags |= SND_LARGEENTITY;
1434 sflags |= SND_LARGESOUND;
1436 WriteByte(dest, SVC_SOUND);
1437 WriteByte(dest, sflags);
1438 if (sflags & SND_VOLUME)
1439 WriteByte(dest, vol);
1440 if (sflags & SND_ATTENUATION)
1441 WriteByte(dest, atten);
1442 if (sflags & SND_LARGEENTITY)
1444 WriteShort(dest, entno);
1445 WriteByte(dest, chan);
1449 WriteShort(dest, entno * 8 + chan);
1451 if (sflags & SND_LARGESOUND)
1452 WriteShort(dest, idx);
1454 WriteByte(dest, idx);
1456 WriteCoord(dest, o_x);
1457 WriteCoord(dest, o_y);
1458 WriteCoord(dest, o_z);
1460 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1464 if (!sound_allowed(dest, e))
1467 o = e.origin + 0.5 * (e.mins + e.maxs);
1468 soundtoat(dest, e, o, chan, samp, vol, atten);
1470 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1472 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1474 void stopsoundto(float dest, entity e, float chan)
1478 if (!sound_allowed(dest, e))
1481 entno = num_for_edict(e);
1486 idx = precache_sound_index("misc/null.wav");
1487 sflags = SND_LARGEENTITY;
1489 sflags |= SND_LARGESOUND;
1490 WriteByte(dest, SVC_SOUND);
1491 WriteByte(dest, sflags);
1492 WriteShort(dest, entno);
1493 WriteByte(dest, chan);
1494 if (sflags & SND_LARGESOUND)
1495 WriteShort(dest, idx);
1497 WriteByte(dest, idx);
1498 WriteCoord(dest, e.origin_x);
1499 WriteCoord(dest, e.origin_y);
1500 WriteCoord(dest, e.origin_z);
1504 WriteByte(dest, SVC_STOPSOUND);
1505 WriteShort(dest, entno * 8 + chan);
1508 void stopsound(entity e, float chan)
1510 if (!sound_allowed(MSG_BROADCAST, e))
1513 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1514 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1517 void play2(entity e, string filename)
1519 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1521 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1524 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1526 float spamsound(entity e, float chan, string samp, float vol, float atten)
1528 if (!sound_allowed(MSG_BROADCAST, e))
1531 if (time > e.spamtime)
1534 sound(e, chan, samp, vol, atten);
1540 void play2team(float t, string filename)
1544 if (cvar("bot_sound_monopoly"))
1547 FOR_EACH_REALPLAYER(head)
1550 play2(head, filename);
1554 void play2all(string samp)
1556 if (cvar("bot_sound_monopoly"))
1559 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1562 void PrecachePlayerSounds(string f);
1563 void precache_all_models(string pattern)
1565 float globhandle, i, n;
1568 globhandle = search_begin(pattern, TRUE, FALSE);
1571 n = search_getsize(globhandle);
1572 for (i = 0; i < n; ++i)
1574 //print(search_getfilename(globhandle, i), "\n");
1575 f = search_getfilename(globhandle, i);
1578 if(substring(f, -9,5) == "_lod1")
1580 if(substring(f, -9,5) == "_lod2")
1582 if(!sv_loddistance1)
1584 PrecachePlayerSounds(strcat(f, ".sounds"));
1586 search_end(globhandle);
1591 // gamemode related things
1592 precache_model ("models/misc/chatbubble.spr");
1593 precache_model ("models/misc/teambubble.spr");
1596 precache_model ("models/runematch/curse.mdl");
1597 precache_model ("models/runematch/rune.mdl");
1600 #ifdef TTURRETS_ENABLED
1601 if (cvar("g_turrets"))
1605 // Precache all player models if desired
1606 if (cvar("sv_precacheplayermodels"))
1608 PrecachePlayerSounds("sound/player/default.sounds");
1609 precache_all_models("models/player/*.zym");
1610 precache_all_models("models/player/*.dpm");
1611 precache_all_models("models/player/*.md3");
1612 precache_all_models("models/player/*.psk");
1613 //precache_model("models/player/carni.zym");
1614 //precache_model("models/player/crash.zym");
1615 //precache_model("models/player/grunt.zym");
1616 //precache_model("models/player/headhunter.zym");
1617 //precache_model("models/player/insurrectionist.zym");
1618 //precache_model("models/player/jeandarc.zym");
1619 //precache_model("models/player/lurk.zym");
1620 //precache_model("models/player/lycanthrope.zym");
1621 //precache_model("models/player/marine.zym");
1622 //precache_model("models/player/nexus.zym");
1623 //precache_model("models/player/pyria.zym");
1624 //precache_model("models/player/shock.zym");
1625 //precache_model("models/player/skadi.zym");
1626 //precache_model("models/player/specop.zym");
1627 //precache_model("models/player/visitant.zym");
1630 if (cvar("sv_defaultcharacter"))
1633 s = cvar_string("sv_defaultplayermodel_red");
1637 PrecachePlayerSounds(strcat(s, ".sounds"));
1639 s = cvar_string("sv_defaultplayermodel_blue");
1643 PrecachePlayerSounds(strcat(s, ".sounds"));
1645 s = cvar_string("sv_defaultplayermodel_yellow");
1649 PrecachePlayerSounds(strcat(s, ".sounds"));
1651 s = cvar_string("sv_defaultplayermodel_pink");
1655 PrecachePlayerSounds(strcat(s, ".sounds"));
1657 s = cvar_string("sv_defaultplayermodel");
1661 PrecachePlayerSounds(strcat(s, ".sounds"));
1667 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1668 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1671 // gore and miscellaneous sounds
1672 //precache_sound ("misc/h2ohit.wav");
1673 precache_model ("models/hook.md3");
1674 precache_sound ("misc/armorimpact.wav");
1675 precache_sound ("misc/bodyimpact1.wav");
1676 precache_sound ("misc/bodyimpact2.wav");
1677 precache_sound ("misc/gib.wav");
1678 precache_sound ("misc/gib_splat01.wav");
1679 precache_sound ("misc/gib_splat02.wav");
1680 precache_sound ("misc/gib_splat03.wav");
1681 precache_sound ("misc/gib_splat04.wav");
1682 precache_sound ("misc/hit.wav");
1683 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1684 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1685 precache_sound ("misc/null.wav");
1686 precache_sound ("misc/spawn.wav");
1687 precache_sound ("misc/talk.wav");
1688 precache_sound ("misc/teleport.wav");
1689 precache_sound ("misc/poweroff.wav");
1690 precache_sound ("player/lava.wav");
1691 precache_sound ("player/slime.wav");
1694 precache_sound ("misc/jetpack_fly.wav");
1696 precache_model ("models/sprites/0.spr32");
1697 precache_model ("models/sprites/1.spr32");
1698 precache_model ("models/sprites/2.spr32");
1699 precache_model ("models/sprites/3.spr32");
1700 precache_model ("models/sprites/4.spr32");
1701 precache_model ("models/sprites/5.spr32");
1702 precache_model ("models/sprites/6.spr32");
1703 precache_model ("models/sprites/7.spr32");
1704 precache_model ("models/sprites/8.spr32");
1705 precache_model ("models/sprites/9.spr32");
1706 precache_model ("models/sprites/10.spr32");
1708 // common weapon precaches
1709 precache_sound ("weapons/weapon_switch.wav");
1710 precache_sound ("weapons/weaponpickup.wav");
1711 precache_sound ("weapons/unavailable.wav");
1712 if (g_grappling_hook)
1714 precache_sound ("weapons/hook_fire.wav"); // hook
1715 precache_sound ("weapons/hook_impact.wav"); // hook
1718 if (cvar("sv_precacheweapons") || g_nixnex)
1720 //precache weapon models/sounds
1723 while (wep <= WEP_LAST)
1725 weapon_action(wep, WR_PRECACHE);
1730 precache_model("models/elaser.mdl");
1731 precache_model("models/laser.mdl");
1732 precache_model("models/ebomb.mdl");
1735 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1737 if (!self.noise && self.music) // quake 3 uses the music field
1738 self.noise = self.music;
1740 // plays music for the level if there is any
1743 precache_sound (self.noise);
1744 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1749 // sorry, but using \ in macros breaks line numbers
1750 #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
1751 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1752 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1754 vector ExactTriggerHit_mins;
1755 vector ExactTriggerHit_maxs;
1756 float ExactTriggerHit_Recurse()
1762 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1765 if (trace_ent == self)
1770 se.solid = SOLID_NOT;
1771 f = ExactTriggerHit_Recurse();
1777 float ExactTriggerHit()
1781 if not(self.modelindex)
1785 self.solid = SOLID_BSP;
1786 ExactTriggerHit_mins = other.absmin;
1787 ExactTriggerHit_maxs = other.absmax;
1788 f = ExactTriggerHit_Recurse();
1794 // WARNING: this kills the trace globals
1795 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1796 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1798 #define INITPRIO_FIRST 0
1799 #define INITPRIO_GAMETYPE 0
1800 #define INITPRIO_GAMETYPE_FALLBACK 1
1801 #define INITPRIO_CVARS 5
1802 #define INITPRIO_FINDTARGET 10
1803 #define INITPRIO_DROPTOFLOOR 20
1804 #define INITPRIO_SETLOCATION 90
1805 #define INITPRIO_LINKDOORS 91
1806 #define INITPRIO_LAST 99
1808 .void(void) initialize_entity;
1809 .float initialize_entity_order;
1810 .entity initialize_entity_next;
1811 entity initialize_entity_first;
1813 void make_safe_for_remove(entity e)
1815 if (e.initialize_entity)
1818 for (ent = initialize_entity_first; ent; )
1820 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1822 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1823 // skip it in linked list
1826 prev.initialize_entity_next = ent.initialize_entity_next;
1827 ent = prev.initialize_entity_next;
1831 initialize_entity_first = ent.initialize_entity_next;
1832 ent = initialize_entity_first;
1838 ent = ent.initialize_entity_next;
1844 void objerror(string s)
1846 make_safe_for_remove(self);
1847 objerror_builtin(s);
1850 void remove_unsafely(entity e)
1855 void remove_safely(entity e)
1857 make_safe_for_remove(e);
1861 void InitializeEntity(entity e, void(void) func, float order)
1865 if (!e || e.initialize_entity)
1867 // make a proxy initializer entity
1871 e.classname = "initialize_entity";
1875 e.initialize_entity = func;
1876 e.initialize_entity_order = order;
1878 cur = initialize_entity_first;
1881 if (!cur || cur.initialize_entity_order > order)
1883 // insert between prev and cur
1885 prev.initialize_entity_next = e;
1887 initialize_entity_first = e;
1888 e.initialize_entity_next = cur;
1892 cur = cur.initialize_entity_next;
1895 void InitializeEntitiesRun()
1898 startoflist = initialize_entity_first;
1899 initialize_entity_first = world;
1900 for (self = startoflist; self; )
1903 var void(void) func;
1904 e = self.initialize_entity_next;
1905 func = self.initialize_entity;
1906 self.initialize_entity_order = 0;
1907 self.initialize_entity = func_null;
1908 self.initialize_entity_next = world;
1909 if (self.classname == "initialize_entity")
1913 remove_builtin(self);
1916 //dprint("Delayed initialization: ", self.classname, "\n");
1922 .float uncustomizeentityforclient_set;
1923 .void(void) uncustomizeentityforclient;
1924 void(void) SUB_Nullpointer = #0;
1925 void UncustomizeEntitiesRun()
1929 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1930 self.uncustomizeentityforclient();
1933 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1935 e.customizeentityforclient = customizer;
1936 e.uncustomizeentityforclient = uncustomizer;
1937 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1941 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1944 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1948 if (e.classname == "")
1949 e.classname = "net_linked";
1951 if (e.model == "" || self.modelindex == 0)
1955 setmodel(e, "null");
1959 e.SendEntity = sendfunc;
1960 e.SendFlags = 0xFFFFFF;
1963 e.effects |= EF_NODEPTHTEST;
1967 e.nextthink = time + dt;
1968 e.think = SUB_Remove;
1972 void adaptor_think2touch()
1981 void adaptor_think2use()
1993 // deferred dropping
1994 void DropToFloor_Handler()
1996 droptofloor_builtin();
1997 self.dropped_origin = self.origin;
2002 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
2007 float trace_hits_box_a0, trace_hits_box_a1;
2009 float trace_hits_box_1d(float end, float thmi, float thma)
2013 // just check if x is in range
2021 // do the trace with respect to x
2022 // 0 -> end has to stay in thmi -> thma
2023 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
2024 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
2025 if (trace_hits_box_a0 > trace_hits_box_a1)
2031 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
2036 // now it is a trace from 0 to end
2038 trace_hits_box_a0 = 0;
2039 trace_hits_box_a1 = 1;
2041 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
2043 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
2045 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
2051 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
2053 return trace_hits_box(start, end, thmi - ma, thma - mi);
2056 float SUB_NoImpactCheck()
2058 // zero hitcontents = this is not the real impact, but either the
2059 // mirror-impact of something hitting the projectile instead of the
2060 // projectile hitting the something, or a touchareagrid one. Neither of
2061 // these stop the projectile from moving, so...
2062 if(trace_dphitcontents == 0)
2064 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
2067 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2069 if (other == world && self.size != '0 0 0')
2072 tic = self.velocity * sys_frametime;
2073 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2074 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2075 if (trace_fraction >= 1)
2077 dprint("Odd... did not hit...?\n");
2079 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2081 dprint("Detected and prevented the sky-grapple bug.\n");
2089 #define SUB_OwnerCheck() (other && (other == self.owner))
2091 #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)
2093 float MAX_IPBAN_URIS = 16;
2095 float URI_GET_DISCARD = 0;
2096 float URI_GET_IPBAN = 1;
2097 float URI_GET_IPBAN_END = 16;
2099 void URI_Get_Callback(float id, float status, string data)
2101 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2103 dprint("\nEnd of data.\n");
2105 if (id == URI_GET_DISCARD)
2109 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2112 OnlineBanList_URI_Get_Callback(id, status, data);
2116 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2120 void print_to(entity e, string s)
2123 sprint(e, strcat(s, "\n"));
2128 string getrecords(float page) // 50 records per page
2142 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2144 if (MapInfo_Get_ByID(i))
2146 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 = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2165 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
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 = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2181 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
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_GetTime(i);
2214 n = race_GetName(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 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2230 vector start, org, delta, end, enddown, mstart;
2232 m = e.dphitcontentsmask;
2233 e.dphitcontentsmask = goodcontents | badcontents;
2236 delta = world.maxs - world.mins;
2238 for (i = 0; i < attempts; ++i)
2240 start_x = org_x + random() * delta_x;
2241 start_y = org_y + random() * delta_y;
2242 start_z = org_z + random() * delta_z;
2244 // rule 1: start inside world bounds, and outside
2245 // solid, and don't start from somewhere where you can
2246 // fall down to evil
2247 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2248 if (trace_fraction >= 1)
2250 if (trace_startsolid)
2252 if (trace_dphitcontents & badcontents)
2254 if (trace_dphitq3surfaceflags & badsurfaceflags)
2257 // rule 2: if we are too high, lower the point
2258 if (trace_fraction * delta_z > maxaboveground)
2259 start = trace_endpos + '0 0 1' * maxaboveground;
2260 enddown = trace_endpos;
2262 // rule 3: make sure we aren't outside the map. This only works
2263 // for somewhat well formed maps. A good rule of thumb is that
2264 // the map should have a convex outside hull.
2265 // these can be traceLINES as we already verified the starting box
2266 mstart = start + 0.5 * (e.mins + e.maxs);
2267 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2268 if (trace_fraction >= 1)
2270 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2271 if (trace_fraction >= 1)
2273 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2274 if (trace_fraction >= 1)
2276 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2277 if (trace_fraction >= 1)
2279 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2280 if (trace_fraction >= 1)
2283 // find a random vector to "look at"
2284 end_x = org_x + random() * delta_x;
2285 end_y = org_y + random() * delta_y;
2286 end_z = org_z + random() * delta_z;
2287 end = start + normalize(end - start) * vlen(delta);
2289 // rule 4: start TO end must not be too short
2290 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2291 if (trace_startsolid)
2293 if (trace_fraction < minviewdistance / vlen(delta))
2296 // rule 5: don't want to look at sky
2297 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2300 // rule 6: we must not end up in trigger_hurt
2301 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2303 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2310 e.dphitcontentsmask = m;
2314 setorigin(e, start);
2315 e.angles = vectoangles(end - start);
2316 dprint("Needed ", ftos(i + 1), " attempts\n");
2323 float zcurveparticles_effectno;
2324 vector zcurveparticles_start;
2325 float zcurveparticles_spd;
2327 void endzcurveparticles()
2329 if(zcurveparticles_effectno)
2332 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2334 zcurveparticles_effectno = 0;
2337 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2339 spd = bound(0, floor(spd / 16), 32767);
2340 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2342 endzcurveparticles();
2343 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2344 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2345 WriteShort(MSG_BROADCAST, effectno);
2346 WriteCoord(MSG_BROADCAST, start_x);
2347 WriteCoord(MSG_BROADCAST, start_y);
2348 WriteCoord(MSG_BROADCAST, start_z);
2349 zcurveparticles_effectno = effectno;
2350 zcurveparticles_start = start;
2353 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2354 WriteCoord(MSG_BROADCAST, end_x);
2355 WriteCoord(MSG_BROADCAST, end_y);
2356 WriteCoord(MSG_BROADCAST, end_z);
2357 WriteCoord(MSG_BROADCAST, end_dz);
2358 zcurveparticles_spd = spd;
2361 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2364 vector vecxy, velxy;
2366 vecxy = end - start;
2371 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2373 endzcurveparticles();
2374 trailparticles(world, effectno, start, end);
2378 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2379 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2382 string GetGametype(); // g_world.qc
2383 void write_recordmarker(entity pl, float tstart, float dt)
2385 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2387 // also write a marker into demo files for demotc-race-record-extractor to find
2390 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2391 " ", ftos(tstart), " ", ftos(dt), "\n"));
2394 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2396 switch(self.owner.cvar_cl_gunalign)
2407 if(allowcenter) // 2: allow center handedness
2420 if(allowcenter) // 2: allow center handedness
2436 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2441 if (cvar("g_shootfromeye"))
2445 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2453 else if (cvar("g_shootfromcenter"))
2457 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2465 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2475 else if (cvar("g_shootfromclient"))
2477 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2484 void attach_sameorigin(entity e, entity to, string tag)
2486 vector org, t_forward, t_left, t_up, e_forward, e_up;
2493 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2494 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2495 t_forward = v_forward * tagscale;
2496 t_left = v_right * -tagscale;
2497 t_up = v_up * tagscale;
2499 e.origin_x = org * t_forward;
2500 e.origin_y = org * t_left;
2501 e.origin_z = org * t_up;
2503 // current forward and up directions
2504 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2505 e.angles_x = -e.angles_x;
2506 fixedmakevectors(e.angles);
2508 // untransform forward, up!
2509 e_forward_x = v_forward * t_forward;
2510 e_forward_y = v_forward * t_left;
2511 e_forward_z = v_forward * t_up;
2512 e_up_x = v_up * t_forward;
2513 e_up_y = v_up * t_left;
2514 e_up_z = v_up * t_up;
2516 e.angles = fixedvectoangles2(e_forward, e_up);
2517 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2518 e.angles_x = -e.angles_x;
2520 setattachment(e, to, tag);
2521 setorigin(e, e.origin);
2524 void detach_sameorigin(entity e)
2527 org = gettaginfo(e, 0);
2528 e.angles = fixedvectoangles2(v_forward, v_up);
2529 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2530 e.angles_x = -e.angles_x;
2532 setattachment(e, world, "");
2533 setorigin(e, e.origin);
2536 void follow_sameorigin(entity e, entity to)
2538 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2539 e.aiment = to; // make the hole follow bmodel
2540 e.punchangle = to.angles; // the original angles of bmodel
2541 e.view_ofs = e.origin - to.origin; // relative origin
2542 e.v_angle = e.angles - to.angles; // relative angles
2545 void unfollow_sameorigin(entity e)
2547 e.movetype = MOVETYPE_NONE;
2550 entity gettaginfo_relative_ent;
2551 vector gettaginfo_relative(entity e, float tag)
2553 if (!gettaginfo_relative_ent)
2555 gettaginfo_relative_ent = spawn();
2556 gettaginfo_relative_ent.effects = EF_NODRAW;
2558 gettaginfo_relative_ent.model = e.model;
2559 gettaginfo_relative_ent.modelindex = e.modelindex;
2560 gettaginfo_relative_ent.frame = e.frame;
2561 return gettaginfo(gettaginfo_relative_ent, tag);
2564 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2568 if (pl.soundentity.cnt & p)
2570 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2571 pl.soundentity.cnt |= p;
2574 void SoundEntity_StopSound(entity pl, float chan)
2578 if (pl.soundentity.cnt & p)
2580 stopsoundto(MSG_ALL, pl.soundentity, chan);
2581 pl.soundentity.cnt &~= p;
2585 void SoundEntity_Attach(entity pl)
2587 pl.soundentity = spawn();
2588 pl.soundentity.classname = "soundentity";
2589 pl.soundentity.owner = pl;
2590 setattachment(pl.soundentity, pl, "");
2591 setmodel(pl.soundentity, "null");
2594 void SoundEntity_Detach(entity pl)
2597 for (i = 0; i <= 7; ++i)
2598 SoundEntity_StopSound(pl, i);
2602 float ParseCommandPlayerSlotTarget_firsttoken;
2603 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2611 ParseCommandPlayerSlotTarget_firsttoken = -1;
2615 if (substring(argv(idx), 0, 1) == "#")
2617 s = substring(argv(idx), 1, -1);
2625 ParseCommandPlayerSlotTarget_firsttoken = idx;
2626 if (s == ftos(stof(s)))
2628 e = edict_num(stof(s));
2629 if (e.flags & FL_CLIENT)
2635 // it must be a nick name
2638 ParseCommandPlayerSlotTarget_firsttoken = idx;
2641 FOR_EACH_CLIENT(head)
2642 if (head.netname == s)
2650 s = strdecolorize(s);
2652 FOR_EACH_CLIENT(head)
2653 if (strdecolorize(head.netname) == s)
2668 float modeleffect_SendEntity(entity to, float sf)
2671 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2674 if(self.velocity != '0 0 0')
2676 if(self.angles != '0 0 0')
2678 if(self.avelocity != '0 0 0')
2681 WriteByte(MSG_ENTITY, f);
2682 WriteShort(MSG_ENTITY, self.modelindex);
2683 WriteByte(MSG_ENTITY, self.skin);
2684 WriteByte(MSG_ENTITY, self.frame);
2685 WriteCoord(MSG_ENTITY, self.origin_x);
2686 WriteCoord(MSG_ENTITY, self.origin_y);
2687 WriteCoord(MSG_ENTITY, self.origin_z);
2690 WriteCoord(MSG_ENTITY, self.velocity_x);
2691 WriteCoord(MSG_ENTITY, self.velocity_y);
2692 WriteCoord(MSG_ENTITY, self.velocity_z);
2696 WriteCoord(MSG_ENTITY, self.angles_x);
2697 WriteCoord(MSG_ENTITY, self.angles_y);
2698 WriteCoord(MSG_ENTITY, self.angles_z);
2702 WriteCoord(MSG_ENTITY, self.avelocity_x);
2703 WriteCoord(MSG_ENTITY, self.avelocity_y);
2704 WriteCoord(MSG_ENTITY, self.avelocity_z);
2706 WriteShort(MSG_ENTITY, self.scale * 256.0);
2707 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2708 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2709 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2710 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2715 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)
2720 e.classname = "modeleffect";
2728 e.teleport_time = t1;
2732 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2736 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2739 sz = max(e.scale, e.scale2);
2740 setsize(e, e.mins * sz, e.maxs * sz);
2741 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2744 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2746 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2749 float randombit(float bits)
2751 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2760 for(f = 1; f <= bits; f *= 2)
2769 r = (r - 1) / (n - 1);
2776 float randombits(float bits, float k, float error_return)
2780 while(k > 0 && bits != r)
2782 r += randombit(bits - r);
2791 void randombit_test(float bits, float iter)
2795 print(ftos(randombit(bits)), "\n");
2800 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2802 if(halflifedist > 0)
2803 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2804 else if(halflifedist < 0)
2805 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2814 #define cvar_string_normal cvar_string_builtin
2815 #define cvar_normal cvar_builtin
2817 string cvar_string_normal(string n)
2819 if not(cvar_type(n) & 1)
2820 backtrace(strcat("Attempt to access undefined cvar: ", n));
2821 return cvar_string_builtin(n);
2824 float cvar_normal(string n)
2826 return stof(cvar_string_normal(n));
2829 #define cvar_set_normal cvar_set_builtin
2837 oself.think = SUB_Remove;
2838 oself.nextthink = time;
2844 Execute func() after time + fdelay.
2845 self when func is executed = self when defer is called
2847 void defer(float fdelay, void() func)
2854 e.think = defer_think;
2855 e.nextthink = time + fdelay;