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)
597 s = strcat1(argv(f));
598 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
599 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
600 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
601 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
602 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
603 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
604 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
605 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
606 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
607 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
608 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
609 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
610 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
611 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
612 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
613 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
614 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
615 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
616 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
617 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
618 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
619 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
620 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
621 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
623 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
624 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
626 #ifdef ALLOW_FORCEMODELS
627 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
628 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
630 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
632 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
635 if (s == "cl_weaponpriority")
636 self.switchweapon = w_getbestweapon(self);
640 float fexists(string f)
643 fh = fopen(f, FILE_READ);
650 void backtrace(string msg)
653 dev = cvar("developer");
654 war = cvar("prvm_backtraceforwarnings");
655 cvar_set("developer", "1");
656 cvar_set("prvm_backtraceforwarnings", "1");
658 print("--- CUT HERE ---\nWARNING: ");
661 remove(world); // isn't there any better way to cause a backtrace?
662 print("\n--- CUT UNTIL HERE ---\n");
663 cvar_set("developer", ftos(dev));
664 cvar_set("prvm_backtraceforwarnings", ftos(war));
667 string Team_ColorCode(float teamid)
669 if (teamid == COLOR_TEAM1)
671 else if (teamid == COLOR_TEAM2)
673 else if (teamid == COLOR_TEAM3)
675 else if (teamid == COLOR_TEAM4)
681 string Team_ColorName(float t)
683 // fixme: Search for team entities and get their .netname's!
684 if (t == COLOR_TEAM1)
686 if (t == COLOR_TEAM2)
688 if (t == COLOR_TEAM3)
690 if (t == COLOR_TEAM4)
695 string Team_ColorNameLowerCase(float t)
697 // fixme: Search for team entities and get their .netname's!
698 if (t == COLOR_TEAM1)
700 if (t == COLOR_TEAM2)
702 if (t == COLOR_TEAM3)
704 if (t == COLOR_TEAM4)
709 float ColourToNumber(string team_colour)
711 if (team_colour == "red")
714 if (team_colour == "blue")
717 if (team_colour == "yellow")
720 if (team_colour == "pink")
723 if (team_colour == "auto")
729 float NumberToTeamNumber(float number)
746 #define CENTERPRIO_POINT 1
747 #define CENTERPRIO_SPAM 2
748 #define CENTERPRIO_VOTE 4
749 #define CENTERPRIO_NORMAL 5
750 #define CENTERPRIO_SHIELDING 7
751 #define CENTERPRIO_MAPVOTE 9
752 #define CENTERPRIO_IDLEKICK 50
753 #define CENTERPRIO_ADMIN 99
754 .float centerprint_priority;
755 .float centerprint_expires;
756 void centerprint_atprio(entity e, float prio, string s)
758 if (intermission_running)
759 if (prio < CENTERPRIO_MAPVOTE)
761 if (time > e.centerprint_expires)
762 e.centerprint_priority = 0;
763 if (prio >= e.centerprint_priority)
765 e.centerprint_priority = prio;
766 if (timeoutStatus == 2)
767 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
769 e.centerprint_expires = time + e.cvar_scr_centertime;
770 centerprint_builtin(e, s);
773 void centerprint_expire(entity e, float prio)
775 if (prio == e.centerprint_priority)
777 e.centerprint_priority = 0;
778 centerprint_builtin(e, "");
781 void centerprint(entity e, string s)
783 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
786 // decolorizes and team colors the player name when needed
787 string playername(entity p)
790 if (teams_matter && !intermission_running && p.classname == "player")
792 t = Team_ColorCode(p.team);
793 return strcat(t, strdecolorize(p.netname));
799 vector randompos(vector m1, vector m2)
803 v_x = m2_x * random() + m1_x;
804 v_y = m2_y * random() + m1_y;
805 v_z = m2_z * random() + m1_z;
809 float g_pickup_shells;
810 float g_pickup_shells_max;
811 float g_pickup_nails;
812 float g_pickup_nails_max;
813 float g_pickup_rockets;
814 float g_pickup_rockets_max;
815 float g_pickup_cells;
816 float g_pickup_cells_max;
818 float g_pickup_fuel_jetpack;
819 float g_pickup_fuel_max;
820 float g_pickup_armorsmall;
821 float g_pickup_armorsmall_max;
822 float g_pickup_armormedium;
823 float g_pickup_armormedium_max;
824 float g_pickup_armorbig;
825 float g_pickup_armorbig_max;
826 float g_pickup_armorlarge;
827 float g_pickup_armorlarge_max;
828 float g_pickup_healthsmall;
829 float g_pickup_healthsmall_max;
830 float g_pickup_healthmedium;
831 float g_pickup_healthmedium_max;
832 float g_pickup_healthlarge;
833 float g_pickup_healthlarge_max;
834 float g_pickup_healthmega;
835 float g_pickup_healthmega_max;
837 float g_weaponarena_random;
838 string g_weaponarena_list;
839 float g_weaponspeedfactor;
840 float g_weaponratefactor;
841 float g_weapondamagefactor;
842 float g_weaponforcefactor;
843 float g_weaponspreadfactor;
847 float start_ammo_shells;
848 float start_ammo_nails;
849 float start_ammo_rockets;
850 float start_ammo_cells;
851 float start_ammo_fuel;
853 float start_armorvalue;
854 float warmup_start_weapons;
855 float warmup_start_ammo_shells;
856 float warmup_start_ammo_nails;
857 float warmup_start_ammo_rockets;
858 float warmup_start_ammo_cells;
859 float warmup_start_ammo_fuel;
860 float warmup_start_health;
861 float warmup_start_armorvalue;
865 entity get_weaponinfo(float w);
867 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
869 var float i = weaponinfo.weapon;
874 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
876 if (t < 0) // "default" weapon selection
878 if (g_lms || g_ca || allguns)
879 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
882 else if (g_race || g_cts)
883 t = (i == WEP_LASER);
885 t = 0; // weapon is set a few lines later
887 t = (i == WEP_LASER || i == WEP_SHOTGUN);
888 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
889 t |= (i == WEP_HOOK);
892 // we cannot disable porto in Nexball, we must force it
893 if(g_nexball && i == WEP_PORTO)
899 void readplayerstartcvars()
905 // initialize starting values for players
908 start_ammo_shells = 0;
909 start_ammo_nails = 0;
910 start_ammo_rockets = 0;
911 start_ammo_cells = 0;
912 start_health = cvar("g_balance_health_start");
913 start_armorvalue = cvar("g_balance_armor_start");
916 s = cvar_string("g_weaponarena");
922 g_weaponarena_list = "All Weapons";
923 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
925 e = get_weaponinfo(j);
926 g_weaponarena |= e.weapons;
927 weapon_action(e.weapon, WR_PRECACHE);
930 else if (s == "most")
932 g_weaponarena_list = "Most Weapons";
933 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
935 e = get_weaponinfo(j);
936 if (e.spawnflags & WEP_FLAG_NORMAL)
938 g_weaponarena |= e.weapons;
939 weapon_action(e.weapon, WR_PRECACHE);
943 else if (s == "none")
945 g_weaponarena_list = "No Weapons";
946 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
950 t = tokenize_console(s);
951 g_weaponarena_list = "";
952 for (i = 0; i < t; ++i)
955 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
957 e = get_weaponinfo(j);
960 g_weaponarena |= e.weapons;
961 weapon_action(e.weapon, WR_PRECACHE);
962 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
968 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
971 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
975 g_weaponarena_random = cvar("g_weaponarena_random");
977 g_weaponarena_random = 0;
981 start_weapons = g_weaponarena;
982 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
983 start_ammo_rockets = 999;
984 if (g_weaponarena & WEPBIT_SHOTGUN)
985 start_ammo_shells = 999;
986 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
987 start_ammo_cells = 999;
988 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
989 start_ammo_nails = 999;
990 if (g_weaponarena & WEPBIT_HOOK)
991 start_ammo_fuel = 999;
992 start_items |= IT_UNLIMITED_AMMO;
994 else if (g_minstagib)
997 start_armorvalue = 0;
998 start_weapons = WEPBIT_MINSTANEX;
999 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
1000 start_ammo_cells = cvar("g_minstagib_ammo_start");
1001 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
1002 start_ammo_fuel = cvar("g_start_ammo_fuel");
1004 if (g_minstagib_invis_alpha <= 0)
1005 g_minstagib_invis_alpha = -1;
1011 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1012 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1013 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1014 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1015 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1016 start_health = cvar("g_lms_start_health");
1017 start_armorvalue = cvar("g_lms_start_armor");
1021 start_ammo_shells = cvar("g_start_ammo_shells");
1022 start_ammo_nails = cvar("g_start_ammo_nails");
1023 start_ammo_rockets = cvar("g_start_ammo_rockets");
1024 start_ammo_cells = cvar("g_start_ammo_cells");
1025 start_ammo_fuel = cvar("g_start_ammo_fuel");
1028 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1030 e = get_weaponinfo(i);
1031 if(want_weapon("g_start_weapon_", e, FALSE))
1032 start_weapons |= e.weapons;
1038 warmup_start_ammo_shells = start_ammo_shells;
1039 warmup_start_ammo_nails = start_ammo_nails;
1040 warmup_start_ammo_rockets = start_ammo_rockets;
1041 warmup_start_ammo_cells = start_ammo_cells;
1042 warmup_start_ammo_fuel = start_ammo_fuel;
1043 warmup_start_health = start_health;
1044 warmup_start_armorvalue = start_armorvalue;
1045 warmup_start_weapons = start_weapons;
1047 if (!g_weaponarena && !g_minstagib && !g_ca)
1049 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1050 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1051 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1052 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1053 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1054 warmup_start_health = cvar("g_warmup_start_health");
1055 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1056 warmup_start_weapons = 0;
1057 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1059 e = get_weaponinfo(i);
1060 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1061 warmup_start_weapons |= e.weapons;
1066 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1068 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1069 start_items |= IT_FUEL_REGEN;
1070 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1071 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1074 if(!cvar("g_use_ammunition"))
1076 start_ammo_shells = cvar("g_pickup_shells_max");
1077 start_ammo_nails = cvar("g_pickup_nails_max");
1078 start_ammo_rockets = cvar("g_pickup_rockets_max");
1079 start_ammo_cells = cvar("g_pickup_cells_max");
1080 start_ammo_fuel = cvar("g_pickup_fuel_max");
1081 start_items |= IT_UNLIMITED_AMMO;
1082 warmup_start_ammo_shells = cvar("g_pickup_shells_max");
1083 warmup_start_ammo_nails = cvar("g_pickup_nails_max");
1084 warmup_start_ammo_rockets = cvar("g_pickup_rockets_max");
1085 warmup_start_ammo_cells = cvar("g_pickup_cells_max");
1086 warmup_start_ammo_fuel = cvar("g_pickup_fuel_max");
1087 //warmup_start_items |= IT_UNLIMITED_AMMO;
1091 start_items |= IT_JETPACK;
1093 if (g_weapon_stay == 2)
1095 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1096 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1097 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1098 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1099 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1100 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1101 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1102 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1103 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1104 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1107 MUTATOR_CALLHOOK(SetStartItems);
1109 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1111 e = get_weaponinfo(i);
1112 if(e.weapons & (start_weapons | warmup_start_weapons))
1113 weapon_action(e.weapon, WR_PRECACHE);
1116 start_ammo_shells = max(0, start_ammo_shells);
1117 start_ammo_nails = max(0, start_ammo_nails);
1118 start_ammo_cells = max(0, start_ammo_cells);
1119 start_ammo_rockets = max(0, start_ammo_rockets);
1120 start_ammo_fuel = max(0, start_ammo_fuel);
1122 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1123 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1124 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1125 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1126 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1130 float g_bugrigs_planar_movement;
1131 float g_bugrigs_planar_movement_car_jumping;
1132 float g_bugrigs_reverse_spinning;
1133 float g_bugrigs_reverse_speeding;
1134 float g_bugrigs_reverse_stopping;
1135 float g_bugrigs_air_steering;
1136 float g_bugrigs_angle_smoothing;
1137 float g_bugrigs_friction_floor;
1138 float g_bugrigs_friction_brake;
1139 float g_bugrigs_friction_air;
1140 float g_bugrigs_accel;
1141 float g_bugrigs_speed_ref;
1142 float g_bugrigs_speed_pow;
1143 float g_bugrigs_steer;
1145 float g_touchexplode;
1146 float g_touchexplode_radius;
1147 float g_touchexplode_damage;
1148 float g_touchexplode_edgedamage;
1149 float g_touchexplode_force;
1156 float sv_pitch_fixyaw;
1158 float sv_accuracy_data_share;
1160 void readlevelcvars(void)
1162 // first load all the mutators
1164 MUTATOR_ADD(mutator_nix);
1166 g_bugrigs = cvar("g_bugrigs");
1167 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1168 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1169 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1170 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1171 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1172 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1173 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1174 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1175 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1176 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1177 g_bugrigs_accel = cvar("g_bugrigs_accel");
1178 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1179 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1180 g_bugrigs_steer = cvar("g_bugrigs_steer");
1182 g_touchexplode = cvar("g_touchexplode");
1183 g_touchexplode_radius = cvar("g_touchexplode_radius");
1184 g_touchexplode_damage = cvar("g_touchexplode_damage");
1185 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1186 g_touchexplode_force = cvar("g_touchexplode_force");
1188 #ifdef ALLOW_FORCEMODELS
1189 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1191 sv_loddistance1 = cvar("sv_loddistance1");
1192 sv_loddistance2 = cvar("sv_loddistance2");
1194 if(sv_loddistance2 <= sv_loddistance1)
1195 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1197 sv_clones = cvar("sv_clones");
1198 sv_gentle = cvar("sv_gentle");
1199 sv_foginterval = cvar("sv_foginterval");
1200 g_cloaked = cvar("g_cloaked");
1201 g_jump_grunt = cvar("g_jump_grunt");
1202 g_footsteps = cvar("g_footsteps");
1203 g_grappling_hook = cvar("g_grappling_hook");
1204 g_jetpack = cvar("g_jetpack");
1205 g_laserguided_missile = cvar("g_laserguided_missile");
1206 g_midair = cvar("g_midair");
1207 g_minstagib = cvar("g_minstagib");
1208 g_norecoil = cvar("g_norecoil");
1209 g_vampire = cvar("g_vampire");
1210 g_bloodloss = cvar("g_bloodloss");
1211 sv_maxidle = cvar("sv_maxidle");
1212 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1213 sv_pogostick = cvar("sv_pogostick");
1214 sv_doublejump = cvar("sv_doublejump");
1215 g_ctf_reverse = cvar("g_ctf_reverse");
1216 sv_autotaunt = cvar("sv_autotaunt");
1217 sv_taunt = cvar("sv_taunt");
1219 inWarmupStage = cvar("g_warmup");
1220 g_warmup_limit = cvar("g_warmup_limit");
1221 g_warmup_allguns = cvar("g_warmup_allguns");
1222 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1224 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1225 inWarmupStage = 0; // these modes cannot work together, sorry
1227 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1228 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1229 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1230 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1231 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1232 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1233 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1234 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1235 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1236 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1237 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1238 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1240 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1241 g_weaponratefactor = cvar("g_weaponratefactor");
1242 g_weapondamagefactor = cvar("g_weapondamagefactor");
1243 g_weaponforcefactor = cvar("g_weaponforcefactor");
1244 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1246 g_pickup_shells = cvar("g_pickup_shells");
1247 g_pickup_shells_max = cvar("g_pickup_shells_max");
1248 g_pickup_nails = cvar("g_pickup_nails");
1249 g_pickup_nails_max = cvar("g_pickup_nails_max");
1250 g_pickup_rockets = cvar("g_pickup_rockets");
1251 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1252 g_pickup_cells = cvar("g_pickup_cells");
1253 g_pickup_cells_max = cvar("g_pickup_cells_max");
1254 g_pickup_fuel = cvar("g_pickup_fuel");
1255 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1256 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1257 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1258 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1259 g_pickup_armormedium = cvar("g_pickup_armormedium");
1260 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1261 g_pickup_armorbig = cvar("g_pickup_armorbig");
1262 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1263 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1264 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1265 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1266 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1267 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1268 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1269 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1270 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1271 g_pickup_healthmega = cvar("g_pickup_healthmega");
1272 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1274 g_pinata = cvar("g_pinata");
1276 g_weapon_stay = cvar("g_weapon_stay");
1278 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1281 g_ghost_items = cvar("g_ghost_items");
1283 if(g_ghost_items >= 1)
1284 g_ghost_items = 0.25; // default alpha value
1286 if not(inWarmupStage && !g_ca)
1287 game_starttime = cvar("g_start_delay");
1289 sv_pitch_min = cvar("sv_pitch_min");
1290 sv_pitch_max = cvar("sv_pitch_max");
1291 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1293 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1295 readplayerstartcvars();
1299 // TODO sound pack system
1302 string precache_sound_builtin (string s) = #19;
1303 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1304 string precache_sound(string s)
1306 return precache_sound_builtin(strcat(soundpack, s));
1308 void play2(entity e, string filename)
1310 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1312 void sound(entity e, float chan, string samp, float vol, float atten)
1314 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1319 string precache_sound (string s) = #19;
1320 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1321 float precache_sound_index (string s) = #19;
1323 #define SND_VOLUME 1
1324 #define SND_ATTENUATION 2
1325 #define SND_LARGEENTITY 8
1326 #define SND_LARGESOUND 16
1328 float sound_allowed(float dest, entity e)
1330 // sounds from world may always pass
1333 if (e.classname == "body")
1335 if (e.owner && e.owner != e)
1340 // sounds to self may always pass
1341 if (dest == MSG_ONE)
1342 if (e == msg_entity)
1344 // sounds by players can be removed
1345 if (cvar("bot_sound_monopoly"))
1346 if (clienttype(e) == CLIENTTYPE_REAL)
1348 // anything else may pass
1352 void sound(entity e, float chan, string samp, float vol, float atten)
1354 if (!sound_allowed(MSG_BROADCAST, e))
1356 sound_builtin(e, chan, samp, vol, atten);
1358 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1362 if (!sound_allowed(dest, e))
1365 entno = num_for_edict(e);
1366 idx = precache_sound_index(samp);
1371 atten = floor(atten * 64);
1372 vol = floor(vol * 255);
1375 sflags |= SND_VOLUME;
1377 sflags |= SND_ATTENUATION;
1379 sflags |= SND_LARGEENTITY;
1381 sflags |= SND_LARGESOUND;
1383 WriteByte(dest, SVC_SOUND);
1384 WriteByte(dest, sflags);
1385 if (sflags & SND_VOLUME)
1386 WriteByte(dest, vol);
1387 if (sflags & SND_ATTENUATION)
1388 WriteByte(dest, atten);
1389 if (sflags & SND_LARGEENTITY)
1391 WriteShort(dest, entno);
1392 WriteByte(dest, chan);
1396 WriteShort(dest, entno * 8 + chan);
1398 if (sflags & SND_LARGESOUND)
1399 WriteShort(dest, idx);
1401 WriteByte(dest, idx);
1403 WriteCoord(dest, o_x);
1404 WriteCoord(dest, o_y);
1405 WriteCoord(dest, o_z);
1407 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1411 if (!sound_allowed(dest, e))
1414 o = e.origin + 0.5 * (e.mins + e.maxs);
1415 soundtoat(dest, e, o, chan, samp, vol, atten);
1417 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1419 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1421 void stopsoundto(float dest, entity e, float chan)
1425 if (!sound_allowed(dest, e))
1428 entno = num_for_edict(e);
1433 idx = precache_sound_index("misc/null.wav");
1434 sflags = SND_LARGEENTITY;
1436 sflags |= SND_LARGESOUND;
1437 WriteByte(dest, SVC_SOUND);
1438 WriteByte(dest, sflags);
1439 WriteShort(dest, entno);
1440 WriteByte(dest, chan);
1441 if (sflags & SND_LARGESOUND)
1442 WriteShort(dest, idx);
1444 WriteByte(dest, idx);
1445 WriteCoord(dest, e.origin_x);
1446 WriteCoord(dest, e.origin_y);
1447 WriteCoord(dest, e.origin_z);
1451 WriteByte(dest, SVC_STOPSOUND);
1452 WriteShort(dest, entno * 8 + chan);
1455 void stopsound(entity e, float chan)
1457 if (!sound_allowed(MSG_BROADCAST, e))
1460 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1461 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1464 void play2(entity e, string filename)
1466 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1468 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1471 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1473 float spamsound(entity e, float chan, string samp, float vol, float atten)
1475 if (!sound_allowed(MSG_BROADCAST, e))
1478 if (time > e.spamtime)
1481 sound(e, chan, samp, vol, atten);
1487 void play2team(float t, string filename)
1491 if (cvar("bot_sound_monopoly"))
1494 FOR_EACH_REALPLAYER(head)
1497 play2(head, filename);
1501 void play2all(string samp)
1503 if (cvar("bot_sound_monopoly"))
1506 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1509 void PrecachePlayerSounds(string f);
1510 void precache_all_models(string pattern)
1512 float globhandle, i, n;
1515 globhandle = search_begin(pattern, TRUE, FALSE);
1518 n = search_getsize(globhandle);
1519 for (i = 0; i < n; ++i)
1521 //print(search_getfilename(globhandle, i), "\n");
1522 f = search_getfilename(globhandle, i);
1525 if(substring(f, -9,5) == "_lod1")
1527 if(substring(f, -9,5) == "_lod2")
1529 if(!sv_loddistance1)
1531 PrecachePlayerSounds(strcat(f, ".sounds"));
1533 search_end(globhandle);
1538 // gamemode related things
1539 precache_model ("models/misc/chatbubble.spr");
1540 precache_model ("models/misc/teambubble.spr");
1543 precache_model ("models/runematch/curse.mdl");
1544 precache_model ("models/runematch/rune.mdl");
1547 #ifdef TTURRETS_ENABLED
1548 if (cvar("g_turrets"))
1552 // Precache all player models if desired
1553 if (cvar("sv_precacheplayermodels"))
1555 PrecachePlayerSounds("sound/player/default.sounds");
1556 precache_all_models("models/player/*.zym");
1557 precache_all_models("models/player/*.dpm");
1558 precache_all_models("models/player/*.md3");
1559 precache_all_models("models/player/*.psk");
1560 //precache_model("models/player/carni.zym");
1561 //precache_model("models/player/crash.zym");
1562 //precache_model("models/player/grunt.zym");
1563 //precache_model("models/player/headhunter.zym");
1564 //precache_model("models/player/insurrectionist.zym");
1565 //precache_model("models/player/jeandarc.zym");
1566 //precache_model("models/player/lurk.zym");
1567 //precache_model("models/player/lycanthrope.zym");
1568 //precache_model("models/player/marine.zym");
1569 //precache_model("models/player/nexus.zym");
1570 //precache_model("models/player/pyria.zym");
1571 //precache_model("models/player/shock.zym");
1572 //precache_model("models/player/skadi.zym");
1573 //precache_model("models/player/specop.zym");
1574 //precache_model("models/player/visitant.zym");
1577 if (cvar("sv_defaultcharacter"))
1580 s = cvar_string("sv_defaultplayermodel_red");
1584 PrecachePlayerSounds(strcat(s, ".sounds"));
1586 s = cvar_string("sv_defaultplayermodel_blue");
1590 PrecachePlayerSounds(strcat(s, ".sounds"));
1592 s = cvar_string("sv_defaultplayermodel_yellow");
1596 PrecachePlayerSounds(strcat(s, ".sounds"));
1598 s = cvar_string("sv_defaultplayermodel_pink");
1602 PrecachePlayerSounds(strcat(s, ".sounds"));
1604 s = cvar_string("sv_defaultplayermodel");
1608 PrecachePlayerSounds(strcat(s, ".sounds"));
1614 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1615 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1618 // gore and miscellaneous sounds
1619 //precache_sound ("misc/h2ohit.wav");
1620 precache_model ("models/hook.md3");
1621 precache_sound ("misc/armorimpact.wav");
1622 precache_sound ("misc/bodyimpact1.wav");
1623 precache_sound ("misc/bodyimpact2.wav");
1624 precache_sound ("misc/gib.wav");
1625 precache_sound ("misc/gib_splat01.wav");
1626 precache_sound ("misc/gib_splat02.wav");
1627 precache_sound ("misc/gib_splat03.wav");
1628 precache_sound ("misc/gib_splat04.wav");
1629 precache_sound ("misc/hit.wav");
1630 precache_sound ("misc/typehit.wav");
1631 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1632 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1633 precache_sound ("misc/null.wav");
1634 precache_sound ("misc/spawn.wav");
1635 precache_sound ("misc/talk.wav");
1636 precache_sound ("misc/teleport.wav");
1637 precache_sound ("misc/poweroff.wav");
1638 precache_sound ("player/lava.wav");
1639 precache_sound ("player/slime.wav");
1642 precache_sound ("misc/jetpack_fly.wav");
1644 precache_model ("models/sprites/0.spr32");
1645 precache_model ("models/sprites/1.spr32");
1646 precache_model ("models/sprites/2.spr32");
1647 precache_model ("models/sprites/3.spr32");
1648 precache_model ("models/sprites/4.spr32");
1649 precache_model ("models/sprites/5.spr32");
1650 precache_model ("models/sprites/6.spr32");
1651 precache_model ("models/sprites/7.spr32");
1652 precache_model ("models/sprites/8.spr32");
1653 precache_model ("models/sprites/9.spr32");
1654 precache_model ("models/sprites/10.spr32");
1656 // common weapon precaches
1657 precache_sound ("weapons/weapon_switch.wav");
1658 precache_sound ("weapons/weaponpickup.wav");
1659 precache_sound ("weapons/unavailable.wav");
1660 if (g_grappling_hook)
1662 precache_sound ("weapons/hook_fire.wav"); // hook
1663 precache_sound ("weapons/hook_impact.wav"); // hook
1666 if(cvar("sv_precacheweapons"))
1668 //precache weapon models/sounds
1671 while (wep <= WEP_LAST)
1673 weapon_action(wep, WR_PRECACHE);
1678 precache_model("models/elaser.mdl");
1679 precache_model("models/laser.mdl");
1680 precache_model("models/ebomb.mdl");
1683 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1685 if (!self.noise && self.music) // quake 3 uses the music field
1686 self.noise = self.music;
1688 // plays music for the level if there is any
1691 precache_sound (self.noise);
1692 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1697 // sorry, but using \ in macros breaks line numbers
1698 #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
1699 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1700 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1702 // WARNING: this kills the trace globals
1703 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1704 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1706 #define INITPRIO_FIRST 0
1707 #define INITPRIO_GAMETYPE 0
1708 #define INITPRIO_GAMETYPE_FALLBACK 1
1709 #define INITPRIO_CVARS 5
1710 #define INITPRIO_FINDTARGET 10
1711 #define INITPRIO_DROPTOFLOOR 20
1712 #define INITPRIO_SETLOCATION 90
1713 #define INITPRIO_LINKDOORS 91
1714 #define INITPRIO_LAST 99
1716 .void(void) initialize_entity;
1717 .float initialize_entity_order;
1718 .entity initialize_entity_next;
1719 entity initialize_entity_first;
1721 void make_safe_for_remove(entity e)
1723 if (e.initialize_entity)
1726 for (ent = initialize_entity_first; ent; )
1728 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1730 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1731 // skip it in linked list
1734 prev.initialize_entity_next = ent.initialize_entity_next;
1735 ent = prev.initialize_entity_next;
1739 initialize_entity_first = ent.initialize_entity_next;
1740 ent = initialize_entity_first;
1746 ent = ent.initialize_entity_next;
1752 void objerror(string s)
1754 make_safe_for_remove(self);
1755 objerror_builtin(s);
1758 void remove_unsafely(entity e)
1763 void remove_safely(entity e)
1765 make_safe_for_remove(e);
1769 void InitializeEntity(entity e, void(void) func, float order)
1773 if (!e || e.initialize_entity)
1775 // make a proxy initializer entity
1779 e.classname = "initialize_entity";
1783 e.initialize_entity = func;
1784 e.initialize_entity_order = order;
1786 cur = initialize_entity_first;
1789 if (!cur || cur.initialize_entity_order > order)
1791 // insert between prev and cur
1793 prev.initialize_entity_next = e;
1795 initialize_entity_first = e;
1796 e.initialize_entity_next = cur;
1800 cur = cur.initialize_entity_next;
1803 void InitializeEntitiesRun()
1806 startoflist = initialize_entity_first;
1807 initialize_entity_first = world;
1808 for (self = startoflist; self; )
1811 var void(void) func;
1812 e = self.initialize_entity_next;
1813 func = self.initialize_entity;
1814 self.initialize_entity_order = 0;
1815 self.initialize_entity = func_null;
1816 self.initialize_entity_next = world;
1817 if (self.classname == "initialize_entity")
1821 remove_builtin(self);
1824 //dprint("Delayed initialization: ", self.classname, "\n");
1830 .float uncustomizeentityforclient_set;
1831 .void(void) uncustomizeentityforclient;
1832 void(void) SUB_Nullpointer = #0;
1833 void UncustomizeEntitiesRun()
1837 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1838 self.uncustomizeentityforclient();
1841 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1843 e.customizeentityforclient = customizer;
1844 e.uncustomizeentityforclient = uncustomizer;
1845 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1849 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1852 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1856 if (e.classname == "")
1857 e.classname = "net_linked";
1859 if (e.model == "" || self.modelindex == 0)
1863 setmodel(e, "null");
1867 e.SendEntity = sendfunc;
1868 e.SendFlags = 0xFFFFFF;
1871 e.effects |= EF_NODEPTHTEST;
1875 e.nextthink = time + dt;
1876 e.think = SUB_Remove;
1880 void adaptor_think2touch()
1889 void adaptor_think2use()
1901 // deferred dropping
1902 void DropToFloor_Handler()
1904 droptofloor_builtin();
1905 self.dropped_origin = self.origin;
1910 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1915 float trace_hits_box_a0, trace_hits_box_a1;
1917 float trace_hits_box_1d(float end, float thmi, float thma)
1921 // just check if x is in range
1929 // do the trace with respect to x
1930 // 0 -> end has to stay in thmi -> thma
1931 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1932 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1933 if (trace_hits_box_a0 > trace_hits_box_a1)
1939 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1944 // now it is a trace from 0 to end
1946 trace_hits_box_a0 = 0;
1947 trace_hits_box_a1 = 1;
1949 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1951 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1953 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1959 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1961 return trace_hits_box(start, end, thmi - ma, thma - mi);
1964 float SUB_NoImpactCheck()
1966 // zero hitcontents = this is not the real impact, but either the
1967 // mirror-impact of something hitting the projectile instead of the
1968 // projectile hitting the something, or a touchareagrid one. Neither of
1969 // these stop the projectile from moving, so...
1970 if(trace_dphitcontents == 0)
1972 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1975 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1977 if (other == world && self.size != '0 0 0')
1980 tic = self.velocity * sys_frametime;
1981 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1982 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1983 if (trace_fraction >= 1)
1985 dprint("Odd... did not hit...?\n");
1987 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1989 dprint("Detected and prevented the sky-grapple bug.\n");
1997 #define SUB_OwnerCheck() (other && (other == self.owner))
1999 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
2001 if(SUB_OwnerCheck())
2003 if(SUB_NoImpactCheck())
2008 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2009 UpdateCSQCProjectileNextFrame(self);
2012 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2014 float MAX_IPBAN_URIS = 16;
2016 float URI_GET_DISCARD = 0;
2017 float URI_GET_IPBAN = 1;
2018 float URI_GET_IPBAN_END = 16;
2020 void URI_Get_Callback(float id, float status, string data)
2022 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2024 dprint("\nEnd of data.\n");
2026 if (id == URI_GET_DISCARD)
2030 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2033 OnlineBanList_URI_Get_Callback(id, status, data);
2037 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2041 void print_to(entity e, string s)
2044 sprint(e, strcat(s, "\n"));
2049 string getrecords(float page) // 50 records per page
2063 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2065 if (MapInfo_Get_ByID(i))
2067 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2070 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2071 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2079 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2081 if (MapInfo_Get_ByID(i))
2083 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2086 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2087 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2095 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2097 if (MapInfo_Get_ByID(i))
2099 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2102 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2103 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2109 MapInfo_ClearTemps();
2111 if (s == "" && page == 0)
2112 return "No records are available on this server.\n";
2117 string getrankings()
2130 for (i = 1; i <= RANKINGS_CNT; ++i)
2132 t = race_GetTime(i);
2135 n = race_GetName(i);
2136 p = race_PlaceName(i);
2137 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2140 MapInfo_ClearTemps();
2143 return strcat("No records are available for the map: ", map, "\n");
2145 return strcat("Records for ", map, ":\n", s);
2148 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2151 vector start, org, delta, end, enddown, mstart;
2153 m = e.dphitcontentsmask;
2154 e.dphitcontentsmask = goodcontents | badcontents;
2157 delta = world.maxs - world.mins;
2159 for (i = 0; i < attempts; ++i)
2161 start_x = org_x + random() * delta_x;
2162 start_y = org_y + random() * delta_y;
2163 start_z = org_z + random() * delta_z;
2165 // rule 1: start inside world bounds, and outside
2166 // solid, and don't start from somewhere where you can
2167 // fall down to evil
2168 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2169 if (trace_fraction >= 1)
2171 if (trace_startsolid)
2173 if (trace_dphitcontents & badcontents)
2175 if (trace_dphitq3surfaceflags & badsurfaceflags)
2178 // rule 2: if we are too high, lower the point
2179 if (trace_fraction * delta_z > maxaboveground)
2180 start = trace_endpos + '0 0 1' * maxaboveground;
2181 enddown = trace_endpos;
2183 // rule 3: make sure we aren't outside the map. This only works
2184 // for somewhat well formed maps. A good rule of thumb is that
2185 // the map should have a convex outside hull.
2186 // these can be traceLINES as we already verified the starting box
2187 mstart = start + 0.5 * (e.mins + e.maxs);
2188 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2189 if (trace_fraction >= 1)
2191 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2192 if (trace_fraction >= 1)
2194 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2195 if (trace_fraction >= 1)
2197 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2198 if (trace_fraction >= 1)
2200 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2201 if (trace_fraction >= 1)
2204 // find a random vector to "look at"
2205 end_x = org_x + random() * delta_x;
2206 end_y = org_y + random() * delta_y;
2207 end_z = org_z + random() * delta_z;
2208 end = start + normalize(end - start) * vlen(delta);
2210 // rule 4: start TO end must not be too short
2211 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2212 if (trace_startsolid)
2214 if (trace_fraction < minviewdistance / vlen(delta))
2217 // rule 5: don't want to look at sky
2218 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2221 // rule 6: we must not end up in trigger_hurt
2222 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2224 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2231 e.dphitcontentsmask = m;
2235 setorigin(e, start);
2236 e.angles = vectoangles(end - start);
2237 dprint("Needed ", ftos(i + 1), " attempts\n");
2244 float zcurveparticles_effectno;
2245 vector zcurveparticles_start;
2246 float zcurveparticles_spd;
2248 void endzcurveparticles()
2250 if(zcurveparticles_effectno)
2253 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2255 zcurveparticles_effectno = 0;
2258 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2260 spd = bound(0, floor(spd / 16), 32767);
2261 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2263 endzcurveparticles();
2264 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2265 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2266 WriteShort(MSG_BROADCAST, effectno);
2267 WriteCoord(MSG_BROADCAST, start_x);
2268 WriteCoord(MSG_BROADCAST, start_y);
2269 WriteCoord(MSG_BROADCAST, start_z);
2270 zcurveparticles_effectno = effectno;
2271 zcurveparticles_start = start;
2274 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2275 WriteCoord(MSG_BROADCAST, end_x);
2276 WriteCoord(MSG_BROADCAST, end_y);
2277 WriteCoord(MSG_BROADCAST, end_z);
2278 WriteCoord(MSG_BROADCAST, end_dz);
2279 zcurveparticles_spd = spd;
2282 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2285 vector vecxy, velxy;
2287 vecxy = end - start;
2292 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2294 endzcurveparticles();
2295 trailparticles(world, effectno, start, end);
2299 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2300 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2303 string GetGametype(); // g_world.qc
2304 void write_recordmarker(entity pl, float tstart, float dt)
2306 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2308 // also write a marker into demo files for demotc-race-record-extractor to find
2311 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2312 " ", ftos(tstart), " ", ftos(dt), "\n"));
2315 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2317 switch(self.owner.cvar_cl_gunalign)
2328 if(allowcenter) // 2: allow center handedness
2341 if(allowcenter) // 2: allow center handedness
2357 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2362 if (cvar("g_shootfromeye"))
2366 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2374 else if (cvar("g_shootfromcenter"))
2378 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2386 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2396 else if (cvar("g_shootfromclient"))
2398 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2405 void attach_sameorigin(entity e, entity to, string tag)
2407 vector org, t_forward, t_left, t_up, e_forward, e_up;
2414 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2415 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2416 t_forward = v_forward * tagscale;
2417 t_left = v_right * -tagscale;
2418 t_up = v_up * tagscale;
2420 e.origin_x = org * t_forward;
2421 e.origin_y = org * t_left;
2422 e.origin_z = org * t_up;
2424 // current forward and up directions
2425 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2426 e.angles = AnglesTransform_FromVAngles(e.angles);
2428 e.angles = AnglesTransform_FromAngles(e.angles);
2429 fixedmakevectors(e.angles);
2431 // untransform forward, up!
2432 e_forward_x = v_forward * t_forward;
2433 e_forward_y = v_forward * t_left;
2434 e_forward_z = v_forward * t_up;
2435 e_up_x = v_up * t_forward;
2436 e_up_y = v_up * t_left;
2437 e_up_z = v_up * t_up;
2439 e.angles = fixedvectoangles2(e_forward, e_up);
2440 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2441 e.angles = AnglesTransform_ToVAngles(e.angles);
2443 e.angles = AnglesTransform_ToAngles(e.angles);
2445 setattachment(e, to, tag);
2446 setorigin(e, e.origin);
2449 void detach_sameorigin(entity e)
2452 org = gettaginfo(e, 0);
2453 e.angles = fixedvectoangles2(v_forward, v_up);
2454 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2455 e.angles = AnglesTransform_ToVAngles(e.angles);
2457 e.angles = AnglesTransform_ToAngles(e.angles);
2459 setattachment(e, world, "");
2460 setorigin(e, e.origin);
2463 void follow_sameorigin(entity e, entity to)
2465 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2466 e.aiment = to; // make the hole follow bmodel
2467 e.punchangle = to.angles; // the original angles of bmodel
2468 e.view_ofs = e.origin - to.origin; // relative origin
2469 e.v_angle = e.angles - to.angles; // relative angles
2472 void unfollow_sameorigin(entity e)
2474 e.movetype = MOVETYPE_NONE;
2477 entity gettaginfo_relative_ent;
2478 vector gettaginfo_relative(entity e, float tag)
2480 if (!gettaginfo_relative_ent)
2482 gettaginfo_relative_ent = spawn();
2483 gettaginfo_relative_ent.effects = EF_NODRAW;
2485 gettaginfo_relative_ent.model = e.model;
2486 gettaginfo_relative_ent.modelindex = e.modelindex;
2487 gettaginfo_relative_ent.frame = e.frame;
2488 return gettaginfo(gettaginfo_relative_ent, tag);
2491 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2495 if (pl.soundentity.cnt & p)
2497 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2498 pl.soundentity.cnt |= p;
2501 void SoundEntity_StopSound(entity pl, float chan)
2505 if (pl.soundentity.cnt & p)
2507 stopsoundto(MSG_ALL, pl.soundentity, chan);
2508 pl.soundentity.cnt &~= p;
2512 void SoundEntity_Attach(entity pl)
2514 pl.soundentity = spawn();
2515 pl.soundentity.classname = "soundentity";
2516 pl.soundentity.owner = pl;
2517 setattachment(pl.soundentity, pl, "");
2518 setmodel(pl.soundentity, "null");
2521 void SoundEntity_Detach(entity pl)
2524 for (i = 0; i <= 7; ++i)
2525 SoundEntity_StopSound(pl, i);
2529 float ParseCommandPlayerSlotTarget_firsttoken;
2530 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2538 ParseCommandPlayerSlotTarget_firsttoken = -1;
2542 if (substring(argv(idx), 0, 1) == "#")
2544 s = substring(argv(idx), 1, -1);
2552 ParseCommandPlayerSlotTarget_firsttoken = idx;
2553 if (s == ftos(stof(s)))
2555 e = edict_num(stof(s));
2556 if (e.flags & FL_CLIENT)
2562 // it must be a nick name
2565 ParseCommandPlayerSlotTarget_firsttoken = idx;
2568 FOR_EACH_CLIENT(head)
2569 if (head.netname == s)
2577 s = strdecolorize(s);
2579 FOR_EACH_CLIENT(head)
2580 if (strdecolorize(head.netname) == s)
2595 float modeleffect_SendEntity(entity to, float sf)
2598 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2601 if(self.velocity != '0 0 0')
2603 if(self.angles != '0 0 0')
2605 if(self.avelocity != '0 0 0')
2608 WriteByte(MSG_ENTITY, f);
2609 WriteShort(MSG_ENTITY, self.modelindex);
2610 WriteByte(MSG_ENTITY, self.skin);
2611 WriteByte(MSG_ENTITY, self.frame);
2612 WriteCoord(MSG_ENTITY, self.origin_x);
2613 WriteCoord(MSG_ENTITY, self.origin_y);
2614 WriteCoord(MSG_ENTITY, self.origin_z);
2617 WriteCoord(MSG_ENTITY, self.velocity_x);
2618 WriteCoord(MSG_ENTITY, self.velocity_y);
2619 WriteCoord(MSG_ENTITY, self.velocity_z);
2623 WriteCoord(MSG_ENTITY, self.angles_x);
2624 WriteCoord(MSG_ENTITY, self.angles_y);
2625 WriteCoord(MSG_ENTITY, self.angles_z);
2629 WriteCoord(MSG_ENTITY, self.avelocity_x);
2630 WriteCoord(MSG_ENTITY, self.avelocity_y);
2631 WriteCoord(MSG_ENTITY, self.avelocity_z);
2633 WriteShort(MSG_ENTITY, self.scale * 256.0);
2634 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2635 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2636 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2637 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2642 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)
2647 e.classname = "modeleffect";
2655 e.teleport_time = t1;
2659 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2663 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2666 sz = max(e.scale, e.scale2);
2667 setsize(e, e.mins * sz, e.maxs * sz);
2668 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2671 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2673 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2676 float randombit(float bits)
2678 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2687 for(f = 1; f <= bits; f *= 2)
2696 r = (r - 1) / (n - 1);
2703 float randombits(float bits, float k, float error_return)
2707 while(k > 0 && bits != r)
2709 r += randombit(bits - r);
2718 void randombit_test(float bits, float iter)
2722 print(ftos(randombit(bits)), "\n");
2727 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2729 if(halflifedist > 0)
2730 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2731 else if(halflifedist < 0)
2732 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2741 #define cvar_string_normal cvar_string_builtin
2742 #define cvar_normal cvar_builtin
2744 string cvar_string_normal(string n)
2746 if not(cvar_type(n) & 1)
2747 backtrace(strcat("Attempt to access undefined cvar: ", n));
2748 return cvar_string_builtin(n);
2751 float cvar_normal(string n)
2753 return stof(cvar_string_normal(n));
2756 #define cvar_set_normal cvar_set_builtin
2764 oself.think = SUB_Remove;
2765 oself.nextthink = time;
2771 Execute func() after time + fdelay.
2772 self when func is executed = self when defer is called
2774 void defer(float fdelay, void() func)
2781 e.think = defer_think;
2782 e.nextthink = time + fdelay;