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 // WARNING: this kills the trace globals
1755 #define EXACTTRIGGER_TOUCH if not(BoxTouchesBrush(other.absmin, other.absmax, self, other)) return
1756 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1758 #define INITPRIO_FIRST 0
1759 #define INITPRIO_GAMETYPE 0
1760 #define INITPRIO_GAMETYPE_FALLBACK 1
1761 #define INITPRIO_CVARS 5
1762 #define INITPRIO_FINDTARGET 10
1763 #define INITPRIO_DROPTOFLOOR 20
1764 #define INITPRIO_SETLOCATION 90
1765 #define INITPRIO_LINKDOORS 91
1766 #define INITPRIO_LAST 99
1768 .void(void) initialize_entity;
1769 .float initialize_entity_order;
1770 .entity initialize_entity_next;
1771 entity initialize_entity_first;
1773 void make_safe_for_remove(entity e)
1775 if (e.initialize_entity)
1778 for (ent = initialize_entity_first; ent; )
1780 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1782 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1783 // skip it in linked list
1786 prev.initialize_entity_next = ent.initialize_entity_next;
1787 ent = prev.initialize_entity_next;
1791 initialize_entity_first = ent.initialize_entity_next;
1792 ent = initialize_entity_first;
1798 ent = ent.initialize_entity_next;
1804 void objerror(string s)
1806 make_safe_for_remove(self);
1807 objerror_builtin(s);
1810 void remove_unsafely(entity e)
1815 void remove_safely(entity e)
1817 make_safe_for_remove(e);
1821 void InitializeEntity(entity e, void(void) func, float order)
1825 if (!e || e.initialize_entity)
1827 // make a proxy initializer entity
1831 e.classname = "initialize_entity";
1835 e.initialize_entity = func;
1836 e.initialize_entity_order = order;
1838 cur = initialize_entity_first;
1841 if (!cur || cur.initialize_entity_order > order)
1843 // insert between prev and cur
1845 prev.initialize_entity_next = e;
1847 initialize_entity_first = e;
1848 e.initialize_entity_next = cur;
1852 cur = cur.initialize_entity_next;
1855 void InitializeEntitiesRun()
1858 startoflist = initialize_entity_first;
1859 initialize_entity_first = world;
1860 for (self = startoflist; self; )
1863 var void(void) func;
1864 e = self.initialize_entity_next;
1865 func = self.initialize_entity;
1866 self.initialize_entity_order = 0;
1867 self.initialize_entity = func_null;
1868 self.initialize_entity_next = world;
1869 if (self.classname == "initialize_entity")
1873 remove_builtin(self);
1876 //dprint("Delayed initialization: ", self.classname, "\n");
1882 .float uncustomizeentityforclient_set;
1883 .void(void) uncustomizeentityforclient;
1884 void(void) SUB_Nullpointer = #0;
1885 void UncustomizeEntitiesRun()
1889 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1890 self.uncustomizeentityforclient();
1893 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1895 e.customizeentityforclient = customizer;
1896 e.uncustomizeentityforclient = uncustomizer;
1897 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1901 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1904 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1908 if (e.classname == "")
1909 e.classname = "net_linked";
1911 if (e.model == "" || self.modelindex == 0)
1915 setmodel(e, "null");
1919 e.SendEntity = sendfunc;
1920 e.SendFlags = 0xFFFFFF;
1923 e.effects |= EF_NODEPTHTEST;
1927 e.nextthink = time + dt;
1928 e.think = SUB_Remove;
1932 void adaptor_think2touch()
1941 void adaptor_think2use()
1953 // deferred dropping
1954 void DropToFloor_Handler()
1956 droptofloor_builtin();
1957 self.dropped_origin = self.origin;
1962 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1967 float trace_hits_box_a0, trace_hits_box_a1;
1969 float trace_hits_box_1d(float end, float thmi, float thma)
1973 // just check if x is in range
1981 // do the trace with respect to x
1982 // 0 -> end has to stay in thmi -> thma
1983 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1984 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1985 if (trace_hits_box_a0 > trace_hits_box_a1)
1991 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1996 // now it is a trace from 0 to end
1998 trace_hits_box_a0 = 0;
1999 trace_hits_box_a1 = 1;
2001 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
2003 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
2005 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
2011 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
2013 return trace_hits_box(start, end, thmi - ma, thma - mi);
2016 float SUB_NoImpactCheck()
2018 // zero hitcontents = this is not the real impact, but either the
2019 // mirror-impact of something hitting the projectile instead of the
2020 // projectile hitting the something, or a touchareagrid one. Neither of
2021 // these stop the projectile from moving, so...
2022 if(trace_dphitcontents == 0)
2024 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
2027 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2029 if (other == world && self.size != '0 0 0')
2032 tic = self.velocity * sys_frametime;
2033 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2034 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2035 if (trace_fraction >= 1)
2037 dprint("Odd... did not hit...?\n");
2039 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2041 dprint("Detected and prevented the sky-grapple bug.\n");
2049 #define SUB_OwnerCheck() (other && (other == self.owner))
2051 #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)
2053 float MAX_IPBAN_URIS = 16;
2055 float URI_GET_DISCARD = 0;
2056 float URI_GET_IPBAN = 1;
2057 float URI_GET_IPBAN_END = 16;
2059 void URI_Get_Callback(float id, float status, string data)
2061 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2063 dprint("\nEnd of data.\n");
2065 if (id == URI_GET_DISCARD)
2069 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2072 OnlineBanList_URI_Get_Callback(id, status, data);
2076 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2080 void print_to(entity e, string s)
2083 sprint(e, strcat(s, "\n"));
2088 string getrecords(float page) // 50 records per page
2102 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2104 if (MapInfo_Get_ByID(i))
2106 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2109 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2110 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2118 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2120 if (MapInfo_Get_ByID(i))
2122 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2125 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2126 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2134 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2136 if (MapInfo_Get_ByID(i))
2138 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2141 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2142 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2148 MapInfo_ClearTemps();
2150 if (s == "" && page == 0)
2151 return "No records are available on this server.\n";
2156 string getrankings()
2169 for (i = 1; i <= RANKINGS_CNT; ++i)
2171 t = race_GetTime(i);
2174 n = race_GetName(i);
2175 p = race_PlaceName(i);
2176 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2179 MapInfo_ClearTemps();
2182 return strcat("No records are available for the map: ", map, "\n");
2184 return strcat("Records for ", map, ":\n", s);
2187 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2190 vector start, org, delta, end, enddown, mstart;
2192 m = e.dphitcontentsmask;
2193 e.dphitcontentsmask = goodcontents | badcontents;
2196 delta = world.maxs - world.mins;
2198 for (i = 0; i < attempts; ++i)
2200 start_x = org_x + random() * delta_x;
2201 start_y = org_y + random() * delta_y;
2202 start_z = org_z + random() * delta_z;
2204 // rule 1: start inside world bounds, and outside
2205 // solid, and don't start from somewhere where you can
2206 // fall down to evil
2207 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2208 if (trace_fraction >= 1)
2210 if (trace_startsolid)
2212 if (trace_dphitcontents & badcontents)
2214 if (trace_dphitq3surfaceflags & badsurfaceflags)
2217 // rule 2: if we are too high, lower the point
2218 if (trace_fraction * delta_z > maxaboveground)
2219 start = trace_endpos + '0 0 1' * maxaboveground;
2220 enddown = trace_endpos;
2222 // rule 3: make sure we aren't outside the map. This only works
2223 // for somewhat well formed maps. A good rule of thumb is that
2224 // the map should have a convex outside hull.
2225 // these can be traceLINES as we already verified the starting box
2226 mstart = start + 0.5 * (e.mins + e.maxs);
2227 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2228 if (trace_fraction >= 1)
2230 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2231 if (trace_fraction >= 1)
2233 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2234 if (trace_fraction >= 1)
2236 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2237 if (trace_fraction >= 1)
2239 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2240 if (trace_fraction >= 1)
2243 // find a random vector to "look at"
2244 end_x = org_x + random() * delta_x;
2245 end_y = org_y + random() * delta_y;
2246 end_z = org_z + random() * delta_z;
2247 end = start + normalize(end - start) * vlen(delta);
2249 // rule 4: start TO end must not be too short
2250 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2251 if (trace_startsolid)
2253 if (trace_fraction < minviewdistance / vlen(delta))
2256 // rule 5: don't want to look at sky
2257 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2260 // rule 6: we must not end up in trigger_hurt
2261 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2263 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2270 e.dphitcontentsmask = m;
2274 setorigin(e, start);
2275 e.angles = vectoangles(end - start);
2276 dprint("Needed ", ftos(i + 1), " attempts\n");
2283 float zcurveparticles_effectno;
2284 vector zcurveparticles_start;
2285 float zcurveparticles_spd;
2287 void endzcurveparticles()
2289 if(zcurveparticles_effectno)
2292 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2294 zcurveparticles_effectno = 0;
2297 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2299 spd = bound(0, floor(spd / 16), 32767);
2300 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2302 endzcurveparticles();
2303 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2304 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2305 WriteShort(MSG_BROADCAST, effectno);
2306 WriteCoord(MSG_BROADCAST, start_x);
2307 WriteCoord(MSG_BROADCAST, start_y);
2308 WriteCoord(MSG_BROADCAST, start_z);
2309 zcurveparticles_effectno = effectno;
2310 zcurveparticles_start = start;
2313 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2314 WriteCoord(MSG_BROADCAST, end_x);
2315 WriteCoord(MSG_BROADCAST, end_y);
2316 WriteCoord(MSG_BROADCAST, end_z);
2317 WriteCoord(MSG_BROADCAST, end_dz);
2318 zcurveparticles_spd = spd;
2321 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2324 vector vecxy, velxy;
2326 vecxy = end - start;
2331 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2333 endzcurveparticles();
2334 trailparticles(world, effectno, start, end);
2338 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2339 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2342 string GetGametype(); // g_world.qc
2343 void write_recordmarker(entity pl, float tstart, float dt)
2345 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2347 // also write a marker into demo files for demotc-race-record-extractor to find
2350 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2351 " ", ftos(tstart), " ", ftos(dt), "\n"));
2354 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2356 switch(self.owner.cvar_cl_gunalign)
2367 if(allowcenter) // 2: allow center handedness
2380 if(allowcenter) // 2: allow center handedness
2396 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2401 if (cvar("g_shootfromeye"))
2405 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2413 else if (cvar("g_shootfromcenter"))
2417 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2425 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2435 else if (cvar("g_shootfromclient"))
2437 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2444 void attach_sameorigin(entity e, entity to, string tag)
2446 vector org, t_forward, t_left, t_up, e_forward, e_up;
2453 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2454 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2455 t_forward = v_forward * tagscale;
2456 t_left = v_right * -tagscale;
2457 t_up = v_up * tagscale;
2459 e.origin_x = org * t_forward;
2460 e.origin_y = org * t_left;
2461 e.origin_z = org * t_up;
2463 // current forward and up directions
2464 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2465 e.angles_x = -e.angles_x;
2466 fixedmakevectors(e.angles);
2468 // untransform forward, up!
2469 e_forward_x = v_forward * t_forward;
2470 e_forward_y = v_forward * t_left;
2471 e_forward_z = v_forward * t_up;
2472 e_up_x = v_up * t_forward;
2473 e_up_y = v_up * t_left;
2474 e_up_z = v_up * t_up;
2476 e.angles = fixedvectoangles2(e_forward, e_up);
2477 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2478 e.angles_x = -e.angles_x;
2480 setattachment(e, to, tag);
2481 setorigin(e, e.origin);
2484 void detach_sameorigin(entity e)
2487 org = gettaginfo(e, 0);
2488 e.angles = fixedvectoangles2(v_forward, v_up);
2489 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2490 e.angles_x = -e.angles_x;
2492 setattachment(e, world, "");
2493 setorigin(e, e.origin);
2496 void follow_sameorigin(entity e, entity to)
2498 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2499 e.aiment = to; // make the hole follow bmodel
2500 e.punchangle = to.angles; // the original angles of bmodel
2501 e.view_ofs = e.origin - to.origin; // relative origin
2502 e.v_angle = e.angles - to.angles; // relative angles
2505 void unfollow_sameorigin(entity e)
2507 e.movetype = MOVETYPE_NONE;
2510 entity gettaginfo_relative_ent;
2511 vector gettaginfo_relative(entity e, float tag)
2513 if (!gettaginfo_relative_ent)
2515 gettaginfo_relative_ent = spawn();
2516 gettaginfo_relative_ent.effects = EF_NODRAW;
2518 gettaginfo_relative_ent.model = e.model;
2519 gettaginfo_relative_ent.modelindex = e.modelindex;
2520 gettaginfo_relative_ent.frame = e.frame;
2521 return gettaginfo(gettaginfo_relative_ent, tag);
2524 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2528 if (pl.soundentity.cnt & p)
2530 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2531 pl.soundentity.cnt |= p;
2534 void SoundEntity_StopSound(entity pl, float chan)
2538 if (pl.soundentity.cnt & p)
2540 stopsoundto(MSG_ALL, pl.soundentity, chan);
2541 pl.soundentity.cnt &~= p;
2545 void SoundEntity_Attach(entity pl)
2547 pl.soundentity = spawn();
2548 pl.soundentity.classname = "soundentity";
2549 pl.soundentity.owner = pl;
2550 setattachment(pl.soundentity, pl, "");
2551 setmodel(pl.soundentity, "null");
2554 void SoundEntity_Detach(entity pl)
2557 for (i = 0; i <= 7; ++i)
2558 SoundEntity_StopSound(pl, i);
2562 float ParseCommandPlayerSlotTarget_firsttoken;
2563 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2571 ParseCommandPlayerSlotTarget_firsttoken = -1;
2575 if (substring(argv(idx), 0, 1) == "#")
2577 s = substring(argv(idx), 1, -1);
2585 ParseCommandPlayerSlotTarget_firsttoken = idx;
2586 if (s == ftos(stof(s)))
2588 e = edict_num(stof(s));
2589 if (e.flags & FL_CLIENT)
2595 // it must be a nick name
2598 ParseCommandPlayerSlotTarget_firsttoken = idx;
2601 FOR_EACH_CLIENT(head)
2602 if (head.netname == s)
2610 s = strdecolorize(s);
2612 FOR_EACH_CLIENT(head)
2613 if (strdecolorize(head.netname) == s)
2628 float modeleffect_SendEntity(entity to, float sf)
2631 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2634 if(self.velocity != '0 0 0')
2636 if(self.angles != '0 0 0')
2638 if(self.avelocity != '0 0 0')
2641 WriteByte(MSG_ENTITY, f);
2642 WriteShort(MSG_ENTITY, self.modelindex);
2643 WriteByte(MSG_ENTITY, self.skin);
2644 WriteByte(MSG_ENTITY, self.frame);
2645 WriteCoord(MSG_ENTITY, self.origin_x);
2646 WriteCoord(MSG_ENTITY, self.origin_y);
2647 WriteCoord(MSG_ENTITY, self.origin_z);
2650 WriteCoord(MSG_ENTITY, self.velocity_x);
2651 WriteCoord(MSG_ENTITY, self.velocity_y);
2652 WriteCoord(MSG_ENTITY, self.velocity_z);
2656 WriteCoord(MSG_ENTITY, self.angles_x);
2657 WriteCoord(MSG_ENTITY, self.angles_y);
2658 WriteCoord(MSG_ENTITY, self.angles_z);
2662 WriteCoord(MSG_ENTITY, self.avelocity_x);
2663 WriteCoord(MSG_ENTITY, self.avelocity_y);
2664 WriteCoord(MSG_ENTITY, self.avelocity_z);
2666 WriteShort(MSG_ENTITY, self.scale * 256.0);
2667 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2668 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2669 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2670 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2675 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)
2680 e.classname = "modeleffect";
2688 e.teleport_time = t1;
2692 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2696 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2699 sz = max(e.scale, e.scale2);
2700 setsize(e, e.mins * sz, e.maxs * sz);
2701 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2704 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2706 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2709 float randombit(float bits)
2711 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2720 for(f = 1; f <= bits; f *= 2)
2729 r = (r - 1) / (n - 1);
2736 float randombits(float bits, float k, float error_return)
2740 while(k > 0 && bits != r)
2742 r += randombit(bits - r);
2751 void randombit_test(float bits, float iter)
2755 print(ftos(randombit(bits)), "\n");
2760 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2762 if(halflifedist > 0)
2763 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2764 else if(halflifedist < 0)
2765 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2774 #define cvar_string_normal cvar_string_builtin
2775 #define cvar_normal cvar_builtin
2777 string cvar_string_normal(string n)
2779 if not(cvar_type(n) & 1)
2780 backtrace(strcat("Attempt to access undefined cvar: ", n));
2781 return cvar_string_builtin(n);
2784 float cvar_normal(string n)
2786 return stof(cvar_string_normal(n));
2789 #define cvar_set_normal cvar_set_builtin
2797 oself.think = SUB_Remove;
2798 oself.nextthink = time;
2804 Execute func() after time + fdelay.
2805 self when func is executed = self when defer is called
2807 void defer(float fdelay, void() func)
2814 e.think = defer_think;
2815 e.nextthink = time + fdelay;