1 var void remove(entity e);
2 void objerror(string s);
4 .vector dropped_origin;
6 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
7 void crosshair_trace(entity pl)
9 traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
12 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
13 void() spawnpoint_use;
14 float race_GetTime(float pos);
15 string race_GetName(float pos);
16 string race_PlaceName(float pos);
18 string ColoredTeamName(float t);
20 string admin_name(void)
22 if(cvar_string("sv_adminnick") != "")
23 return cvar_string("sv_adminnick");
25 return "SERVER ADMIN";
28 float DistributeEvenly_amount;
29 float DistributeEvenly_totalweight;
30 void DistributeEvenly_Init(float amount, float totalweight)
32 if (DistributeEvenly_amount)
34 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
35 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
38 DistributeEvenly_amount = 0;
40 DistributeEvenly_amount = amount;
41 DistributeEvenly_totalweight = totalweight;
43 float DistributeEvenly_Get(float weight)
48 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
49 DistributeEvenly_totalweight -= weight;
50 DistributeEvenly_amount -= f;
54 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
57 string STR_PLAYER = "player";
58 string STR_SPECTATOR = "spectator";
59 string STR_OBSERVER = "observer";
62 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
63 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
64 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
65 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
67 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
68 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
69 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
70 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
71 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
74 // copies a string to a tempstring (so one can strunzone it)
75 string strcat1(string s) = #115; // FRIK_FILE
80 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
82 local float nPlayerHealth = rint(enPlayer.health);
83 local float nPlayerArmor = rint(enPlayer.armorvalue);
84 local float nPlayerHandicap = enPlayer.cvar_cl_handicap;
85 local float nPlayerPing = rint(enPlayer.ping);
86 local string strPlayerPingColor;
87 local string strMessage;
88 if(nPlayerPing >= 150)
89 strPlayerPingColor = "^1";
91 strPlayerPingColor = "^2";
93 if((cvar("sv_fragmessage_information_stats")) && (enPlayer.health >= 1))
94 strMessage = strcat(strMessage, "\n^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
96 if(cvar("sv_fragmessage_information_ping")) {
97 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
98 strMessage = strcat(strMessage, "\n^7(^2Bot");
100 strMessage = strcat(strMessage, "\n^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
101 if(cvar("sv_fragmessage_information_handicap"))
102 if(cvar("sv_fragmessage_information_handicap") == 2)
103 if(nPlayerHandicap <= 1)
104 strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
106 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
107 else if not(nPlayerHandicap <= 1)
108 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
110 strMessage = strcat(strMessage, "^7)");
111 } else if(cvar("sv_fragmessage_information_handicap")) {
112 if(cvar("sv_fragmessage_information_handicap") == 2)
113 if(nPlayerHandicap <= 1)
114 strMessage = strcat(strMessage, "\n^7(Handicap ^2Off^7)");
116 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
117 else if(nPlayerHandicap > 1)
118 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
122 void bcenterprint(string s)
124 // TODO replace by MSG_ALL (would show it to spectators too, though)?
126 FOR_EACH_PLAYER(head)
127 if (clienttype(head) == CLIENTTYPE_REAL)
128 centerprint(head, s);
131 void GameLogEcho(string s)
136 if (cvar("sv_eventlog_files"))
141 matches = cvar("sv_eventlog_files_counter") + 1;
142 cvar_set("sv_eventlog_files_counter", ftos(matches));
145 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
146 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
147 logfile = fopen(fn, FILE_APPEND);
148 fputs(logfile, ":logversion:3\n");
152 if (cvar("sv_eventlog_files_timestamps"))
153 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
155 fputs(logfile, strcat(s, "\n"));
158 if (cvar("sv_eventlog_console"))
167 // will be opened later
172 if (logfile_open && logfile >= 0)
182 vector PL_CROUCH_VIEW_OFS;
183 vector PL_CROUCH_MIN;
184 vector PL_CROUCH_MAX;
186 float spawnpoint_nag;
187 void relocate_spawnpoint()
189 PL_VIEW_OFS = stov(cvar_string("sv_player_viewoffset"));
190 PL_MIN = stov(cvar_string("sv_player_mins"));
191 PL_MAX = stov(cvar_string("sv_player_maxs"));
192 PL_CROUCH_VIEW_OFS = stov(cvar_string("sv_player_crouch_viewoffset"));
193 PL_CROUCH_MIN = stov(cvar_string("sv_player_crouch_mins"));
194 PL_CROUCH_MAX = stov(cvar_string("sv_player_crouch_maxs"));
196 // nudge off the floor
197 setorigin(self, self.origin + '0 0 1');
199 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
200 if (trace_startsolid)
206 if (!move_out_of_solid(self))
207 objerror("could not get out of solid at all!");
208 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
209 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
210 print(" ", ftos(self.origin_y - o_y));
211 print(" ", ftos(self.origin_z - o_z), "'\n");
212 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
215 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
221 self.mins = self.maxs = '0 0 0';
222 objerror("player spawn point in solid, mapper sucks!\n");
227 if (cvar("g_spawnpoints_autodrop"))
229 setsize(self, PL_MIN, PL_MAX);
233 self.use = spawnpoint_use;
234 self.team_saved = self.team;
238 if (have_team_spawns != 0)
240 have_team_spawns = 1;
242 if (cvar("r_showbboxes"))
244 // show where spawnpoints point at too
245 makevectors(self.angles);
248 e.classname = "info_player_foo";
249 setorigin(e, self.origin + v_forward * 24);
250 setsize(e, '-8 -8 -8', '8 8 8');
251 e.solid = SOLID_TRIGGER;
255 #define strstr strstrofs
257 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
258 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
259 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
260 // BE CONSTANT OR strzoneD!
261 float strstr(string haystack, string needle, float offset)
265 len = strlen(needle);
266 endpos = strlen(haystack) - len;
267 while(offset <= endpos)
269 found = substring(haystack, offset, len);
278 float NUM_NEAREST_ENTITIES = 4;
279 entity nearest_entity[NUM_NEAREST_ENTITIES];
280 float nearest_length[NUM_NEAREST_ENTITIES];
281 entity findnearest(vector point, .string field, string value, vector axismod)
292 localhead = find(world, field, value);
295 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
296 dist = localhead.oldorigin;
298 dist = localhead.origin;
300 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
303 for (i = 0; i < num_nearest; ++i)
305 if (len < nearest_length[i])
309 // now i tells us where to insert at
310 // INSERTION SORT! YOU'VE SEEN IT! RUN!
311 if (i < NUM_NEAREST_ENTITIES)
313 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
315 nearest_length[j + 1] = nearest_length[j];
316 nearest_entity[j + 1] = nearest_entity[j];
318 nearest_length[i] = len;
319 nearest_entity[i] = localhead;
320 if (num_nearest < NUM_NEAREST_ENTITIES)
321 num_nearest = num_nearest + 1;
324 localhead = find(localhead, field, value);
327 // now use the first one from our list that we can see
328 for (i = 0; i < num_nearest; ++i)
330 traceline(point, nearest_entity[i].origin, TRUE, world);
331 if (trace_fraction == 1)
335 dprint("Nearest point (");
336 dprint(nearest_entity[0].netname);
337 dprint(") is not visible, using a visible one.\n");
339 return nearest_entity[i];
343 if (num_nearest == 0)
346 dprint("Not seeing any location point, using nearest as fallback.\n");
348 dprint("Candidates were: ");
349 for(j = 0; j < num_nearest; ++j)
353 dprint(nearest_entity[j].netname);
358 return nearest_entity[0];
361 void spawnfunc_target_location()
363 self.classname = "target_location";
364 // location name in netname
365 // eventually support: count, teamgame selectors, line of sight?
368 void spawnfunc_info_location()
370 self.classname = "target_location";
371 self.message = self.netname;
374 string NearestLocation(vector p)
379 loc = findnearest(p, classname, "target_location", '1 1 1');
386 loc = findnearest(p, target, "###item###", '1 1 4');
393 string formatmessage(string msg)
404 crosshair_trace(self);
405 cursor = trace_endpos;
406 cursor_ent = trace_ent;
410 break; // too many replacements
413 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
414 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
427 replacement = substring(msg, p, 2);
428 escape = substring(msg, p + 1, 1);
432 else if (escape == "\\")
434 else if (escape == "n")
436 else if (escape == "a")
437 replacement = ftos(floor(self.armorvalue));
438 else if (escape == "h")
439 replacement = ftos(floor(self.health));
440 else if (escape == "l")
441 replacement = NearestLocation(self.origin);
442 else if (escape == "y")
443 replacement = NearestLocation(cursor);
444 else if (escape == "d")
445 replacement = NearestLocation(self.death_origin);
446 else if (escape == "w") {
450 wep = self.switchweapon;
453 replacement = W_Name(wep);
454 } else if (escape == "W") {
455 if (self.items & IT_SHELLS) replacement = "shells";
456 else if (self.items & IT_NAILS) replacement = "bullets";
457 else if (self.items & IT_ROCKETS) replacement = "rockets";
458 else if (self.items & IT_CELLS) replacement = "cells";
459 else replacement = "batteries"; // ;)
460 } else if (escape == "x") {
461 replacement = cursor_ent.netname;
462 if (!replacement || !cursor_ent)
463 replacement = "nothing";
464 } else if (escape == "p") {
465 if (self.last_selected_player)
466 replacement = self.last_selected_player.netname;
468 replacement = "(nobody)";
469 } else if (escape == "s")
470 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
471 else if (escape == "S")
472 replacement = ftos(vlen(self.velocity));
473 else if (escape == "v") {
477 if(self.classname == "spectator")
482 weapon_number = stats.weapon;
485 weapon_number = stats.switchweapon;
488 weapon_number = stats.cnt;
490 if(stats.cvar_cl_accuracy_data_share && stats.stats_fired[weapon_number - 1])
491 replacement = ftos(bound(0, floor(100 * stats.stats_hit[weapon_number - 1] / stats.stats_fired[weapon_number - 1]), 100));
493 replacement = "~"; // or something to indicate NULL, not available
496 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
497 p = p + strlen(replacement);
502 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
503 return (value == 0) ? FALSE : TRUE;
512 >0: receives a cvar from name=argv(f) value=argv(f+1)
514 void GetCvars_handleString(string thisname, float f, .string field, string name)
519 strunzone(self.field);
520 self.field = string_null;
524 if (thisname == name)
527 strunzone(self.field);
528 self.field = strzone(argv(f + 1));
532 stuffcmd(self, strcat("sendcvar ", name, "\n"));
534 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
536 GetCvars_handleString(thisname, f, field, name);
537 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
538 if (thisname == name)
541 s = func(strcat1(self.field));
544 strunzone(self.field);
545 self.field = strzone(s);
549 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
556 if (thisname == name)
557 self.field = stof(argv(f + 1));
560 stuffcmd(self, strcat("sendcvar ", name, "\n"));
562 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
569 if (thisname == name)
573 self.field = stof(argv(f + 1));
582 stuffcmd(self, strcat("sendcvar ", name, "\n"));
585 string W_FixWeaponOrder_ForceComplete(string s);
586 string W_FixWeaponOrder_AllowIncomplete(string s);
587 float w_getbestweapon(entity e);
588 void GetCvars(float f)
592 s = strcat1(argv(f));
593 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
594 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
595 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
596 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
597 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
598 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
599 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
600 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
601 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
602 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
603 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
604 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
605 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
606 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
607 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
608 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
609 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
610 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
611 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
612 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
613 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
614 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
615 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
616 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
618 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
619 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
621 #ifdef ALLOW_FORCEMODELS
622 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
623 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
625 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
627 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
630 if (s == "cl_weaponpriority")
631 self.switchweapon = w_getbestweapon(self);
635 float fexists(string f)
638 fh = fopen(f, FILE_READ);
645 void backtrace(string msg)
648 dev = cvar("developer");
649 war = cvar("prvm_backtraceforwarnings");
650 cvar_set("developer", "1");
651 cvar_set("prvm_backtraceforwarnings", "1");
653 print("--- CUT HERE ---\nWARNING: ");
656 remove(world); // isn't there any better way to cause a backtrace?
657 print("\n--- CUT UNTIL HERE ---\n");
658 cvar_set("developer", ftos(dev));
659 cvar_set("prvm_backtraceforwarnings", ftos(war));
662 string Team_ColorCode(float teamid)
664 if (teamid == COLOR_TEAM1)
666 else if (teamid == COLOR_TEAM2)
668 else if (teamid == COLOR_TEAM3)
670 else if (teamid == COLOR_TEAM4)
676 string Team_ColorName(float t)
678 // fixme: Search for team entities and get their .netname's!
679 if (t == COLOR_TEAM1)
681 if (t == COLOR_TEAM2)
683 if (t == COLOR_TEAM3)
685 if (t == COLOR_TEAM4)
690 string Team_ColorNameLowerCase(float t)
692 // fixme: Search for team entities and get their .netname's!
693 if (t == COLOR_TEAM1)
695 if (t == COLOR_TEAM2)
697 if (t == COLOR_TEAM3)
699 if (t == COLOR_TEAM4)
704 float ColourToNumber(string team_colour)
706 if (team_colour == "red")
709 if (team_colour == "blue")
712 if (team_colour == "yellow")
715 if (team_colour == "pink")
718 if (team_colour == "auto")
724 float NumberToTeamNumber(float number)
741 #define CENTERPRIO_POINT 1
742 #define CENTERPRIO_SPAM 2
743 #define CENTERPRIO_VOTE 4
744 #define CENTERPRIO_NORMAL 5
745 #define CENTERPRIO_SHIELDING 7
746 #define CENTERPRIO_MAPVOTE 9
747 #define CENTERPRIO_IDLEKICK 50
748 #define CENTERPRIO_ADMIN 99
749 .float centerprint_priority;
750 .float centerprint_expires;
751 void centerprint_atprio(entity e, float prio, string s)
753 if (intermission_running)
754 if (prio < CENTERPRIO_MAPVOTE)
756 if (time > e.centerprint_expires)
757 e.centerprint_priority = 0;
758 if (prio >= e.centerprint_priority)
760 e.centerprint_priority = prio;
761 if (timeoutStatus == 2)
762 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
764 e.centerprint_expires = time + e.cvar_scr_centertime;
765 centerprint_builtin(e, s);
768 void centerprint_expire(entity e, float prio)
770 if (prio == e.centerprint_priority)
772 e.centerprint_priority = 0;
773 centerprint_builtin(e, "");
776 void centerprint(entity e, string s)
778 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
781 // decolorizes and team colors the player name when needed
782 string playername(entity p)
785 if (teams_matter && !intermission_running && p.classname == "player")
787 t = Team_ColorCode(p.team);
788 return strcat(t, strdecolorize(p.netname));
794 vector randompos(vector m1, vector m2)
798 v_x = m2_x * random() + m1_x;
799 v_y = m2_y * random() + m1_y;
800 v_z = m2_z * random() + m1_z;
804 float g_pickup_shells;
805 float g_pickup_shells_max;
806 float g_pickup_nails;
807 float g_pickup_nails_max;
808 float g_pickup_rockets;
809 float g_pickup_rockets_max;
810 float g_pickup_cells;
811 float g_pickup_cells_max;
813 float g_pickup_fuel_jetpack;
814 float g_pickup_fuel_max;
815 float g_pickup_armorsmall;
816 float g_pickup_armorsmall_max;
817 float g_pickup_armormedium;
818 float g_pickup_armormedium_max;
819 float g_pickup_armorbig;
820 float g_pickup_armorbig_max;
821 float g_pickup_armorlarge;
822 float g_pickup_armorlarge_max;
823 float g_pickup_healthsmall;
824 float g_pickup_healthsmall_max;
825 float g_pickup_healthmedium;
826 float g_pickup_healthmedium_max;
827 float g_pickup_healthlarge;
828 float g_pickup_healthlarge_max;
829 float g_pickup_healthmega;
830 float g_pickup_healthmega_max;
832 float g_weaponarena_random;
833 string g_weaponarena_list;
834 float g_weaponspeedfactor;
835 float g_weaponratefactor;
836 float g_weapondamagefactor;
837 float g_weaponforcefactor;
838 float g_weaponspreadfactor;
842 float start_ammo_shells;
843 float start_ammo_nails;
844 float start_ammo_rockets;
845 float start_ammo_cells;
846 float start_ammo_fuel;
848 float start_armorvalue;
849 float warmup_start_weapons;
850 float warmup_start_ammo_shells;
851 float warmup_start_ammo_nails;
852 float warmup_start_ammo_rockets;
853 float warmup_start_ammo_cells;
854 float warmup_start_ammo_fuel;
855 float warmup_start_health;
856 float warmup_start_armorvalue;
860 entity get_weaponinfo(float w);
862 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
864 var float i = weaponinfo.weapon;
869 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
871 if (t < 0) // "default" weapon selection
873 if (g_lms || g_ca || allguns)
874 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
877 else if (g_race || g_cts)
878 t = (i == WEP_LASER);
880 t = 0; // weapon is set a few lines later
882 t = (i == WEP_LASER || i == WEP_SHOTGUN);
883 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
884 t |= (i == WEP_HOOK);
887 // we cannot disable porto in Nexball, we must force it
888 if(g_nexball && i == WEP_PORTO)
894 float NixNex_CanChooseWeapon(float wpn);
895 void readplayerstartcvars()
901 // initialize starting values for players
904 start_ammo_shells = 0;
905 start_ammo_nails = 0;
906 start_ammo_rockets = 0;
907 start_ammo_cells = 0;
908 start_health = cvar("g_balance_health_start");
909 start_armorvalue = cvar("g_balance_armor_start");
912 s = cvar_string("g_weaponarena");
918 g_weaponarena_list = "All Weapons";
919 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
921 e = get_weaponinfo(j);
922 g_weaponarena |= e.weapons;
923 weapon_action(e.weapon, WR_PRECACHE);
926 else if (s == "most")
928 g_weaponarena_list = "Most Weapons";
929 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
931 e = get_weaponinfo(j);
932 if (e.spawnflags & WEP_FLAG_NORMAL)
934 g_weaponarena |= e.weapons;
935 weapon_action(e.weapon, WR_PRECACHE);
939 else if (s == "none")
941 g_weaponarena_list = "No Weapons";
942 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
946 t = tokenize_console(s);
947 g_weaponarena_list = "";
948 for (i = 0; i < t; ++i)
951 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
953 e = get_weaponinfo(j);
956 g_weaponarena |= e.weapons;
957 weapon_action(e.weapon, WR_PRECACHE);
958 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
964 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
967 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
971 g_weaponarena_random = cvar("g_weaponarena_random");
973 g_weaponarena_random = 0;
978 // will be done later
979 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
980 if (NixNex_CanChooseWeapon(i))
981 weapon_action(i, WR_PRECACHE);
982 if(!cvar("g_use_ammunition"))
983 start_items |= IT_UNLIMITED_AMMO;
985 else if (g_weaponarena)
987 start_weapons = g_weaponarena;
988 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
989 start_ammo_rockets = 999;
990 if (g_weaponarena & WEPBIT_SHOTGUN)
991 start_ammo_shells = 999;
992 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
993 start_ammo_cells = 999;
994 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
995 start_ammo_nails = 999;
996 if (g_weaponarena & WEPBIT_HOOK)
997 start_ammo_fuel = 999;
998 start_items |= IT_UNLIMITED_AMMO;
1000 else if (g_minstagib)
1003 start_armorvalue = 0;
1004 start_weapons = WEPBIT_MINSTANEX;
1005 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
1006 start_ammo_cells = cvar("g_minstagib_ammo_start");
1007 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
1008 start_ammo_fuel = cvar("g_start_ammo_fuel");
1010 if (g_minstagib_invis_alpha <= 0)
1011 g_minstagib_invis_alpha = -1;
1017 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1018 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1019 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1020 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1021 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1022 start_health = cvar("g_lms_start_health");
1023 start_armorvalue = cvar("g_lms_start_armor");
1025 else if (cvar("g_use_ammunition"))
1027 start_ammo_shells = cvar("g_start_ammo_shells");
1028 start_ammo_nails = cvar("g_start_ammo_nails");
1029 start_ammo_rockets = cvar("g_start_ammo_rockets");
1030 start_ammo_cells = cvar("g_start_ammo_cells");
1031 start_ammo_fuel = cvar("g_start_ammo_fuel");
1035 start_ammo_shells = cvar("g_pickup_shells_max");
1036 start_ammo_nails = cvar("g_pickup_nails_max");
1037 start_ammo_rockets = cvar("g_pickup_rockets_max");
1038 start_ammo_cells = cvar("g_pickup_cells_max");
1039 start_ammo_fuel = cvar("g_pickup_fuel_max");
1040 start_items |= IT_UNLIMITED_AMMO;
1043 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1045 e = get_weaponinfo(i);
1046 if(want_weapon("g_start_weapon_", e, FALSE))
1048 start_weapons |= e.weapons;
1049 weapon_action(e.weapon, WR_PRECACHE);
1056 warmup_start_ammo_shells = start_ammo_shells;
1057 warmup_start_ammo_nails = start_ammo_nails;
1058 warmup_start_ammo_rockets = start_ammo_rockets;
1059 warmup_start_ammo_cells = start_ammo_cells;
1060 warmup_start_ammo_fuel = start_ammo_fuel;
1061 warmup_start_health = start_health;
1062 warmup_start_armorvalue = start_armorvalue;
1063 warmup_start_weapons = start_weapons;
1065 if (!g_weaponarena && !g_nixnex && !g_minstagib && !g_ca)
1067 if (cvar("g_use_ammunition"))
1069 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1070 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1071 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1072 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1073 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1075 warmup_start_health = cvar("g_warmup_start_health");
1076 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1077 warmup_start_weapons = 0;
1078 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1080 e = get_weaponinfo(i);
1081 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1083 warmup_start_weapons |= e.weapons;
1084 weapon_action(e.weapon, WR_PRECACHE);
1090 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1092 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1093 start_items |= IT_FUEL_REGEN;
1094 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1095 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1099 start_items |= IT_JETPACK;
1101 if (g_weapon_stay == 2)
1103 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1104 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1105 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1106 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1107 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1108 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1109 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1110 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1111 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1112 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1115 start_ammo_shells = max(0, start_ammo_shells);
1116 start_ammo_nails = max(0, start_ammo_nails);
1117 start_ammo_cells = max(0, start_ammo_cells);
1118 start_ammo_rockets = max(0, start_ammo_rockets);
1119 start_ammo_fuel = max(0, start_ammo_fuel);
1121 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1122 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1123 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1124 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1125 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1129 float g_bugrigs_planar_movement;
1130 float g_bugrigs_planar_movement_car_jumping;
1131 float g_bugrigs_reverse_spinning;
1132 float g_bugrigs_reverse_speeding;
1133 float g_bugrigs_reverse_stopping;
1134 float g_bugrigs_air_steering;
1135 float g_bugrigs_angle_smoothing;
1136 float g_bugrigs_friction_floor;
1137 float g_bugrigs_friction_brake;
1138 float g_bugrigs_friction_air;
1139 float g_bugrigs_accel;
1140 float g_bugrigs_speed_ref;
1141 float g_bugrigs_speed_pow;
1142 float g_bugrigs_steer;
1144 float g_touchexplode;
1145 float g_touchexplode_radius;
1146 float g_touchexplode_damage;
1147 float g_touchexplode_edgedamage;
1148 float g_touchexplode_force;
1155 float sv_pitch_fixyaw;
1157 float sv_accuracy_data_share;
1159 void readlevelcvars(void)
1161 g_bugrigs = cvar("g_bugrigs");
1162 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1163 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1164 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1165 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1166 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1167 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1168 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1169 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1170 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1171 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1172 g_bugrigs_accel = cvar("g_bugrigs_accel");
1173 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1174 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1175 g_bugrigs_steer = cvar("g_bugrigs_steer");
1177 g_touchexplode = cvar("g_touchexplode");
1178 g_touchexplode_radius = cvar("g_touchexplode_radius");
1179 g_touchexplode_damage = cvar("g_touchexplode_damage");
1180 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1181 g_touchexplode_force = cvar("g_touchexplode_force");
1183 #ifdef ALLOW_FORCEMODELS
1184 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1186 sv_loddistance1 = cvar("sv_loddistance1");
1187 sv_loddistance2 = cvar("sv_loddistance2");
1189 if(sv_loddistance2 <= sv_loddistance1)
1190 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1192 sv_clones = cvar("sv_clones");
1193 sv_gentle = cvar("sv_gentle");
1194 sv_foginterval = cvar("sv_foginterval");
1195 g_cloaked = cvar("g_cloaked");
1196 g_jump_grunt = cvar("g_jump_grunt");
1197 g_footsteps = cvar("g_footsteps");
1198 g_grappling_hook = cvar("g_grappling_hook");
1199 g_jetpack = cvar("g_jetpack");
1200 g_laserguided_missile = cvar("g_laserguided_missile");
1201 g_midair = cvar("g_midair");
1202 g_minstagib = cvar("g_minstagib");
1203 g_nixnex = cvar("g_nixnex");
1204 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1205 g_norecoil = cvar("g_norecoil");
1206 g_vampire = cvar("g_vampire");
1207 g_bloodloss = cvar("g_bloodloss");
1208 sv_maxidle = cvar("sv_maxidle");
1209 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1210 sv_pogostick = cvar("sv_pogostick");
1211 sv_doublejump = cvar("sv_doublejump");
1212 g_ctf_reverse = cvar("g_ctf_reverse");
1213 sv_autotaunt = cvar("sv_autotaunt");
1214 sv_taunt = cvar("sv_taunt");
1216 inWarmupStage = cvar("g_warmup");
1217 g_warmup_limit = cvar("g_warmup_limit");
1218 g_warmup_allguns = cvar("g_warmup_allguns");
1219 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1221 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1222 inWarmupStage = 0; // these modes cannot work together, sorry
1224 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1225 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1226 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1227 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1228 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1229 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1230 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1231 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1232 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1233 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1234 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1235 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1237 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1238 if (g_nixnex) g_weaponarena = 0;
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 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1632 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1633 precache_sound ("misc/null.wav");
1634 precache_sound ("misc/spawn.wav");
1635 precache_sound ("misc/talk.wav");
1636 precache_sound ("misc/teleport.wav");
1637 precache_sound ("misc/poweroff.wav");
1638 precache_sound ("player/lava.wav");
1639 precache_sound ("player/slime.wav");
1642 precache_sound ("misc/jetpack_fly.wav");
1644 precache_model ("models/sprites/0.spr32");
1645 precache_model ("models/sprites/1.spr32");
1646 precache_model ("models/sprites/2.spr32");
1647 precache_model ("models/sprites/3.spr32");
1648 precache_model ("models/sprites/4.spr32");
1649 precache_model ("models/sprites/5.spr32");
1650 precache_model ("models/sprites/6.spr32");
1651 precache_model ("models/sprites/7.spr32");
1652 precache_model ("models/sprites/8.spr32");
1653 precache_model ("models/sprites/9.spr32");
1654 precache_model ("models/sprites/10.spr32");
1656 // common weapon precaches
1657 precache_sound ("weapons/weapon_switch.wav");
1658 precache_sound ("weapons/weaponpickup.wav");
1659 precache_sound ("weapons/unavailable.wav");
1660 if (g_grappling_hook)
1662 precache_sound ("weapons/hook_fire.wav"); // hook
1663 precache_sound ("weapons/hook_impact.wav"); // hook
1666 if (cvar("sv_precacheweapons") || g_nixnex)
1668 //precache weapon models/sounds
1671 while (wep <= WEP_LAST)
1673 weapon_action(wep, WR_PRECACHE);
1678 precache_model("models/elaser.mdl");
1679 precache_model("models/laser.mdl");
1680 precache_model("models/ebomb.mdl");
1683 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1685 if (!self.noise && self.music) // quake 3 uses the music field
1686 self.noise = self.music;
1688 // plays music for the level if there is any
1691 precache_sound (self.noise);
1692 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1697 // sorry, but using \ in macros breaks line numbers
1698 #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
1699 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1700 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1702 // WARNING: this kills the trace globals
1703 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1704 #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init()
1706 #define INITPRIO_FIRST 0
1707 #define INITPRIO_GAMETYPE 0
1708 #define INITPRIO_GAMETYPE_FALLBACK 1
1709 #define INITPRIO_CVARS 5
1710 #define INITPRIO_FINDTARGET 10
1711 #define INITPRIO_DROPTOFLOOR 20
1712 #define INITPRIO_SETLOCATION 90
1713 #define INITPRIO_LINKDOORS 91
1714 #define INITPRIO_LAST 99
1716 .void(void) initialize_entity;
1717 .float initialize_entity_order;
1718 .entity initialize_entity_next;
1719 entity initialize_entity_first;
1721 void make_safe_for_remove(entity e)
1723 if (e.initialize_entity)
1726 for (ent = initialize_entity_first; ent; )
1728 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1730 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1731 // skip it in linked list
1734 prev.initialize_entity_next = ent.initialize_entity_next;
1735 ent = prev.initialize_entity_next;
1739 initialize_entity_first = ent.initialize_entity_next;
1740 ent = initialize_entity_first;
1746 ent = ent.initialize_entity_next;
1752 void objerror(string s)
1754 make_safe_for_remove(self);
1755 objerror_builtin(s);
1758 void remove_unsafely(entity e)
1763 void remove_safely(entity e)
1765 make_safe_for_remove(e);
1769 void InitializeEntity(entity e, void(void) func, float order)
1773 if (!e || e.initialize_entity)
1775 // make a proxy initializer entity
1779 e.classname = "initialize_entity";
1783 e.initialize_entity = func;
1784 e.initialize_entity_order = order;
1786 cur = initialize_entity_first;
1789 if (!cur || cur.initialize_entity_order > order)
1791 // insert between prev and cur
1793 prev.initialize_entity_next = e;
1795 initialize_entity_first = e;
1796 e.initialize_entity_next = cur;
1800 cur = cur.initialize_entity_next;
1803 void InitializeEntitiesRun()
1806 startoflist = initialize_entity_first;
1807 initialize_entity_first = world;
1808 for (self = startoflist; self; )
1811 var void(void) func;
1812 e = self.initialize_entity_next;
1813 func = self.initialize_entity;
1814 self.initialize_entity_order = 0;
1815 self.initialize_entity = func_null;
1816 self.initialize_entity_next = world;
1817 if (self.classname == "initialize_entity")
1821 remove_builtin(self);
1824 //dprint("Delayed initialization: ", self.classname, "\n");
1830 .float uncustomizeentityforclient_set;
1831 .void(void) uncustomizeentityforclient;
1832 void(void) SUB_Nullpointer = #0;
1833 void UncustomizeEntitiesRun()
1837 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1838 self.uncustomizeentityforclient();
1841 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1843 e.customizeentityforclient = customizer;
1844 e.uncustomizeentityforclient = uncustomizer;
1845 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1849 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1852 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1856 if (e.classname == "")
1857 e.classname = "net_linked";
1859 if (e.model == "" || self.modelindex == 0)
1863 setmodel(e, "null");
1867 e.SendEntity = sendfunc;
1868 e.SendFlags = 0xFFFFFF;
1871 e.effects |= EF_NODEPTHTEST;
1875 e.nextthink = time + dt;
1876 e.think = SUB_Remove;
1880 void adaptor_think2touch()
1889 void adaptor_think2use()
1901 // deferred dropping
1902 void DropToFloor_Handler()
1904 droptofloor_builtin();
1905 self.dropped_origin = self.origin;
1910 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1915 float trace_hits_box_a0, trace_hits_box_a1;
1917 float trace_hits_box_1d(float end, float thmi, float thma)
1921 // just check if x is in range
1929 // do the trace with respect to x
1930 // 0 -> end has to stay in thmi -> thma
1931 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1932 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1933 if (trace_hits_box_a0 > trace_hits_box_a1)
1939 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1944 // now it is a trace from 0 to end
1946 trace_hits_box_a0 = 0;
1947 trace_hits_box_a1 = 1;
1949 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1951 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1953 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1959 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1961 return trace_hits_box(start, end, thmi - ma, thma - mi);
1964 float SUB_NoImpactCheck()
1966 // zero hitcontents = this is not the real impact, but either the
1967 // mirror-impact of something hitting the projectile instead of the
1968 // projectile hitting the something, or a touchareagrid one. Neither of
1969 // these stop the projectile from moving, so...
1970 if(trace_dphitcontents == 0)
1972 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1975 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1977 if (other == world && self.size != '0 0 0')
1980 tic = self.velocity * sys_frametime;
1981 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1982 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1983 if (trace_fraction >= 1)
1985 dprint("Odd... did not hit...?\n");
1987 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1989 dprint("Detected and prevented the sky-grapple bug.\n");
1997 #define SUB_OwnerCheck() (other && (other == self.owner))
1999 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
2001 if(SUB_OwnerCheck())
2003 if(SUB_NoImpactCheck())
2008 if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2009 UpdateCSQCProjectileNextFrame(self);
2012 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2014 float MAX_IPBAN_URIS = 16;
2016 float URI_GET_DISCARD = 0;
2017 float URI_GET_IPBAN = 1;
2018 float URI_GET_IPBAN_END = 16;
2020 void URI_Get_Callback(float id, float status, string data)
2022 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2024 dprint("\nEnd of data.\n");
2026 if (id == URI_GET_DISCARD)
2030 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2033 OnlineBanList_URI_Get_Callback(id, status, data);
2037 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2041 void print_to(entity e, string s)
2044 sprint(e, strcat(s, "\n"));
2049 string getrecords(float page) // 50 records per page
2063 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2065 if (MapInfo_Get_ByID(i))
2067 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2070 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2071 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2079 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2081 if (MapInfo_Get_ByID(i))
2083 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2086 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2087 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2095 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2097 if (MapInfo_Get_ByID(i))
2099 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2102 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2103 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2109 MapInfo_ClearTemps();
2111 if (s == "" && page == 0)
2112 return "No records are available on this server.\n";
2117 string getrankings()
2130 for (i = 1; i <= RANKINGS_CNT; ++i)
2132 t = race_GetTime(i);
2135 n = race_GetName(i);
2136 p = race_PlaceName(i);
2137 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2140 MapInfo_ClearTemps();
2143 return strcat("No records are available for the map: ", map, "\n");
2145 return strcat("Records for ", map, ":\n", s);
2148 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2151 vector start, org, delta, end, enddown, mstart;
2153 m = e.dphitcontentsmask;
2154 e.dphitcontentsmask = goodcontents | badcontents;
2157 delta = world.maxs - world.mins;
2159 for (i = 0; i < attempts; ++i)
2161 start_x = org_x + random() * delta_x;
2162 start_y = org_y + random() * delta_y;
2163 start_z = org_z + random() * delta_z;
2165 // rule 1: start inside world bounds, and outside
2166 // solid, and don't start from somewhere where you can
2167 // fall down to evil
2168 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2169 if (trace_fraction >= 1)
2171 if (trace_startsolid)
2173 if (trace_dphitcontents & badcontents)
2175 if (trace_dphitq3surfaceflags & badsurfaceflags)
2178 // rule 2: if we are too high, lower the point
2179 if (trace_fraction * delta_z > maxaboveground)
2180 start = trace_endpos + '0 0 1' * maxaboveground;
2181 enddown = trace_endpos;
2183 // rule 3: make sure we aren't outside the map. This only works
2184 // for somewhat well formed maps. A good rule of thumb is that
2185 // the map should have a convex outside hull.
2186 // these can be traceLINES as we already verified the starting box
2187 mstart = start + 0.5 * (e.mins + e.maxs);
2188 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2189 if (trace_fraction >= 1)
2191 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2192 if (trace_fraction >= 1)
2194 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2195 if (trace_fraction >= 1)
2197 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2198 if (trace_fraction >= 1)
2200 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2201 if (trace_fraction >= 1)
2204 // find a random vector to "look at"
2205 end_x = org_x + random() * delta_x;
2206 end_y = org_y + random() * delta_y;
2207 end_z = org_z + random() * delta_z;
2208 end = start + normalize(end - start) * vlen(delta);
2210 // rule 4: start TO end must not be too short
2211 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2212 if (trace_startsolid)
2214 if (trace_fraction < minviewdistance / vlen(delta))
2217 // rule 5: don't want to look at sky
2218 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2221 // rule 6: we must not end up in trigger_hurt
2222 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2224 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2231 e.dphitcontentsmask = m;
2235 setorigin(e, start);
2236 e.angles = vectoangles(end - start);
2237 dprint("Needed ", ftos(i + 1), " attempts\n");
2244 float zcurveparticles_effectno;
2245 vector zcurveparticles_start;
2246 float zcurveparticles_spd;
2248 void endzcurveparticles()
2250 if(zcurveparticles_effectno)
2253 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2255 zcurveparticles_effectno = 0;
2258 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2260 spd = bound(0, floor(spd / 16), 32767);
2261 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2263 endzcurveparticles();
2264 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2265 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2266 WriteShort(MSG_BROADCAST, effectno);
2267 WriteCoord(MSG_BROADCAST, start_x);
2268 WriteCoord(MSG_BROADCAST, start_y);
2269 WriteCoord(MSG_BROADCAST, start_z);
2270 zcurveparticles_effectno = effectno;
2271 zcurveparticles_start = start;
2274 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2275 WriteCoord(MSG_BROADCAST, end_x);
2276 WriteCoord(MSG_BROADCAST, end_y);
2277 WriteCoord(MSG_BROADCAST, end_z);
2278 WriteCoord(MSG_BROADCAST, end_dz);
2279 zcurveparticles_spd = spd;
2282 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2285 vector vecxy, velxy;
2287 vecxy = end - start;
2292 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2294 endzcurveparticles();
2295 trailparticles(world, effectno, start, end);
2299 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2300 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2303 string GetGametype(); // g_world.qc
2304 void write_recordmarker(entity pl, float tstart, float dt)
2306 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2308 // also write a marker into demo files for demotc-race-record-extractor to find
2311 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2312 " ", ftos(tstart), " ", ftos(dt), "\n"));
2315 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2317 switch(self.owner.cvar_cl_gunalign)
2328 if(allowcenter) // 2: allow center handedness
2341 if(allowcenter) // 2: allow center handedness
2357 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2362 if (cvar("g_shootfromeye"))
2366 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2374 else if (cvar("g_shootfromcenter"))
2378 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2386 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2396 else if (cvar("g_shootfromclient"))
2398 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2405 void attach_sameorigin(entity e, entity to, string tag)
2407 vector org, t_forward, t_left, t_up, e_forward, e_up;
2414 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2415 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2416 t_forward = v_forward * tagscale;
2417 t_left = v_right * -tagscale;
2418 t_up = v_up * tagscale;
2420 e.origin_x = org * t_forward;
2421 e.origin_y = org * t_left;
2422 e.origin_z = org * t_up;
2424 // current forward and up directions
2425 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2426 e.angles_x = -e.angles_x;
2427 fixedmakevectors(e.angles);
2429 // untransform forward, up!
2430 e_forward_x = v_forward * t_forward;
2431 e_forward_y = v_forward * t_left;
2432 e_forward_z = v_forward * t_up;
2433 e_up_x = v_up * t_forward;
2434 e_up_y = v_up * t_left;
2435 e_up_z = v_up * t_up;
2437 e.angles = fixedvectoangles2(e_forward, e_up);
2438 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2439 e.angles_x = -e.angles_x;
2441 setattachment(e, to, tag);
2442 setorigin(e, e.origin);
2445 void detach_sameorigin(entity e)
2448 org = gettaginfo(e, 0);
2449 e.angles = fixedvectoangles2(v_forward, v_up);
2450 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2451 e.angles_x = -e.angles_x;
2453 setattachment(e, world, "");
2454 setorigin(e, e.origin);
2457 void follow_sameorigin(entity e, entity to)
2459 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2460 e.aiment = to; // make the hole follow bmodel
2461 e.punchangle = to.angles; // the original angles of bmodel
2462 e.view_ofs = e.origin - to.origin; // relative origin
2463 e.v_angle = e.angles - to.angles; // relative angles
2466 void unfollow_sameorigin(entity e)
2468 e.movetype = MOVETYPE_NONE;
2471 entity gettaginfo_relative_ent;
2472 vector gettaginfo_relative(entity e, float tag)
2474 if (!gettaginfo_relative_ent)
2476 gettaginfo_relative_ent = spawn();
2477 gettaginfo_relative_ent.effects = EF_NODRAW;
2479 gettaginfo_relative_ent.model = e.model;
2480 gettaginfo_relative_ent.modelindex = e.modelindex;
2481 gettaginfo_relative_ent.frame = e.frame;
2482 return gettaginfo(gettaginfo_relative_ent, tag);
2485 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2489 if (pl.soundentity.cnt & p)
2491 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2492 pl.soundentity.cnt |= p;
2495 void SoundEntity_StopSound(entity pl, float chan)
2499 if (pl.soundentity.cnt & p)
2501 stopsoundto(MSG_ALL, pl.soundentity, chan);
2502 pl.soundentity.cnt &~= p;
2506 void SoundEntity_Attach(entity pl)
2508 pl.soundentity = spawn();
2509 pl.soundentity.classname = "soundentity";
2510 pl.soundentity.owner = pl;
2511 setattachment(pl.soundentity, pl, "");
2512 setmodel(pl.soundentity, "null");
2515 void SoundEntity_Detach(entity pl)
2518 for (i = 0; i <= 7; ++i)
2519 SoundEntity_StopSound(pl, i);
2523 float ParseCommandPlayerSlotTarget_firsttoken;
2524 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2532 ParseCommandPlayerSlotTarget_firsttoken = -1;
2536 if (substring(argv(idx), 0, 1) == "#")
2538 s = substring(argv(idx), 1, -1);
2546 ParseCommandPlayerSlotTarget_firsttoken = idx;
2547 if (s == ftos(stof(s)))
2549 e = edict_num(stof(s));
2550 if (e.flags & FL_CLIENT)
2556 // it must be a nick name
2559 ParseCommandPlayerSlotTarget_firsttoken = idx;
2562 FOR_EACH_CLIENT(head)
2563 if (head.netname == s)
2571 s = strdecolorize(s);
2573 FOR_EACH_CLIENT(head)
2574 if (strdecolorize(head.netname) == s)
2589 float modeleffect_SendEntity(entity to, float sf)
2592 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2595 if(self.velocity != '0 0 0')
2597 if(self.angles != '0 0 0')
2599 if(self.avelocity != '0 0 0')
2602 WriteByte(MSG_ENTITY, f);
2603 WriteShort(MSG_ENTITY, self.modelindex);
2604 WriteByte(MSG_ENTITY, self.skin);
2605 WriteByte(MSG_ENTITY, self.frame);
2606 WriteCoord(MSG_ENTITY, self.origin_x);
2607 WriteCoord(MSG_ENTITY, self.origin_y);
2608 WriteCoord(MSG_ENTITY, self.origin_z);
2611 WriteCoord(MSG_ENTITY, self.velocity_x);
2612 WriteCoord(MSG_ENTITY, self.velocity_y);
2613 WriteCoord(MSG_ENTITY, self.velocity_z);
2617 WriteCoord(MSG_ENTITY, self.angles_x);
2618 WriteCoord(MSG_ENTITY, self.angles_y);
2619 WriteCoord(MSG_ENTITY, self.angles_z);
2623 WriteCoord(MSG_ENTITY, self.avelocity_x);
2624 WriteCoord(MSG_ENTITY, self.avelocity_y);
2625 WriteCoord(MSG_ENTITY, self.avelocity_z);
2627 WriteShort(MSG_ENTITY, self.scale * 256.0);
2628 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2629 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2630 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2631 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2636 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)
2641 e.classname = "modeleffect";
2649 e.teleport_time = t1;
2653 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2657 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2660 sz = max(e.scale, e.scale2);
2661 setsize(e, e.mins * sz, e.maxs * sz);
2662 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2665 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2667 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2670 float randombit(float bits)
2672 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2681 for(f = 1; f <= bits; f *= 2)
2690 r = (r - 1) / (n - 1);
2697 float randombits(float bits, float k, float error_return)
2701 while(k > 0 && bits != r)
2703 r += randombit(bits - r);
2712 void randombit_test(float bits, float iter)
2716 print(ftos(randombit(bits)), "\n");
2721 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2723 if(halflifedist > 0)
2724 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2725 else if(halflifedist < 0)
2726 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2735 #define cvar_string_normal cvar_string_builtin
2736 #define cvar_normal cvar_builtin
2738 string cvar_string_normal(string n)
2740 if not(cvar_type(n) & 1)
2741 backtrace(strcat("Attempt to access undefined cvar: ", n));
2742 return cvar_string_builtin(n);
2745 float cvar_normal(string n)
2747 return stof(cvar_string_normal(n));
2750 #define cvar_set_normal cvar_set_builtin
2758 oself.think = SUB_Remove;
2759 oself.nextthink = time;
2765 Execute func() after time + fdelay.
2766 self when func is executed = self when defer is called
2768 void defer(float fdelay, void() func)
2775 e.think = defer_think;
2776 e.nextthink = time + fdelay;