1 var void remove(entity e);
2 void objerror(string s);
4 .vector dropped_origin;
6 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
8 string ColoredTeamName(float t);
10 string admin_name(void)
12 if(cvar_string("sv_adminnick") != "")
13 return cvar_string("sv_adminnick");
15 return "SERVER ADMIN";
18 float DistributeEvenly_amount;
19 float DistributeEvenly_totalweight;
20 void DistributeEvenly_Init(float amount, float totalweight)
22 if (DistributeEvenly_amount)
24 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
25 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
28 DistributeEvenly_amount = 0;
30 DistributeEvenly_amount = amount;
31 DistributeEvenly_totalweight = totalweight;
33 float DistributeEvenly_Get(float weight)
38 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
39 DistributeEvenly_totalweight -= weight;
40 DistributeEvenly_amount -= f;
44 void move_out_of_solid_expand(entity e, vector by)
47 tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
50 if (trace_fraction < 1)
53 // adjust origin in the other direction...
54 setorigin(e,e.origin - by * (1 - trace_fraction));
58 float move_out_of_solid(entity e)
63 traceline(o, o, MOVE_WORLDONLY, e);
67 tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
68 if (!trace_startsolid)
75 move_out_of_solid_expand(e, '1 0 0' * m0_x);
77 move_out_of_solid_expand(e, '1 0 0' * m1_x);
79 move_out_of_solid_expand(e, '0 1 0' * m0_y);
81 move_out_of_solid_expand(e, '0 1 0' * m1_y);
83 move_out_of_solid_expand(e, '0 0 1' * m0_z);
85 move_out_of_solid_expand(e, '0 0 1' * m1_z);
87 setorigin(e, e.origin);
89 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
99 string STR_PLAYER = "player";
100 string STR_SPECTATOR = "spectator";
101 string STR_OBSERVER = "observer";
104 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
105 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
106 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
107 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
109 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
110 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
111 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
112 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
113 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
116 // copies a string to a tempstring (so one can strunzone it)
117 string strcat1(string s) = #115; // FRIK_FILE
122 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
124 local float nPlayerHealth = rint(enPlayer.health);
125 local float nPlayerArmor = rint(enPlayer.armorvalue);
126 local float nPlayerHandicap = enPlayer.cvar_cl_handicap;
127 local float nPlayerPing = rint(enPlayer.ping);
128 local string strPlayerPingColor;
129 local string strMessage;
130 if(nPlayerPing >= 150)
131 strPlayerPingColor = "^1";
133 strPlayerPingColor = "^2";
135 if((cvar("sv_fragmessage_information_stats")) && (enPlayer.health >= 1))
136 strMessage = strcat(strMessage, "\n^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
138 if(cvar("sv_fragmessage_information_ping")) {
139 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
140 strMessage = strcat(strMessage, "\n^7(^2Bot");
142 strMessage = strcat(strMessage, "\n^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
143 if(cvar("sv_fragmessage_information_handicap"))
144 if(cvar("sv_fragmessage_information_handicap") == 2)
145 if(nPlayerHandicap <= 1)
146 strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
148 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
149 else if not(nPlayerHandicap <= 1)
150 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
152 strMessage = strcat(strMessage, "^7)");
153 } else if(cvar("sv_fragmessage_information_handicap")) {
154 if(cvar("sv_fragmessage_information_handicap") == 2)
155 if(nPlayerHandicap <= 1)
156 strMessage = strcat(strMessage, "\n^7(Handicap ^2Off^7)");
158 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
159 else if(nPlayerHandicap > 1)
160 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
164 void bcenterprint(string s)
166 // TODO replace by MSG_ALL (would show it to spectators too, though)?
168 FOR_EACH_PLAYER(head)
169 if (clienttype(head) == CLIENTTYPE_REAL)
170 centerprint(head, s);
173 void GameLogEcho(string s)
178 if (cvar("sv_eventlog_files"))
183 matches = cvar("sv_eventlog_files_counter") + 1;
184 cvar_set("sv_eventlog_files_counter", ftos(matches));
187 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
188 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
189 logfile = fopen(fn, FILE_APPEND);
190 fputs(logfile, ":logversion:3\n");
194 if (cvar("sv_eventlog_files_timestamps"))
195 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
197 fputs(logfile, strcat(s, "\n"));
200 if (cvar("sv_eventlog_console"))
209 // will be opened later
214 if (logfile_open && logfile >= 0)
224 vector PL_CROUCH_VIEW_OFS;
225 vector PL_CROUCH_MIN;
226 vector PL_CROUCH_MAX;
228 float spawnpoint_nag;
229 void relocate_spawnpoint()
231 PL_VIEW_OFS = stov(cvar_string("sv_player_viewoffset"));
232 PL_MIN = stov(cvar_string("sv_player_mins"));
233 PL_MAX = stov(cvar_string("sv_player_maxs"));
234 PL_CROUCH_VIEW_OFS = stov(cvar_string("sv_player_crouch_viewoffset"));
235 PL_CROUCH_MIN = stov(cvar_string("sv_player_crouch_mins"));
236 PL_CROUCH_MAX = stov(cvar_string("sv_player_crouch_maxs"));
238 // nudge off the floor
239 setorigin(self, self.origin + '0 0 1');
241 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
242 if (trace_startsolid)
248 if (!move_out_of_solid(self))
249 objerror("could not get out of solid at all!");
250 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
251 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
252 print(" ", ftos(self.origin_y - o_y));
253 print(" ", ftos(self.origin_z - o_z), "'\n");
254 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
257 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
263 self.mins = self.maxs = '0 0 0';
264 objerror("player spawn point in solid, mapper sucks!\n");
269 if (cvar("g_spawnpoints_autodrop"))
271 setsize(self, PL_MIN, PL_MAX);
275 self.use = spawnpoint_use;
276 self.team_saved = self.team;
280 if (have_team_spawns != 0)
282 have_team_spawns = 1;
284 if (cvar("r_showbboxes"))
286 // show where spawnpoints point at too
287 makevectors(self.angles);
290 e.classname = "info_player_foo";
291 setorigin(e, self.origin + v_forward * 24);
292 setsize(e, '-8 -8 -8', '8 8 8');
293 e.solid = SOLID_TRIGGER;
297 #define strstr strstrofs
299 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
300 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
301 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
302 // BE CONSTANT OR strzoneD!
303 float strstr(string haystack, string needle, float offset)
307 len = strlen(needle);
308 endpos = strlen(haystack) - len;
309 while(offset <= endpos)
311 found = substring(haystack, offset, len);
320 float NUM_NEAREST_ENTITIES = 4;
321 entity nearest_entity[NUM_NEAREST_ENTITIES];
322 float nearest_length[NUM_NEAREST_ENTITIES];
323 entity findnearest(vector point, .string field, string value, vector axismod)
334 localhead = find(world, field, value);
337 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
338 dist = localhead.oldorigin;
340 dist = localhead.origin;
342 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
345 for (i = 0; i < num_nearest; ++i)
347 if (len < nearest_length[i])
351 // now i tells us where to insert at
352 // INSERTION SORT! YOU'VE SEEN IT! RUN!
353 if (i < NUM_NEAREST_ENTITIES)
355 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
357 nearest_length[j + 1] = nearest_length[j];
358 nearest_entity[j + 1] = nearest_entity[j];
360 nearest_length[i] = len;
361 nearest_entity[i] = localhead;
362 if (num_nearest < NUM_NEAREST_ENTITIES)
363 num_nearest = num_nearest + 1;
366 localhead = find(localhead, field, value);
369 // now use the first one from our list that we can see
370 for (i = 0; i < num_nearest; ++i)
372 traceline(point, nearest_entity[i].origin, TRUE, world);
373 if (trace_fraction == 1)
377 dprint("Nearest point (");
378 dprint(nearest_entity[0].netname);
379 dprint(") is not visible, using a visible one.\n");
381 return nearest_entity[i];
385 if (num_nearest == 0)
388 dprint("Not seeing any location point, using nearest as fallback.\n");
390 dprint("Candidates were: ");
391 for(j = 0; j < num_nearest; ++j)
395 dprint(nearest_entity[j].netname);
400 return nearest_entity[0];
403 void spawnfunc_target_location()
405 self.classname = "target_location";
406 // location name in netname
407 // eventually support: count, teamgame selectors, line of sight?
410 void spawnfunc_info_location()
412 self.classname = "target_location";
413 self.message = self.netname;
416 string NearestLocation(vector p)
421 loc = findnearest(p, classname, "target_location", '1 1 1');
428 loc = findnearest(p, target, "###item###", '1 1 4');
435 string formatmessage(string msg)
446 break; // too many replacements
449 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
450 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
463 replacement = substring(msg, p, 2);
464 escape = substring(msg, p + 1, 1);
468 else if (escape == "\\")
470 else if (escape == "n")
472 else if (escape == "a")
473 replacement = ftos(floor(self.armorvalue));
474 else if (escape == "h")
475 replacement = ftos(floor(self.health));
476 else if (escape == "l")
477 replacement = NearestLocation(self.origin);
478 else if (escape == "y")
479 replacement = NearestLocation(self.cursor_trace_endpos);
480 else if (escape == "d")
481 replacement = NearestLocation(self.death_origin);
482 else if (escape == "w") {
486 wep = self.switchweapon;
489 replacement = W_Name(wep);
490 } else if (escape == "W") {
491 if (self.items & IT_SHELLS) replacement = "shells";
492 else if (self.items & IT_NAILS) replacement = "bullets";
493 else if (self.items & IT_ROCKETS) replacement = "rockets";
494 else if (self.items & IT_CELLS) replacement = "cells";
495 else replacement = "batteries"; // ;)
496 } else if (escape == "x") {
497 replacement = self.cursor_trace_ent.netname;
498 if (!replacement || !self.cursor_trace_ent)
499 replacement = "nothing";
500 } else if (escape == "p") {
501 if (self.last_selected_player)
502 replacement = self.last_selected_player.netname;
504 replacement = "(nobody)";
505 } else if (escape == "s")
506 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
507 else if (escape == "S")
508 replacement = ftos(vlen(self.velocity));
509 else if (escape == "v") {
513 if(self.classname == "spectator")
518 weapon_number = stats.weapon;
521 weapon_number = stats.switchweapon;
524 weapon_number = stats.cnt;
526 if(stats.cvar_cl_accuracy_data_share && stats.stats_fired[weapon_number - 1])
527 replacement = ftos(bound(0, floor(100 * stats.stats_hit[weapon_number - 1] / stats.stats_fired[weapon_number - 1]), 100));
529 replacement = "~"; // or something to indicate NULL, not available
532 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
533 p = p + strlen(replacement);
538 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
539 return (value == 0) ? FALSE : TRUE;
548 >0: receives a cvar from name=argv(f) value=argv(f+1)
550 void GetCvars_handleString(string thisname, float f, .string field, string name)
555 strunzone(self.field);
556 self.field = string_null;
560 if (thisname == name)
563 strunzone(self.field);
564 self.field = strzone(argv(f + 1));
568 stuffcmd(self, strcat("sendcvar ", name, "\n"));
570 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
572 GetCvars_handleString(thisname, f, field, name);
573 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
574 if (thisname == name)
577 s = func(strcat1(self.field));
580 strunzone(self.field);
581 self.field = strzone(s);
585 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
592 if (thisname == name)
593 self.field = stof(argv(f + 1));
596 stuffcmd(self, strcat("sendcvar ", name, "\n"));
598 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
605 if (thisname == name)
609 self.field = stof(argv(f + 1));
618 stuffcmd(self, strcat("sendcvar ", name, "\n"));
621 string W_FixWeaponOrder_ForceComplete(string s);
622 string W_FixWeaponOrder_AllowIncomplete(string s);
623 float w_getbestweapon(entity e);
624 void GetCvars(float f)
628 s = strcat1(argv(f));
629 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
630 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
631 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
632 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
633 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
634 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
635 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
636 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
637 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
638 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
639 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
640 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
641 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
642 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
643 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
644 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
645 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
646 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
647 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
648 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
649 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
650 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
651 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
652 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
654 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
655 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
657 #ifdef ALLOW_FORCEMODELS
658 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
659 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
661 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
663 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
666 if (s == "cl_weaponpriority")
667 self.switchweapon = w_getbestweapon(self);
671 float fexists(string f)
674 fh = fopen(f, FILE_READ);
681 void backtrace(string msg)
684 dev = cvar("developer");
685 war = cvar("prvm_backtraceforwarnings");
686 cvar_set("developer", "1");
687 cvar_set("prvm_backtraceforwarnings", "1");
689 dprint("--- CUT HERE ---\nWARNING: ");
692 remove(world); // isn't there any better way to cause a backtrace?
693 dprint("\n--- CUT UNTIL HERE ---\n");
694 cvar_set("developer", ftos(dev));
695 cvar_set("prvm_backtraceforwarnings", ftos(war));
698 string Team_ColorCode(float teamid)
700 if (teamid == COLOR_TEAM1)
702 else if (teamid == COLOR_TEAM2)
704 else if (teamid == COLOR_TEAM3)
706 else if (teamid == COLOR_TEAM4)
712 string Team_ColorName(float t)
714 // fixme: Search for team entities and get their .netname's!
715 if (t == COLOR_TEAM1)
717 if (t == COLOR_TEAM2)
719 if (t == COLOR_TEAM3)
721 if (t == COLOR_TEAM4)
726 string Team_ColorNameLowerCase(float t)
728 // fixme: Search for team entities and get their .netname's!
729 if (t == COLOR_TEAM1)
731 if (t == COLOR_TEAM2)
733 if (t == COLOR_TEAM3)
735 if (t == COLOR_TEAM4)
740 float ColourToNumber(string team_colour)
742 if (team_colour == "red")
745 if (team_colour == "blue")
748 if (team_colour == "yellow")
751 if (team_colour == "pink")
754 if (team_colour == "auto")
760 float NumberToTeamNumber(float number)
777 #define CENTERPRIO_POINT 1
778 #define CENTERPRIO_SPAM 2
779 #define CENTERPRIO_VOTE 4
780 #define CENTERPRIO_NORMAL 5
781 #define CENTERPRIO_SHIELDING 7
782 #define CENTERPRIO_MAPVOTE 9
783 #define CENTERPRIO_IDLEKICK 50
784 #define CENTERPRIO_ADMIN 99
785 .float centerprint_priority;
786 .float centerprint_expires;
787 void centerprint_atprio(entity e, float prio, string s)
789 if (intermission_running)
790 if (prio < CENTERPRIO_MAPVOTE)
792 if (time > e.centerprint_expires)
793 e.centerprint_priority = 0;
794 if (prio >= e.centerprint_priority)
796 e.centerprint_priority = prio;
797 if (timeoutStatus == 2)
798 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
800 e.centerprint_expires = time + e.cvar_scr_centertime;
801 centerprint_builtin(e, s);
804 void centerprint_expire(entity e, float prio)
806 if (prio == e.centerprint_priority)
808 e.centerprint_priority = 0;
809 centerprint_builtin(e, "");
812 void centerprint(entity e, string s)
814 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
817 // decolorizes and team colors the player name when needed
818 string playername(entity p)
821 if (teams_matter && !intermission_running && p.classname == "player")
823 t = Team_ColorCode(p.team);
824 return strcat(t, strdecolorize(p.netname));
830 vector randompos(vector m1, vector m2)
834 v_x = m2_x * random() + m1_x;
835 v_y = m2_y * random() + m1_y;
836 v_z = m2_z * random() + m1_z;
840 float g_pickup_shells;
841 float g_pickup_shells_max;
842 float g_pickup_nails;
843 float g_pickup_nails_max;
844 float g_pickup_rockets;
845 float g_pickup_rockets_max;
846 float g_pickup_cells;
847 float g_pickup_cells_max;
849 float g_pickup_fuel_jetpack;
850 float g_pickup_fuel_max;
851 float g_pickup_armorsmall;
852 float g_pickup_armorsmall_max;
853 float g_pickup_armormedium;
854 float g_pickup_armormedium_max;
855 float g_pickup_armorbig;
856 float g_pickup_armorbig_max;
857 float g_pickup_armorlarge;
858 float g_pickup_armorlarge_max;
859 float g_pickup_healthsmall;
860 float g_pickup_healthsmall_max;
861 float g_pickup_healthmedium;
862 float g_pickup_healthmedium_max;
863 float g_pickup_healthlarge;
864 float g_pickup_healthlarge_max;
865 float g_pickup_healthmega;
866 float g_pickup_healthmega_max;
868 float g_weaponarena_random;
869 string g_weaponarena_list;
870 float g_weaponspeedfactor;
871 float g_weaponratefactor;
872 float g_weapondamagefactor;
873 float g_weaponforcefactor;
877 float start_ammo_shells;
878 float start_ammo_nails;
879 float start_ammo_rockets;
880 float start_ammo_cells;
881 float start_ammo_fuel;
883 float start_armorvalue;
884 float warmup_start_weapons;
885 float warmup_start_ammo_shells;
886 float warmup_start_ammo_nails;
887 float warmup_start_ammo_rockets;
888 float warmup_start_ammo_cells;
889 float warmup_start_ammo_fuel;
890 float warmup_start_health;
891 float warmup_start_armorvalue;
895 entity get_weaponinfo(float w);
897 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
899 var float i = weaponinfo.weapon;
904 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
906 if (t < 0) // "default" weapon selection
908 if (g_lms || g_ca || allguns)
909 t = (weaponinfo.spawnflags & WEPSPAWNFLAG_NORMAL);
912 else if (g_race || g_cts)
913 t = (i == WEP_LASER);
915 t = 0; // weapon is set a few lines later
917 t = (i == WEP_LASER || i == WEP_SHOTGUN);
918 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
919 t |= (i == WEP_HOOK);
922 // we cannot disable porto in Nexball, we must force it
923 if(g_nexball && i == WEP_PORTO)
929 float NixNex_CanChooseWeapon(float wpn);
930 void readplayerstartcvars()
936 // initialize starting values for players
939 start_ammo_shells = 0;
940 start_ammo_nails = 0;
941 start_ammo_rockets = 0;
942 start_ammo_cells = 0;
943 start_health = cvar("g_balance_health_start");
944 start_armorvalue = cvar("g_balance_armor_start");
947 s = cvar_string("g_weaponarena");
953 g_weaponarena_list = "All Weapons";
954 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
956 e = get_weaponinfo(j);
957 g_weaponarena |= e.weapons;
958 weapon_action(e.weapon, WR_PRECACHE);
961 else if (s == "most")
963 g_weaponarena_list = "Most Weapons";
964 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
966 e = get_weaponinfo(j);
967 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
969 g_weaponarena |= e.weapons;
970 weapon_action(e.weapon, WR_PRECACHE);
974 else if (s == "none")
976 g_weaponarena_list = "No Weapons";
977 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
981 t = tokenize_console(s);
982 g_weaponarena_list = "";
983 for (i = 0; i < t; ++i)
986 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
988 e = get_weaponinfo(j);
991 g_weaponarena |= e.weapons;
992 weapon_action(e.weapon, WR_PRECACHE);
993 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
999 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
1002 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
1006 g_weaponarena_random = cvar("g_weaponarena_random");
1008 g_weaponarena_random = 0;
1013 // will be done later
1014 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1015 if (NixNex_CanChooseWeapon(i))
1016 weapon_action(i, WR_PRECACHE);
1017 if(!cvar("g_use_ammunition"))
1018 start_items |= IT_UNLIMITED_AMMO;
1020 else if (g_weaponarena)
1022 start_weapons = g_weaponarena;
1023 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
1024 start_ammo_rockets = 999;
1025 if (g_weaponarena & WEPBIT_SHOTGUN)
1026 start_ammo_shells = 999;
1027 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
1028 start_ammo_cells = 999;
1029 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
1030 start_ammo_nails = 999;
1031 if (g_weaponarena & WEPBIT_HOOK)
1032 start_ammo_fuel = 999;
1033 start_items |= IT_UNLIMITED_AMMO;
1035 else if (g_minstagib)
1038 start_armorvalue = 0;
1039 start_weapons = WEPBIT_MINSTANEX;
1040 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
1041 start_ammo_cells = cvar("g_minstagib_ammo_start");
1042 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
1043 start_ammo_fuel = cvar("g_start_ammo_fuel");
1045 if (g_minstagib_invis_alpha <= 0)
1046 g_minstagib_invis_alpha = -1;
1052 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1053 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1054 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1055 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1056 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1057 start_health = cvar("g_lms_start_health");
1058 start_armorvalue = cvar("g_lms_start_armor");
1060 else if (cvar("g_use_ammunition"))
1062 start_ammo_shells = cvar("g_start_ammo_shells");
1063 start_ammo_nails = cvar("g_start_ammo_nails");
1064 start_ammo_rockets = cvar("g_start_ammo_rockets");
1065 start_ammo_cells = cvar("g_start_ammo_cells");
1066 start_ammo_fuel = cvar("g_start_ammo_fuel");
1070 start_ammo_shells = cvar("g_pickup_shells_max");
1071 start_ammo_nails = cvar("g_pickup_nails_max");
1072 start_ammo_rockets = cvar("g_pickup_rockets_max");
1073 start_ammo_cells = cvar("g_pickup_cells_max");
1074 start_ammo_fuel = cvar("g_pickup_fuel_max");
1075 start_items |= IT_UNLIMITED_AMMO;
1078 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1080 e = get_weaponinfo(i);
1081 if(want_weapon("g_start_weapon_", e, FALSE))
1083 start_weapons |= e.weapons;
1084 weapon_action(e.weapon, WR_PRECACHE);
1091 warmup_start_ammo_shells = start_ammo_shells;
1092 warmup_start_ammo_nails = start_ammo_nails;
1093 warmup_start_ammo_rockets = start_ammo_rockets;
1094 warmup_start_ammo_cells = start_ammo_cells;
1095 warmup_start_ammo_fuel = start_ammo_fuel;
1096 warmup_start_health = start_health;
1097 warmup_start_armorvalue = start_armorvalue;
1098 warmup_start_weapons = start_weapons;
1100 if (!g_weaponarena && !g_nixnex && !g_minstagib && !g_ca)
1102 if (cvar("g_use_ammunition"))
1104 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1105 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1106 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1107 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1108 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1110 warmup_start_health = cvar("g_warmup_start_health");
1111 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1112 warmup_start_weapons = 0;
1113 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1115 e = get_weaponinfo(i);
1116 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1118 warmup_start_weapons |= e.weapons;
1119 weapon_action(e.weapon, WR_PRECACHE);
1125 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1127 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1128 start_items |= IT_FUEL_REGEN;
1129 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1130 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1134 start_items |= IT_JETPACK;
1136 if (g_weapon_stay == 2)
1138 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1139 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1140 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1141 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1142 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1143 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1144 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1145 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1146 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1147 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1150 start_ammo_shells = max(0, start_ammo_shells);
1151 start_ammo_nails = max(0, start_ammo_nails);
1152 start_ammo_cells = max(0, start_ammo_cells);
1153 start_ammo_rockets = max(0, start_ammo_rockets);
1154 start_ammo_fuel = max(0, start_ammo_fuel);
1156 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1157 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1158 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1159 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1160 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1164 float g_bugrigs_planar_movement;
1165 float g_bugrigs_planar_movement_car_jumping;
1166 float g_bugrigs_reverse_spinning;
1167 float g_bugrigs_reverse_speeding;
1168 float g_bugrigs_reverse_stopping;
1169 float g_bugrigs_air_steering;
1170 float g_bugrigs_angle_smoothing;
1171 float g_bugrigs_friction_floor;
1172 float g_bugrigs_friction_brake;
1173 float g_bugrigs_friction_air;
1174 float g_bugrigs_accel;
1175 float g_bugrigs_speed_ref;
1176 float g_bugrigs_speed_pow;
1177 float g_bugrigs_steer;
1179 float g_touchexplode;
1180 float g_touchexplode_radius;
1181 float g_touchexplode_damage;
1182 float g_touchexplode_edgedamage;
1183 float g_touchexplode_force;
1190 float sv_pitch_fixyaw;
1192 float sv_accuracy_data_share;
1194 void readlevelcvars(void)
1196 g_bugrigs = cvar("g_bugrigs");
1197 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1198 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1199 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1200 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1201 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1202 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1203 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1204 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1205 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1206 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1207 g_bugrigs_accel = cvar("g_bugrigs_accel");
1208 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1209 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1210 g_bugrigs_steer = cvar("g_bugrigs_steer");
1212 g_touchexplode = cvar("g_touchexplode");
1213 g_touchexplode_radius = cvar("g_touchexplode_radius");
1214 g_touchexplode_damage = cvar("g_touchexplode_damage");
1215 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1216 g_touchexplode_force = cvar("g_touchexplode_force");
1218 #ifdef ALLOW_FORCEMODELS
1219 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1221 sv_loddistance1 = cvar("sv_loddistance1");
1222 sv_loddistance2 = cvar("sv_loddistance2");
1224 if(sv_loddistance2 <= sv_loddistance1)
1225 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1227 sv_clones = cvar("sv_clones");
1228 sv_cheats = cvar("sv_cheats");
1229 sv_gentle = cvar("sv_gentle");
1230 sv_foginterval = cvar("sv_foginterval");
1231 g_cloaked = cvar("g_cloaked");
1232 g_jump_grunt = cvar("g_jump_grunt");
1233 g_footsteps = cvar("g_footsteps");
1234 g_grappling_hook = cvar("g_grappling_hook");
1235 g_jetpack = cvar("g_jetpack");
1236 g_laserguided_missile = cvar("g_laserguided_missile");
1237 g_midair = cvar("g_midair");
1238 g_minstagib = cvar("g_minstagib");
1239 g_nixnex = cvar("g_nixnex");
1240 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1241 g_norecoil = cvar("g_norecoil");
1242 g_vampire = cvar("g_vampire");
1243 g_bloodloss = cvar("g_bloodloss");
1244 sv_maxidle = cvar("sv_maxidle");
1245 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1246 sv_pogostick = cvar("sv_pogostick");
1247 sv_doublejump = cvar("sv_doublejump");
1248 g_ctf_reverse = cvar("g_ctf_reverse");
1249 sv_autotaunt = cvar("sv_autotaunt");
1250 sv_taunt = cvar("sv_taunt");
1252 inWarmupStage = cvar("g_warmup");
1253 g_warmup_limit = cvar("g_warmup_limit");
1254 g_warmup_allguns = cvar("g_warmup_allguns");
1255 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1257 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1258 inWarmupStage = 0; // these modes cannot work together, sorry
1260 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1261 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1262 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1263 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1264 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1265 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1266 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1267 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1268 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1269 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1270 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1271 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1273 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1274 if (g_nixnex) g_weaponarena = 0;
1277 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1278 g_weaponratefactor = cvar("g_weaponratefactor");
1279 g_weapondamagefactor = cvar("g_weapondamagefactor");
1280 g_weaponforcefactor = cvar("g_weaponforcefactor");
1282 g_pickup_shells = cvar("g_pickup_shells");
1283 g_pickup_shells_max = cvar("g_pickup_shells_max");
1284 g_pickup_nails = cvar("g_pickup_nails");
1285 g_pickup_nails_max = cvar("g_pickup_nails_max");
1286 g_pickup_rockets = cvar("g_pickup_rockets");
1287 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1288 g_pickup_cells = cvar("g_pickup_cells");
1289 g_pickup_cells_max = cvar("g_pickup_cells_max");
1290 g_pickup_fuel = cvar("g_pickup_fuel");
1291 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1292 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1293 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1294 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1295 g_pickup_armormedium = cvar("g_pickup_armormedium");
1296 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1297 g_pickup_armorbig = cvar("g_pickup_armorbig");
1298 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1299 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1300 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1301 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1302 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1303 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1304 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1305 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1306 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1307 g_pickup_healthmega = cvar("g_pickup_healthmega");
1308 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1310 g_pinata = cvar("g_pinata");
1312 g_weapon_stay = cvar("g_weapon_stay");
1314 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1317 g_ghost_items = cvar("g_ghost_items");
1319 if(g_ghost_items >= 1)
1320 g_ghost_items = 0.13; // default alpha value
1322 if not(inWarmupStage && !g_ca)
1323 game_starttime = cvar("g_start_delay");
1325 sv_pitch_min = cvar("sv_pitch_min");
1326 sv_pitch_max = cvar("sv_pitch_max");
1327 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1329 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1331 readplayerstartcvars();
1335 // TODO sound pack system
1338 string precache_sound_builtin (string s) = #19;
1339 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1340 string precache_sound(string s)
1342 return precache_sound_builtin(strcat(soundpack, s));
1344 void play2(entity e, string filename)
1346 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1348 void sound(entity e, float chan, string samp, float vol, float atten)
1350 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1355 string precache_sound (string s) = #19;
1356 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1357 float precache_sound_index (string s) = #19;
1359 #define SND_VOLUME 1
1360 #define SND_ATTENUATION 2
1361 #define SND_LARGEENTITY 8
1362 #define SND_LARGESOUND 16
1364 float sound_allowed(float dest, entity e)
1366 // sounds from world may always pass
1369 if (e.classname == "body")
1371 if (e.owner && e.owner != e)
1376 // sounds to self may always pass
1377 if (dest == MSG_ONE)
1378 if (e == msg_entity)
1380 // sounds by players can be removed
1381 if (cvar("bot_sound_monopoly"))
1382 if (clienttype(e) == CLIENTTYPE_REAL)
1384 // anything else may pass
1388 void sound(entity e, float chan, string samp, float vol, float atten)
1390 if (!sound_allowed(MSG_BROADCAST, e))
1392 sound_builtin(e, chan, samp, vol, atten);
1394 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1398 if (!sound_allowed(dest, e))
1401 entno = num_for_edict(e);
1402 idx = precache_sound_index(samp);
1407 atten = floor(atten * 64);
1408 vol = floor(vol * 255);
1411 sflags |= SND_VOLUME;
1413 sflags |= SND_ATTENUATION;
1415 sflags |= SND_LARGEENTITY;
1417 sflags |= SND_LARGESOUND;
1419 WriteByte(dest, SVC_SOUND);
1420 WriteByte(dest, sflags);
1421 if (sflags & SND_VOLUME)
1422 WriteByte(dest, vol);
1423 if (sflags & SND_ATTENUATION)
1424 WriteByte(dest, atten);
1425 if (sflags & SND_LARGEENTITY)
1427 WriteShort(dest, entno);
1428 WriteByte(dest, chan);
1432 WriteShort(dest, entno * 8 + chan);
1434 if (sflags & SND_LARGESOUND)
1435 WriteShort(dest, idx);
1437 WriteByte(dest, idx);
1439 WriteCoord(dest, o_x);
1440 WriteCoord(dest, o_y);
1441 WriteCoord(dest, o_z);
1443 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1447 if (!sound_allowed(dest, e))
1450 o = e.origin + 0.5 * (e.mins + e.maxs);
1451 soundtoat(dest, e, o, chan, samp, vol, atten);
1453 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1455 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1457 void stopsoundto(float dest, entity e, float chan)
1461 if (!sound_allowed(dest, e))
1464 entno = num_for_edict(e);
1469 idx = precache_sound_index("misc/null.wav");
1470 sflags = SND_LARGEENTITY;
1472 sflags |= SND_LARGESOUND;
1473 WriteByte(dest, SVC_SOUND);
1474 WriteByte(dest, sflags);
1475 WriteShort(dest, entno);
1476 WriteByte(dest, chan);
1477 if (sflags & SND_LARGESOUND)
1478 WriteShort(dest, idx);
1480 WriteByte(dest, idx);
1481 WriteCoord(dest, e.origin_x);
1482 WriteCoord(dest, e.origin_y);
1483 WriteCoord(dest, e.origin_z);
1487 WriteByte(dest, SVC_STOPSOUND);
1488 WriteShort(dest, entno * 8 + chan);
1491 void stopsound(entity e, float chan)
1493 if (!sound_allowed(MSG_BROADCAST, e))
1496 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1497 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1500 void play2(entity e, string filename)
1502 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1504 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1507 .float announcetime;
1508 float announce(entity player, string msg)
1510 if (time > player.announcetime)
1511 if (clienttype(player) == CLIENTTYPE_REAL)
1513 player.announcetime = time + 0.8;
1519 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1520 float spamsound(entity e, float chan, string samp, float vol, float atten)
1522 if (!sound_allowed(MSG_BROADCAST, e))
1525 if (time > e.announcetime)
1527 e.announcetime = time;
1528 sound(e, chan, samp, vol, atten);
1534 void play2team(float t, string filename)
1538 if (cvar("bot_sound_monopoly"))
1541 FOR_EACH_REALPLAYER(head)
1544 play2(head, filename);
1548 void play2all(string samp)
1550 if (cvar("bot_sound_monopoly"))
1553 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1556 void PrecachePlayerSounds(string f);
1557 void precache_all_models(string pattern)
1559 float globhandle, i, n;
1562 globhandle = search_begin(pattern, TRUE, FALSE);
1565 n = search_getsize(globhandle);
1566 for (i = 0; i < n; ++i)
1568 //print(search_getfilename(globhandle, i), "\n");
1569 f = search_getfilename(globhandle, i);
1572 if(substring(f, -9,5) == "_lod1")
1574 if(substring(f, -9,5) == "_lod2")
1576 if(!sv_loddistance1)
1578 PrecachePlayerSounds(strcat(f, ".sounds"));
1580 search_end(globhandle);
1585 // gamemode related things
1586 precache_model ("models/misc/chatbubble.spr");
1587 precache_model ("models/misc/teambubble.spr");
1590 precache_model ("models/runematch/curse.mdl");
1591 precache_model ("models/runematch/rune.mdl");
1594 #ifdef TTURRETS_ENABLED
1595 if (cvar("g_turrets"))
1599 // Precache all player models if desired
1600 if (cvar("sv_precacheplayermodels"))
1602 PrecachePlayerSounds("sound/player/default.sounds");
1603 precache_all_models("models/player/*.zym");
1604 precache_all_models("models/player/*.dpm");
1605 precache_all_models("models/player/*.md3");
1606 precache_all_models("models/player/*.psk");
1607 //precache_model("models/player/carni.zym");
1608 //precache_model("models/player/crash.zym");
1609 //precache_model("models/player/grunt.zym");
1610 //precache_model("models/player/headhunter.zym");
1611 //precache_model("models/player/insurrectionist.zym");
1612 //precache_model("models/player/jeandarc.zym");
1613 //precache_model("models/player/lurk.zym");
1614 //precache_model("models/player/lycanthrope.zym");
1615 //precache_model("models/player/marine.zym");
1616 //precache_model("models/player/nexus.zym");
1617 //precache_model("models/player/pyria.zym");
1618 //precache_model("models/player/shock.zym");
1619 //precache_model("models/player/skadi.zym");
1620 //precache_model("models/player/specop.zym");
1621 //precache_model("models/player/visitant.zym");
1624 if (cvar("sv_defaultcharacter"))
1627 s = cvar_string("sv_defaultplayermodel_red");
1631 PrecachePlayerSounds(strcat(s, ".sounds"));
1633 s = cvar_string("sv_defaultplayermodel_blue");
1637 PrecachePlayerSounds(strcat(s, ".sounds"));
1639 s = cvar_string("sv_defaultplayermodel_yellow");
1643 PrecachePlayerSounds(strcat(s, ".sounds"));
1645 s = cvar_string("sv_defaultplayermodel_pink");
1649 PrecachePlayerSounds(strcat(s, ".sounds"));
1651 s = cvar_string("sv_defaultplayermodel");
1655 PrecachePlayerSounds(strcat(s, ".sounds"));
1661 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1662 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1665 // gore and miscellaneous sounds
1666 //precache_sound ("misc/h2ohit.wav");
1667 precache_model ("models/hook.md3");
1668 precache_sound ("misc/armorimpact.wav");
1669 precache_sound ("misc/bodyimpact1.wav");
1670 precache_sound ("misc/bodyimpact2.wav");
1671 precache_sound ("misc/gib.wav");
1672 precache_sound ("misc/gib_splat01.wav");
1673 precache_sound ("misc/gib_splat02.wav");
1674 precache_sound ("misc/gib_splat03.wav");
1675 precache_sound ("misc/gib_splat04.wav");
1676 precache_sound ("misc/hit.wav");
1677 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1678 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1679 precache_sound ("misc/null.wav");
1680 precache_sound ("misc/spawn.wav");
1681 precache_sound ("misc/talk.wav");
1682 precache_sound ("misc/teleport.wav");
1683 precache_sound ("misc/poweroff.wav");
1684 precache_sound ("player/lava.wav");
1685 precache_sound ("player/slime.wav");
1688 precache_sound ("misc/jetpack_fly.wav");
1690 // announcer sounds - male
1691 precache_sound ("announcer/male/electrobitch.wav");
1692 precache_sound ("announcer/male/airshot.wav");
1693 precache_sound ("announcer/male/03kills.wav");
1694 precache_sound ("announcer/male/05kills.wav");
1695 precache_sound ("announcer/male/10kills.wav");
1696 precache_sound ("announcer/male/15kills.wav");
1697 precache_sound ("announcer/male/20kills.wav");
1698 precache_sound ("announcer/male/25kills.wav");
1699 precache_sound ("announcer/male/30kills.wav");
1700 precache_sound ("announcer/male/botlike.wav");
1701 precache_sound ("announcer/male/yoda.wav");
1702 precache_sound ("announcer/male/amazing.wav");
1703 precache_sound ("announcer/male/awesome.wav");
1704 precache_sound ("announcer/male/headshot.wav");
1705 precache_sound ("announcer/male/impressive.wav");
1707 // announcer sounds - robotic
1708 precache_sound ("announcer/robotic/prepareforbattle.wav");
1709 precache_sound ("announcer/robotic/begin.wav");
1710 precache_sound ("announcer/robotic/timeoutcalled.wav");
1711 precache_sound ("announcer/robotic/1fragleft.wav");
1712 precache_sound ("announcer/robotic/2fragsleft.wav");
1713 precache_sound ("announcer/robotic/3fragsleft.wav");
1714 precache_sound ("announcer/robotic/terminated.wav");
1717 precache_sound ("announcer/robotic/lastsecond.wav");
1718 precache_sound ("announcer/robotic/narrowly.wav");
1721 precache_model ("models/sprites/0.spr32");
1722 precache_model ("models/sprites/1.spr32");
1723 precache_model ("models/sprites/2.spr32");
1724 precache_model ("models/sprites/3.spr32");
1725 precache_model ("models/sprites/4.spr32");
1726 precache_model ("models/sprites/5.spr32");
1727 precache_model ("models/sprites/6.spr32");
1728 precache_model ("models/sprites/7.spr32");
1729 precache_model ("models/sprites/8.spr32");
1730 precache_model ("models/sprites/9.spr32");
1731 precache_model ("models/sprites/10.spr32");
1732 precache_sound ("announcer/robotic/1.wav");
1733 precache_sound ("announcer/robotic/2.wav");
1734 precache_sound ("announcer/robotic/3.wav");
1735 precache_sound ("announcer/robotic/4.wav");
1736 precache_sound ("announcer/robotic/5.wav");
1737 precache_sound ("announcer/robotic/6.wav");
1738 precache_sound ("announcer/robotic/7.wav");
1739 precache_sound ("announcer/robotic/8.wav");
1740 precache_sound ("announcer/robotic/9.wav");
1741 precache_sound ("announcer/robotic/10.wav");
1743 // common weapon precaches
1744 precache_sound ("weapons/weapon_switch.wav");
1745 precache_sound ("weapons/weaponpickup.wav");
1746 precache_sound ("weapons/unavailable.wav");
1747 if (g_grappling_hook)
1749 precache_sound ("weapons/hook_fire.wav"); // hook
1750 precache_sound ("weapons/hook_impact.wav"); // hook
1753 if (cvar("sv_precacheweapons") || g_nixnex)
1755 //precache weapon models/sounds
1758 while (wep <= WEP_LAST)
1760 weapon_action(wep, WR_PRECACHE);
1765 precache_model("models/elaser.mdl");
1766 precache_model("models/laser.mdl");
1767 precache_model("models/ebomb.mdl");
1770 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1772 if (!self.noise && self.music) // quake 3 uses the music field
1773 self.noise = self.music;
1775 // plays music for the level if there is any
1778 precache_sound (self.noise);
1779 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1784 // sorry, but using \ in macros breaks line numbers
1785 #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
1786 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1787 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1789 vector ExactTriggerHit_mins;
1790 vector ExactTriggerHit_maxs;
1791 float ExactTriggerHit_Recurse()
1797 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1800 if (trace_ent == self)
1805 se.solid = SOLID_NOT;
1806 f = ExactTriggerHit_Recurse();
1812 float ExactTriggerHit()
1816 if not(self.modelindex)
1820 self.solid = SOLID_BSP;
1821 ExactTriggerHit_mins = other.absmin;
1822 ExactTriggerHit_maxs = other.absmax;
1823 f = ExactTriggerHit_Recurse();
1829 // WARNING: this kills the trace globals
1830 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1831 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1833 #define INITPRIO_FIRST 0
1834 #define INITPRIO_GAMETYPE 0
1835 #define INITPRIO_GAMETYPE_FALLBACK 1
1836 #define INITPRIO_CVARS 5
1837 #define INITPRIO_FINDTARGET 10
1838 #define INITPRIO_DROPTOFLOOR 20
1839 #define INITPRIO_SETLOCATION 90
1840 #define INITPRIO_LINKDOORS 91
1841 #define INITPRIO_LAST 99
1843 .void(void) initialize_entity;
1844 .float initialize_entity_order;
1845 .entity initialize_entity_next;
1846 entity initialize_entity_first;
1848 void make_safe_for_remove(entity e)
1850 if (e.initialize_entity)
1853 for (ent = initialize_entity_first; ent; )
1855 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1857 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1858 // skip it in linked list
1861 prev.initialize_entity_next = ent.initialize_entity_next;
1862 ent = prev.initialize_entity_next;
1866 initialize_entity_first = ent.initialize_entity_next;
1867 ent = initialize_entity_first;
1873 ent = ent.initialize_entity_next;
1879 void objerror(string s)
1881 make_safe_for_remove(self);
1882 objerror_builtin(s);
1885 void remove_unsafely(entity e)
1890 void remove_safely(entity e)
1892 make_safe_for_remove(e);
1896 void InitializeEntity(entity e, void(void) func, float order)
1900 if (!e || e.initialize_entity)
1902 // make a proxy initializer entity
1906 e.classname = "initialize_entity";
1910 e.initialize_entity = func;
1911 e.initialize_entity_order = order;
1913 cur = initialize_entity_first;
1916 if (!cur || cur.initialize_entity_order > order)
1918 // insert between prev and cur
1920 prev.initialize_entity_next = e;
1922 initialize_entity_first = e;
1923 e.initialize_entity_next = cur;
1927 cur = cur.initialize_entity_next;
1930 void InitializeEntitiesRun()
1933 startoflist = initialize_entity_first;
1934 initialize_entity_first = world;
1935 for (self = startoflist; self; )
1938 var void(void) func;
1939 e = self.initialize_entity_next;
1940 func = self.initialize_entity;
1941 self.initialize_entity_order = 0;
1942 self.initialize_entity = func_null;
1943 self.initialize_entity_next = world;
1944 if (self.classname == "initialize_entity")
1948 remove_builtin(self);
1951 //dprint("Delayed initialization: ", self.classname, "\n");
1957 .float uncustomizeentityforclient_set;
1958 .void(void) uncustomizeentityforclient;
1959 void(void) SUB_Nullpointer = #0;
1960 void UncustomizeEntitiesRun()
1964 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1965 self.uncustomizeentityforclient();
1968 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1970 e.customizeentityforclient = customizer;
1971 e.uncustomizeentityforclient = uncustomizer;
1972 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1976 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1979 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1983 if (e.classname == "")
1984 e.classname = "net_linked";
1986 if (e.model == "" || self.modelindex == 0)
1990 setmodel(e, "null");
1994 e.SendEntity = sendfunc;
1995 e.SendFlags = 0xFFFFFF;
1998 e.effects |= EF_NODEPTHTEST;
2002 e.nextthink = time + dt;
2003 e.think = SUB_Remove;
2007 void adaptor_think2touch()
2016 void adaptor_think2use()
2028 // deferred dropping
2029 void DropToFloor_Handler()
2031 droptofloor_builtin();
2032 self.dropped_origin = self.origin;
2037 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
2042 float trace_hits_box_a0, trace_hits_box_a1;
2044 float trace_hits_box_1d(float end, float thmi, float thma)
2048 // just check if x is in range
2056 // do the trace with respect to x
2057 // 0 -> end has to stay in thmi -> thma
2058 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
2059 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
2060 if (trace_hits_box_a0 > trace_hits_box_a1)
2066 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
2071 // now it is a trace from 0 to end
2073 trace_hits_box_a0 = 0;
2074 trace_hits_box_a1 = 1;
2076 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
2078 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
2080 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
2086 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
2088 return trace_hits_box(start, end, thmi - ma, thma - mi);
2091 float SUB_NoImpactCheck()
2093 // zero hitcontents = this is not the real impact, but either the
2094 // mirror-impact of something hitting the projectile instead of the
2095 // projectile hitting the something, or a touchareagrid one. Neither of
2096 // these stop the projectile from moving, so...
2097 if(trace_dphitcontents == 0)
2099 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
2102 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2104 if (other == world && self.size != '0 0 0')
2107 tic = self.velocity * sys_ticrate;
2108 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2109 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2110 if (trace_fraction >= 1)
2112 dprint("Odd... did not hit...?\n");
2114 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2116 dprint("Detected and prevented the sky-grapple bug.\n");
2124 #define SUB_OwnerCheck() (other && (other == self.owner))
2126 #define PROJECTILE_TOUCH do { if(SUB_OwnerCheck()) return; if(SUB_NoImpactCheck()) { remove(self); return; } if(trace_ent && trace_ent.solid > SOLID_TRIGGER) UpdateCSQCProjectileNextFrame(self); } while(0)
2128 float MAX_IPBAN_URIS = 16;
2130 float URI_GET_DISCARD = 0;
2131 float URI_GET_IPBAN = 1;
2132 float URI_GET_IPBAN_END = 16;
2134 void URI_Get_Callback(float id, float status, string data)
2136 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2138 dprint("\nEnd of data.\n");
2140 if (id == URI_GET_DISCARD)
2144 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2147 OnlineBanList_URI_Get_Callback(id, status, data);
2151 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2155 void print_to(entity e, string s)
2158 sprint(e, strcat(s, "\n"));
2177 for (i = 0; i < MapInfo_count; ++i)
2179 if (MapInfo_Get_ByID(i))
2181 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2184 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2185 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2193 for (i = 0; i < MapInfo_count; ++i)
2195 if (MapInfo_Get_ByID(i))
2197 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2200 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2201 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2209 for (i = 0; i < MapInfo_count; ++i)
2211 if (MapInfo_Get_ByID(i))
2213 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2216 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2217 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2223 MapInfo_ClearTemps();
2226 return "No records are available on this server.\n";
2228 return strcat("Records on this server:\n", s);
2231 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2234 vector start, org, delta, end, enddown, mstart;
2236 m = e.dphitcontentsmask;
2237 e.dphitcontentsmask = goodcontents | badcontents;
2240 delta = world.maxs - world.mins;
2242 for (i = 0; i < attempts; ++i)
2244 start_x = org_x + random() * delta_x;
2245 start_y = org_y + random() * delta_y;
2246 start_z = org_z + random() * delta_z;
2248 // rule 1: start inside world bounds, and outside
2249 // solid, and don't start from somewhere where you can
2250 // fall down to evil
2251 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2252 if (trace_fraction >= 1)
2254 if (trace_startsolid)
2256 if (trace_dphitcontents & badcontents)
2258 if (trace_dphitq3surfaceflags & badsurfaceflags)
2261 // rule 2: if we are too high, lower the point
2262 if (trace_fraction * delta_z > maxaboveground)
2263 start = trace_endpos + '0 0 1' * maxaboveground;
2264 enddown = trace_endpos;
2266 // rule 3: make sure we aren't outside the map. This only works
2267 // for somewhat well formed maps. A good rule of thumb is that
2268 // the map should have a convex outside hull.
2269 // these can be traceLINES as we already verified the starting box
2270 mstart = start + 0.5 * (e.mins + e.maxs);
2271 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2272 if (trace_fraction >= 1)
2274 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2275 if (trace_fraction >= 1)
2277 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2278 if (trace_fraction >= 1)
2280 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2281 if (trace_fraction >= 1)
2283 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2284 if (trace_fraction >= 1)
2287 // find a random vector to "look at"
2288 end_x = org_x + random() * delta_x;
2289 end_y = org_y + random() * delta_y;
2290 end_z = org_z + random() * delta_z;
2291 end = start + normalize(end - start) * vlen(delta);
2293 // rule 4: start TO end must not be too short
2294 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2295 if (trace_startsolid)
2297 if (trace_fraction < minviewdistance / vlen(delta))
2300 // rule 5: don't want to look at sky
2301 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2304 // rule 6: we must not end up in trigger_hurt
2305 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2307 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2314 e.dphitcontentsmask = m;
2318 setorigin(e, start);
2319 e.angles = vectoangles(end - start);
2320 dprint("Needed ", ftos(i + 1), " attempts\n");
2327 float zcurveparticles_effectno;
2328 vector zcurveparticles_start;
2329 float zcurveparticles_spd;
2331 void endzcurveparticles()
2333 if(zcurveparticles_effectno)
2336 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2338 zcurveparticles_effectno = 0;
2341 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2343 spd = bound(0, floor(spd / 16), 32767);
2344 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2346 endzcurveparticles();
2347 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2348 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2349 WriteShort(MSG_BROADCAST, effectno);
2350 WriteCoord(MSG_BROADCAST, start_x);
2351 WriteCoord(MSG_BROADCAST, start_y);
2352 WriteCoord(MSG_BROADCAST, start_z);
2353 zcurveparticles_effectno = effectno;
2354 zcurveparticles_start = start;
2357 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2358 WriteCoord(MSG_BROADCAST, end_x);
2359 WriteCoord(MSG_BROADCAST, end_y);
2360 WriteCoord(MSG_BROADCAST, end_z);
2361 WriteCoord(MSG_BROADCAST, end_dz);
2362 zcurveparticles_spd = spd;
2365 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2368 vector vecxy, velxy;
2370 vecxy = end - start;
2375 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2377 endzcurveparticles();
2378 trailparticles(world, effectno, start, end);
2382 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2383 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2386 string GetGametype(); // g_world.qc
2387 void write_recordmarker(entity pl, float tstart, float dt)
2389 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2391 // also write a marker into demo files for demotc-race-record-extractor to find
2394 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2395 " ", ftos(tstart), " ", ftos(dt), "\n"));
2398 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2400 switch(self.owner.cvar_cl_gunalign)
2411 if(allowcenter) // 2: allow center handedness
2424 if(allowcenter) // 2: allow center handedness
2440 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2445 if (cvar("g_shootfromeye"))
2449 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2457 else if (cvar("g_shootfromcenter"))
2461 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2469 else if (cvar("g_shootfromclient"))
2471 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2473 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2488 void attach_sameorigin(entity e, entity to, string tag)
2490 vector org, t_forward, t_left, t_up, e_forward, e_up;
2497 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2498 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2499 t_forward = v_forward * tagscale;
2500 t_left = v_right * -tagscale;
2501 t_up = v_up * tagscale;
2503 e.origin_x = org * t_forward;
2504 e.origin_y = org * t_left;
2505 e.origin_z = org * t_up;
2507 // current forward and up directions
2508 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2509 e.angles_x = -e.angles_x;
2510 fixedmakevectors(e.angles);
2512 // untransform forward, up!
2513 e_forward_x = v_forward * t_forward;
2514 e_forward_y = v_forward * t_left;
2515 e_forward_z = v_forward * t_up;
2516 e_up_x = v_up * t_forward;
2517 e_up_y = v_up * t_left;
2518 e_up_z = v_up * t_up;
2520 e.angles = fixedvectoangles2(e_forward, e_up);
2521 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2522 e.angles_x = -e.angles_x;
2524 setattachment(e, to, tag);
2525 setorigin(e, e.origin);
2528 void detach_sameorigin(entity e)
2531 org = gettaginfo(e, 0);
2532 e.angles = fixedvectoangles2(v_forward, v_up);
2533 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2534 e.angles_x = -e.angles_x;
2536 setattachment(e, world, "");
2537 setorigin(e, e.origin);
2540 void follow_sameorigin(entity e, entity to)
2542 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2543 e.aiment = to; // make the hole follow bmodel
2544 e.punchangle = to.angles; // the original angles of bmodel
2545 e.view_ofs = e.origin - to.origin; // relative origin
2546 e.v_angle = e.angles - to.angles; // relative angles
2549 void unfollow_sameorigin(entity e)
2551 e.movetype = MOVETYPE_NONE;
2554 entity gettaginfo_relative_ent;
2555 vector gettaginfo_relative(entity e, float tag)
2557 if (!gettaginfo_relative_ent)
2559 gettaginfo_relative_ent = spawn();
2560 gettaginfo_relative_ent.effects = EF_NODRAW;
2562 gettaginfo_relative_ent.model = e.model;
2563 gettaginfo_relative_ent.modelindex = e.modelindex;
2564 gettaginfo_relative_ent.frame = e.frame;
2565 return gettaginfo(gettaginfo_relative_ent, tag);
2568 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2572 if (pl.soundentity.cnt & p)
2574 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2575 pl.soundentity.cnt |= p;
2578 void SoundEntity_StopSound(entity pl, float chan)
2582 if (pl.soundentity.cnt & p)
2584 stopsoundto(MSG_ALL, pl.soundentity, chan);
2585 pl.soundentity.cnt &~= p;
2589 void SoundEntity_Attach(entity pl)
2591 pl.soundentity = spawn();
2592 pl.soundentity.classname = "soundentity";
2593 pl.soundentity.owner = pl;
2594 setattachment(pl.soundentity, pl, "");
2595 setmodel(pl.soundentity, "null");
2598 void SoundEntity_Detach(entity pl)
2601 for (i = 0; i <= 7; ++i)
2602 SoundEntity_StopSound(pl, i);
2606 float ParseCommandPlayerSlotTarget_firsttoken;
2607 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2615 ParseCommandPlayerSlotTarget_firsttoken = -1;
2619 if (substring(argv(idx), 0, 1) == "#")
2621 s = substring(argv(idx), 1, -1);
2629 ParseCommandPlayerSlotTarget_firsttoken = idx;
2630 if (s == ftos(stof(s)))
2632 e = edict_num(stof(s));
2633 if (e.flags & FL_CLIENT)
2639 // it must be a nick name
2642 ParseCommandPlayerSlotTarget_firsttoken = idx;
2645 FOR_EACH_CLIENT(head)
2646 if (head.netname == s)
2654 s = strdecolorize(s);
2656 FOR_EACH_CLIENT(head)
2657 if (strdecolorize(head.netname) == s)
2672 float modeleffect_SendEntity(entity to, float sf)
2675 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2678 if(self.velocity != '0 0 0')
2680 if(self.angles != '0 0 0')
2682 if(self.avelocity != '0 0 0')
2685 WriteByte(MSG_ENTITY, f);
2686 WriteShort(MSG_ENTITY, self.modelindex);
2687 WriteByte(MSG_ENTITY, self.skin);
2688 WriteByte(MSG_ENTITY, self.frame);
2689 WriteCoord(MSG_ENTITY, self.origin_x);
2690 WriteCoord(MSG_ENTITY, self.origin_y);
2691 WriteCoord(MSG_ENTITY, self.origin_z);
2694 WriteCoord(MSG_ENTITY, self.velocity_x);
2695 WriteCoord(MSG_ENTITY, self.velocity_y);
2696 WriteCoord(MSG_ENTITY, self.velocity_z);
2700 WriteCoord(MSG_ENTITY, self.angles_x);
2701 WriteCoord(MSG_ENTITY, self.angles_y);
2702 WriteCoord(MSG_ENTITY, self.angles_z);
2706 WriteCoord(MSG_ENTITY, self.avelocity_x);
2707 WriteCoord(MSG_ENTITY, self.avelocity_y);
2708 WriteCoord(MSG_ENTITY, self.avelocity_z);
2710 WriteShort(MSG_ENTITY, self.scale * 256.0);
2711 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2712 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2713 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2714 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2719 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)
2724 e.classname = "modeleffect";
2732 e.teleport_time = t1;
2736 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2740 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2743 sz = max(e.scale, e.scale2);
2744 setsize(e, e.mins * sz, e.maxs * sz);
2745 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2748 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2750 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2753 float randombit(float bits)
2755 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2764 for(f = 1; f <= bits; f *= 2)
2773 r = (r - 1) / (n - 1);
2780 float randombits(float bits, float k, float error_return)
2784 while(k > 0 && bits != r)
2786 r += randombit(bits - r);
2795 void randombit_test(float bits, float iter)
2799 print(ftos(randombit(bits)), "\n");
2804 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2806 if(halflifedist > 0)
2807 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2808 else if(halflifedist < 0)
2809 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2818 #define cvar_string_normal cvar_string_builtin
2819 #define cvar_normal cvar_builtin
2821 string cvar_string_normal(string n)
2823 if not(cvar_type(n) & 1)
2824 backtrace(strcat("Attempt to access undefined cvar: ", n));
2825 return cvar_string_builtin(n);
2828 float cvar_normal(string n)
2830 return stof(cvar_string_normal(n));
2833 #define cvar_set_normal cvar_set_builtin