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("cl_cmd 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("cl_cmd 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("cl_cmd 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)
598 s = strcat1(argv(f));
602 MUTATOR_CALLHOOK(GetCvars);
603 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
604 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
605 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
606 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
607 GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
608 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
609 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
610 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
611 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
612 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
613 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
614 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
615 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
616 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
617 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
618 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
619 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
620 GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
621 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
622 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
623 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
624 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
625 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
626 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
627 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
629 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
630 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
632 #ifdef ALLOW_FORCEMODELS
633 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
634 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
636 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
638 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
641 if (s == "cl_weaponpriority")
642 self.switchweapon = w_getbestweapon(self);
646 float fexists(string f)
649 fh = fopen(f, FILE_READ);
656 void backtrace(string msg)
659 dev = cvar("developer");
660 war = cvar("prvm_backtraceforwarnings");
661 cvar_set("developer", "1");
662 cvar_set("prvm_backtraceforwarnings", "1");
664 print("--- CUT HERE ---\nWARNING: ");
667 remove(world); // isn't there any better way to cause a backtrace?
668 print("\n--- CUT UNTIL HERE ---\n");
669 cvar_set("developer", ftos(dev));
670 cvar_set("prvm_backtraceforwarnings", ftos(war));
673 string Team_ColorCode(float teamid)
675 if (teamid == COLOR_TEAM1)
677 else if (teamid == COLOR_TEAM2)
679 else if (teamid == COLOR_TEAM3)
681 else if (teamid == COLOR_TEAM4)
687 string Team_ColorName(float t)
689 // fixme: Search for team entities and get their .netname's!
690 if (t == COLOR_TEAM1)
692 if (t == COLOR_TEAM2)
694 if (t == COLOR_TEAM3)
696 if (t == COLOR_TEAM4)
701 string Team_ColorNameLowerCase(float t)
703 // fixme: Search for team entities and get their .netname's!
704 if (t == COLOR_TEAM1)
706 if (t == COLOR_TEAM2)
708 if (t == COLOR_TEAM3)
710 if (t == COLOR_TEAM4)
715 float ColourToNumber(string team_colour)
717 if (team_colour == "red")
720 if (team_colour == "blue")
723 if (team_colour == "yellow")
726 if (team_colour == "pink")
729 if (team_colour == "auto")
735 float NumberToTeamNumber(float number)
752 #define CENTERPRIO_POINT 1
753 #define CENTERPRIO_SPAM 2
754 #define CENTERPRIO_VOTE 4
755 #define CENTERPRIO_NORMAL 5
756 #define CENTERPRIO_SHIELDING 7
757 #define CENTERPRIO_MAPVOTE 9
758 #define CENTERPRIO_IDLEKICK 50
759 #define CENTERPRIO_ADMIN 99
760 .float centerprint_priority;
761 .float centerprint_expires;
762 void centerprint_atprio(entity e, float prio, string s)
764 if (intermission_running)
765 if (prio < CENTERPRIO_MAPVOTE)
767 if (time > e.centerprint_expires)
768 e.centerprint_priority = 0;
769 if (prio >= e.centerprint_priority)
771 e.centerprint_priority = prio;
772 if (timeoutStatus == 2)
773 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
775 e.centerprint_expires = time + e.cvar_scr_centertime;
776 centerprint_builtin(e, s);
779 void centerprint_expire(entity e, float prio)
781 if (prio == e.centerprint_priority)
783 e.centerprint_priority = 0;
784 centerprint_builtin(e, "");
787 void centerprint(entity e, string s)
789 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
792 // decolorizes and team colors the player name when needed
793 string playername(entity p)
796 if (teams_matter && !intermission_running && p.classname == "player")
798 t = Team_ColorCode(p.team);
799 return strcat(t, strdecolorize(p.netname));
805 vector randompos(vector m1, vector m2)
809 v_x = m2_x * random() + m1_x;
810 v_y = m2_y * random() + m1_y;
811 v_z = m2_z * random() + m1_z;
815 float g_pickup_shells;
816 float g_pickup_shells_max;
817 float g_pickup_nails;
818 float g_pickup_nails_max;
819 float g_pickup_rockets;
820 float g_pickup_rockets_max;
821 float g_pickup_cells;
822 float g_pickup_cells_max;
824 float g_pickup_fuel_jetpack;
825 float g_pickup_fuel_max;
826 float g_pickup_armorsmall;
827 float g_pickup_armorsmall_max;
828 float g_pickup_armorsmall_anyway;
829 float g_pickup_armormedium;
830 float g_pickup_armormedium_max;
831 float g_pickup_armormedium_anyway;
832 float g_pickup_armorbig;
833 float g_pickup_armorbig_max;
834 float g_pickup_armorbig_anyway;
835 float g_pickup_armorlarge;
836 float g_pickup_armorlarge_max;
837 float g_pickup_armorlarge_anyway;
838 float g_pickup_healthsmall;
839 float g_pickup_healthsmall_max;
840 float g_pickup_healthsmall_anyway;
841 float g_pickup_healthmedium;
842 float g_pickup_healthmedium_max;
843 float g_pickup_healthmedium_anyway;
844 float g_pickup_healthlarge;
845 float g_pickup_healthlarge_max;
846 float g_pickup_healthlarge_anyway;
847 float g_pickup_healthmega;
848 float g_pickup_healthmega_max;
849 float g_pickup_healthmega_anyway;
851 float g_weaponarena_random;
852 string g_weaponarena_list;
853 float g_weaponspeedfactor;
854 float g_weaponratefactor;
855 float g_weapondamagefactor;
856 float g_weaponforcefactor;
857 float g_weaponspreadfactor;
861 float start_ammo_shells;
862 float start_ammo_nails;
863 float start_ammo_rockets;
864 float start_ammo_cells;
865 float start_ammo_fuel;
867 float start_armorvalue;
868 float warmup_start_weapons;
869 float warmup_start_ammo_shells;
870 float warmup_start_ammo_nails;
871 float warmup_start_ammo_rockets;
872 float warmup_start_ammo_cells;
873 float warmup_start_ammo_fuel;
874 float warmup_start_health;
875 float warmup_start_armorvalue;
879 entity get_weaponinfo(float w);
881 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
883 var float i = weaponinfo.weapon;
888 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
890 if (t < 0) // "default" weapon selection
892 if (g_lms || g_ca || allguns)
893 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
896 else if (g_race || g_cts)
897 t = (i == WEP_LASER);
899 t = 0; // weapon is set a few lines later
901 t = (i == WEP_LASER || i == WEP_SHOTGUN);
902 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
903 t |= (i == WEP_HOOK);
906 // we cannot disable porto in Nexball, we must force it
907 if(g_nexball && i == WEP_PORTO)
913 void readplayerstartcvars()
919 // initialize starting values for players
922 start_ammo_shells = 0;
923 start_ammo_nails = 0;
924 start_ammo_rockets = 0;
925 start_ammo_cells = 0;
926 start_health = cvar("g_balance_health_start");
927 start_armorvalue = cvar("g_balance_armor_start");
930 s = cvar_string("g_weaponarena");
936 g_weaponarena_list = "All Weapons";
937 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
939 e = get_weaponinfo(j);
940 g_weaponarena |= e.weapons;
941 weapon_action(e.weapon, WR_PRECACHE);
944 else if (s == "most")
946 g_weaponarena_list = "Most Weapons";
947 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
949 e = get_weaponinfo(j);
950 if (e.spawnflags & WEP_FLAG_NORMAL)
952 g_weaponarena |= e.weapons;
953 weapon_action(e.weapon, WR_PRECACHE);
957 else if (s == "none")
959 g_weaponarena_list = "No Weapons";
960 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
964 t = tokenize_console(s);
965 g_weaponarena_list = "";
966 for (i = 0; i < t; ++i)
969 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
971 e = get_weaponinfo(j);
974 g_weaponarena |= e.weapons;
975 weapon_action(e.weapon, WR_PRECACHE);
976 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
982 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
985 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
989 g_weaponarena_random = cvar("g_weaponarena_random");
991 g_weaponarena_random = 0;
995 start_weapons = g_weaponarena;
996 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
997 start_ammo_rockets = 999;
998 if (g_weaponarena & WEPBIT_SHOTGUN)
999 start_ammo_shells = 999;
1000 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
1001 start_ammo_cells = 999;
1002 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
1003 start_ammo_nails = 999;
1004 if (g_weaponarena & WEPBIT_HOOK)
1005 start_ammo_fuel = 999;
1006 start_items |= IT_UNLIMITED_AMMO;
1008 else if (g_minstagib)
1011 start_armorvalue = 0;
1012 start_weapons = WEPBIT_MINSTANEX;
1013 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
1014 start_ammo_cells = cvar("g_minstagib_ammo_start");
1015 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
1016 start_ammo_fuel = cvar("g_start_ammo_fuel");
1018 if (g_minstagib_invis_alpha <= 0)
1019 g_minstagib_invis_alpha = -1;
1025 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1026 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1027 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1028 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1029 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1030 start_health = cvar("g_lms_start_health");
1031 start_armorvalue = cvar("g_lms_start_armor");
1035 start_ammo_shells = cvar("g_start_ammo_shells");
1036 start_ammo_nails = cvar("g_start_ammo_nails");
1037 start_ammo_rockets = cvar("g_start_ammo_rockets");
1038 start_ammo_cells = cvar("g_start_ammo_cells");
1039 start_ammo_fuel = cvar("g_start_ammo_fuel");
1042 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1044 e = get_weaponinfo(i);
1045 if(want_weapon("g_start_weapon_", e, FALSE))
1046 start_weapons |= e.weapons;
1052 warmup_start_ammo_shells = start_ammo_shells;
1053 warmup_start_ammo_nails = start_ammo_nails;
1054 warmup_start_ammo_rockets = start_ammo_rockets;
1055 warmup_start_ammo_cells = start_ammo_cells;
1056 warmup_start_ammo_fuel = start_ammo_fuel;
1057 warmup_start_health = start_health;
1058 warmup_start_armorvalue = start_armorvalue;
1059 warmup_start_weapons = start_weapons;
1061 if (!g_weaponarena && !g_minstagib && !g_ca)
1063 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1064 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1065 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1066 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1067 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1068 warmup_start_health = cvar("g_warmup_start_health");
1069 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1070 warmup_start_weapons = 0;
1071 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1073 e = get_weaponinfo(i);
1074 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1075 warmup_start_weapons |= e.weapons;
1080 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1082 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1083 start_items |= IT_FUEL_REGEN;
1084 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1085 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1088 if(!cvar("g_use_ammunition"))
1090 start_ammo_shells = cvar("g_pickup_shells_max");
1091 start_ammo_nails = cvar("g_pickup_nails_max");
1092 start_ammo_rockets = cvar("g_pickup_rockets_max");
1093 start_ammo_cells = cvar("g_pickup_cells_max");
1094 start_ammo_fuel = cvar("g_pickup_fuel_max");
1095 start_items |= IT_UNLIMITED_AMMO;
1096 warmup_start_ammo_shells = cvar("g_pickup_shells_max");
1097 warmup_start_ammo_nails = cvar("g_pickup_nails_max");
1098 warmup_start_ammo_rockets = cvar("g_pickup_rockets_max");
1099 warmup_start_ammo_cells = cvar("g_pickup_cells_max");
1100 warmup_start_ammo_fuel = cvar("g_pickup_fuel_max");
1101 //warmup_start_items |= IT_UNLIMITED_AMMO;
1105 start_items |= IT_JETPACK;
1107 if (g_weapon_stay == 2)
1109 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1110 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1111 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1112 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1113 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1114 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1115 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1116 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1117 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1118 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1121 MUTATOR_CALLHOOK(SetStartItems);
1123 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1125 e = get_weaponinfo(i);
1126 if(e.weapons & (start_weapons | warmup_start_weapons))
1127 weapon_action(e.weapon, WR_PRECACHE);
1130 start_ammo_shells = max(0, start_ammo_shells);
1131 start_ammo_nails = max(0, start_ammo_nails);
1132 start_ammo_cells = max(0, start_ammo_cells);
1133 start_ammo_rockets = max(0, start_ammo_rockets);
1134 start_ammo_fuel = max(0, start_ammo_fuel);
1136 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1137 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1138 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1139 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1140 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1144 float g_bugrigs_planar_movement;
1145 float g_bugrigs_planar_movement_car_jumping;
1146 float g_bugrigs_reverse_spinning;
1147 float g_bugrigs_reverse_speeding;
1148 float g_bugrigs_reverse_stopping;
1149 float g_bugrigs_air_steering;
1150 float g_bugrigs_angle_smoothing;
1151 float g_bugrigs_friction_floor;
1152 float g_bugrigs_friction_brake;
1153 float g_bugrigs_friction_air;
1154 float g_bugrigs_accel;
1155 float g_bugrigs_speed_ref;
1156 float g_bugrigs_speed_pow;
1157 float g_bugrigs_steer;
1159 float g_touchexplode;
1160 float g_touchexplode_radius;
1161 float g_touchexplode_damage;
1162 float g_touchexplode_edgedamage;
1163 float g_touchexplode_force;
1170 float sv_pitch_fixyaw;
1172 float sv_accuracy_data_share;
1174 void readlevelcvars(void)
1176 // first load all the mutators
1178 MUTATOR_ADD(mutator_nix);
1180 g_bugrigs = cvar("g_bugrigs");
1181 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1182 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1183 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1184 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1185 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1186 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1187 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1188 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1189 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1190 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1191 g_bugrigs_accel = cvar("g_bugrigs_accel");
1192 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1193 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1194 g_bugrigs_steer = cvar("g_bugrigs_steer");
1196 g_touchexplode = cvar("g_touchexplode");
1197 g_touchexplode_radius = cvar("g_touchexplode_radius");
1198 g_touchexplode_damage = cvar("g_touchexplode_damage");
1199 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1200 g_touchexplode_force = cvar("g_touchexplode_force");
1202 #ifdef ALLOW_FORCEMODELS
1203 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1205 sv_loddistance1 = cvar("sv_loddistance1");
1206 sv_loddistance2 = cvar("sv_loddistance2");
1208 if(sv_loddistance2 <= sv_loddistance1)
1209 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1211 sv_clones = cvar("sv_clones");
1212 sv_gentle = cvar("sv_gentle");
1213 sv_foginterval = cvar("sv_foginterval");
1214 g_cloaked = cvar("g_cloaked");
1215 g_jump_grunt = cvar("g_jump_grunt");
1216 g_footsteps = cvar("g_footsteps");
1217 g_grappling_hook = cvar("g_grappling_hook");
1218 g_jetpack = cvar("g_jetpack");
1219 g_laserguided_missile = cvar("g_laserguided_missile");
1220 g_midair = cvar("g_midair");
1221 g_minstagib = cvar("g_minstagib");
1222 g_norecoil = cvar("g_norecoil");
1223 g_vampire = cvar("g_vampire");
1224 g_bloodloss = cvar("g_bloodloss");
1225 sv_maxidle = cvar("sv_maxidle");
1226 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1227 sv_pogostick = cvar("sv_pogostick");
1228 sv_doublejump = cvar("sv_doublejump");
1229 g_ctf_reverse = cvar("g_ctf_reverse");
1230 sv_autotaunt = cvar("sv_autotaunt");
1231 sv_taunt = cvar("sv_taunt");
1233 inWarmupStage = cvar("g_warmup");
1234 g_warmup_limit = cvar("g_warmup_limit");
1235 g_warmup_allguns = cvar("g_warmup_allguns");
1236 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1238 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1239 inWarmupStage = 0; // these modes cannot work together, sorry
1241 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1242 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1243 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1244 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1245 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1246 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1247 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1248 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1249 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1250 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1251 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1252 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1254 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1255 g_weaponratefactor = cvar("g_weaponratefactor");
1256 g_weapondamagefactor = cvar("g_weapondamagefactor");
1257 g_weaponforcefactor = cvar("g_weaponforcefactor");
1258 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1260 g_pickup_shells = cvar("g_pickup_shells");
1261 g_pickup_shells_max = cvar("g_pickup_shells_max");
1262 g_pickup_nails = cvar("g_pickup_nails");
1263 g_pickup_nails_max = cvar("g_pickup_nails_max");
1264 g_pickup_rockets = cvar("g_pickup_rockets");
1265 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1266 g_pickup_cells = cvar("g_pickup_cells");
1267 g_pickup_cells_max = cvar("g_pickup_cells_max");
1268 g_pickup_fuel = cvar("g_pickup_fuel");
1269 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1270 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1271 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1272 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1273 g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1274 g_pickup_armormedium = cvar("g_pickup_armormedium");
1275 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1276 g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1277 g_pickup_armorbig = cvar("g_pickup_armorbig");
1278 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1279 g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1280 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1281 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1282 g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1283 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1284 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1285 g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1286 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1287 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1288 g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1289 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1290 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1291 g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1292 g_pickup_healthmega = cvar("g_pickup_healthmega");
1293 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1294 g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1296 g_pinata = cvar("g_pinata");
1298 g_weapon_stay = cvar("g_weapon_stay");
1300 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1303 g_ghost_items = cvar("g_ghost_items");
1305 if(g_ghost_items >= 1)
1306 g_ghost_items = 0.25; // default alpha value
1308 if not(inWarmupStage && !g_ca)
1309 game_starttime = cvar("g_start_delay");
1311 sv_pitch_min = cvar("sv_pitch_min");
1312 sv_pitch_max = cvar("sv_pitch_max");
1313 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1315 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1317 readplayerstartcvars();
1321 // TODO sound pack system
1324 string precache_sound_builtin (string s) = #19;
1325 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1326 string precache_sound(string s)
1328 return precache_sound_builtin(strcat(soundpack, s));
1330 void play2(entity e, string filename)
1332 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1334 void sound(entity e, float chan, string samp, float vol, float atten)
1336 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1341 string precache_sound (string s) = #19;
1342 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1343 float precache_sound_index (string s) = #19;
1345 #define SND_VOLUME 1
1346 #define SND_ATTENUATION 2
1347 #define SND_LARGEENTITY 8
1348 #define SND_LARGESOUND 16
1350 float sound_allowed(float dest, entity e)
1352 // sounds from world may always pass
1355 if (e.classname == "body")
1357 if (e.owner && e.owner != e)
1362 // sounds to self may always pass
1363 if (dest == MSG_ONE)
1364 if (e == msg_entity)
1366 // sounds by players can be removed
1367 if (cvar("bot_sound_monopoly"))
1368 if (clienttype(e) == CLIENTTYPE_REAL)
1370 // anything else may pass
1374 void sound(entity e, float chan, string samp, float vol, float atten)
1376 if (!sound_allowed(MSG_BROADCAST, e))
1378 sound_builtin(e, chan, samp, vol, atten);
1380 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1384 if (!sound_allowed(dest, e))
1387 entno = num_for_edict(e);
1388 idx = precache_sound_index(samp);
1393 atten = floor(atten * 64);
1394 vol = floor(vol * 255);
1397 sflags |= SND_VOLUME;
1399 sflags |= SND_ATTENUATION;
1401 sflags |= SND_LARGEENTITY;
1403 sflags |= SND_LARGESOUND;
1405 WriteByte(dest, SVC_SOUND);
1406 WriteByte(dest, sflags);
1407 if (sflags & SND_VOLUME)
1408 WriteByte(dest, vol);
1409 if (sflags & SND_ATTENUATION)
1410 WriteByte(dest, atten);
1411 if (sflags & SND_LARGEENTITY)
1413 WriteShort(dest, entno);
1414 WriteByte(dest, chan);
1418 WriteShort(dest, entno * 8 + chan);
1420 if (sflags & SND_LARGESOUND)
1421 WriteShort(dest, idx);
1423 WriteByte(dest, idx);
1425 WriteCoord(dest, o_x);
1426 WriteCoord(dest, o_y);
1427 WriteCoord(dest, o_z);
1429 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1433 if (!sound_allowed(dest, e))
1436 o = e.origin + 0.5 * (e.mins + e.maxs);
1437 soundtoat(dest, e, o, chan, samp, vol, atten);
1439 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1441 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1443 void stopsoundto(float dest, entity e, float chan)
1447 if (!sound_allowed(dest, e))
1450 entno = num_for_edict(e);
1455 idx = precache_sound_index("misc/null.wav");
1456 sflags = SND_LARGEENTITY;
1458 sflags |= SND_LARGESOUND;
1459 WriteByte(dest, SVC_SOUND);
1460 WriteByte(dest, sflags);
1461 WriteShort(dest, entno);
1462 WriteByte(dest, chan);
1463 if (sflags & SND_LARGESOUND)
1464 WriteShort(dest, idx);
1466 WriteByte(dest, idx);
1467 WriteCoord(dest, e.origin_x);
1468 WriteCoord(dest, e.origin_y);
1469 WriteCoord(dest, e.origin_z);
1473 WriteByte(dest, SVC_STOPSOUND);
1474 WriteShort(dest, entno * 8 + chan);
1477 void stopsound(entity e, float chan)
1479 if (!sound_allowed(MSG_BROADCAST, e))
1482 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1483 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1486 void play2(entity e, string filename)
1488 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1490 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1493 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1495 float spamsound(entity e, float chan, string samp, float vol, float atten)
1497 if (!sound_allowed(MSG_BROADCAST, e))
1500 if (time > e.spamtime)
1503 sound(e, chan, samp, vol, atten);
1509 void play2team(float t, string filename)
1513 if (cvar("bot_sound_monopoly"))
1516 FOR_EACH_REALPLAYER(head)
1519 play2(head, filename);
1523 void play2all(string samp)
1525 if (cvar("bot_sound_monopoly"))
1528 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1531 void PrecachePlayerSounds(string f);
1532 void precache_playermodel(string m)
1534 float globhandle, i, n;
1537 if(substring(m, -9,5) == "_lod1")
1539 if(substring(m, -9,5) == "_lod2")
1544 precache_model(strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1)));
1545 precache_model(strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1)));
1548 globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1551 n = search_getsize(globhandle);
1552 for (i = 0; i < n; ++i)
1554 //print(search_getfilename(globhandle, i), "\n");
1555 f = search_getfilename(globhandle, i);
1556 PrecachePlayerSounds(f);
1558 search_end(globhandle);
1560 void precache_all_playermodels(string pattern)
1562 float globhandle, i, n;
1565 globhandle = search_begin(pattern, TRUE, FALSE);
1568 n = search_getsize(globhandle);
1569 for (i = 0; i < n; ++i)
1571 //print(search_getfilename(globhandle, i), "\n");
1572 f = search_getfilename(globhandle, i);
1573 precache_playermodel(f);
1575 search_end(globhandle);
1580 // gamemode related things
1581 precache_model ("models/misc/chatbubble.spr");
1582 precache_model ("models/misc/teambubble.spr");
1585 precache_model ("models/runematch/curse.mdl");
1586 precache_model ("models/runematch/rune.mdl");
1589 #ifdef TTURRETS_ENABLED
1590 if (cvar("g_turrets"))
1594 // Precache all player models if desired
1595 if (cvar("sv_precacheplayermodels"))
1597 PrecachePlayerSounds("sound/player/default.sounds");
1598 precache_all_playermodels("models/player/*.zym");
1599 precache_all_playermodels("models/player/*.dpm");
1600 precache_all_playermodels("models/player/*.md3");
1601 precache_all_playermodels("models/player/*.psk");
1602 precache_all_playermodels("models/player/*.iqm");
1605 if (cvar("sv_defaultcharacter"))
1608 s = cvar_string("sv_defaultplayermodel_red");
1610 precache_playermodel(s);
1611 s = cvar_string("sv_defaultplayermodel_blue");
1613 precache_playermodel(s);
1614 s = cvar_string("sv_defaultplayermodel_yellow");
1616 precache_playermodel(s);
1617 s = cvar_string("sv_defaultplayermodel_pink");
1619 precache_playermodel(s);
1620 s = cvar_string("sv_defaultplayermodel");
1622 precache_playermodel(s);
1627 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1628 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1631 // gore and miscellaneous sounds
1632 //precache_sound ("misc/h2ohit.wav");
1633 precache_model ("models/hook.md3");
1634 precache_sound ("misc/armorimpact.wav");
1635 precache_sound ("misc/bodyimpact1.wav");
1636 precache_sound ("misc/bodyimpact2.wav");
1637 precache_sound ("misc/gib.wav");
1638 precache_sound ("misc/gib_splat01.wav");
1639 precache_sound ("misc/gib_splat02.wav");
1640 precache_sound ("misc/gib_splat03.wav");
1641 precache_sound ("misc/gib_splat04.wav");
1642 precache_sound ("misc/hit.wav");
1643 precache_sound ("misc/typehit.wav");
1644 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1645 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1646 precache_sound ("misc/null.wav");
1647 precache_sound ("misc/spawn.wav");
1648 precache_sound ("misc/talk.wav");
1649 precache_sound ("misc/teleport.wav");
1650 precache_sound ("misc/poweroff.wav");
1651 precache_sound ("player/lava.wav");
1652 precache_sound ("player/slime.wav");
1655 precache_sound ("misc/jetpack_fly.wav");
1657 precache_model ("models/sprites/0.spr32");
1658 precache_model ("models/sprites/1.spr32");
1659 precache_model ("models/sprites/2.spr32");
1660 precache_model ("models/sprites/3.spr32");
1661 precache_model ("models/sprites/4.spr32");
1662 precache_model ("models/sprites/5.spr32");
1663 precache_model ("models/sprites/6.spr32");
1664 precache_model ("models/sprites/7.spr32");
1665 precache_model ("models/sprites/8.spr32");
1666 precache_model ("models/sprites/9.spr32");
1667 precache_model ("models/sprites/10.spr32");
1669 // common weapon precaches
1670 precache_sound ("weapons/weapon_switch.wav");
1671 precache_sound ("weapons/weaponpickup.wav");
1672 precache_sound ("weapons/unavailable.wav");
1673 if (g_grappling_hook)
1675 precache_sound ("weapons/hook_fire.wav"); // hook
1676 precache_sound ("weapons/hook_impact.wav"); // hook
1679 if(cvar("sv_precacheweapons"))
1681 //precache weapon models/sounds
1684 while (wep <= WEP_LAST)
1686 weapon_action(wep, WR_PRECACHE);
1691 precache_model("models/elaser.mdl");
1692 precache_model("models/laser.mdl");
1693 precache_model("models/ebomb.mdl");
1696 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1698 if (!self.noise && self.music) // quake 3 uses the music field
1699 self.noise = self.music;
1701 // plays music for the level if there is any
1704 precache_sound (self.noise);
1705 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1710 // sorry, but using \ in macros breaks line numbers
1711 #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
1712 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1713 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1715 // WARNING: this kills the trace globals
1716 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1717 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1719 #define INITPRIO_FIRST 0
1720 #define INITPRIO_GAMETYPE 0
1721 #define INITPRIO_GAMETYPE_FALLBACK 1
1722 #define INITPRIO_CVARS 5
1723 #define INITPRIO_FINDTARGET 10
1724 #define INITPRIO_DROPTOFLOOR 20
1725 #define INITPRIO_SETLOCATION 90
1726 #define INITPRIO_LINKDOORS 91
1727 #define INITPRIO_LAST 99
1729 .void(void) initialize_entity;
1730 .float initialize_entity_order;
1731 .entity initialize_entity_next;
1732 entity initialize_entity_first;
1734 void make_safe_for_remove(entity e)
1736 if (e.initialize_entity)
1739 for (ent = initialize_entity_first; ent; )
1741 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1743 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1744 // skip it in linked list
1747 prev.initialize_entity_next = ent.initialize_entity_next;
1748 ent = prev.initialize_entity_next;
1752 initialize_entity_first = ent.initialize_entity_next;
1753 ent = initialize_entity_first;
1759 ent = ent.initialize_entity_next;
1765 void objerror(string s)
1767 make_safe_for_remove(self);
1768 objerror_builtin(s);
1771 void remove_unsafely(entity e)
1776 void remove_safely(entity e)
1778 make_safe_for_remove(e);
1782 void InitializeEntity(entity e, void(void) func, float order)
1786 if (!e || e.initialize_entity)
1788 // make a proxy initializer entity
1792 e.classname = "initialize_entity";
1796 e.initialize_entity = func;
1797 e.initialize_entity_order = order;
1799 cur = initialize_entity_first;
1802 if (!cur || cur.initialize_entity_order > order)
1804 // insert between prev and cur
1806 prev.initialize_entity_next = e;
1808 initialize_entity_first = e;
1809 e.initialize_entity_next = cur;
1813 cur = cur.initialize_entity_next;
1816 void InitializeEntitiesRun()
1819 startoflist = initialize_entity_first;
1820 initialize_entity_first = world;
1821 for (self = startoflist; self; )
1824 var void(void) func;
1825 e = self.initialize_entity_next;
1826 func = self.initialize_entity;
1827 self.initialize_entity_order = 0;
1828 self.initialize_entity = func_null;
1829 self.initialize_entity_next = world;
1830 if (self.classname == "initialize_entity")
1834 remove_builtin(self);
1837 //dprint("Delayed initialization: ", self.classname, "\n");
1843 .float uncustomizeentityforclient_set;
1844 .void(void) uncustomizeentityforclient;
1845 void(void) SUB_Nullpointer = #0;
1846 void UncustomizeEntitiesRun()
1850 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1851 self.uncustomizeentityforclient();
1854 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1856 e.customizeentityforclient = customizer;
1857 e.uncustomizeentityforclient = uncustomizer;
1858 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1862 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1865 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1869 if (e.classname == "")
1870 e.classname = "net_linked";
1872 if (e.model == "" || self.modelindex == 0)
1876 setmodel(e, "null");
1880 e.SendEntity = sendfunc;
1881 e.SendFlags = 0xFFFFFF;
1884 e.effects |= EF_NODEPTHTEST;
1888 e.nextthink = time + dt;
1889 e.think = SUB_Remove;
1893 void adaptor_think2touch()
1902 void adaptor_think2use()
1914 // deferred dropping
1915 void DropToFloor_Handler()
1917 droptofloor_builtin();
1918 self.dropped_origin = self.origin;
1923 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1928 float trace_hits_box_a0, trace_hits_box_a1;
1930 float trace_hits_box_1d(float end, float thmi, float thma)
1934 // just check if x is in range
1942 // do the trace with respect to x
1943 // 0 -> end has to stay in thmi -> thma
1944 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1945 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1946 if (trace_hits_box_a0 > trace_hits_box_a1)
1952 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1957 // now it is a trace from 0 to end
1959 trace_hits_box_a0 = 0;
1960 trace_hits_box_a1 = 1;
1962 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1964 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1966 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1972 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1974 return trace_hits_box(start, end, thmi - ma, thma - mi);
1977 float SUB_NoImpactCheck()
1979 // zero hitcontents = this is not the real impact, but either the
1980 // mirror-impact of something hitting the projectile instead of the
1981 // projectile hitting the something, or a touchareagrid one. Neither of
1982 // these stop the projectile from moving, so...
1983 if(trace_dphitcontents == 0)
1985 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1988 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1990 if (other == world && self.size != '0 0 0')
1993 tic = self.velocity * sys_frametime;
1994 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1995 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1996 if (trace_fraction >= 1)
1998 dprint("Odd... did not hit...?\n");
2000 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2002 dprint("Detected and prevented the sky-grapple bug.\n");
2010 #define SUB_OwnerCheck() (other && (other == self.owner))
2012 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
2014 if(SUB_OwnerCheck())
2016 if(SUB_NoImpactCheck())
2021 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2022 UpdateCSQCProjectileNextFrame(self);
2025 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2027 float MAX_IPBAN_URIS = 16;
2029 float URI_GET_DISCARD = 0;
2030 float URI_GET_IPBAN = 1;
2031 float URI_GET_IPBAN_END = 16;
2033 void URI_Get_Callback(float id, float status, string data)
2035 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2037 dprint("\nEnd of data.\n");
2039 if (id == URI_GET_DISCARD)
2043 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2046 OnlineBanList_URI_Get_Callback(id, status, data);
2050 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2054 void print_to(entity e, string s)
2057 sprint(e, strcat(s, "\n"));
2062 string getrecords(float page) // 50 records per page
2076 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2078 if (MapInfo_Get_ByID(i))
2080 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2083 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2084 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2092 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2094 if (MapInfo_Get_ByID(i))
2096 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2099 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2100 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2108 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2110 if (MapInfo_Get_ByID(i))
2112 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2115 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2116 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2122 MapInfo_ClearTemps();
2124 if (s == "" && page == 0)
2125 return "No records are available on this server.\n";
2130 string getrankings()
2143 for (i = 1; i <= RANKINGS_CNT; ++i)
2145 t = race_GetTime(i);
2148 n = race_GetName(i);
2149 p = race_PlaceName(i);
2150 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2153 MapInfo_ClearTemps();
2156 return strcat("No records are available for the map: ", map, "\n");
2158 return strcat("Records for ", map, ":\n", s);
2161 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2164 vector start, org, delta, end, enddown, mstart;
2166 m = e.dphitcontentsmask;
2167 e.dphitcontentsmask = goodcontents | badcontents;
2170 delta = world.maxs - world.mins;
2172 for (i = 0; i < attempts; ++i)
2174 start_x = org_x + random() * delta_x;
2175 start_y = org_y + random() * delta_y;
2176 start_z = org_z + random() * delta_z;
2178 // rule 1: start inside world bounds, and outside
2179 // solid, and don't start from somewhere where you can
2180 // fall down to evil
2181 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2182 if (trace_fraction >= 1)
2184 if (trace_startsolid)
2186 if (trace_dphitcontents & badcontents)
2188 if (trace_dphitq3surfaceflags & badsurfaceflags)
2191 // rule 2: if we are too high, lower the point
2192 if (trace_fraction * delta_z > maxaboveground)
2193 start = trace_endpos + '0 0 1' * maxaboveground;
2194 enddown = trace_endpos;
2196 // rule 3: make sure we aren't outside the map. This only works
2197 // for somewhat well formed maps. A good rule of thumb is that
2198 // the map should have a convex outside hull.
2199 // these can be traceLINES as we already verified the starting box
2200 mstart = start + 0.5 * (e.mins + e.maxs);
2201 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2202 if (trace_fraction >= 1)
2204 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2205 if (trace_fraction >= 1)
2207 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2208 if (trace_fraction >= 1)
2210 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2211 if (trace_fraction >= 1)
2213 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2214 if (trace_fraction >= 1)
2217 // find a random vector to "look at"
2218 end_x = org_x + random() * delta_x;
2219 end_y = org_y + random() * delta_y;
2220 end_z = org_z + random() * delta_z;
2221 end = start + normalize(end - start) * vlen(delta);
2223 // rule 4: start TO end must not be too short
2224 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2225 if (trace_startsolid)
2227 if (trace_fraction < minviewdistance / vlen(delta))
2230 // rule 5: don't want to look at sky
2231 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2234 // rule 6: we must not end up in trigger_hurt
2235 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2237 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2244 e.dphitcontentsmask = m;
2248 setorigin(e, start);
2249 e.angles = vectoangles(end - start);
2250 dprint("Needed ", ftos(i + 1), " attempts\n");
2257 float zcurveparticles_effectno;
2258 vector zcurveparticles_start;
2259 float zcurveparticles_spd;
2261 void endzcurveparticles()
2263 if(zcurveparticles_effectno)
2266 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2268 zcurveparticles_effectno = 0;
2271 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2273 spd = bound(0, floor(spd / 16), 32767);
2274 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2276 endzcurveparticles();
2277 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2278 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2279 WriteShort(MSG_BROADCAST, effectno);
2280 WriteCoord(MSG_BROADCAST, start_x);
2281 WriteCoord(MSG_BROADCAST, start_y);
2282 WriteCoord(MSG_BROADCAST, start_z);
2283 zcurveparticles_effectno = effectno;
2284 zcurveparticles_start = start;
2287 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2288 WriteCoord(MSG_BROADCAST, end_x);
2289 WriteCoord(MSG_BROADCAST, end_y);
2290 WriteCoord(MSG_BROADCAST, end_z);
2291 WriteCoord(MSG_BROADCAST, end_dz);
2292 zcurveparticles_spd = spd;
2295 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2298 vector vecxy, velxy;
2300 vecxy = end - start;
2305 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2307 endzcurveparticles();
2308 trailparticles(world, effectno, start, end);
2312 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2313 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2316 string GetGametype(); // g_world.qc
2317 void write_recordmarker(entity pl, float tstart, float dt)
2319 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2321 // also write a marker into demo files for demotc-race-record-extractor to find
2324 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2325 " ", ftos(tstart), " ", ftos(dt), "\n"));
2328 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2330 switch(self.owner.cvar_cl_gunalign)
2341 if(allowcenter) // 2: allow center handedness
2354 if(allowcenter) // 2: allow center handedness
2370 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2375 if (cvar("g_shootfromeye"))
2379 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2387 else if (cvar("g_shootfromcenter"))
2391 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2399 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2409 else if (cvar("g_shootfromclient"))
2411 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2418 void attach_sameorigin(entity e, entity to, string tag)
2420 vector org, t_forward, t_left, t_up, e_forward, e_up;
2427 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2428 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2429 t_forward = v_forward * tagscale;
2430 t_left = v_right * -tagscale;
2431 t_up = v_up * tagscale;
2433 e.origin_x = org * t_forward;
2434 e.origin_y = org * t_left;
2435 e.origin_z = org * t_up;
2437 // current forward and up directions
2438 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2439 e.angles = AnglesTransform_FromVAngles(e.angles);
2441 e.angles = AnglesTransform_FromAngles(e.angles);
2442 fixedmakevectors(e.angles);
2444 // untransform forward, up!
2445 e_forward_x = v_forward * t_forward;
2446 e_forward_y = v_forward * t_left;
2447 e_forward_z = v_forward * t_up;
2448 e_up_x = v_up * t_forward;
2449 e_up_y = v_up * t_left;
2450 e_up_z = v_up * t_up;
2452 e.angles = fixedvectoangles2(e_forward, e_up);
2453 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2454 e.angles = AnglesTransform_ToVAngles(e.angles);
2456 e.angles = AnglesTransform_ToAngles(e.angles);
2458 setattachment(e, to, tag);
2459 setorigin(e, e.origin);
2462 void detach_sameorigin(entity e)
2465 org = gettaginfo(e, 0);
2466 e.angles = fixedvectoangles2(v_forward, v_up);
2467 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2468 e.angles = AnglesTransform_ToVAngles(e.angles);
2470 e.angles = AnglesTransform_ToAngles(e.angles);
2472 setattachment(e, world, "");
2473 setorigin(e, e.origin);
2476 void follow_sameorigin(entity e, entity to)
2478 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2479 e.aiment = to; // make the hole follow bmodel
2480 e.punchangle = to.angles; // the original angles of bmodel
2481 e.view_ofs = e.origin - to.origin; // relative origin
2482 e.v_angle = e.angles - to.angles; // relative angles
2485 void unfollow_sameorigin(entity e)
2487 e.movetype = MOVETYPE_NONE;
2490 entity gettaginfo_relative_ent;
2491 vector gettaginfo_relative(entity e, float tag)
2493 if (!gettaginfo_relative_ent)
2495 gettaginfo_relative_ent = spawn();
2496 gettaginfo_relative_ent.effects = EF_NODRAW;
2498 gettaginfo_relative_ent.model = e.model;
2499 gettaginfo_relative_ent.modelindex = e.modelindex;
2500 gettaginfo_relative_ent.frame = e.frame;
2501 return gettaginfo(gettaginfo_relative_ent, tag);
2504 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2508 if (pl.soundentity.cnt & p)
2510 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2511 pl.soundentity.cnt |= p;
2514 void SoundEntity_StopSound(entity pl, float chan)
2518 if (pl.soundentity.cnt & p)
2520 stopsoundto(MSG_ALL, pl.soundentity, chan);
2521 pl.soundentity.cnt &~= p;
2525 void SoundEntity_Attach(entity pl)
2527 pl.soundentity = spawn();
2528 pl.soundentity.classname = "soundentity";
2529 pl.soundentity.owner = pl;
2530 setattachment(pl.soundentity, pl, "");
2531 setmodel(pl.soundentity, "null");
2534 void SoundEntity_Detach(entity pl)
2537 for (i = 0; i <= 7; ++i)
2538 SoundEntity_StopSound(pl, i);
2542 float ParseCommandPlayerSlotTarget_firsttoken;
2543 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2551 ParseCommandPlayerSlotTarget_firsttoken = -1;
2555 if (substring(argv(idx), 0, 1) == "#")
2557 s = substring(argv(idx), 1, -1);
2565 ParseCommandPlayerSlotTarget_firsttoken = idx;
2566 if (s == ftos(stof(s)))
2568 e = edict_num(stof(s));
2569 if (e.flags & FL_CLIENT)
2575 // it must be a nick name
2578 ParseCommandPlayerSlotTarget_firsttoken = idx;
2581 FOR_EACH_CLIENT(head)
2582 if (head.netname == s)
2590 s = strdecolorize(s);
2592 FOR_EACH_CLIENT(head)
2593 if (strdecolorize(head.netname) == s)
2608 float modeleffect_SendEntity(entity to, float sf)
2611 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2614 if(self.velocity != '0 0 0')
2616 if(self.angles != '0 0 0')
2618 if(self.avelocity != '0 0 0')
2621 WriteByte(MSG_ENTITY, f);
2622 WriteShort(MSG_ENTITY, self.modelindex);
2623 WriteByte(MSG_ENTITY, self.skin);
2624 WriteByte(MSG_ENTITY, self.frame);
2625 WriteCoord(MSG_ENTITY, self.origin_x);
2626 WriteCoord(MSG_ENTITY, self.origin_y);
2627 WriteCoord(MSG_ENTITY, self.origin_z);
2630 WriteCoord(MSG_ENTITY, self.velocity_x);
2631 WriteCoord(MSG_ENTITY, self.velocity_y);
2632 WriteCoord(MSG_ENTITY, self.velocity_z);
2636 WriteCoord(MSG_ENTITY, self.angles_x);
2637 WriteCoord(MSG_ENTITY, self.angles_y);
2638 WriteCoord(MSG_ENTITY, self.angles_z);
2642 WriteCoord(MSG_ENTITY, self.avelocity_x);
2643 WriteCoord(MSG_ENTITY, self.avelocity_y);
2644 WriteCoord(MSG_ENTITY, self.avelocity_z);
2646 WriteShort(MSG_ENTITY, self.scale * 256.0);
2647 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2648 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2649 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2650 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2655 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)
2660 e.classname = "modeleffect";
2668 e.teleport_time = t1;
2672 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2676 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2679 sz = max(e.scale, e.scale2);
2680 setsize(e, e.mins * sz, e.maxs * sz);
2681 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2684 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2686 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2689 float randombit(float bits)
2691 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2700 for(f = 1; f <= bits; f *= 2)
2709 r = (r - 1) / (n - 1);
2716 float randombits(float bits, float k, float error_return)
2720 while(k > 0 && bits != r)
2722 r += randombit(bits - r);
2731 void randombit_test(float bits, float iter)
2735 print(ftos(randombit(bits)), "\n");
2740 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2742 if(halflifedist > 0)
2743 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2744 else if(halflifedist < 0)
2745 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2754 #define cvar_string_normal cvar_string_builtin
2755 #define cvar_normal cvar_builtin
2757 string cvar_string_normal(string n)
2759 if not(cvar_type(n) & 1)
2760 backtrace(strcat("Attempt to access undefined cvar: ", n));
2761 return cvar_string_builtin(n);
2764 float cvar_normal(string n)
2766 return stof(cvar_string_normal(n));
2769 #define cvar_set_normal cvar_set_builtin
2777 oself.think = SUB_Remove;
2778 oself.nextthink = time;
2784 Execute func() after time + fdelay.
2785 self when func is executed = self when defer is called
2787 void defer(float fdelay, void() func)
2794 e.think = defer_think;
2795 e.nextthink = time + fdelay;