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");
622 GetCvars_handleFloat(s, f, cvar_cl_dodging_timeout, "cl_dodging_timeout");
624 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
625 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
627 #ifdef ALLOW_FORCEMODELS
628 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
629 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
631 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
633 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
636 if (s == "cl_weaponpriority")
637 self.switchweapon = w_getbestweapon(self);
641 float fexists(string f)
644 fh = fopen(f, FILE_READ);
651 void backtrace(string msg)
654 dev = cvar("developer");
655 war = cvar("prvm_backtraceforwarnings");
656 cvar_set("developer", "1");
657 cvar_set("prvm_backtraceforwarnings", "1");
659 print("--- CUT HERE ---\nWARNING: ");
662 remove(world); // isn't there any better way to cause a backtrace?
663 print("\n--- CUT UNTIL HERE ---\n");
664 cvar_set("developer", ftos(dev));
665 cvar_set("prvm_backtraceforwarnings", ftos(war));
668 string Team_ColorCode(float teamid)
670 if (teamid == COLOR_TEAM1)
672 else if (teamid == COLOR_TEAM2)
674 else if (teamid == COLOR_TEAM3)
676 else if (teamid == COLOR_TEAM4)
682 string Team_ColorName(float t)
684 // fixme: Search for team entities and get their .netname's!
685 if (t == COLOR_TEAM1)
687 if (t == COLOR_TEAM2)
689 if (t == COLOR_TEAM3)
691 if (t == COLOR_TEAM4)
696 string Team_ColorNameLowerCase(float t)
698 // fixme: Search for team entities and get their .netname's!
699 if (t == COLOR_TEAM1)
701 if (t == COLOR_TEAM2)
703 if (t == COLOR_TEAM3)
705 if (t == COLOR_TEAM4)
710 float ColourToNumber(string team_colour)
712 if (team_colour == "red")
715 if (team_colour == "blue")
718 if (team_colour == "yellow")
721 if (team_colour == "pink")
724 if (team_colour == "auto")
730 float NumberToTeamNumber(float number)
747 #define CENTERPRIO_POINT 1
748 #define CENTERPRIO_SPAM 2
749 #define CENTERPRIO_VOTE 4
750 #define CENTERPRIO_NORMAL 5
751 #define CENTERPRIO_SHIELDING 7
752 #define CENTERPRIO_MAPVOTE 9
753 #define CENTERPRIO_IDLEKICK 50
754 #define CENTERPRIO_ADMIN 99
755 .float centerprint_priority;
756 .float centerprint_expires;
757 void centerprint_atprio(entity e, float prio, string s)
759 if (intermission_running)
760 if (prio < CENTERPRIO_MAPVOTE)
762 if (time > e.centerprint_expires)
763 e.centerprint_priority = 0;
764 if (prio >= e.centerprint_priority)
766 e.centerprint_priority = prio;
767 if (timeoutStatus == 2)
768 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
770 e.centerprint_expires = time + e.cvar_scr_centertime;
771 centerprint_builtin(e, s);
774 void centerprint_expire(entity e, float prio)
776 if (prio == e.centerprint_priority)
778 e.centerprint_priority = 0;
779 centerprint_builtin(e, "");
782 void centerprint(entity e, string s)
784 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
787 // decolorizes and team colors the player name when needed
788 string playername(entity p)
791 if (teams_matter && !intermission_running && p.classname == "player")
793 t = Team_ColorCode(p.team);
794 return strcat(t, strdecolorize(p.netname));
800 vector randompos(vector m1, vector m2)
804 v_x = m2_x * random() + m1_x;
805 v_y = m2_y * random() + m1_y;
806 v_z = m2_z * random() + m1_z;
810 float g_pickup_shells;
811 float g_pickup_shells_max;
812 float g_pickup_nails;
813 float g_pickup_nails_max;
814 float g_pickup_rockets;
815 float g_pickup_rockets_max;
816 float g_pickup_cells;
817 float g_pickup_cells_max;
819 float g_pickup_fuel_jetpack;
820 float g_pickup_fuel_max;
821 float g_pickup_armorsmall;
822 float g_pickup_armorsmall_max;
823 float g_pickup_armormedium;
824 float g_pickup_armormedium_max;
825 float g_pickup_armorbig;
826 float g_pickup_armorbig_max;
827 float g_pickup_armorlarge;
828 float g_pickup_armorlarge_max;
829 float g_pickup_healthsmall;
830 float g_pickup_healthsmall_max;
831 float g_pickup_healthmedium;
832 float g_pickup_healthmedium_max;
833 float g_pickup_healthlarge;
834 float g_pickup_healthlarge_max;
835 float g_pickup_healthmega;
836 float g_pickup_healthmega_max;
838 float g_weaponarena_random;
839 string g_weaponarena_list;
840 float g_weaponspeedfactor;
841 float g_weaponratefactor;
842 float g_weapondamagefactor;
843 float g_weaponforcefactor;
844 float g_weaponspreadfactor;
848 float start_ammo_shells;
849 float start_ammo_nails;
850 float start_ammo_rockets;
851 float start_ammo_cells;
852 float start_ammo_fuel;
854 float start_armorvalue;
855 float warmup_start_weapons;
856 float warmup_start_ammo_shells;
857 float warmup_start_ammo_nails;
858 float warmup_start_ammo_rockets;
859 float warmup_start_ammo_cells;
860 float warmup_start_ammo_fuel;
861 float warmup_start_health;
862 float warmup_start_armorvalue;
866 entity get_weaponinfo(float w);
868 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
870 var float i = weaponinfo.weapon;
875 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
877 if (t < 0) // "default" weapon selection
879 if (g_lms || g_ca || allguns)
880 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
883 else if (g_race || g_cts)
884 t = (i == WEP_LASER);
886 t = 0; // weapon is set a few lines later
888 t = (i == WEP_LASER || i == WEP_SHOTGUN);
889 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
890 t |= (i == WEP_HOOK);
893 // we cannot disable porto in Nexball, we must force it
894 if(g_nexball && i == WEP_PORTO)
900 void readplayerstartcvars()
906 // initialize starting values for players
909 start_ammo_shells = 0;
910 start_ammo_nails = 0;
911 start_ammo_rockets = 0;
912 start_ammo_cells = 0;
913 start_health = cvar("g_balance_health_start");
914 start_armorvalue = cvar("g_balance_armor_start");
917 s = cvar_string("g_weaponarena");
923 g_weaponarena_list = "All Weapons";
924 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
926 e = get_weaponinfo(j);
927 g_weaponarena |= e.weapons;
928 weapon_action(e.weapon, WR_PRECACHE);
931 else if (s == "most")
933 g_weaponarena_list = "Most Weapons";
934 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
936 e = get_weaponinfo(j);
937 if (e.spawnflags & WEP_FLAG_NORMAL)
939 g_weaponarena |= e.weapons;
940 weapon_action(e.weapon, WR_PRECACHE);
944 else if (s == "none")
946 g_weaponarena_list = "No Weapons";
947 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
951 t = tokenize_console(s);
952 g_weaponarena_list = "";
953 for (i = 0; i < t; ++i)
956 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
958 e = get_weaponinfo(j);
961 g_weaponarena |= e.weapons;
962 weapon_action(e.weapon, WR_PRECACHE);
963 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
969 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
972 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
976 g_weaponarena_random = cvar("g_weaponarena_random");
978 g_weaponarena_random = 0;
982 start_weapons = g_weaponarena;
983 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
984 start_ammo_rockets = 999;
985 if (g_weaponarena & WEPBIT_SHOTGUN)
986 start_ammo_shells = 999;
987 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
988 start_ammo_cells = 999;
989 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
990 start_ammo_nails = 999;
991 if (g_weaponarena & WEPBIT_HOOK)
992 start_ammo_fuel = 999;
993 start_items |= IT_UNLIMITED_AMMO;
995 else if (g_minstagib)
998 start_armorvalue = 0;
999 start_weapons = WEPBIT_MINSTANEX;
1000 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
1001 start_ammo_cells = cvar("g_minstagib_ammo_start");
1002 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
1003 start_ammo_fuel = cvar("g_start_ammo_fuel");
1005 if (g_minstagib_invis_alpha <= 0)
1006 g_minstagib_invis_alpha = -1;
1012 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1013 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1014 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1015 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1016 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1017 start_health = cvar("g_lms_start_health");
1018 start_armorvalue = cvar("g_lms_start_armor");
1022 start_ammo_shells = cvar("g_start_ammo_shells");
1023 start_ammo_nails = cvar("g_start_ammo_nails");
1024 start_ammo_rockets = cvar("g_start_ammo_rockets");
1025 start_ammo_cells = cvar("g_start_ammo_cells");
1026 start_ammo_fuel = cvar("g_start_ammo_fuel");
1029 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1031 e = get_weaponinfo(i);
1032 if(want_weapon("g_start_weapon_", e, FALSE))
1033 start_weapons |= e.weapons;
1039 warmup_start_ammo_shells = start_ammo_shells;
1040 warmup_start_ammo_nails = start_ammo_nails;
1041 warmup_start_ammo_rockets = start_ammo_rockets;
1042 warmup_start_ammo_cells = start_ammo_cells;
1043 warmup_start_ammo_fuel = start_ammo_fuel;
1044 warmup_start_health = start_health;
1045 warmup_start_armorvalue = start_armorvalue;
1046 warmup_start_weapons = start_weapons;
1048 if (!g_weaponarena && !g_minstagib && !g_ca)
1050 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1051 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1052 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1053 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1054 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1055 warmup_start_health = cvar("g_warmup_start_health");
1056 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1057 warmup_start_weapons = 0;
1058 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1060 e = get_weaponinfo(i);
1061 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1062 warmup_start_weapons |= e.weapons;
1067 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1069 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1070 start_items |= IT_FUEL_REGEN;
1071 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1072 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1075 if(!cvar("g_use_ammunition"))
1077 start_ammo_shells = cvar("g_pickup_shells_max");
1078 start_ammo_nails = cvar("g_pickup_nails_max");
1079 start_ammo_rockets = cvar("g_pickup_rockets_max");
1080 start_ammo_cells = cvar("g_pickup_cells_max");
1081 start_ammo_fuel = cvar("g_pickup_fuel_max");
1082 start_items |= IT_UNLIMITED_AMMO;
1083 warmup_start_ammo_shells = cvar("g_pickup_shells_max");
1084 warmup_start_ammo_nails = cvar("g_pickup_nails_max");
1085 warmup_start_ammo_rockets = cvar("g_pickup_rockets_max");
1086 warmup_start_ammo_cells = cvar("g_pickup_cells_max");
1087 warmup_start_ammo_fuel = cvar("g_pickup_fuel_max");
1088 //warmup_start_items |= IT_UNLIMITED_AMMO;
1092 start_items |= IT_JETPACK;
1094 if (g_weapon_stay == 2)
1096 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1097 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1098 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1099 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1100 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1101 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1102 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1103 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1104 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1105 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1108 MUTATOR_CALLHOOK(SetStartItems);
1110 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1112 e = get_weaponinfo(i);
1113 if(e.weapons & (start_weapons | warmup_start_weapons))
1114 weapon_action(e.weapon, WR_PRECACHE);
1117 start_ammo_shells = max(0, start_ammo_shells);
1118 start_ammo_nails = max(0, start_ammo_nails);
1119 start_ammo_cells = max(0, start_ammo_cells);
1120 start_ammo_rockets = max(0, start_ammo_rockets);
1121 start_ammo_fuel = max(0, start_ammo_fuel);
1123 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1124 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1125 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1126 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1127 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1131 float g_bugrigs_planar_movement;
1132 float g_bugrigs_planar_movement_car_jumping;
1133 float g_bugrigs_reverse_spinning;
1134 float g_bugrigs_reverse_speeding;
1135 float g_bugrigs_reverse_stopping;
1136 float g_bugrigs_air_steering;
1137 float g_bugrigs_angle_smoothing;
1138 float g_bugrigs_friction_floor;
1139 float g_bugrigs_friction_brake;
1140 float g_bugrigs_friction_air;
1141 float g_bugrigs_accel;
1142 float g_bugrigs_speed_ref;
1143 float g_bugrigs_speed_pow;
1144 float g_bugrigs_steer;
1146 float g_touchexplode;
1147 float g_touchexplode_radius;
1148 float g_touchexplode_damage;
1149 float g_touchexplode_edgedamage;
1150 float g_touchexplode_force;
1157 float sv_pitch_fixyaw;
1159 float sv_accuracy_data_share;
1161 void readlevelcvars(void)
1163 // first load all the mutators
1165 MUTATOR_ADD(mutator_nix);
1167 g_bugrigs = cvar("g_bugrigs");
1168 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1169 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1170 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1171 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1172 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1173 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1174 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1175 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1176 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1177 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1178 g_bugrigs_accel = cvar("g_bugrigs_accel");
1179 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1180 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1181 g_bugrigs_steer = cvar("g_bugrigs_steer");
1183 g_touchexplode = cvar("g_touchexplode");
1184 g_touchexplode_radius = cvar("g_touchexplode_radius");
1185 g_touchexplode_damage = cvar("g_touchexplode_damage");
1186 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1187 g_touchexplode_force = cvar("g_touchexplode_force");
1189 #ifdef ALLOW_FORCEMODELS
1190 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1192 sv_loddistance1 = cvar("sv_loddistance1");
1193 sv_loddistance2 = cvar("sv_loddistance2");
1195 if(sv_loddistance2 <= sv_loddistance1)
1196 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1198 sv_clones = cvar("sv_clones");
1199 sv_gentle = cvar("sv_gentle");
1200 sv_foginterval = cvar("sv_foginterval");
1201 g_cloaked = cvar("g_cloaked");
1202 g_jump_grunt = cvar("g_jump_grunt");
1203 g_footsteps = cvar("g_footsteps");
1204 g_grappling_hook = cvar("g_grappling_hook");
1205 g_jetpack = cvar("g_jetpack");
1206 g_laserguided_missile = cvar("g_laserguided_missile");
1207 g_midair = cvar("g_midair");
1208 g_minstagib = cvar("g_minstagib");
1209 g_norecoil = cvar("g_norecoil");
1210 g_vampire = cvar("g_vampire");
1211 g_bloodloss = cvar("g_bloodloss");
1212 sv_maxidle = cvar("sv_maxidle");
1213 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1214 sv_pogostick = cvar("sv_pogostick");
1215 sv_doublejump = cvar("sv_doublejump");
1216 g_ctf_reverse = cvar("g_ctf_reverse");
1217 sv_autotaunt = cvar("sv_autotaunt");
1218 sv_taunt = cvar("sv_taunt");
1220 inWarmupStage = cvar("g_warmup");
1221 g_warmup_limit = cvar("g_warmup_limit");
1222 g_warmup_allguns = cvar("g_warmup_allguns");
1223 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1225 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1226 inWarmupStage = 0; // these modes cannot work together, sorry
1228 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1229 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1230 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1231 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1232 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1233 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1234 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1235 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1236 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1237 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1238 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1239 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1241 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1242 g_weaponratefactor = cvar("g_weaponratefactor");
1243 g_weapondamagefactor = cvar("g_weapondamagefactor");
1244 g_weaponforcefactor = cvar("g_weaponforcefactor");
1245 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1247 g_pickup_shells = cvar("g_pickup_shells");
1248 g_pickup_shells_max = cvar("g_pickup_shells_max");
1249 g_pickup_nails = cvar("g_pickup_nails");
1250 g_pickup_nails_max = cvar("g_pickup_nails_max");
1251 g_pickup_rockets = cvar("g_pickup_rockets");
1252 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1253 g_pickup_cells = cvar("g_pickup_cells");
1254 g_pickup_cells_max = cvar("g_pickup_cells_max");
1255 g_pickup_fuel = cvar("g_pickup_fuel");
1256 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1257 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1258 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1259 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1260 g_pickup_armormedium = cvar("g_pickup_armormedium");
1261 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1262 g_pickup_armorbig = cvar("g_pickup_armorbig");
1263 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1264 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1265 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1266 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1267 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1268 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1269 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1270 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1271 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1272 g_pickup_healthmega = cvar("g_pickup_healthmega");
1273 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1275 g_pinata = cvar("g_pinata");
1277 g_weapon_stay = cvar("g_weapon_stay");
1279 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1282 g_ghost_items = cvar("g_ghost_items");
1284 if(g_ghost_items >= 1)
1285 g_ghost_items = 0.25; // default alpha value
1287 if not(inWarmupStage && !g_ca)
1288 game_starttime = cvar("g_start_delay");
1290 sv_pitch_min = cvar("sv_pitch_min");
1291 sv_pitch_max = cvar("sv_pitch_max");
1292 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1294 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1296 readplayerstartcvars();
1300 // TODO sound pack system
1303 string precache_sound_builtin (string s) = #19;
1304 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1305 string precache_sound(string s)
1307 return precache_sound_builtin(strcat(soundpack, s));
1309 void play2(entity e, string filename)
1311 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1313 void sound(entity e, float chan, string samp, float vol, float atten)
1315 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1320 string precache_sound (string s) = #19;
1321 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1322 float precache_sound_index (string s) = #19;
1324 #define SND_VOLUME 1
1325 #define SND_ATTENUATION 2
1326 #define SND_LARGEENTITY 8
1327 #define SND_LARGESOUND 16
1329 float sound_allowed(float dest, entity e)
1331 // sounds from world may always pass
1334 if (e.classname == "body")
1336 if (e.owner && e.owner != e)
1341 // sounds to self may always pass
1342 if (dest == MSG_ONE)
1343 if (e == msg_entity)
1345 // sounds by players can be removed
1346 if (cvar("bot_sound_monopoly"))
1347 if (clienttype(e) == CLIENTTYPE_REAL)
1349 // anything else may pass
1353 void sound(entity e, float chan, string samp, float vol, float atten)
1355 if (!sound_allowed(MSG_BROADCAST, e))
1357 sound_builtin(e, chan, samp, vol, atten);
1359 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1363 if (!sound_allowed(dest, e))
1366 entno = num_for_edict(e);
1367 idx = precache_sound_index(samp);
1372 atten = floor(atten * 64);
1373 vol = floor(vol * 255);
1376 sflags |= SND_VOLUME;
1378 sflags |= SND_ATTENUATION;
1380 sflags |= SND_LARGEENTITY;
1382 sflags |= SND_LARGESOUND;
1384 WriteByte(dest, SVC_SOUND);
1385 WriteByte(dest, sflags);
1386 if (sflags & SND_VOLUME)
1387 WriteByte(dest, vol);
1388 if (sflags & SND_ATTENUATION)
1389 WriteByte(dest, atten);
1390 if (sflags & SND_LARGEENTITY)
1392 WriteShort(dest, entno);
1393 WriteByte(dest, chan);
1397 WriteShort(dest, entno * 8 + chan);
1399 if (sflags & SND_LARGESOUND)
1400 WriteShort(dest, idx);
1402 WriteByte(dest, idx);
1404 WriteCoord(dest, o_x);
1405 WriteCoord(dest, o_y);
1406 WriteCoord(dest, o_z);
1408 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1412 if (!sound_allowed(dest, e))
1415 o = e.origin + 0.5 * (e.mins + e.maxs);
1416 soundtoat(dest, e, o, chan, samp, vol, atten);
1418 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1420 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1422 void stopsoundto(float dest, entity e, float chan)
1426 if (!sound_allowed(dest, e))
1429 entno = num_for_edict(e);
1434 idx = precache_sound_index("misc/null.wav");
1435 sflags = SND_LARGEENTITY;
1437 sflags |= SND_LARGESOUND;
1438 WriteByte(dest, SVC_SOUND);
1439 WriteByte(dest, sflags);
1440 WriteShort(dest, entno);
1441 WriteByte(dest, chan);
1442 if (sflags & SND_LARGESOUND)
1443 WriteShort(dest, idx);
1445 WriteByte(dest, idx);
1446 WriteCoord(dest, e.origin_x);
1447 WriteCoord(dest, e.origin_y);
1448 WriteCoord(dest, e.origin_z);
1452 WriteByte(dest, SVC_STOPSOUND);
1453 WriteShort(dest, entno * 8 + chan);
1456 void stopsound(entity e, float chan)
1458 if (!sound_allowed(MSG_BROADCAST, e))
1461 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1462 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1465 void play2(entity e, string filename)
1467 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1469 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1472 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1474 float spamsound(entity e, float chan, string samp, float vol, float atten)
1476 if (!sound_allowed(MSG_BROADCAST, e))
1479 if (time > e.spamtime)
1482 sound(e, chan, samp, vol, atten);
1488 void play2team(float t, string filename)
1492 if (cvar("bot_sound_monopoly"))
1495 FOR_EACH_REALPLAYER(head)
1498 play2(head, filename);
1502 void play2all(string samp)
1504 if (cvar("bot_sound_monopoly"))
1507 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1510 void PrecachePlayerSounds(string f);
1511 void precache_all_models(string pattern)
1513 float globhandle, i, n;
1516 globhandle = search_begin(pattern, TRUE, FALSE);
1519 n = search_getsize(globhandle);
1520 for (i = 0; i < n; ++i)
1522 //print(search_getfilename(globhandle, i), "\n");
1523 f = search_getfilename(globhandle, i);
1526 if(substring(f, -9,5) == "_lod1")
1528 if(substring(f, -9,5) == "_lod2")
1530 if(!sv_loddistance1)
1532 PrecachePlayerSounds(strcat(f, ".sounds"));
1534 search_end(globhandle);
1539 // gamemode related things
1540 precache_model ("models/misc/chatbubble.spr");
1541 precache_model ("models/misc/teambubble.spr");
1544 precache_model ("models/runematch/curse.mdl");
1545 precache_model ("models/runematch/rune.mdl");
1548 #ifdef TTURRETS_ENABLED
1549 if (cvar("g_turrets"))
1553 // Precache all player models if desired
1554 if (cvar("sv_precacheplayermodels"))
1556 PrecachePlayerSounds("sound/player/default.sounds");
1557 precache_all_models("models/player/*.zym");
1558 precache_all_models("models/player/*.dpm");
1559 precache_all_models("models/player/*.md3");
1560 precache_all_models("models/player/*.psk");
1561 //precache_model("models/player/carni.zym");
1562 //precache_model("models/player/crash.zym");
1563 //precache_model("models/player/grunt.zym");
1564 //precache_model("models/player/headhunter.zym");
1565 //precache_model("models/player/insurrectionist.zym");
1566 //precache_model("models/player/jeandarc.zym");
1567 //precache_model("models/player/lurk.zym");
1568 //precache_model("models/player/lycanthrope.zym");
1569 //precache_model("models/player/marine.zym");
1570 //precache_model("models/player/nexus.zym");
1571 //precache_model("models/player/pyria.zym");
1572 //precache_model("models/player/shock.zym");
1573 //precache_model("models/player/skadi.zym");
1574 //precache_model("models/player/specop.zym");
1575 //precache_model("models/player/visitant.zym");
1578 if (cvar("sv_defaultcharacter"))
1581 s = cvar_string("sv_defaultplayermodel_red");
1585 PrecachePlayerSounds(strcat(s, ".sounds"));
1587 s = cvar_string("sv_defaultplayermodel_blue");
1591 PrecachePlayerSounds(strcat(s, ".sounds"));
1593 s = cvar_string("sv_defaultplayermodel_yellow");
1597 PrecachePlayerSounds(strcat(s, ".sounds"));
1599 s = cvar_string("sv_defaultplayermodel_pink");
1603 PrecachePlayerSounds(strcat(s, ".sounds"));
1605 s = cvar_string("sv_defaultplayermodel");
1609 PrecachePlayerSounds(strcat(s, ".sounds"));
1615 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1616 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1619 // gore and miscellaneous sounds
1620 //precache_sound ("misc/h2ohit.wav");
1621 precache_model ("models/hook.md3");
1622 precache_sound ("misc/armorimpact.wav");
1623 precache_sound ("misc/bodyimpact1.wav");
1624 precache_sound ("misc/bodyimpact2.wav");
1625 precache_sound ("misc/gib.wav");
1626 precache_sound ("misc/gib_splat01.wav");
1627 precache_sound ("misc/gib_splat02.wav");
1628 precache_sound ("misc/gib_splat03.wav");
1629 precache_sound ("misc/gib_splat04.wav");
1630 precache_sound ("misc/hit.wav");
1631 precache_sound ("misc/typehit.wav");
1632 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1633 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1634 precache_sound ("misc/null.wav");
1635 precache_sound ("misc/spawn.wav");
1636 precache_sound ("misc/talk.wav");
1637 precache_sound ("misc/teleport.wav");
1638 precache_sound ("misc/poweroff.wav");
1639 precache_sound ("player/lava.wav");
1640 precache_sound ("player/slime.wav");
1643 precache_sound ("misc/jetpack_fly.wav");
1645 precache_model ("models/sprites/0.spr32");
1646 precache_model ("models/sprites/1.spr32");
1647 precache_model ("models/sprites/2.spr32");
1648 precache_model ("models/sprites/3.spr32");
1649 precache_model ("models/sprites/4.spr32");
1650 precache_model ("models/sprites/5.spr32");
1651 precache_model ("models/sprites/6.spr32");
1652 precache_model ("models/sprites/7.spr32");
1653 precache_model ("models/sprites/8.spr32");
1654 precache_model ("models/sprites/9.spr32");
1655 precache_model ("models/sprites/10.spr32");
1657 // common weapon precaches
1658 precache_sound ("weapons/weapon_switch.wav");
1659 precache_sound ("weapons/weaponpickup.wav");
1660 precache_sound ("weapons/unavailable.wav");
1661 if (g_grappling_hook)
1663 precache_sound ("weapons/hook_fire.wav"); // hook
1664 precache_sound ("weapons/hook_impact.wav"); // hook
1667 if(cvar("sv_precacheweapons"))
1669 //precache weapon models/sounds
1672 while (wep <= WEP_LAST)
1674 weapon_action(wep, WR_PRECACHE);
1679 precache_model("models/elaser.mdl");
1680 precache_model("models/laser.mdl");
1681 precache_model("models/ebomb.mdl");
1684 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1686 if (!self.noise && self.music) // quake 3 uses the music field
1687 self.noise = self.music;
1689 // plays music for the level if there is any
1692 precache_sound (self.noise);
1693 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1698 // sorry, but using \ in macros breaks line numbers
1699 #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
1700 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1701 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1703 // WARNING: this kills the trace globals
1704 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1705 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1707 #define INITPRIO_FIRST 0
1708 #define INITPRIO_GAMETYPE 0
1709 #define INITPRIO_GAMETYPE_FALLBACK 1
1710 #define INITPRIO_CVARS 5
1711 #define INITPRIO_FINDTARGET 10
1712 #define INITPRIO_DROPTOFLOOR 20
1713 #define INITPRIO_SETLOCATION 90
1714 #define INITPRIO_LINKDOORS 91
1715 #define INITPRIO_LAST 99
1717 .void(void) initialize_entity;
1718 .float initialize_entity_order;
1719 .entity initialize_entity_next;
1720 entity initialize_entity_first;
1722 void make_safe_for_remove(entity e)
1724 if (e.initialize_entity)
1727 for (ent = initialize_entity_first; ent; )
1729 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1731 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1732 // skip it in linked list
1735 prev.initialize_entity_next = ent.initialize_entity_next;
1736 ent = prev.initialize_entity_next;
1740 initialize_entity_first = ent.initialize_entity_next;
1741 ent = initialize_entity_first;
1747 ent = ent.initialize_entity_next;
1753 void objerror(string s)
1755 make_safe_for_remove(self);
1756 objerror_builtin(s);
1759 void remove_unsafely(entity e)
1764 void remove_safely(entity e)
1766 make_safe_for_remove(e);
1770 void InitializeEntity(entity e, void(void) func, float order)
1774 if (!e || e.initialize_entity)
1776 // make a proxy initializer entity
1780 e.classname = "initialize_entity";
1784 e.initialize_entity = func;
1785 e.initialize_entity_order = order;
1787 cur = initialize_entity_first;
1790 if (!cur || cur.initialize_entity_order > order)
1792 // insert between prev and cur
1794 prev.initialize_entity_next = e;
1796 initialize_entity_first = e;
1797 e.initialize_entity_next = cur;
1801 cur = cur.initialize_entity_next;
1804 void InitializeEntitiesRun()
1807 startoflist = initialize_entity_first;
1808 initialize_entity_first = world;
1809 for (self = startoflist; self; )
1812 var void(void) func;
1813 e = self.initialize_entity_next;
1814 func = self.initialize_entity;
1815 self.initialize_entity_order = 0;
1816 self.initialize_entity = func_null;
1817 self.initialize_entity_next = world;
1818 if (self.classname == "initialize_entity")
1822 remove_builtin(self);
1825 //dprint("Delayed initialization: ", self.classname, "\n");
1831 .float uncustomizeentityforclient_set;
1832 .void(void) uncustomizeentityforclient;
1833 void(void) SUB_Nullpointer = #0;
1834 void UncustomizeEntitiesRun()
1838 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1839 self.uncustomizeentityforclient();
1842 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1844 e.customizeentityforclient = customizer;
1845 e.uncustomizeentityforclient = uncustomizer;
1846 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1850 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1853 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1857 if (e.classname == "")
1858 e.classname = "net_linked";
1860 if (e.model == "" || self.modelindex == 0)
1864 setmodel(e, "null");
1868 e.SendEntity = sendfunc;
1869 e.SendFlags = 0xFFFFFF;
1872 e.effects |= EF_NODEPTHTEST;
1876 e.nextthink = time + dt;
1877 e.think = SUB_Remove;
1881 void adaptor_think2touch()
1890 void adaptor_think2use()
1902 // deferred dropping
1903 void DropToFloor_Handler()
1905 droptofloor_builtin();
1906 self.dropped_origin = self.origin;
1911 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1916 float trace_hits_box_a0, trace_hits_box_a1;
1918 float trace_hits_box_1d(float end, float thmi, float thma)
1922 // just check if x is in range
1930 // do the trace with respect to x
1931 // 0 -> end has to stay in thmi -> thma
1932 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1933 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1934 if (trace_hits_box_a0 > trace_hits_box_a1)
1940 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1945 // now it is a trace from 0 to end
1947 trace_hits_box_a0 = 0;
1948 trace_hits_box_a1 = 1;
1950 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1952 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1954 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1960 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1962 return trace_hits_box(start, end, thmi - ma, thma - mi);
1965 float SUB_NoImpactCheck()
1967 // zero hitcontents = this is not the real impact, but either the
1968 // mirror-impact of something hitting the projectile instead of the
1969 // projectile hitting the something, or a touchareagrid one. Neither of
1970 // these stop the projectile from moving, so...
1971 if(trace_dphitcontents == 0)
1973 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1976 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1978 if (other == world && self.size != '0 0 0')
1981 tic = self.velocity * sys_frametime;
1982 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1983 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1984 if (trace_fraction >= 1)
1986 dprint("Odd... did not hit...?\n");
1988 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1990 dprint("Detected and prevented the sky-grapple bug.\n");
1998 #define SUB_OwnerCheck() (other && (other == self.owner))
2000 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
2002 if(SUB_OwnerCheck())
2004 if(SUB_NoImpactCheck())
2009 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2010 UpdateCSQCProjectileNextFrame(self);
2013 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2015 float MAX_IPBAN_URIS = 16;
2017 float URI_GET_DISCARD = 0;
2018 float URI_GET_IPBAN = 1;
2019 float URI_GET_IPBAN_END = 16;
2021 void URI_Get_Callback(float id, float status, string data)
2023 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2025 dprint("\nEnd of data.\n");
2027 if (id == URI_GET_DISCARD)
2031 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2034 OnlineBanList_URI_Get_Callback(id, status, data);
2038 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2042 void print_to(entity e, string s)
2045 sprint(e, strcat(s, "\n"));
2050 string getrecords(float page) // 50 records per page
2064 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2066 if (MapInfo_Get_ByID(i))
2068 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2071 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2072 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2080 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2082 if (MapInfo_Get_ByID(i))
2084 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2087 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2088 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2096 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2098 if (MapInfo_Get_ByID(i))
2100 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2103 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2104 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2110 MapInfo_ClearTemps();
2112 if (s == "" && page == 0)
2113 return "No records are available on this server.\n";
2118 string getrankings()
2131 for (i = 1; i <= RANKINGS_CNT; ++i)
2133 t = race_GetTime(i);
2136 n = race_GetName(i);
2137 p = race_PlaceName(i);
2138 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2141 MapInfo_ClearTemps();
2144 return strcat("No records are available for the map: ", map, "\n");
2146 return strcat("Records for ", map, ":\n", s);
2149 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2152 vector start, org, delta, end, enddown, mstart;
2154 m = e.dphitcontentsmask;
2155 e.dphitcontentsmask = goodcontents | badcontents;
2158 delta = world.maxs - world.mins;
2160 for (i = 0; i < attempts; ++i)
2162 start_x = org_x + random() * delta_x;
2163 start_y = org_y + random() * delta_y;
2164 start_z = org_z + random() * delta_z;
2166 // rule 1: start inside world bounds, and outside
2167 // solid, and don't start from somewhere where you can
2168 // fall down to evil
2169 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2170 if (trace_fraction >= 1)
2172 if (trace_startsolid)
2174 if (trace_dphitcontents & badcontents)
2176 if (trace_dphitq3surfaceflags & badsurfaceflags)
2179 // rule 2: if we are too high, lower the point
2180 if (trace_fraction * delta_z > maxaboveground)
2181 start = trace_endpos + '0 0 1' * maxaboveground;
2182 enddown = trace_endpos;
2184 // rule 3: make sure we aren't outside the map. This only works
2185 // for somewhat well formed maps. A good rule of thumb is that
2186 // the map should have a convex outside hull.
2187 // these can be traceLINES as we already verified the starting box
2188 mstart = start + 0.5 * (e.mins + e.maxs);
2189 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2190 if (trace_fraction >= 1)
2192 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2193 if (trace_fraction >= 1)
2195 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2196 if (trace_fraction >= 1)
2198 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2199 if (trace_fraction >= 1)
2201 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2202 if (trace_fraction >= 1)
2205 // find a random vector to "look at"
2206 end_x = org_x + random() * delta_x;
2207 end_y = org_y + random() * delta_y;
2208 end_z = org_z + random() * delta_z;
2209 end = start + normalize(end - start) * vlen(delta);
2211 // rule 4: start TO end must not be too short
2212 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2213 if (trace_startsolid)
2215 if (trace_fraction < minviewdistance / vlen(delta))
2218 // rule 5: don't want to look at sky
2219 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2222 // rule 6: we must not end up in trigger_hurt
2223 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2225 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2232 e.dphitcontentsmask = m;
2236 setorigin(e, start);
2237 e.angles = vectoangles(end - start);
2238 dprint("Needed ", ftos(i + 1), " attempts\n");
2245 float zcurveparticles_effectno;
2246 vector zcurveparticles_start;
2247 float zcurveparticles_spd;
2249 void endzcurveparticles()
2251 if(zcurveparticles_effectno)
2254 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2256 zcurveparticles_effectno = 0;
2259 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2261 spd = bound(0, floor(spd / 16), 32767);
2262 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2264 endzcurveparticles();
2265 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2266 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2267 WriteShort(MSG_BROADCAST, effectno);
2268 WriteCoord(MSG_BROADCAST, start_x);
2269 WriteCoord(MSG_BROADCAST, start_y);
2270 WriteCoord(MSG_BROADCAST, start_z);
2271 zcurveparticles_effectno = effectno;
2272 zcurveparticles_start = start;
2275 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2276 WriteCoord(MSG_BROADCAST, end_x);
2277 WriteCoord(MSG_BROADCAST, end_y);
2278 WriteCoord(MSG_BROADCAST, end_z);
2279 WriteCoord(MSG_BROADCAST, end_dz);
2280 zcurveparticles_spd = spd;
2283 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2286 vector vecxy, velxy;
2288 vecxy = end - start;
2293 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2295 endzcurveparticles();
2296 trailparticles(world, effectno, start, end);
2300 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2301 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2304 string GetGametype(); // g_world.qc
2305 void write_recordmarker(entity pl, float tstart, float dt)
2307 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2309 // also write a marker into demo files for demotc-race-record-extractor to find
2312 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2313 " ", ftos(tstart), " ", ftos(dt), "\n"));
2316 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2318 switch(self.owner.cvar_cl_gunalign)
2329 if(allowcenter) // 2: allow center handedness
2342 if(allowcenter) // 2: allow center handedness
2358 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2363 if (cvar("g_shootfromeye"))
2367 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2375 else if (cvar("g_shootfromcenter"))
2379 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2387 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2397 else if (cvar("g_shootfromclient"))
2399 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2406 void attach_sameorigin(entity e, entity to, string tag)
2408 vector org, t_forward, t_left, t_up, e_forward, e_up;
2415 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2416 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2417 t_forward = v_forward * tagscale;
2418 t_left = v_right * -tagscale;
2419 t_up = v_up * tagscale;
2421 e.origin_x = org * t_forward;
2422 e.origin_y = org * t_left;
2423 e.origin_z = org * t_up;
2425 // current forward and up directions
2426 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2427 e.angles = AnglesTransform_FromVAngles(e.angles);
2429 e.angles = AnglesTransform_FromAngles(e.angles);
2430 fixedmakevectors(e.angles);
2432 // untransform forward, up!
2433 e_forward_x = v_forward * t_forward;
2434 e_forward_y = v_forward * t_left;
2435 e_forward_z = v_forward * t_up;
2436 e_up_x = v_up * t_forward;
2437 e_up_y = v_up * t_left;
2438 e_up_z = v_up * t_up;
2440 e.angles = fixedvectoangles2(e_forward, e_up);
2441 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2442 e.angles = AnglesTransform_ToVAngles(e.angles);
2444 e.angles = AnglesTransform_ToAngles(e.angles);
2446 setattachment(e, to, tag);
2447 setorigin(e, e.origin);
2450 void detach_sameorigin(entity e)
2453 org = gettaginfo(e, 0);
2454 e.angles = fixedvectoangles2(v_forward, v_up);
2455 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2456 e.angles = AnglesTransform_ToVAngles(e.angles);
2458 e.angles = AnglesTransform_ToAngles(e.angles);
2460 setattachment(e, world, "");
2461 setorigin(e, e.origin);
2464 void follow_sameorigin(entity e, entity to)
2466 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2467 e.aiment = to; // make the hole follow bmodel
2468 e.punchangle = to.angles; // the original angles of bmodel
2469 e.view_ofs = e.origin - to.origin; // relative origin
2470 e.v_angle = e.angles - to.angles; // relative angles
2473 void unfollow_sameorigin(entity e)
2475 e.movetype = MOVETYPE_NONE;
2478 entity gettaginfo_relative_ent;
2479 vector gettaginfo_relative(entity e, float tag)
2481 if (!gettaginfo_relative_ent)
2483 gettaginfo_relative_ent = spawn();
2484 gettaginfo_relative_ent.effects = EF_NODRAW;
2486 gettaginfo_relative_ent.model = e.model;
2487 gettaginfo_relative_ent.modelindex = e.modelindex;
2488 gettaginfo_relative_ent.frame = e.frame;
2489 return gettaginfo(gettaginfo_relative_ent, tag);
2492 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2496 if (pl.soundentity.cnt & p)
2498 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2499 pl.soundentity.cnt |= p;
2502 void SoundEntity_StopSound(entity pl, float chan)
2506 if (pl.soundentity.cnt & p)
2508 stopsoundto(MSG_ALL, pl.soundentity, chan);
2509 pl.soundentity.cnt &~= p;
2513 void SoundEntity_Attach(entity pl)
2515 pl.soundentity = spawn();
2516 pl.soundentity.classname = "soundentity";
2517 pl.soundentity.owner = pl;
2518 setattachment(pl.soundentity, pl, "");
2519 setmodel(pl.soundentity, "null");
2522 void SoundEntity_Detach(entity pl)
2525 for (i = 0; i <= 7; ++i)
2526 SoundEntity_StopSound(pl, i);
2530 float ParseCommandPlayerSlotTarget_firsttoken;
2531 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2539 ParseCommandPlayerSlotTarget_firsttoken = -1;
2543 if (substring(argv(idx), 0, 1) == "#")
2545 s = substring(argv(idx), 1, -1);
2553 ParseCommandPlayerSlotTarget_firsttoken = idx;
2554 if (s == ftos(stof(s)))
2556 e = edict_num(stof(s));
2557 if (e.flags & FL_CLIENT)
2563 // it must be a nick name
2566 ParseCommandPlayerSlotTarget_firsttoken = idx;
2569 FOR_EACH_CLIENT(head)
2570 if (head.netname == s)
2578 s = strdecolorize(s);
2580 FOR_EACH_CLIENT(head)
2581 if (strdecolorize(head.netname) == s)
2596 float modeleffect_SendEntity(entity to, float sf)
2599 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2602 if(self.velocity != '0 0 0')
2604 if(self.angles != '0 0 0')
2606 if(self.avelocity != '0 0 0')
2609 WriteByte(MSG_ENTITY, f);
2610 WriteShort(MSG_ENTITY, self.modelindex);
2611 WriteByte(MSG_ENTITY, self.skin);
2612 WriteByte(MSG_ENTITY, self.frame);
2613 WriteCoord(MSG_ENTITY, self.origin_x);
2614 WriteCoord(MSG_ENTITY, self.origin_y);
2615 WriteCoord(MSG_ENTITY, self.origin_z);
2618 WriteCoord(MSG_ENTITY, self.velocity_x);
2619 WriteCoord(MSG_ENTITY, self.velocity_y);
2620 WriteCoord(MSG_ENTITY, self.velocity_z);
2624 WriteCoord(MSG_ENTITY, self.angles_x);
2625 WriteCoord(MSG_ENTITY, self.angles_y);
2626 WriteCoord(MSG_ENTITY, self.angles_z);
2630 WriteCoord(MSG_ENTITY, self.avelocity_x);
2631 WriteCoord(MSG_ENTITY, self.avelocity_y);
2632 WriteCoord(MSG_ENTITY, self.avelocity_z);
2634 WriteShort(MSG_ENTITY, self.scale * 256.0);
2635 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2636 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2637 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2638 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2643 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)
2648 e.classname = "modeleffect";
2656 e.teleport_time = t1;
2660 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2664 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2667 sz = max(e.scale, e.scale2);
2668 setsize(e, e.mins * sz, e.maxs * sz);
2669 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2672 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2674 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2677 float randombit(float bits)
2679 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2688 for(f = 1; f <= bits; f *= 2)
2697 r = (r - 1) / (n - 1);
2704 float randombits(float bits, float k, float error_return)
2708 while(k > 0 && bits != r)
2710 r += randombit(bits - r);
2719 void randombit_test(float bits, float iter)
2723 print(ftos(randombit(bits)), "\n");
2728 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2730 if(halflifedist > 0)
2731 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2732 else if(halflifedist < 0)
2733 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2742 #define cvar_string_normal cvar_string_builtin
2743 #define cvar_normal cvar_builtin
2745 string cvar_string_normal(string n)
2747 if not(cvar_type(n) & 1)
2748 backtrace(strcat("Attempt to access undefined cvar: ", n));
2749 return cvar_string_builtin(n);
2752 float cvar_normal(string n)
2754 return stof(cvar_string_normal(n));
2757 #define cvar_set_normal cvar_set_builtin
2765 oself.think = SUB_Remove;
2766 oself.nextthink = time;
2772 Execute func() after time + fdelay.
2773 self when func is executed = self when defer is called
2775 void defer(float fdelay, void() func)
2782 e.think = defer_think;
2783 e.nextthink = time + fdelay;