1 var void remove(entity e);
2 void objerror(string s);
4 .vector dropped_origin;
6 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
7 void crosshair_trace(entity pl)
9 traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
11 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
12 void WarpZone_crosshair_trace(entity pl)
14 WarpZone_traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
17 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
18 void() spawnpoint_use;
19 float race_GetTime(float pos);
20 string race_GetName(float pos);
21 string race_PlaceName(float pos);
23 string ColoredTeamName(float t);
25 string admin_name(void)
27 if(cvar_string("sv_adminnick") != "")
28 return cvar_string("sv_adminnick");
30 return "SERVER ADMIN";
33 float DistributeEvenly_amount;
34 float DistributeEvenly_totalweight;
35 void DistributeEvenly_Init(float amount, float totalweight)
37 if (DistributeEvenly_amount)
39 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
40 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
43 DistributeEvenly_amount = 0;
45 DistributeEvenly_amount = amount;
46 DistributeEvenly_totalweight = totalweight;
48 float DistributeEvenly_Get(float weight)
53 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
54 DistributeEvenly_totalweight -= weight;
55 DistributeEvenly_amount -= f;
59 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
62 string STR_PLAYER = "player";
63 string STR_SPECTATOR = "spectator";
64 string STR_OBSERVER = "observer";
67 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
68 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
69 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
70 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
72 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
73 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
74 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
75 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
76 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
79 // copies a string to a tempstring (so one can strunzone it)
80 string strcat1(string s) = #115; // FRIK_FILE
85 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
87 local float nPlayerHealth = rint(enPlayer.health);
88 local float nPlayerArmor = rint(enPlayer.armorvalue);
89 local float nPlayerHandicap = enPlayer.cvar_cl_handicap;
90 local float nPlayerPing = rint(enPlayer.ping);
91 local string strPlayerPingColor;
92 local string strMessage;
93 if(nPlayerPing >= 150)
94 strPlayerPingColor = "^1";
96 strPlayerPingColor = "^2";
98 if((cvar("sv_fragmessage_information_stats")) && (enPlayer.health >= 1))
99 strMessage = strcat(strMessage, "\n^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
101 if(cvar("sv_fragmessage_information_ping")) {
102 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
103 strMessage = strcat(strMessage, "\n^7(^2Bot");
105 strMessage = strcat(strMessage, "\n^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
106 if(cvar("sv_fragmessage_information_handicap"))
107 if(cvar("sv_fragmessage_information_handicap") == 2)
108 if(nPlayerHandicap <= 1)
109 strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
111 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
112 else if not(nPlayerHandicap <= 1)
113 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
115 strMessage = strcat(strMessage, "^7)");
116 } else if(cvar("sv_fragmessage_information_handicap")) {
117 if(cvar("sv_fragmessage_information_handicap") == 2)
118 if(nPlayerHandicap <= 1)
119 strMessage = strcat(strMessage, "\n^7(Handicap ^2Off^7)");
121 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
122 else if(nPlayerHandicap > 1)
123 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
127 void bcenterprint(string s)
129 // TODO replace by MSG_ALL (would show it to spectators too, though)?
131 FOR_EACH_PLAYER(head)
132 if (clienttype(head) == CLIENTTYPE_REAL)
133 centerprint(head, s);
136 void GameLogEcho(string s)
141 if (cvar("sv_eventlog_files"))
146 matches = cvar("sv_eventlog_files_counter") + 1;
147 cvar_set("sv_eventlog_files_counter", ftos(matches));
150 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
151 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
152 logfile = fopen(fn, FILE_APPEND);
153 fputs(logfile, ":logversion:3\n");
157 if (cvar("sv_eventlog_files_timestamps"))
158 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
160 fputs(logfile, strcat(s, "\n"));
163 if (cvar("sv_eventlog_console"))
172 // will be opened later
177 if (logfile_open && logfile >= 0)
187 vector PL_CROUCH_VIEW_OFS;
188 vector PL_CROUCH_MIN;
189 vector PL_CROUCH_MAX;
191 float spawnpoint_nag;
192 void relocate_spawnpoint()
194 PL_VIEW_OFS = stov(cvar_string("sv_player_viewoffset"));
195 PL_MIN = stov(cvar_string("sv_player_mins"));
196 PL_MAX = stov(cvar_string("sv_player_maxs"));
197 PL_CROUCH_VIEW_OFS = stov(cvar_string("sv_player_crouch_viewoffset"));
198 PL_CROUCH_MIN = stov(cvar_string("sv_player_crouch_mins"));
199 PL_CROUCH_MAX = stov(cvar_string("sv_player_crouch_maxs"));
201 // nudge off the floor
202 setorigin(self, self.origin + '0 0 1');
204 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
205 if (trace_startsolid)
211 if (!move_out_of_solid(self))
212 objerror("could not get out of solid at all!");
213 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
214 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
215 print(" ", ftos(self.origin_y - o_y));
216 print(" ", ftos(self.origin_z - o_z), "'\n");
217 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
220 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
226 self.mins = self.maxs = '0 0 0';
227 objerror("player spawn point in solid, mapper sucks!\n");
232 if (cvar("g_spawnpoints_autodrop"))
234 setsize(self, PL_MIN, PL_MAX);
238 self.use = spawnpoint_use;
239 self.team_saved = self.team;
243 if (have_team_spawns != 0)
245 have_team_spawns = 1;
247 if (cvar("r_showbboxes"))
249 // show where spawnpoints point at too
250 makevectors(self.angles);
253 e.classname = "info_player_foo";
254 setorigin(e, self.origin + v_forward * 24);
255 setsize(e, '-8 -8 -8', '8 8 8');
256 e.solid = SOLID_TRIGGER;
260 #define strstr strstrofs
262 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
263 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
264 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
265 // BE CONSTANT OR strzoneD!
266 float strstr(string haystack, string needle, float offset)
270 len = strlen(needle);
271 endpos = strlen(haystack) - len;
272 while(offset <= endpos)
274 found = substring(haystack, offset, len);
283 float NUM_NEAREST_ENTITIES = 4;
284 entity nearest_entity[NUM_NEAREST_ENTITIES];
285 float nearest_length[NUM_NEAREST_ENTITIES];
286 entity findnearest(vector point, .string field, string value, vector axismod)
297 localhead = find(world, field, value);
300 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
301 dist = localhead.oldorigin;
303 dist = localhead.origin;
305 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
308 for (i = 0; i < num_nearest; ++i)
310 if (len < nearest_length[i])
314 // now i tells us where to insert at
315 // INSERTION SORT! YOU'VE SEEN IT! RUN!
316 if (i < NUM_NEAREST_ENTITIES)
318 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
320 nearest_length[j + 1] = nearest_length[j];
321 nearest_entity[j + 1] = nearest_entity[j];
323 nearest_length[i] = len;
324 nearest_entity[i] = localhead;
325 if (num_nearest < NUM_NEAREST_ENTITIES)
326 num_nearest = num_nearest + 1;
329 localhead = find(localhead, field, value);
332 // now use the first one from our list that we can see
333 for (i = 0; i < num_nearest; ++i)
335 traceline(point, nearest_entity[i].origin, TRUE, world);
336 if (trace_fraction == 1)
340 dprint("Nearest point (");
341 dprint(nearest_entity[0].netname);
342 dprint(") is not visible, using a visible one.\n");
344 return nearest_entity[i];
348 if (num_nearest == 0)
351 dprint("Not seeing any location point, using nearest as fallback.\n");
353 dprint("Candidates were: ");
354 for(j = 0; j < num_nearest; ++j)
358 dprint(nearest_entity[j].netname);
363 return nearest_entity[0];
366 void spawnfunc_target_location()
368 self.classname = "target_location";
369 // location name in netname
370 // eventually support: count, teamgame selectors, line of sight?
373 void spawnfunc_info_location()
375 self.classname = "target_location";
376 self.message = self.netname;
379 string NearestLocation(vector p)
384 loc = findnearest(p, classname, "target_location", '1 1 1');
391 loc = findnearest(p, target, "###item###", '1 1 4');
398 string formatmessage(string msg)
409 WarpZone_crosshair_trace(self);
410 cursor = trace_endpos;
411 cursor_ent = trace_ent;
415 break; // too many replacements
418 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
419 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
432 replacement = substring(msg, p, 2);
433 escape = substring(msg, p + 1, 1);
437 else if (escape == "\\")
439 else if (escape == "n")
441 else if (escape == "a")
442 replacement = ftos(floor(self.armorvalue));
443 else if (escape == "h")
444 replacement = ftos(floor(self.health));
445 else if (escape == "l")
446 replacement = NearestLocation(self.origin);
447 else if (escape == "y")
448 replacement = NearestLocation(cursor);
449 else if (escape == "d")
450 replacement = NearestLocation(self.death_origin);
451 else if (escape == "w") {
455 wep = self.switchweapon;
458 replacement = W_Name(wep);
459 } else if (escape == "W") {
460 if (self.items & IT_SHELLS) replacement = "shells";
461 else if (self.items & IT_NAILS) replacement = "bullets";
462 else if (self.items & IT_ROCKETS) replacement = "rockets";
463 else if (self.items & IT_CELLS) replacement = "cells";
464 else replacement = "batteries"; // ;)
465 } else if (escape == "x") {
466 replacement = cursor_ent.netname;
467 if (!replacement || !cursor_ent)
468 replacement = "nothing";
469 } else if (escape == "p") {
470 if (self.last_selected_player)
471 replacement = self.last_selected_player.netname;
473 replacement = "(nobody)";
474 } else if (escape == "s")
475 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
476 else if (escape == "S")
477 replacement = ftos(vlen(self.velocity));
478 else if (escape == "v") {
482 if(self.classname == "spectator")
487 weapon_number = stats.weapon;
490 weapon_number = stats.switchweapon;
493 weapon_number = stats.cnt;
495 if(stats.cvar_cl_accuracy_data_share && stats.stats_fired[weapon_number - 1])
496 replacement = ftos(bound(0, floor(100 * stats.stats_hit[weapon_number - 1] / stats.stats_fired[weapon_number - 1]), 100));
498 replacement = "~"; // or something to indicate NULL, not available
501 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
502 p = p + strlen(replacement);
507 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
508 return (value == 0) ? FALSE : TRUE;
517 >0: receives a cvar from name=argv(f) value=argv(f+1)
519 void GetCvars_handleString(string thisname, float f, .string field, string name)
524 strunzone(self.field);
525 self.field = string_null;
529 if (thisname == name)
532 strunzone(self.field);
533 self.field = strzone(argv(f + 1));
537 stuffcmd(self, strcat("sendcvar ", name, "\n"));
539 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
541 GetCvars_handleString(thisname, f, field, name);
542 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
543 if (thisname == name)
546 s = func(strcat1(self.field));
549 strunzone(self.field);
550 self.field = strzone(s);
554 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
561 if (thisname == name)
562 self.field = stof(argv(f + 1));
565 stuffcmd(self, strcat("sendcvar ", name, "\n"));
567 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
574 if (thisname == name)
578 self.field = stof(argv(f + 1));
587 stuffcmd(self, strcat("sendcvar ", name, "\n"));
590 string W_FixWeaponOrder_ForceComplete(string s);
591 string W_FixWeaponOrder_AllowIncomplete(string s);
592 float w_getbestweapon(entity e);
593 void GetCvars(float f)
596 MUTATOR_CALLHOOK(GetCvars);
599 s = strcat1(argv(f));
600 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
601 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
602 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
603 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
604 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
605 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
606 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
607 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
608 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
609 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
610 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
611 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
612 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
613 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
614 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
615 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
616 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
617 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
618 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
619 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
620 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
621 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
622 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
623 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
625 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
626 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
628 #ifdef ALLOW_FORCEMODELS
629 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
630 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
632 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
634 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
637 if (s == "cl_weaponpriority")
638 self.switchweapon = w_getbestweapon(self);
642 float fexists(string f)
645 fh = fopen(f, FILE_READ);
652 void backtrace(string msg)
655 dev = cvar("developer");
656 war = cvar("prvm_backtraceforwarnings");
657 cvar_set("developer", "1");
658 cvar_set("prvm_backtraceforwarnings", "1");
660 print("--- CUT HERE ---\nWARNING: ");
663 remove(world); // isn't there any better way to cause a backtrace?
664 print("\n--- CUT UNTIL HERE ---\n");
665 cvar_set("developer", ftos(dev));
666 cvar_set("prvm_backtraceforwarnings", ftos(war));
669 string Team_ColorCode(float teamid)
671 if (teamid == COLOR_TEAM1)
673 else if (teamid == COLOR_TEAM2)
675 else if (teamid == COLOR_TEAM3)
677 else if (teamid == COLOR_TEAM4)
683 string Team_ColorName(float t)
685 // fixme: Search for team entities and get their .netname's!
686 if (t == COLOR_TEAM1)
688 if (t == COLOR_TEAM2)
690 if (t == COLOR_TEAM3)
692 if (t == COLOR_TEAM4)
697 string Team_ColorNameLowerCase(float t)
699 // fixme: Search for team entities and get their .netname's!
700 if (t == COLOR_TEAM1)
702 if (t == COLOR_TEAM2)
704 if (t == COLOR_TEAM3)
706 if (t == COLOR_TEAM4)
711 float ColourToNumber(string team_colour)
713 if (team_colour == "red")
716 if (team_colour == "blue")
719 if (team_colour == "yellow")
722 if (team_colour == "pink")
725 if (team_colour == "auto")
731 float NumberToTeamNumber(float number)
748 #define CENTERPRIO_POINT 1
749 #define CENTERPRIO_SPAM 2
750 #define CENTERPRIO_VOTE 4
751 #define CENTERPRIO_NORMAL 5
752 #define CENTERPRIO_SHIELDING 7
753 #define CENTERPRIO_MAPVOTE 9
754 #define CENTERPRIO_IDLEKICK 50
755 #define CENTERPRIO_ADMIN 99
756 .float centerprint_priority;
757 .float centerprint_expires;
758 void centerprint_atprio(entity e, float prio, string s)
760 if (intermission_running)
761 if (prio < CENTERPRIO_MAPVOTE)
763 if (time > e.centerprint_expires)
764 e.centerprint_priority = 0;
765 if (prio >= e.centerprint_priority)
767 e.centerprint_priority = prio;
768 if (timeoutStatus == 2)
769 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
771 e.centerprint_expires = time + e.cvar_scr_centertime;
772 centerprint_builtin(e, s);
775 void centerprint_expire(entity e, float prio)
777 if (prio == e.centerprint_priority)
779 e.centerprint_priority = 0;
780 centerprint_builtin(e, "");
783 void centerprint(entity e, string s)
785 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
788 // decolorizes and team colors the player name when needed
789 string playername(entity p)
792 if (teams_matter && !intermission_running && p.classname == "player")
794 t = Team_ColorCode(p.team);
795 return strcat(t, strdecolorize(p.netname));
801 vector randompos(vector m1, vector m2)
805 v_x = m2_x * random() + m1_x;
806 v_y = m2_y * random() + m1_y;
807 v_z = m2_z * random() + m1_z;
811 float g_pickup_shells;
812 float g_pickup_shells_max;
813 float g_pickup_nails;
814 float g_pickup_nails_max;
815 float g_pickup_rockets;
816 float g_pickup_rockets_max;
817 float g_pickup_cells;
818 float g_pickup_cells_max;
820 float g_pickup_fuel_jetpack;
821 float g_pickup_fuel_max;
822 float g_pickup_armorsmall;
823 float g_pickup_armorsmall_max;
824 float g_pickup_armormedium;
825 float g_pickup_armormedium_max;
826 float g_pickup_armorbig;
827 float g_pickup_armorbig_max;
828 float g_pickup_armorlarge;
829 float g_pickup_armorlarge_max;
830 float g_pickup_healthsmall;
831 float g_pickup_healthsmall_max;
832 float g_pickup_healthmedium;
833 float g_pickup_healthmedium_max;
834 float g_pickup_healthlarge;
835 float g_pickup_healthlarge_max;
836 float g_pickup_healthmega;
837 float g_pickup_healthmega_max;
839 float g_weaponarena_random;
840 string g_weaponarena_list;
841 float g_weaponspeedfactor;
842 float g_weaponratefactor;
843 float g_weapondamagefactor;
844 float g_weaponforcefactor;
845 float g_weaponspreadfactor;
849 float start_ammo_shells;
850 float start_ammo_nails;
851 float start_ammo_rockets;
852 float start_ammo_cells;
853 float start_ammo_fuel;
855 float start_armorvalue;
856 float warmup_start_weapons;
857 float warmup_start_ammo_shells;
858 float warmup_start_ammo_nails;
859 float warmup_start_ammo_rockets;
860 float warmup_start_ammo_cells;
861 float warmup_start_ammo_fuel;
862 float warmup_start_health;
863 float warmup_start_armorvalue;
867 entity get_weaponinfo(float w);
869 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
871 var float i = weaponinfo.weapon;
876 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
878 if (t < 0) // "default" weapon selection
880 if (g_lms || g_ca || allguns)
881 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
884 else if (g_race || g_cts)
885 t = (i == WEP_LASER);
887 t = 0; // weapon is set a few lines later
889 t = (i == WEP_LASER || i == WEP_SHOTGUN);
890 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
891 t |= (i == WEP_HOOK);
894 // we cannot disable porto in Nexball, we must force it
895 if(g_nexball && i == WEP_PORTO)
901 void readplayerstartcvars()
907 // initialize starting values for players
910 start_ammo_shells = 0;
911 start_ammo_nails = 0;
912 start_ammo_rockets = 0;
913 start_ammo_cells = 0;
914 start_health = cvar("g_balance_health_start");
915 start_armorvalue = cvar("g_balance_armor_start");
918 s = cvar_string("g_weaponarena");
924 g_weaponarena_list = "All Weapons";
925 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
927 e = get_weaponinfo(j);
928 g_weaponarena |= e.weapons;
929 weapon_action(e.weapon, WR_PRECACHE);
932 else if (s == "most")
934 g_weaponarena_list = "Most Weapons";
935 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
937 e = get_weaponinfo(j);
938 if (e.spawnflags & WEP_FLAG_NORMAL)
940 g_weaponarena |= e.weapons;
941 weapon_action(e.weapon, WR_PRECACHE);
945 else if (s == "none")
947 g_weaponarena_list = "No Weapons";
948 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
952 t = tokenize_console(s);
953 g_weaponarena_list = "";
954 for (i = 0; i < t; ++i)
957 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
959 e = get_weaponinfo(j);
962 g_weaponarena |= e.weapons;
963 weapon_action(e.weapon, WR_PRECACHE);
964 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
970 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
973 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
977 g_weaponarena_random = cvar("g_weaponarena_random");
979 g_weaponarena_random = 0;
983 start_weapons = g_weaponarena;
984 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
985 start_ammo_rockets = 999;
986 if (g_weaponarena & WEPBIT_SHOTGUN)
987 start_ammo_shells = 999;
988 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
989 start_ammo_cells = 999;
990 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
991 start_ammo_nails = 999;
992 if (g_weaponarena & WEPBIT_HOOK)
993 start_ammo_fuel = 999;
994 start_items |= IT_UNLIMITED_AMMO;
996 else if (g_minstagib)
999 start_armorvalue = 0;
1000 start_weapons = WEPBIT_MINSTANEX;
1001 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
1002 start_ammo_cells = cvar("g_minstagib_ammo_start");
1003 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
1004 start_ammo_fuel = cvar("g_start_ammo_fuel");
1006 if (g_minstagib_invis_alpha <= 0)
1007 g_minstagib_invis_alpha = -1;
1013 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1014 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1015 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1016 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1017 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1018 start_health = cvar("g_lms_start_health");
1019 start_armorvalue = cvar("g_lms_start_armor");
1023 start_ammo_shells = cvar("g_start_ammo_shells");
1024 start_ammo_nails = cvar("g_start_ammo_nails");
1025 start_ammo_rockets = cvar("g_start_ammo_rockets");
1026 start_ammo_cells = cvar("g_start_ammo_cells");
1027 start_ammo_fuel = cvar("g_start_ammo_fuel");
1030 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1032 e = get_weaponinfo(i);
1033 if(want_weapon("g_start_weapon_", e, FALSE))
1034 start_weapons |= e.weapons;
1040 warmup_start_ammo_shells = start_ammo_shells;
1041 warmup_start_ammo_nails = start_ammo_nails;
1042 warmup_start_ammo_rockets = start_ammo_rockets;
1043 warmup_start_ammo_cells = start_ammo_cells;
1044 warmup_start_ammo_fuel = start_ammo_fuel;
1045 warmup_start_health = start_health;
1046 warmup_start_armorvalue = start_armorvalue;
1047 warmup_start_weapons = start_weapons;
1049 if (!g_weaponarena && !g_minstagib && !g_ca)
1051 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1052 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1053 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1054 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1055 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1056 warmup_start_health = cvar("g_warmup_start_health");
1057 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1058 warmup_start_weapons = 0;
1059 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1061 e = get_weaponinfo(i);
1062 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1063 warmup_start_weapons |= e.weapons;
1068 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1070 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1071 start_items |= IT_FUEL_REGEN;
1072 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1073 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1076 if(!cvar("g_use_ammunition"))
1078 start_ammo_shells = cvar("g_pickup_shells_max");
1079 start_ammo_nails = cvar("g_pickup_nails_max");
1080 start_ammo_rockets = cvar("g_pickup_rockets_max");
1081 start_ammo_cells = cvar("g_pickup_cells_max");
1082 start_ammo_fuel = cvar("g_pickup_fuel_max");
1083 start_items |= IT_UNLIMITED_AMMO;
1084 warmup_start_ammo_shells = cvar("g_pickup_shells_max");
1085 warmup_start_ammo_nails = cvar("g_pickup_nails_max");
1086 warmup_start_ammo_rockets = cvar("g_pickup_rockets_max");
1087 warmup_start_ammo_cells = cvar("g_pickup_cells_max");
1088 warmup_start_ammo_fuel = cvar("g_pickup_fuel_max");
1089 //warmup_start_items |= IT_UNLIMITED_AMMO;
1093 start_items |= IT_JETPACK;
1095 if (g_weapon_stay == 2)
1097 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1098 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1099 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1100 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1101 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1102 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1103 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1104 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1105 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1106 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1109 MUTATOR_CALLHOOK(SetStartItems);
1111 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1113 e = get_weaponinfo(i);
1114 if(e.weapons & (start_weapons | warmup_start_weapons))
1115 weapon_action(e.weapon, WR_PRECACHE);
1118 start_ammo_shells = max(0, start_ammo_shells);
1119 start_ammo_nails = max(0, start_ammo_nails);
1120 start_ammo_cells = max(0, start_ammo_cells);
1121 start_ammo_rockets = max(0, start_ammo_rockets);
1122 start_ammo_fuel = max(0, start_ammo_fuel);
1124 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1125 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1126 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1127 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1128 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1132 float g_bugrigs_planar_movement;
1133 float g_bugrigs_planar_movement_car_jumping;
1134 float g_bugrigs_reverse_spinning;
1135 float g_bugrigs_reverse_speeding;
1136 float g_bugrigs_reverse_stopping;
1137 float g_bugrigs_air_steering;
1138 float g_bugrigs_angle_smoothing;
1139 float g_bugrigs_friction_floor;
1140 float g_bugrigs_friction_brake;
1141 float g_bugrigs_friction_air;
1142 float g_bugrigs_accel;
1143 float g_bugrigs_speed_ref;
1144 float g_bugrigs_speed_pow;
1145 float g_bugrigs_steer;
1147 float g_touchexplode;
1148 float g_touchexplode_radius;
1149 float g_touchexplode_damage;
1150 float g_touchexplode_edgedamage;
1151 float g_touchexplode_force;
1158 float sv_pitch_fixyaw;
1160 float sv_accuracy_data_share;
1162 void readlevelcvars(void)
1164 // first load all the mutators
1166 MUTATOR_ADD(mutator_nix);
1168 g_bugrigs = cvar("g_bugrigs");
1169 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1170 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1171 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1172 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1173 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1174 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1175 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1176 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1177 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1178 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1179 g_bugrigs_accel = cvar("g_bugrigs_accel");
1180 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1181 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1182 g_bugrigs_steer = cvar("g_bugrigs_steer");
1184 g_touchexplode = cvar("g_touchexplode");
1185 g_touchexplode_radius = cvar("g_touchexplode_radius");
1186 g_touchexplode_damage = cvar("g_touchexplode_damage");
1187 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1188 g_touchexplode_force = cvar("g_touchexplode_force");
1190 #ifdef ALLOW_FORCEMODELS
1191 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1193 sv_loddistance1 = cvar("sv_loddistance1");
1194 sv_loddistance2 = cvar("sv_loddistance2");
1196 if(sv_loddistance2 <= sv_loddistance1)
1197 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1199 sv_clones = cvar("sv_clones");
1200 sv_gentle = cvar("sv_gentle");
1201 sv_foginterval = cvar("sv_foginterval");
1202 g_cloaked = cvar("g_cloaked");
1203 g_jump_grunt = cvar("g_jump_grunt");
1204 g_footsteps = cvar("g_footsteps");
1205 g_grappling_hook = cvar("g_grappling_hook");
1206 g_jetpack = cvar("g_jetpack");
1207 g_laserguided_missile = cvar("g_laserguided_missile");
1208 g_midair = cvar("g_midair");
1209 g_minstagib = cvar("g_minstagib");
1210 g_norecoil = cvar("g_norecoil");
1211 g_vampire = cvar("g_vampire");
1212 g_bloodloss = cvar("g_bloodloss");
1213 sv_maxidle = cvar("sv_maxidle");
1214 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1215 sv_pogostick = cvar("sv_pogostick");
1216 sv_doublejump = cvar("sv_doublejump");
1217 g_ctf_reverse = cvar("g_ctf_reverse");
1218 sv_autotaunt = cvar("sv_autotaunt");
1219 sv_taunt = cvar("sv_taunt");
1221 inWarmupStage = cvar("g_warmup");
1222 g_warmup_limit = cvar("g_warmup_limit");
1223 g_warmup_allguns = cvar("g_warmup_allguns");
1224 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1226 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1227 inWarmupStage = 0; // these modes cannot work together, sorry
1229 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1230 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1231 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1232 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1233 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1234 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1235 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1236 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1237 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1238 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1239 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1240 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1242 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1243 g_weaponratefactor = cvar("g_weaponratefactor");
1244 g_weapondamagefactor = cvar("g_weapondamagefactor");
1245 g_weaponforcefactor = cvar("g_weaponforcefactor");
1246 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1248 g_pickup_shells = cvar("g_pickup_shells");
1249 g_pickup_shells_max = cvar("g_pickup_shells_max");
1250 g_pickup_nails = cvar("g_pickup_nails");
1251 g_pickup_nails_max = cvar("g_pickup_nails_max");
1252 g_pickup_rockets = cvar("g_pickup_rockets");
1253 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1254 g_pickup_cells = cvar("g_pickup_cells");
1255 g_pickup_cells_max = cvar("g_pickup_cells_max");
1256 g_pickup_fuel = cvar("g_pickup_fuel");
1257 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1258 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1259 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1260 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1261 g_pickup_armormedium = cvar("g_pickup_armormedium");
1262 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1263 g_pickup_armorbig = cvar("g_pickup_armorbig");
1264 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1265 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1266 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1267 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1268 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1269 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1270 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1271 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1272 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1273 g_pickup_healthmega = cvar("g_pickup_healthmega");
1274 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1276 g_pinata = cvar("g_pinata");
1278 g_weapon_stay = cvar("g_weapon_stay");
1280 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1283 g_ghost_items = cvar("g_ghost_items");
1285 if(g_ghost_items >= 1)
1286 g_ghost_items = 0.25; // default alpha value
1288 if not(inWarmupStage && !g_ca)
1289 game_starttime = cvar("g_start_delay");
1291 sv_pitch_min = cvar("sv_pitch_min");
1292 sv_pitch_max = cvar("sv_pitch_max");
1293 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1295 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1297 readplayerstartcvars();
1301 // TODO sound pack system
1304 string precache_sound_builtin (string s) = #19;
1305 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1306 string precache_sound(string s)
1308 return precache_sound_builtin(strcat(soundpack, s));
1310 void play2(entity e, string filename)
1312 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1314 void sound(entity e, float chan, string samp, float vol, float atten)
1316 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1321 string precache_sound (string s) = #19;
1322 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1323 float precache_sound_index (string s) = #19;
1325 #define SND_VOLUME 1
1326 #define SND_ATTENUATION 2
1327 #define SND_LARGEENTITY 8
1328 #define SND_LARGESOUND 16
1330 float sound_allowed(float dest, entity e)
1332 // sounds from world may always pass
1335 if (e.classname == "body")
1337 if (e.owner && e.owner != e)
1342 // sounds to self may always pass
1343 if (dest == MSG_ONE)
1344 if (e == msg_entity)
1346 // sounds by players can be removed
1347 if (cvar("bot_sound_monopoly"))
1348 if (clienttype(e) == CLIENTTYPE_REAL)
1350 // anything else may pass
1354 void sound(entity e, float chan, string samp, float vol, float atten)
1356 if (!sound_allowed(MSG_BROADCAST, e))
1358 sound_builtin(e, chan, samp, vol, atten);
1360 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1364 if (!sound_allowed(dest, e))
1367 entno = num_for_edict(e);
1368 idx = precache_sound_index(samp);
1373 atten = floor(atten * 64);
1374 vol = floor(vol * 255);
1377 sflags |= SND_VOLUME;
1379 sflags |= SND_ATTENUATION;
1381 sflags |= SND_LARGEENTITY;
1383 sflags |= SND_LARGESOUND;
1385 WriteByte(dest, SVC_SOUND);
1386 WriteByte(dest, sflags);
1387 if (sflags & SND_VOLUME)
1388 WriteByte(dest, vol);
1389 if (sflags & SND_ATTENUATION)
1390 WriteByte(dest, atten);
1391 if (sflags & SND_LARGEENTITY)
1393 WriteShort(dest, entno);
1394 WriteByte(dest, chan);
1398 WriteShort(dest, entno * 8 + chan);
1400 if (sflags & SND_LARGESOUND)
1401 WriteShort(dest, idx);
1403 WriteByte(dest, idx);
1405 WriteCoord(dest, o_x);
1406 WriteCoord(dest, o_y);
1407 WriteCoord(dest, o_z);
1409 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1413 if (!sound_allowed(dest, e))
1416 o = e.origin + 0.5 * (e.mins + e.maxs);
1417 soundtoat(dest, e, o, chan, samp, vol, atten);
1419 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1421 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1423 void stopsoundto(float dest, entity e, float chan)
1427 if (!sound_allowed(dest, e))
1430 entno = num_for_edict(e);
1435 idx = precache_sound_index("misc/null.wav");
1436 sflags = SND_LARGEENTITY;
1438 sflags |= SND_LARGESOUND;
1439 WriteByte(dest, SVC_SOUND);
1440 WriteByte(dest, sflags);
1441 WriteShort(dest, entno);
1442 WriteByte(dest, chan);
1443 if (sflags & SND_LARGESOUND)
1444 WriteShort(dest, idx);
1446 WriteByte(dest, idx);
1447 WriteCoord(dest, e.origin_x);
1448 WriteCoord(dest, e.origin_y);
1449 WriteCoord(dest, e.origin_z);
1453 WriteByte(dest, SVC_STOPSOUND);
1454 WriteShort(dest, entno * 8 + chan);
1457 void stopsound(entity e, float chan)
1459 if (!sound_allowed(MSG_BROADCAST, e))
1462 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1463 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1466 void play2(entity e, string filename)
1468 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1470 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1473 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1475 float spamsound(entity e, float chan, string samp, float vol, float atten)
1477 if (!sound_allowed(MSG_BROADCAST, e))
1480 if (time > e.spamtime)
1483 sound(e, chan, samp, vol, atten);
1489 void play2team(float t, string filename)
1493 if (cvar("bot_sound_monopoly"))
1496 FOR_EACH_REALPLAYER(head)
1499 play2(head, filename);
1503 void play2all(string samp)
1505 if (cvar("bot_sound_monopoly"))
1508 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1511 void PrecachePlayerSounds(string f);
1512 void precache_all_models(string pattern)
1514 float globhandle, i, n;
1517 globhandle = search_begin(pattern, TRUE, FALSE);
1520 n = search_getsize(globhandle);
1521 for (i = 0; i < n; ++i)
1523 //print(search_getfilename(globhandle, i), "\n");
1524 f = search_getfilename(globhandle, i);
1527 if(substring(f, -9,5) == "_lod1")
1529 if(substring(f, -9,5) == "_lod2")
1531 if(!sv_loddistance1)
1533 PrecachePlayerSounds(strcat(f, ".sounds"));
1535 search_end(globhandle);
1540 // gamemode related things
1541 precache_model ("models/misc/chatbubble.spr");
1542 precache_model ("models/misc/teambubble.spr");
1545 precache_model ("models/runematch/curse.mdl");
1546 precache_model ("models/runematch/rune.mdl");
1549 #ifdef TTURRETS_ENABLED
1550 if (cvar("g_turrets"))
1554 // Precache all player models if desired
1555 if (cvar("sv_precacheplayermodels"))
1557 PrecachePlayerSounds("sound/player/default.sounds");
1558 precache_all_models("models/player/*.zym");
1559 precache_all_models("models/player/*.dpm");
1560 precache_all_models("models/player/*.md3");
1561 precache_all_models("models/player/*.psk");
1562 //precache_model("models/player/carni.zym");
1563 //precache_model("models/player/crash.zym");
1564 //precache_model("models/player/grunt.zym");
1565 //precache_model("models/player/headhunter.zym");
1566 //precache_model("models/player/insurrectionist.zym");
1567 //precache_model("models/player/jeandarc.zym");
1568 //precache_model("models/player/lurk.zym");
1569 //precache_model("models/player/lycanthrope.zym");
1570 //precache_model("models/player/marine.zym");
1571 //precache_model("models/player/nexus.zym");
1572 //precache_model("models/player/pyria.zym");
1573 //precache_model("models/player/shock.zym");
1574 //precache_model("models/player/skadi.zym");
1575 //precache_model("models/player/specop.zym");
1576 //precache_model("models/player/visitant.zym");
1579 if (cvar("sv_defaultcharacter"))
1582 s = cvar_string("sv_defaultplayermodel_red");
1586 PrecachePlayerSounds(strcat(s, ".sounds"));
1588 s = cvar_string("sv_defaultplayermodel_blue");
1592 PrecachePlayerSounds(strcat(s, ".sounds"));
1594 s = cvar_string("sv_defaultplayermodel_yellow");
1598 PrecachePlayerSounds(strcat(s, ".sounds"));
1600 s = cvar_string("sv_defaultplayermodel_pink");
1604 PrecachePlayerSounds(strcat(s, ".sounds"));
1606 s = cvar_string("sv_defaultplayermodel");
1610 PrecachePlayerSounds(strcat(s, ".sounds"));
1616 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1617 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1620 // gore and miscellaneous sounds
1621 //precache_sound ("misc/h2ohit.wav");
1622 precache_model ("models/hook.md3");
1623 precache_sound ("misc/armorimpact.wav");
1624 precache_sound ("misc/bodyimpact1.wav");
1625 precache_sound ("misc/bodyimpact2.wav");
1626 precache_sound ("misc/gib.wav");
1627 precache_sound ("misc/gib_splat01.wav");
1628 precache_sound ("misc/gib_splat02.wav");
1629 precache_sound ("misc/gib_splat03.wav");
1630 precache_sound ("misc/gib_splat04.wav");
1631 precache_sound ("misc/hit.wav");
1632 precache_sound ("misc/typehit.wav");
1633 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1634 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1635 precache_sound ("misc/null.wav");
1636 precache_sound ("misc/spawn.wav");
1637 precache_sound ("misc/talk.wav");
1638 precache_sound ("misc/teleport.wav");
1639 precache_sound ("misc/poweroff.wav");
1640 precache_sound ("player/lava.wav");
1641 precache_sound ("player/slime.wav");
1644 precache_sound ("misc/jetpack_fly.wav");
1646 precache_model ("models/sprites/0.spr32");
1647 precache_model ("models/sprites/1.spr32");
1648 precache_model ("models/sprites/2.spr32");
1649 precache_model ("models/sprites/3.spr32");
1650 precache_model ("models/sprites/4.spr32");
1651 precache_model ("models/sprites/5.spr32");
1652 precache_model ("models/sprites/6.spr32");
1653 precache_model ("models/sprites/7.spr32");
1654 precache_model ("models/sprites/8.spr32");
1655 precache_model ("models/sprites/9.spr32");
1656 precache_model ("models/sprites/10.spr32");
1658 // common weapon precaches
1659 precache_sound ("weapons/weapon_switch.wav");
1660 precache_sound ("weapons/weaponpickup.wav");
1661 precache_sound ("weapons/unavailable.wav");
1662 if (g_grappling_hook)
1664 precache_sound ("weapons/hook_fire.wav"); // hook
1665 precache_sound ("weapons/hook_impact.wav"); // hook
1668 if(cvar("sv_precacheweapons"))
1670 //precache weapon models/sounds
1673 while (wep <= WEP_LAST)
1675 weapon_action(wep, WR_PRECACHE);
1680 precache_model("models/elaser.mdl");
1681 precache_model("models/laser.mdl");
1682 precache_model("models/ebomb.mdl");
1685 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1687 if (!self.noise && self.music) // quake 3 uses the music field
1688 self.noise = self.music;
1690 // plays music for the level if there is any
1693 precache_sound (self.noise);
1694 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1699 // sorry, but using \ in macros breaks line numbers
1700 #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
1701 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1702 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1704 // WARNING: this kills the trace globals
1705 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1706 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1708 #define INITPRIO_FIRST 0
1709 #define INITPRIO_GAMETYPE 0
1710 #define INITPRIO_GAMETYPE_FALLBACK 1
1711 #define INITPRIO_CVARS 5
1712 #define INITPRIO_FINDTARGET 10
1713 #define INITPRIO_DROPTOFLOOR 20
1714 #define INITPRIO_SETLOCATION 90
1715 #define INITPRIO_LINKDOORS 91
1716 #define INITPRIO_LAST 99
1718 .void(void) initialize_entity;
1719 .float initialize_entity_order;
1720 .entity initialize_entity_next;
1721 entity initialize_entity_first;
1723 void make_safe_for_remove(entity e)
1725 if (e.initialize_entity)
1728 for (ent = initialize_entity_first; ent; )
1730 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1732 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1733 // skip it in linked list
1736 prev.initialize_entity_next = ent.initialize_entity_next;
1737 ent = prev.initialize_entity_next;
1741 initialize_entity_first = ent.initialize_entity_next;
1742 ent = initialize_entity_first;
1748 ent = ent.initialize_entity_next;
1754 void objerror(string s)
1756 make_safe_for_remove(self);
1757 objerror_builtin(s);
1760 void remove_unsafely(entity e)
1765 void remove_safely(entity e)
1767 make_safe_for_remove(e);
1771 void InitializeEntity(entity e, void(void) func, float order)
1775 if (!e || e.initialize_entity)
1777 // make a proxy initializer entity
1781 e.classname = "initialize_entity";
1785 e.initialize_entity = func;
1786 e.initialize_entity_order = order;
1788 cur = initialize_entity_first;
1791 if (!cur || cur.initialize_entity_order > order)
1793 // insert between prev and cur
1795 prev.initialize_entity_next = e;
1797 initialize_entity_first = e;
1798 e.initialize_entity_next = cur;
1802 cur = cur.initialize_entity_next;
1805 void InitializeEntitiesRun()
1808 startoflist = initialize_entity_first;
1809 initialize_entity_first = world;
1810 for (self = startoflist; self; )
1813 var void(void) func;
1814 e = self.initialize_entity_next;
1815 func = self.initialize_entity;
1816 self.initialize_entity_order = 0;
1817 self.initialize_entity = func_null;
1818 self.initialize_entity_next = world;
1819 if (self.classname == "initialize_entity")
1823 remove_builtin(self);
1826 //dprint("Delayed initialization: ", self.classname, "\n");
1832 .float uncustomizeentityforclient_set;
1833 .void(void) uncustomizeentityforclient;
1834 void(void) SUB_Nullpointer = #0;
1835 void UncustomizeEntitiesRun()
1839 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1840 self.uncustomizeentityforclient();
1843 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1845 e.customizeentityforclient = customizer;
1846 e.uncustomizeentityforclient = uncustomizer;
1847 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1851 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1854 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1858 if (e.classname == "")
1859 e.classname = "net_linked";
1861 if (e.model == "" || self.modelindex == 0)
1865 setmodel(e, "null");
1869 e.SendEntity = sendfunc;
1870 e.SendFlags = 0xFFFFFF;
1873 e.effects |= EF_NODEPTHTEST;
1877 e.nextthink = time + dt;
1878 e.think = SUB_Remove;
1882 void adaptor_think2touch()
1891 void adaptor_think2use()
1903 // deferred dropping
1904 void DropToFloor_Handler()
1906 droptofloor_builtin();
1907 self.dropped_origin = self.origin;
1912 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1917 float trace_hits_box_a0, trace_hits_box_a1;
1919 float trace_hits_box_1d(float end, float thmi, float thma)
1923 // just check if x is in range
1931 // do the trace with respect to x
1932 // 0 -> end has to stay in thmi -> thma
1933 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1934 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1935 if (trace_hits_box_a0 > trace_hits_box_a1)
1941 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1946 // now it is a trace from 0 to end
1948 trace_hits_box_a0 = 0;
1949 trace_hits_box_a1 = 1;
1951 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1953 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1955 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1961 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1963 return trace_hits_box(start, end, thmi - ma, thma - mi);
1966 float SUB_NoImpactCheck()
1968 // zero hitcontents = this is not the real impact, but either the
1969 // mirror-impact of something hitting the projectile instead of the
1970 // projectile hitting the something, or a touchareagrid one. Neither of
1971 // these stop the projectile from moving, so...
1972 if(trace_dphitcontents == 0)
1974 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1977 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1979 if (other == world && self.size != '0 0 0')
1982 tic = self.velocity * sys_frametime;
1983 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1984 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1985 if (trace_fraction >= 1)
1987 dprint("Odd... did not hit...?\n");
1989 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1991 dprint("Detected and prevented the sky-grapple bug.\n");
1999 #define SUB_OwnerCheck() (other && (other == self.owner))
2001 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
2003 if(SUB_OwnerCheck())
2005 if(SUB_NoImpactCheck())
2010 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2011 UpdateCSQCProjectileNextFrame(self);
2014 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2016 float MAX_IPBAN_URIS = 16;
2018 float URI_GET_DISCARD = 0;
2019 float URI_GET_IPBAN = 1;
2020 float URI_GET_IPBAN_END = 16;
2022 void URI_Get_Callback(float id, float status, string data)
2024 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2026 dprint("\nEnd of data.\n");
2028 if (id == URI_GET_DISCARD)
2032 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2035 OnlineBanList_URI_Get_Callback(id, status, data);
2039 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2043 void print_to(entity e, string s)
2046 sprint(e, strcat(s, "\n"));
2051 string getrecords(float page) // 50 records per page
2065 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2067 if (MapInfo_Get_ByID(i))
2069 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2072 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2073 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2081 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2083 if (MapInfo_Get_ByID(i))
2085 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2088 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2089 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2097 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2099 if (MapInfo_Get_ByID(i))
2101 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2104 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2105 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2111 MapInfo_ClearTemps();
2113 if (s == "" && page == 0)
2114 return "No records are available on this server.\n";
2119 string getrankings()
2132 for (i = 1; i <= RANKINGS_CNT; ++i)
2134 t = race_GetTime(i);
2137 n = race_GetName(i);
2138 p = race_PlaceName(i);
2139 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2142 MapInfo_ClearTemps();
2145 return strcat("No records are available for the map: ", map, "\n");
2147 return strcat("Records for ", map, ":\n", s);
2150 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2153 vector start, org, delta, end, enddown, mstart;
2155 m = e.dphitcontentsmask;
2156 e.dphitcontentsmask = goodcontents | badcontents;
2159 delta = world.maxs - world.mins;
2161 for (i = 0; i < attempts; ++i)
2163 start_x = org_x + random() * delta_x;
2164 start_y = org_y + random() * delta_y;
2165 start_z = org_z + random() * delta_z;
2167 // rule 1: start inside world bounds, and outside
2168 // solid, and don't start from somewhere where you can
2169 // fall down to evil
2170 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2171 if (trace_fraction >= 1)
2173 if (trace_startsolid)
2175 if (trace_dphitcontents & badcontents)
2177 if (trace_dphitq3surfaceflags & badsurfaceflags)
2180 // rule 2: if we are too high, lower the point
2181 if (trace_fraction * delta_z > maxaboveground)
2182 start = trace_endpos + '0 0 1' * maxaboveground;
2183 enddown = trace_endpos;
2185 // rule 3: make sure we aren't outside the map. This only works
2186 // for somewhat well formed maps. A good rule of thumb is that
2187 // the map should have a convex outside hull.
2188 // these can be traceLINES as we already verified the starting box
2189 mstart = start + 0.5 * (e.mins + e.maxs);
2190 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2191 if (trace_fraction >= 1)
2193 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2194 if (trace_fraction >= 1)
2196 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2197 if (trace_fraction >= 1)
2199 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2200 if (trace_fraction >= 1)
2202 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2203 if (trace_fraction >= 1)
2206 // find a random vector to "look at"
2207 end_x = org_x + random() * delta_x;
2208 end_y = org_y + random() * delta_y;
2209 end_z = org_z + random() * delta_z;
2210 end = start + normalize(end - start) * vlen(delta);
2212 // rule 4: start TO end must not be too short
2213 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2214 if (trace_startsolid)
2216 if (trace_fraction < minviewdistance / vlen(delta))
2219 // rule 5: don't want to look at sky
2220 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2223 // rule 6: we must not end up in trigger_hurt
2224 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2226 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2233 e.dphitcontentsmask = m;
2237 setorigin(e, start);
2238 e.angles = vectoangles(end - start);
2239 dprint("Needed ", ftos(i + 1), " attempts\n");
2246 float zcurveparticles_effectno;
2247 vector zcurveparticles_start;
2248 float zcurveparticles_spd;
2250 void endzcurveparticles()
2252 if(zcurveparticles_effectno)
2255 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2257 zcurveparticles_effectno = 0;
2260 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2262 spd = bound(0, floor(spd / 16), 32767);
2263 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2265 endzcurveparticles();
2266 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2267 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2268 WriteShort(MSG_BROADCAST, effectno);
2269 WriteCoord(MSG_BROADCAST, start_x);
2270 WriteCoord(MSG_BROADCAST, start_y);
2271 WriteCoord(MSG_BROADCAST, start_z);
2272 zcurveparticles_effectno = effectno;
2273 zcurveparticles_start = start;
2276 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2277 WriteCoord(MSG_BROADCAST, end_x);
2278 WriteCoord(MSG_BROADCAST, end_y);
2279 WriteCoord(MSG_BROADCAST, end_z);
2280 WriteCoord(MSG_BROADCAST, end_dz);
2281 zcurveparticles_spd = spd;
2284 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2287 vector vecxy, velxy;
2289 vecxy = end - start;
2294 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2296 endzcurveparticles();
2297 trailparticles(world, effectno, start, end);
2301 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2302 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2305 string GetGametype(); // g_world.qc
2306 void write_recordmarker(entity pl, float tstart, float dt)
2308 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2310 // also write a marker into demo files for demotc-race-record-extractor to find
2313 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2314 " ", ftos(tstart), " ", ftos(dt), "\n"));
2317 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2319 switch(self.owner.cvar_cl_gunalign)
2330 if(allowcenter) // 2: allow center handedness
2343 if(allowcenter) // 2: allow center handedness
2359 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2364 if (cvar("g_shootfromeye"))
2368 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2376 else if (cvar("g_shootfromcenter"))
2380 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2388 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2398 else if (cvar("g_shootfromclient"))
2400 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2407 void attach_sameorigin(entity e, entity to, string tag)
2409 vector org, t_forward, t_left, t_up, e_forward, e_up;
2416 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2417 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2418 t_forward = v_forward * tagscale;
2419 t_left = v_right * -tagscale;
2420 t_up = v_up * tagscale;
2422 e.origin_x = org * t_forward;
2423 e.origin_y = org * t_left;
2424 e.origin_z = org * t_up;
2426 // current forward and up directions
2427 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2428 e.angles = AnglesTransform_FromVAngles(e.angles);
2430 e.angles = AnglesTransform_FromAngles(e.angles);
2431 fixedmakevectors(e.angles);
2433 // untransform forward, up!
2434 e_forward_x = v_forward * t_forward;
2435 e_forward_y = v_forward * t_left;
2436 e_forward_z = v_forward * t_up;
2437 e_up_x = v_up * t_forward;
2438 e_up_y = v_up * t_left;
2439 e_up_z = v_up * t_up;
2441 e.angles = fixedvectoangles2(e_forward, e_up);
2442 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2443 e.angles = AnglesTransform_ToVAngles(e.angles);
2445 e.angles = AnglesTransform_ToAngles(e.angles);
2447 setattachment(e, to, tag);
2448 setorigin(e, e.origin);
2451 void detach_sameorigin(entity e)
2454 org = gettaginfo(e, 0);
2455 e.angles = fixedvectoangles2(v_forward, v_up);
2456 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2457 e.angles = AnglesTransform_ToVAngles(e.angles);
2459 e.angles = AnglesTransform_ToAngles(e.angles);
2461 setattachment(e, world, "");
2462 setorigin(e, e.origin);
2465 void follow_sameorigin(entity e, entity to)
2467 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2468 e.aiment = to; // make the hole follow bmodel
2469 e.punchangle = to.angles; // the original angles of bmodel
2470 e.view_ofs = e.origin - to.origin; // relative origin
2471 e.v_angle = e.angles - to.angles; // relative angles
2474 void unfollow_sameorigin(entity e)
2476 e.movetype = MOVETYPE_NONE;
2479 entity gettaginfo_relative_ent;
2480 vector gettaginfo_relative(entity e, float tag)
2482 if (!gettaginfo_relative_ent)
2484 gettaginfo_relative_ent = spawn();
2485 gettaginfo_relative_ent.effects = EF_NODRAW;
2487 gettaginfo_relative_ent.model = e.model;
2488 gettaginfo_relative_ent.modelindex = e.modelindex;
2489 gettaginfo_relative_ent.frame = e.frame;
2490 return gettaginfo(gettaginfo_relative_ent, tag);
2493 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2497 if (pl.soundentity.cnt & p)
2499 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2500 pl.soundentity.cnt |= p;
2503 void SoundEntity_StopSound(entity pl, float chan)
2507 if (pl.soundentity.cnt & p)
2509 stopsoundto(MSG_ALL, pl.soundentity, chan);
2510 pl.soundentity.cnt &~= p;
2514 void SoundEntity_Attach(entity pl)
2516 pl.soundentity = spawn();
2517 pl.soundentity.classname = "soundentity";
2518 pl.soundentity.owner = pl;
2519 setattachment(pl.soundentity, pl, "");
2520 setmodel(pl.soundentity, "null");
2523 void SoundEntity_Detach(entity pl)
2526 for (i = 0; i <= 7; ++i)
2527 SoundEntity_StopSound(pl, i);
2531 float ParseCommandPlayerSlotTarget_firsttoken;
2532 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2540 ParseCommandPlayerSlotTarget_firsttoken = -1;
2544 if (substring(argv(idx), 0, 1) == "#")
2546 s = substring(argv(idx), 1, -1);
2554 ParseCommandPlayerSlotTarget_firsttoken = idx;
2555 if (s == ftos(stof(s)))
2557 e = edict_num(stof(s));
2558 if (e.flags & FL_CLIENT)
2564 // it must be a nick name
2567 ParseCommandPlayerSlotTarget_firsttoken = idx;
2570 FOR_EACH_CLIENT(head)
2571 if (head.netname == s)
2579 s = strdecolorize(s);
2581 FOR_EACH_CLIENT(head)
2582 if (strdecolorize(head.netname) == s)
2597 float modeleffect_SendEntity(entity to, float sf)
2600 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2603 if(self.velocity != '0 0 0')
2605 if(self.angles != '0 0 0')
2607 if(self.avelocity != '0 0 0')
2610 WriteByte(MSG_ENTITY, f);
2611 WriteShort(MSG_ENTITY, self.modelindex);
2612 WriteByte(MSG_ENTITY, self.skin);
2613 WriteByte(MSG_ENTITY, self.frame);
2614 WriteCoord(MSG_ENTITY, self.origin_x);
2615 WriteCoord(MSG_ENTITY, self.origin_y);
2616 WriteCoord(MSG_ENTITY, self.origin_z);
2619 WriteCoord(MSG_ENTITY, self.velocity_x);
2620 WriteCoord(MSG_ENTITY, self.velocity_y);
2621 WriteCoord(MSG_ENTITY, self.velocity_z);
2625 WriteCoord(MSG_ENTITY, self.angles_x);
2626 WriteCoord(MSG_ENTITY, self.angles_y);
2627 WriteCoord(MSG_ENTITY, self.angles_z);
2631 WriteCoord(MSG_ENTITY, self.avelocity_x);
2632 WriteCoord(MSG_ENTITY, self.avelocity_y);
2633 WriteCoord(MSG_ENTITY, self.avelocity_z);
2635 WriteShort(MSG_ENTITY, self.scale * 256.0);
2636 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2637 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2638 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2639 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2644 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)
2649 e.classname = "modeleffect";
2657 e.teleport_time = t1;
2661 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2665 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2668 sz = max(e.scale, e.scale2);
2669 setsize(e, e.mins * sz, e.maxs * sz);
2670 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2673 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2675 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2678 float randombit(float bits)
2680 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2689 for(f = 1; f <= bits; f *= 2)
2698 r = (r - 1) / (n - 1);
2705 float randombits(float bits, float k, float error_return)
2709 while(k > 0 && bits != r)
2711 r += randombit(bits - r);
2720 void randombit_test(float bits, float iter)
2724 print(ftos(randombit(bits)), "\n");
2729 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2731 if(halflifedist > 0)
2732 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2733 else if(halflifedist < 0)
2734 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2743 #define cvar_string_normal cvar_string_builtin
2744 #define cvar_normal cvar_builtin
2746 string cvar_string_normal(string n)
2748 if not(cvar_type(n) & 1)
2749 backtrace(strcat("Attempt to access undefined cvar: ", n));
2750 return cvar_string_builtin(n);
2753 float cvar_normal(string n)
2755 return stof(cvar_string_normal(n));
2758 #define cvar_set_normal cvar_set_builtin
2766 oself.think = SUB_Remove;
2767 oself.nextthink = time;
2773 Execute func() after time + fdelay.
2774 self when func is executed = self when defer is called
2776 void defer(float fdelay, void() func)
2783 e.think = defer_think;
2784 e.nextthink = time + fdelay;