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 float race_GetTime(float pos);
9 string race_GetName(float pos);
10 string race_PlaceName(float pos);
12 string ColoredTeamName(float t);
14 string admin_name(void)
16 if(cvar_string("sv_adminnick") != "")
17 return cvar_string("sv_adminnick");
19 return "SERVER ADMIN";
22 float DistributeEvenly_amount;
23 float DistributeEvenly_totalweight;
24 void DistributeEvenly_Init(float amount, float totalweight)
26 if (DistributeEvenly_amount)
28 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
29 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
32 DistributeEvenly_amount = 0;
34 DistributeEvenly_amount = amount;
35 DistributeEvenly_totalweight = totalweight;
37 float DistributeEvenly_Get(float weight)
42 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
43 DistributeEvenly_totalweight -= weight;
44 DistributeEvenly_amount -= f;
48 void move_out_of_solid_expand(entity e, vector by)
51 tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
54 if (trace_fraction < 1)
57 // adjust origin in the other direction...
58 setorigin(e,e.origin - by * (1 - trace_fraction));
62 float move_out_of_solid(entity e)
67 traceline(o, o, MOVE_WORLDONLY, e);
71 tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
72 if (!trace_startsolid)
79 move_out_of_solid_expand(e, '1 0 0' * m0_x);
81 move_out_of_solid_expand(e, '1 0 0' * m1_x);
83 move_out_of_solid_expand(e, '0 1 0' * m0_y);
85 move_out_of_solid_expand(e, '0 1 0' * m1_y);
87 move_out_of_solid_expand(e, '0 0 1' * m0_z);
89 move_out_of_solid_expand(e, '0 0 1' * m1_z);
91 setorigin(e, e.origin);
93 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
103 string STR_PLAYER = "player";
104 string STR_SPECTATOR = "spectator";
105 string STR_OBSERVER = "observer";
108 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
109 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
110 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
111 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
113 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
114 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
115 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
116 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
117 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
120 // copies a string to a tempstring (so one can strunzone it)
121 string strcat1(string s) = #115; // FRIK_FILE
126 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
128 local float nPlayerHealth = rint(enPlayer.health);
129 local float nPlayerArmor = rint(enPlayer.armorvalue);
130 local float nPlayerHandicap = enPlayer.cvar_cl_handicap;
131 local float nPlayerPing = rint(enPlayer.ping);
132 local string strPlayerPingColor;
133 local string strMessage;
134 if(nPlayerPing >= 150)
135 strPlayerPingColor = "^1";
137 strPlayerPingColor = "^2";
139 if((cvar("sv_fragmessage_information_stats")) && (enPlayer.health >= 1))
140 strMessage = strcat(strMessage, "\n^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
142 if(cvar("sv_fragmessage_information_ping")) {
143 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
144 strMessage = strcat(strMessage, "\n^7(^2Bot");
146 strMessage = strcat(strMessage, "\n^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
147 if(cvar("sv_fragmessage_information_handicap"))
148 if(cvar("sv_fragmessage_information_handicap") == 2)
149 if(nPlayerHandicap <= 1)
150 strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
152 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
153 else if not(nPlayerHandicap <= 1)
154 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
156 strMessage = strcat(strMessage, "^7)");
157 } else if(cvar("sv_fragmessage_information_handicap")) {
158 if(cvar("sv_fragmessage_information_handicap") == 2)
159 if(nPlayerHandicap <= 1)
160 strMessage = strcat(strMessage, "\n^7(Handicap ^2Off^7)");
162 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
163 else if(nPlayerHandicap > 1)
164 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
168 void bcenterprint(string s)
170 // TODO replace by MSG_ALL (would show it to spectators too, though)?
172 FOR_EACH_PLAYER(head)
173 if (clienttype(head) == CLIENTTYPE_REAL)
174 centerprint(head, s);
177 void GameLogEcho(string s)
182 if (cvar("sv_eventlog_files"))
187 matches = cvar("sv_eventlog_files_counter") + 1;
188 cvar_set("sv_eventlog_files_counter", ftos(matches));
191 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
192 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
193 logfile = fopen(fn, FILE_APPEND);
194 fputs(logfile, ":logversion:3\n");
198 if (cvar("sv_eventlog_files_timestamps"))
199 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
201 fputs(logfile, strcat(s, "\n"));
204 if (cvar("sv_eventlog_console"))
213 // will be opened later
218 if (logfile_open && logfile >= 0)
228 vector PL_CROUCH_VIEW_OFS;
229 vector PL_CROUCH_MIN;
230 vector PL_CROUCH_MAX;
232 float spawnpoint_nag;
233 void relocate_spawnpoint()
235 PL_VIEW_OFS = stov(cvar_string("sv_player_viewoffset"));
236 PL_MIN = stov(cvar_string("sv_player_mins"));
237 PL_MAX = stov(cvar_string("sv_player_maxs"));
238 PL_CROUCH_VIEW_OFS = stov(cvar_string("sv_player_crouch_viewoffset"));
239 PL_CROUCH_MIN = stov(cvar_string("sv_player_crouch_mins"));
240 PL_CROUCH_MAX = stov(cvar_string("sv_player_crouch_maxs"));
242 // nudge off the floor
243 setorigin(self, self.origin + '0 0 1');
245 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
246 if (trace_startsolid)
252 if (!move_out_of_solid(self))
253 objerror("could not get out of solid at all!");
254 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
255 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
256 print(" ", ftos(self.origin_y - o_y));
257 print(" ", ftos(self.origin_z - o_z), "'\n");
258 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
261 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
267 self.mins = self.maxs = '0 0 0';
268 objerror("player spawn point in solid, mapper sucks!\n");
273 if (cvar("g_spawnpoints_autodrop"))
275 setsize(self, PL_MIN, PL_MAX);
279 self.use = spawnpoint_use;
280 self.team_saved = self.team;
284 if (have_team_spawns != 0)
286 have_team_spawns = 1;
288 if (cvar("r_showbboxes"))
290 // show where spawnpoints point at too
291 makevectors(self.angles);
294 e.classname = "info_player_foo";
295 setorigin(e, self.origin + v_forward * 24);
296 setsize(e, '-8 -8 -8', '8 8 8');
297 e.solid = SOLID_TRIGGER;
301 #define strstr strstrofs
303 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
304 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
305 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
306 // BE CONSTANT OR strzoneD!
307 float strstr(string haystack, string needle, float offset)
311 len = strlen(needle);
312 endpos = strlen(haystack) - len;
313 while(offset <= endpos)
315 found = substring(haystack, offset, len);
324 float NUM_NEAREST_ENTITIES = 4;
325 entity nearest_entity[NUM_NEAREST_ENTITIES];
326 float nearest_length[NUM_NEAREST_ENTITIES];
327 entity findnearest(vector point, .string field, string value, vector axismod)
338 localhead = find(world, field, value);
341 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
342 dist = localhead.oldorigin;
344 dist = localhead.origin;
346 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
349 for (i = 0; i < num_nearest; ++i)
351 if (len < nearest_length[i])
355 // now i tells us where to insert at
356 // INSERTION SORT! YOU'VE SEEN IT! RUN!
357 if (i < NUM_NEAREST_ENTITIES)
359 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
361 nearest_length[j + 1] = nearest_length[j];
362 nearest_entity[j + 1] = nearest_entity[j];
364 nearest_length[i] = len;
365 nearest_entity[i] = localhead;
366 if (num_nearest < NUM_NEAREST_ENTITIES)
367 num_nearest = num_nearest + 1;
370 localhead = find(localhead, field, value);
373 // now use the first one from our list that we can see
374 for (i = 0; i < num_nearest; ++i)
376 traceline(point, nearest_entity[i].origin, TRUE, world);
377 if (trace_fraction == 1)
381 dprint("Nearest point (");
382 dprint(nearest_entity[0].netname);
383 dprint(") is not visible, using a visible one.\n");
385 return nearest_entity[i];
389 if (num_nearest == 0)
392 dprint("Not seeing any location point, using nearest as fallback.\n");
394 dprint("Candidates were: ");
395 for(j = 0; j < num_nearest; ++j)
399 dprint(nearest_entity[j].netname);
404 return nearest_entity[0];
407 void spawnfunc_target_location()
409 self.classname = "target_location";
410 // location name in netname
411 // eventually support: count, teamgame selectors, line of sight?
414 void spawnfunc_info_location()
416 self.classname = "target_location";
417 self.message = self.netname;
420 string NearestLocation(vector p)
425 loc = findnearest(p, classname, "target_location", '1 1 1');
432 loc = findnearest(p, target, "###item###", '1 1 4');
439 string formatmessage(string msg)
450 break; // too many replacements
453 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
454 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
467 replacement = substring(msg, p, 2);
468 escape = substring(msg, p + 1, 1);
472 else if (escape == "\\")
474 else if (escape == "n")
476 else if (escape == "a")
477 replacement = ftos(floor(self.armorvalue));
478 else if (escape == "h")
479 replacement = ftos(floor(self.health));
480 else if (escape == "l")
481 replacement = NearestLocation(self.origin);
482 else if (escape == "y")
483 replacement = NearestLocation(self.cursor_trace_endpos);
484 else if (escape == "d")
485 replacement = NearestLocation(self.death_origin);
486 else if (escape == "w") {
490 wep = self.switchweapon;
493 replacement = W_Name(wep);
494 } else if (escape == "W") {
495 if (self.items & IT_SHELLS) replacement = "shells";
496 else if (self.items & IT_NAILS) replacement = "bullets";
497 else if (self.items & IT_ROCKETS) replacement = "rockets";
498 else if (self.items & IT_CELLS) replacement = "cells";
499 else replacement = "batteries"; // ;)
500 } else if (escape == "x") {
501 replacement = self.cursor_trace_ent.netname;
502 if (!replacement || !self.cursor_trace_ent)
503 replacement = "nothing";
504 } else if (escape == "p") {
505 if (self.last_selected_player)
506 replacement = self.last_selected_player.netname;
508 replacement = "(nobody)";
509 } else if (escape == "s")
510 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
511 else if (escape == "S")
512 replacement = ftos(vlen(self.velocity));
513 else if (escape == "v") {
517 if(self.classname == "spectator")
522 weapon_number = stats.weapon;
525 weapon_number = stats.switchweapon;
528 weapon_number = stats.cnt;
530 if(stats.cvar_cl_accuracy_data_share && stats.stats_fired[weapon_number - 1])
531 replacement = ftos(bound(0, floor(100 * stats.stats_hit[weapon_number - 1] / stats.stats_fired[weapon_number - 1]), 100));
533 replacement = "~"; // or something to indicate NULL, not available
536 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
537 p = p + strlen(replacement);
542 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
543 return (value == 0) ? FALSE : TRUE;
552 >0: receives a cvar from name=argv(f) value=argv(f+1)
554 void GetCvars_handleString(string thisname, float f, .string field, string name)
559 strunzone(self.field);
560 self.field = string_null;
564 if (thisname == name)
567 strunzone(self.field);
568 self.field = strzone(argv(f + 1));
572 stuffcmd(self, strcat("sendcvar ", name, "\n"));
574 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
576 GetCvars_handleString(thisname, f, field, name);
577 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
578 if (thisname == name)
581 s = func(strcat1(self.field));
584 strunzone(self.field);
585 self.field = strzone(s);
589 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
596 if (thisname == name)
597 self.field = stof(argv(f + 1));
600 stuffcmd(self, strcat("sendcvar ", name, "\n"));
602 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
609 if (thisname == name)
613 self.field = stof(argv(f + 1));
622 stuffcmd(self, strcat("sendcvar ", name, "\n"));
625 string W_FixWeaponOrder_ForceComplete(string s);
626 string W_FixWeaponOrder_AllowIncomplete(string s);
627 float w_getbestweapon(entity e);
628 void GetCvars(float f)
632 s = strcat1(argv(f));
633 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
634 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
635 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
636 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
637 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
638 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
639 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
640 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
641 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
642 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
643 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
644 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
645 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
646 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
647 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
648 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
649 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
650 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
651 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
652 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
653 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
654 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
655 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
656 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
658 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
659 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
661 #ifdef ALLOW_FORCEMODELS
662 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
663 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
665 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
667 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
670 if (s == "cl_weaponpriority")
671 self.switchweapon = w_getbestweapon(self);
675 float fexists(string f)
678 fh = fopen(f, FILE_READ);
685 void backtrace(string msg)
688 dev = cvar("developer");
689 war = cvar("prvm_backtraceforwarnings");
690 cvar_set("developer", "1");
691 cvar_set("prvm_backtraceforwarnings", "1");
693 print("--- CUT HERE ---\nWARNING: ");
696 remove(world); // isn't there any better way to cause a backtrace?
697 print("\n--- CUT UNTIL HERE ---\n");
698 cvar_set("developer", ftos(dev));
699 cvar_set("prvm_backtraceforwarnings", ftos(war));
702 string Team_ColorCode(float teamid)
704 if (teamid == COLOR_TEAM1)
706 else if (teamid == COLOR_TEAM2)
708 else if (teamid == COLOR_TEAM3)
710 else if (teamid == COLOR_TEAM4)
716 string Team_ColorName(float t)
718 // fixme: Search for team entities and get their .netname's!
719 if (t == COLOR_TEAM1)
721 if (t == COLOR_TEAM2)
723 if (t == COLOR_TEAM3)
725 if (t == COLOR_TEAM4)
730 string Team_ColorNameLowerCase(float t)
732 // fixme: Search for team entities and get their .netname's!
733 if (t == COLOR_TEAM1)
735 if (t == COLOR_TEAM2)
737 if (t == COLOR_TEAM3)
739 if (t == COLOR_TEAM4)
744 float ColourToNumber(string team_colour)
746 if (team_colour == "red")
749 if (team_colour == "blue")
752 if (team_colour == "yellow")
755 if (team_colour == "pink")
758 if (team_colour == "auto")
764 float NumberToTeamNumber(float number)
781 #define CENTERPRIO_POINT 1
782 #define CENTERPRIO_SPAM 2
783 #define CENTERPRIO_VOTE 4
784 #define CENTERPRIO_NORMAL 5
785 #define CENTERPRIO_SHIELDING 7
786 #define CENTERPRIO_MAPVOTE 9
787 #define CENTERPRIO_IDLEKICK 50
788 #define CENTERPRIO_ADMIN 99
789 .float centerprint_priority;
790 .float centerprint_expires;
791 void centerprint_atprio(entity e, float prio, string s)
793 if (intermission_running)
794 if (prio < CENTERPRIO_MAPVOTE)
796 if (time > e.centerprint_expires)
797 e.centerprint_priority = 0;
798 if (prio >= e.centerprint_priority)
800 e.centerprint_priority = prio;
801 if (timeoutStatus == 2)
802 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
804 e.centerprint_expires = time + e.cvar_scr_centertime;
805 centerprint_builtin(e, s);
808 void centerprint_expire(entity e, float prio)
810 if (prio == e.centerprint_priority)
812 e.centerprint_priority = 0;
813 centerprint_builtin(e, "");
816 void centerprint(entity e, string s)
818 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
821 // decolorizes and team colors the player name when needed
822 string playername(entity p)
825 if (teams_matter && !intermission_running && p.classname == "player")
827 t = Team_ColorCode(p.team);
828 return strcat(t, strdecolorize(p.netname));
834 vector randompos(vector m1, vector m2)
838 v_x = m2_x * random() + m1_x;
839 v_y = m2_y * random() + m1_y;
840 v_z = m2_z * random() + m1_z;
844 float g_pickup_shells;
845 float g_pickup_shells_max;
846 float g_pickup_nails;
847 float g_pickup_nails_max;
848 float g_pickup_rockets;
849 float g_pickup_rockets_max;
850 float g_pickup_cells;
851 float g_pickup_cells_max;
853 float g_pickup_fuel_jetpack;
854 float g_pickup_fuel_max;
855 float g_pickup_armorsmall;
856 float g_pickup_armorsmall_max;
857 float g_pickup_armormedium;
858 float g_pickup_armormedium_max;
859 float g_pickup_armorbig;
860 float g_pickup_armorbig_max;
861 float g_pickup_armorlarge;
862 float g_pickup_armorlarge_max;
863 float g_pickup_healthsmall;
864 float g_pickup_healthsmall_max;
865 float g_pickup_healthmedium;
866 float g_pickup_healthmedium_max;
867 float g_pickup_healthlarge;
868 float g_pickup_healthlarge_max;
869 float g_pickup_healthmega;
870 float g_pickup_healthmega_max;
872 float g_weaponarena_random;
873 string g_weaponarena_list;
874 float g_weaponspeedfactor;
875 float g_weaponratefactor;
876 float g_weapondamagefactor;
877 float g_weaponforcefactor;
878 float g_weaponspreadfactor;
882 float start_ammo_shells;
883 float start_ammo_nails;
884 float start_ammo_rockets;
885 float start_ammo_cells;
886 float start_ammo_fuel;
888 float start_armorvalue;
889 float warmup_start_weapons;
890 float warmup_start_ammo_shells;
891 float warmup_start_ammo_nails;
892 float warmup_start_ammo_rockets;
893 float warmup_start_ammo_cells;
894 float warmup_start_ammo_fuel;
895 float warmup_start_health;
896 float warmup_start_armorvalue;
900 entity get_weaponinfo(float w);
902 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
904 var float i = weaponinfo.weapon;
909 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
911 if (t < 0) // "default" weapon selection
913 if (g_lms || g_ca || allguns)
914 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
917 else if (g_race || g_cts)
918 t = (i == WEP_LASER);
920 t = 0; // weapon is set a few lines later
922 t = (i == WEP_LASER || i == WEP_SHOTGUN);
923 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
924 t |= (i == WEP_HOOK);
927 // we cannot disable porto in Nexball, we must force it
928 if(g_nexball && i == WEP_PORTO)
934 float NixNex_CanChooseWeapon(float wpn);
935 void readplayerstartcvars()
941 // initialize starting values for players
944 start_ammo_shells = 0;
945 start_ammo_nails = 0;
946 start_ammo_rockets = 0;
947 start_ammo_cells = 0;
948 start_health = cvar("g_balance_health_start");
949 start_armorvalue = cvar("g_balance_armor_start");
952 s = cvar_string("g_weaponarena");
958 g_weaponarena_list = "All Weapons";
959 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
961 e = get_weaponinfo(j);
962 g_weaponarena |= e.weapons;
963 weapon_action(e.weapon, WR_PRECACHE);
966 else if (s == "most")
968 g_weaponarena_list = "Most Weapons";
969 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
971 e = get_weaponinfo(j);
972 if (e.spawnflags & WEP_FLAG_NORMAL)
974 g_weaponarena |= e.weapons;
975 weapon_action(e.weapon, WR_PRECACHE);
979 else if (s == "none")
981 g_weaponarena_list = "No Weapons";
982 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
986 t = tokenize_console(s);
987 g_weaponarena_list = "";
988 for (i = 0; i < t; ++i)
991 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
993 e = get_weaponinfo(j);
996 g_weaponarena |= e.weapons;
997 weapon_action(e.weapon, WR_PRECACHE);
998 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
1004 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
1007 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
1011 g_weaponarena_random = cvar("g_weaponarena_random");
1013 g_weaponarena_random = 0;
1018 // will be done later
1019 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1020 if (NixNex_CanChooseWeapon(i))
1021 weapon_action(i, WR_PRECACHE);
1022 if(!cvar("g_use_ammunition"))
1023 start_items |= IT_UNLIMITED_AMMO;
1025 else if (g_weaponarena)
1027 start_weapons = g_weaponarena;
1028 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
1029 start_ammo_rockets = 999;
1030 if (g_weaponarena & WEPBIT_SHOTGUN)
1031 start_ammo_shells = 999;
1032 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
1033 start_ammo_cells = 999;
1034 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
1035 start_ammo_nails = 999;
1036 if (g_weaponarena & WEPBIT_HOOK)
1037 start_ammo_fuel = 999;
1038 start_items |= IT_UNLIMITED_AMMO;
1040 else if (g_minstagib)
1043 start_armorvalue = 0;
1044 start_weapons = WEPBIT_MINSTANEX;
1045 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
1046 start_ammo_cells = cvar("g_minstagib_ammo_start");
1047 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
1048 start_ammo_fuel = cvar("g_start_ammo_fuel");
1050 if (g_minstagib_invis_alpha <= 0)
1051 g_minstagib_invis_alpha = -1;
1057 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1058 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1059 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1060 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1061 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1062 start_health = cvar("g_lms_start_health");
1063 start_armorvalue = cvar("g_lms_start_armor");
1065 else if (cvar("g_use_ammunition"))
1067 start_ammo_shells = cvar("g_start_ammo_shells");
1068 start_ammo_nails = cvar("g_start_ammo_nails");
1069 start_ammo_rockets = cvar("g_start_ammo_rockets");
1070 start_ammo_cells = cvar("g_start_ammo_cells");
1071 start_ammo_fuel = cvar("g_start_ammo_fuel");
1075 start_ammo_shells = cvar("g_pickup_shells_max");
1076 start_ammo_nails = cvar("g_pickup_nails_max");
1077 start_ammo_rockets = cvar("g_pickup_rockets_max");
1078 start_ammo_cells = cvar("g_pickup_cells_max");
1079 start_ammo_fuel = cvar("g_pickup_fuel_max");
1080 start_items |= IT_UNLIMITED_AMMO;
1083 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1085 e = get_weaponinfo(i);
1086 if(want_weapon("g_start_weapon_", e, FALSE))
1088 start_weapons |= e.weapons;
1089 weapon_action(e.weapon, WR_PRECACHE);
1096 warmup_start_ammo_shells = start_ammo_shells;
1097 warmup_start_ammo_nails = start_ammo_nails;
1098 warmup_start_ammo_rockets = start_ammo_rockets;
1099 warmup_start_ammo_cells = start_ammo_cells;
1100 warmup_start_ammo_fuel = start_ammo_fuel;
1101 warmup_start_health = start_health;
1102 warmup_start_armorvalue = start_armorvalue;
1103 warmup_start_weapons = start_weapons;
1105 if (!g_weaponarena && !g_nixnex && !g_minstagib && !g_ca)
1107 if (cvar("g_use_ammunition"))
1109 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1110 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1111 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1112 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1113 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1115 warmup_start_health = cvar("g_warmup_start_health");
1116 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1117 warmup_start_weapons = 0;
1118 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1120 e = get_weaponinfo(i);
1121 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1123 warmup_start_weapons |= e.weapons;
1124 weapon_action(e.weapon, WR_PRECACHE);
1130 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1132 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1133 start_items |= IT_FUEL_REGEN;
1134 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1135 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1139 start_items |= IT_JETPACK;
1141 if (g_weapon_stay == 2)
1143 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1144 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1145 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1146 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1147 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1148 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1149 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1150 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1151 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1152 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1155 start_ammo_shells = max(0, start_ammo_shells);
1156 start_ammo_nails = max(0, start_ammo_nails);
1157 start_ammo_cells = max(0, start_ammo_cells);
1158 start_ammo_rockets = max(0, start_ammo_rockets);
1159 start_ammo_fuel = max(0, start_ammo_fuel);
1161 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1162 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1163 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1164 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1165 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1169 float g_bugrigs_planar_movement;
1170 float g_bugrigs_planar_movement_car_jumping;
1171 float g_bugrigs_reverse_spinning;
1172 float g_bugrigs_reverse_speeding;
1173 float g_bugrigs_reverse_stopping;
1174 float g_bugrigs_air_steering;
1175 float g_bugrigs_angle_smoothing;
1176 float g_bugrigs_friction_floor;
1177 float g_bugrigs_friction_brake;
1178 float g_bugrigs_friction_air;
1179 float g_bugrigs_accel;
1180 float g_bugrigs_speed_ref;
1181 float g_bugrigs_speed_pow;
1182 float g_bugrigs_steer;
1184 float g_touchexplode;
1185 float g_touchexplode_radius;
1186 float g_touchexplode_damage;
1187 float g_touchexplode_edgedamage;
1188 float g_touchexplode_force;
1195 float sv_pitch_fixyaw;
1197 float sv_accuracy_data_share;
1199 void readlevelcvars(void)
1201 g_bugrigs = cvar("g_bugrigs");
1202 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1203 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1204 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1205 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1206 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1207 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1208 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1209 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1210 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1211 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1212 g_bugrigs_accel = cvar("g_bugrigs_accel");
1213 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1214 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1215 g_bugrigs_steer = cvar("g_bugrigs_steer");
1217 g_touchexplode = cvar("g_touchexplode");
1218 g_touchexplode_radius = cvar("g_touchexplode_radius");
1219 g_touchexplode_damage = cvar("g_touchexplode_damage");
1220 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1221 g_touchexplode_force = cvar("g_touchexplode_force");
1223 #ifdef ALLOW_FORCEMODELS
1224 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1226 sv_loddistance1 = cvar("sv_loddistance1");
1227 sv_loddistance2 = cvar("sv_loddistance2");
1229 if(sv_loddistance2 <= sv_loddistance1)
1230 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1232 sv_clones = cvar("sv_clones");
1233 sv_gentle = cvar("sv_gentle");
1234 sv_foginterval = cvar("sv_foginterval");
1235 g_cloaked = cvar("g_cloaked");
1236 g_jump_grunt = cvar("g_jump_grunt");
1237 g_footsteps = cvar("g_footsteps");
1238 g_grappling_hook = cvar("g_grappling_hook");
1239 g_jetpack = cvar("g_jetpack");
1240 g_laserguided_missile = cvar("g_laserguided_missile");
1241 g_midair = cvar("g_midair");
1242 g_minstagib = cvar("g_minstagib");
1243 g_nixnex = cvar("g_nixnex");
1244 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1245 g_norecoil = cvar("g_norecoil");
1246 g_vampire = cvar("g_vampire");
1247 g_bloodloss = cvar("g_bloodloss");
1248 sv_maxidle = cvar("sv_maxidle");
1249 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1250 sv_pogostick = cvar("sv_pogostick");
1251 sv_doublejump = cvar("sv_doublejump");
1252 g_ctf_reverse = cvar("g_ctf_reverse");
1253 sv_autotaunt = cvar("sv_autotaunt");
1254 sv_taunt = cvar("sv_taunt");
1256 inWarmupStage = cvar("g_warmup");
1257 g_warmup_limit = cvar("g_warmup_limit");
1258 g_warmup_allguns = cvar("g_warmup_allguns");
1259 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1261 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1262 inWarmupStage = 0; // these modes cannot work together, sorry
1264 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1265 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1266 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1267 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1268 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1269 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1270 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1271 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1272 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1273 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1274 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1275 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1277 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1278 if (g_nixnex) g_weaponarena = 0;
1281 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1282 g_weaponratefactor = cvar("g_weaponratefactor");
1283 g_weapondamagefactor = cvar("g_weapondamagefactor");
1284 g_weaponforcefactor = cvar("g_weaponforcefactor");
1285 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1287 g_pickup_shells = cvar("g_pickup_shells");
1288 g_pickup_shells_max = cvar("g_pickup_shells_max");
1289 g_pickup_nails = cvar("g_pickup_nails");
1290 g_pickup_nails_max = cvar("g_pickup_nails_max");
1291 g_pickup_rockets = cvar("g_pickup_rockets");
1292 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1293 g_pickup_cells = cvar("g_pickup_cells");
1294 g_pickup_cells_max = cvar("g_pickup_cells_max");
1295 g_pickup_fuel = cvar("g_pickup_fuel");
1296 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1297 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1298 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1299 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1300 g_pickup_armormedium = cvar("g_pickup_armormedium");
1301 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1302 g_pickup_armorbig = cvar("g_pickup_armorbig");
1303 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1304 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1305 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1306 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1307 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1308 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1309 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1310 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1311 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1312 g_pickup_healthmega = cvar("g_pickup_healthmega");
1313 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1315 g_pinata = cvar("g_pinata");
1317 g_weapon_stay = cvar("g_weapon_stay");
1319 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1322 g_ghost_items = cvar("g_ghost_items");
1324 if(g_ghost_items >= 1)
1325 g_ghost_items = 0.25; // default alpha value
1327 if not(inWarmupStage && !g_ca)
1328 game_starttime = cvar("g_start_delay");
1330 sv_pitch_min = cvar("sv_pitch_min");
1331 sv_pitch_max = cvar("sv_pitch_max");
1332 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1334 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1336 readplayerstartcvars();
1340 // TODO sound pack system
1343 string precache_sound_builtin (string s) = #19;
1344 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1345 string precache_sound(string s)
1347 return precache_sound_builtin(strcat(soundpack, s));
1349 void play2(entity e, string filename)
1351 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1353 void sound(entity e, float chan, string samp, float vol, float atten)
1355 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1360 string precache_sound (string s) = #19;
1361 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1362 float precache_sound_index (string s) = #19;
1364 #define SND_VOLUME 1
1365 #define SND_ATTENUATION 2
1366 #define SND_LARGEENTITY 8
1367 #define SND_LARGESOUND 16
1369 float sound_allowed(float dest, entity e)
1371 // sounds from world may always pass
1374 if (e.classname == "body")
1376 if (e.owner && e.owner != e)
1381 // sounds to self may always pass
1382 if (dest == MSG_ONE)
1383 if (e == msg_entity)
1385 // sounds by players can be removed
1386 if (cvar("bot_sound_monopoly"))
1387 if (clienttype(e) == CLIENTTYPE_REAL)
1389 // anything else may pass
1393 void sound(entity e, float chan, string samp, float vol, float atten)
1395 if (!sound_allowed(MSG_BROADCAST, e))
1397 sound_builtin(e, chan, samp, vol, atten);
1399 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1403 if (!sound_allowed(dest, e))
1406 entno = num_for_edict(e);
1407 idx = precache_sound_index(samp);
1412 atten = floor(atten * 64);
1413 vol = floor(vol * 255);
1416 sflags |= SND_VOLUME;
1418 sflags |= SND_ATTENUATION;
1420 sflags |= SND_LARGEENTITY;
1422 sflags |= SND_LARGESOUND;
1424 WriteByte(dest, SVC_SOUND);
1425 WriteByte(dest, sflags);
1426 if (sflags & SND_VOLUME)
1427 WriteByte(dest, vol);
1428 if (sflags & SND_ATTENUATION)
1429 WriteByte(dest, atten);
1430 if (sflags & SND_LARGEENTITY)
1432 WriteShort(dest, entno);
1433 WriteByte(dest, chan);
1437 WriteShort(dest, entno * 8 + chan);
1439 if (sflags & SND_LARGESOUND)
1440 WriteShort(dest, idx);
1442 WriteByte(dest, idx);
1444 WriteCoord(dest, o_x);
1445 WriteCoord(dest, o_y);
1446 WriteCoord(dest, o_z);
1448 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1452 if (!sound_allowed(dest, e))
1455 o = e.origin + 0.5 * (e.mins + e.maxs);
1456 soundtoat(dest, e, o, chan, samp, vol, atten);
1458 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1460 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1462 void stopsoundto(float dest, entity e, float chan)
1466 if (!sound_allowed(dest, e))
1469 entno = num_for_edict(e);
1474 idx = precache_sound_index("misc/null.wav");
1475 sflags = SND_LARGEENTITY;
1477 sflags |= SND_LARGESOUND;
1478 WriteByte(dest, SVC_SOUND);
1479 WriteByte(dest, sflags);
1480 WriteShort(dest, entno);
1481 WriteByte(dest, chan);
1482 if (sflags & SND_LARGESOUND)
1483 WriteShort(dest, idx);
1485 WriteByte(dest, idx);
1486 WriteCoord(dest, e.origin_x);
1487 WriteCoord(dest, e.origin_y);
1488 WriteCoord(dest, e.origin_z);
1492 WriteByte(dest, SVC_STOPSOUND);
1493 WriteShort(dest, entno * 8 + chan);
1496 void stopsound(entity e, float chan)
1498 if (!sound_allowed(MSG_BROADCAST, e))
1501 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1502 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1505 void play2(entity e, string filename)
1507 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1509 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1512 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1514 float spamsound(entity e, float chan, string samp, float vol, float atten)
1516 if (!sound_allowed(MSG_BROADCAST, e))
1519 if (time > e.spamtime)
1522 sound(e, chan, samp, vol, atten);
1528 void play2team(float t, string filename)
1532 if (cvar("bot_sound_monopoly"))
1535 FOR_EACH_REALPLAYER(head)
1538 play2(head, filename);
1542 void play2all(string samp)
1544 if (cvar("bot_sound_monopoly"))
1547 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1550 void PrecachePlayerSounds(string f);
1551 void precache_all_models(string pattern)
1553 float globhandle, i, n;
1556 globhandle = search_begin(pattern, TRUE, FALSE);
1559 n = search_getsize(globhandle);
1560 for (i = 0; i < n; ++i)
1562 //print(search_getfilename(globhandle, i), "\n");
1563 f = search_getfilename(globhandle, i);
1566 if(substring(f, -9,5) == "_lod1")
1568 if(substring(f, -9,5) == "_lod2")
1570 if(!sv_loddistance1)
1572 PrecachePlayerSounds(strcat(f, ".sounds"));
1574 search_end(globhandle);
1579 // gamemode related things
1580 precache_model ("models/misc/chatbubble.spr");
1581 precache_model ("models/misc/teambubble.spr");
1584 precache_model ("models/runematch/curse.mdl");
1585 precache_model ("models/runematch/rune.mdl");
1588 #ifdef TTURRETS_ENABLED
1589 if (cvar("g_turrets"))
1593 // Precache all player models if desired
1594 if (cvar("sv_precacheplayermodels"))
1596 PrecachePlayerSounds("sound/player/default.sounds");
1597 precache_all_models("models/player/*.zym");
1598 precache_all_models("models/player/*.dpm");
1599 precache_all_models("models/player/*.md3");
1600 precache_all_models("models/player/*.psk");
1601 //precache_model("models/player/carni.zym");
1602 //precache_model("models/player/crash.zym");
1603 //precache_model("models/player/grunt.zym");
1604 //precache_model("models/player/headhunter.zym");
1605 //precache_model("models/player/insurrectionist.zym");
1606 //precache_model("models/player/jeandarc.zym");
1607 //precache_model("models/player/lurk.zym");
1608 //precache_model("models/player/lycanthrope.zym");
1609 //precache_model("models/player/marine.zym");
1610 //precache_model("models/player/nexus.zym");
1611 //precache_model("models/player/pyria.zym");
1612 //precache_model("models/player/shock.zym");
1613 //precache_model("models/player/skadi.zym");
1614 //precache_model("models/player/specop.zym");
1615 //precache_model("models/player/visitant.zym");
1618 if (cvar("sv_defaultcharacter"))
1621 s = cvar_string("sv_defaultplayermodel_red");
1625 PrecachePlayerSounds(strcat(s, ".sounds"));
1627 s = cvar_string("sv_defaultplayermodel_blue");
1631 PrecachePlayerSounds(strcat(s, ".sounds"));
1633 s = cvar_string("sv_defaultplayermodel_yellow");
1637 PrecachePlayerSounds(strcat(s, ".sounds"));
1639 s = cvar_string("sv_defaultplayermodel_pink");
1643 PrecachePlayerSounds(strcat(s, ".sounds"));
1645 s = cvar_string("sv_defaultplayermodel");
1649 PrecachePlayerSounds(strcat(s, ".sounds"));
1655 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1656 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1659 // gore and miscellaneous sounds
1660 //precache_sound ("misc/h2ohit.wav");
1661 precache_model ("models/hook.md3");
1662 precache_sound ("misc/armorimpact.wav");
1663 precache_sound ("misc/bodyimpact1.wav");
1664 precache_sound ("misc/bodyimpact2.wav");
1665 precache_sound ("misc/gib.wav");
1666 precache_sound ("misc/gib_splat01.wav");
1667 precache_sound ("misc/gib_splat02.wav");
1668 precache_sound ("misc/gib_splat03.wav");
1669 precache_sound ("misc/gib_splat04.wav");
1670 precache_sound ("misc/hit.wav");
1671 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1672 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1673 precache_sound ("misc/null.wav");
1674 precache_sound ("misc/spawn.wav");
1675 precache_sound ("misc/talk.wav");
1676 precache_sound ("misc/teleport.wav");
1677 precache_sound ("misc/poweroff.wav");
1678 precache_sound ("player/lava.wav");
1679 precache_sound ("player/slime.wav");
1682 precache_sound ("misc/jetpack_fly.wav");
1684 precache_model ("models/sprites/0.spr32");
1685 precache_model ("models/sprites/1.spr32");
1686 precache_model ("models/sprites/2.spr32");
1687 precache_model ("models/sprites/3.spr32");
1688 precache_model ("models/sprites/4.spr32");
1689 precache_model ("models/sprites/5.spr32");
1690 precache_model ("models/sprites/6.spr32");
1691 precache_model ("models/sprites/7.spr32");
1692 precache_model ("models/sprites/8.spr32");
1693 precache_model ("models/sprites/9.spr32");
1694 precache_model ("models/sprites/10.spr32");
1696 // common weapon precaches
1697 precache_sound ("weapons/weapon_switch.wav");
1698 precache_sound ("weapons/weaponpickup.wav");
1699 precache_sound ("weapons/unavailable.wav");
1700 if (g_grappling_hook)
1702 precache_sound ("weapons/hook_fire.wav"); // hook
1703 precache_sound ("weapons/hook_impact.wav"); // hook
1706 if (cvar("sv_precacheweapons") || g_nixnex)
1708 //precache weapon models/sounds
1711 while (wep <= WEP_LAST)
1713 weapon_action(wep, WR_PRECACHE);
1718 precache_model("models/elaser.mdl");
1719 precache_model("models/laser.mdl");
1720 precache_model("models/ebomb.mdl");
1723 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1725 if (!self.noise && self.music) // quake 3 uses the music field
1726 self.noise = self.music;
1728 // plays music for the level if there is any
1731 precache_sound (self.noise);
1732 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1737 // sorry, but using \ in macros breaks line numbers
1738 #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
1739 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1740 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1742 vector ExactTriggerHit_mins;
1743 vector ExactTriggerHit_maxs;
1744 float ExactTriggerHit_Recurse()
1750 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1753 if (trace_ent == self)
1758 se.solid = SOLID_NOT;
1759 f = ExactTriggerHit_Recurse();
1765 float ExactTriggerHit()
1769 if not(self.modelindex)
1773 self.solid = SOLID_BSP;
1774 ExactTriggerHit_mins = other.absmin;
1775 ExactTriggerHit_maxs = other.absmax;
1776 f = ExactTriggerHit_Recurse();
1782 // WARNING: this kills the trace globals
1783 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1784 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1786 #define INITPRIO_FIRST 0
1787 #define INITPRIO_GAMETYPE 0
1788 #define INITPRIO_GAMETYPE_FALLBACK 1
1789 #define INITPRIO_CVARS 5
1790 #define INITPRIO_FINDTARGET 10
1791 #define INITPRIO_DROPTOFLOOR 20
1792 #define INITPRIO_SETLOCATION 90
1793 #define INITPRIO_LINKDOORS 91
1794 #define INITPRIO_LAST 99
1796 .void(void) initialize_entity;
1797 .float initialize_entity_order;
1798 .entity initialize_entity_next;
1799 entity initialize_entity_first;
1801 void make_safe_for_remove(entity e)
1803 if (e.initialize_entity)
1806 for (ent = initialize_entity_first; ent; )
1808 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1810 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1811 // skip it in linked list
1814 prev.initialize_entity_next = ent.initialize_entity_next;
1815 ent = prev.initialize_entity_next;
1819 initialize_entity_first = ent.initialize_entity_next;
1820 ent = initialize_entity_first;
1826 ent = ent.initialize_entity_next;
1832 void objerror(string s)
1834 make_safe_for_remove(self);
1835 objerror_builtin(s);
1838 void remove_unsafely(entity e)
1843 void remove_safely(entity e)
1845 make_safe_for_remove(e);
1849 void InitializeEntity(entity e, void(void) func, float order)
1853 if (!e || e.initialize_entity)
1855 // make a proxy initializer entity
1859 e.classname = "initialize_entity";
1863 e.initialize_entity = func;
1864 e.initialize_entity_order = order;
1866 cur = initialize_entity_first;
1869 if (!cur || cur.initialize_entity_order > order)
1871 // insert between prev and cur
1873 prev.initialize_entity_next = e;
1875 initialize_entity_first = e;
1876 e.initialize_entity_next = cur;
1880 cur = cur.initialize_entity_next;
1883 void InitializeEntitiesRun()
1886 startoflist = initialize_entity_first;
1887 initialize_entity_first = world;
1888 for (self = startoflist; self; )
1891 var void(void) func;
1892 e = self.initialize_entity_next;
1893 func = self.initialize_entity;
1894 self.initialize_entity_order = 0;
1895 self.initialize_entity = func_null;
1896 self.initialize_entity_next = world;
1897 if (self.classname == "initialize_entity")
1901 remove_builtin(self);
1904 //dprint("Delayed initialization: ", self.classname, "\n");
1910 .float uncustomizeentityforclient_set;
1911 .void(void) uncustomizeentityforclient;
1912 void(void) SUB_Nullpointer = #0;
1913 void UncustomizeEntitiesRun()
1917 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1918 self.uncustomizeentityforclient();
1921 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1923 e.customizeentityforclient = customizer;
1924 e.uncustomizeentityforclient = uncustomizer;
1925 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1929 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1932 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1936 if (e.classname == "")
1937 e.classname = "net_linked";
1939 if (e.model == "" || self.modelindex == 0)
1943 setmodel(e, "null");
1947 e.SendEntity = sendfunc;
1948 e.SendFlags = 0xFFFFFF;
1951 e.effects |= EF_NODEPTHTEST;
1955 e.nextthink = time + dt;
1956 e.think = SUB_Remove;
1960 void adaptor_think2touch()
1969 void adaptor_think2use()
1981 // deferred dropping
1982 void DropToFloor_Handler()
1984 droptofloor_builtin();
1985 self.dropped_origin = self.origin;
1990 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1995 float trace_hits_box_a0, trace_hits_box_a1;
1997 float trace_hits_box_1d(float end, float thmi, float thma)
2001 // just check if x is in range
2009 // do the trace with respect to x
2010 // 0 -> end has to stay in thmi -> thma
2011 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
2012 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
2013 if (trace_hits_box_a0 > trace_hits_box_a1)
2019 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
2024 // now it is a trace from 0 to end
2026 trace_hits_box_a0 = 0;
2027 trace_hits_box_a1 = 1;
2029 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
2031 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
2033 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
2039 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
2041 return trace_hits_box(start, end, thmi - ma, thma - mi);
2044 float SUB_NoImpactCheck()
2046 // zero hitcontents = this is not the real impact, but either the
2047 // mirror-impact of something hitting the projectile instead of the
2048 // projectile hitting the something, or a touchareagrid one. Neither of
2049 // these stop the projectile from moving, so...
2050 if(trace_dphitcontents == 0)
2052 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
2055 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2057 if (other == world && self.size != '0 0 0')
2060 tic = self.velocity * sys_frametime;
2061 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2062 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2063 if (trace_fraction >= 1)
2065 dprint("Odd... did not hit...?\n");
2067 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2069 dprint("Detected and prevented the sky-grapple bug.\n");
2077 #define SUB_OwnerCheck() (other && (other == self.owner))
2079 #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)
2081 float MAX_IPBAN_URIS = 16;
2083 float URI_GET_DISCARD = 0;
2084 float URI_GET_IPBAN = 1;
2085 float URI_GET_IPBAN_END = 16;
2087 void URI_Get_Callback(float id, float status, string data)
2089 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2091 dprint("\nEnd of data.\n");
2093 if (id == URI_GET_DISCARD)
2097 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2100 OnlineBanList_URI_Get_Callback(id, status, data);
2104 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2108 void print_to(entity e, string s)
2111 sprint(e, strcat(s, "\n"));
2116 string getrecords(float page) // 50 records per page
2130 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2132 if (MapInfo_Get_ByID(i))
2134 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2137 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2138 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2146 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2148 if (MapInfo_Get_ByID(i))
2150 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2153 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2154 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2162 for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2164 if (MapInfo_Get_ByID(i))
2166 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2169 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2170 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2176 MapInfo_ClearTemps();
2178 if (s == "" && page == 0)
2179 return "No records are available on this server.\n";
2184 string getrankings()
2197 for (i = 1; i <= RANKINGS_CNT; ++i)
2199 t = race_GetTime(i);
2202 n = race_GetName(i);
2203 p = race_PlaceName(i);
2204 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2207 MapInfo_ClearTemps();
2210 return strcat("No records are available for the map: ", map, "\n");
2212 return strcat("Records for ", map, ":\n", s);
2215 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2218 vector start, org, delta, end, enddown, mstart;
2220 m = e.dphitcontentsmask;
2221 e.dphitcontentsmask = goodcontents | badcontents;
2224 delta = world.maxs - world.mins;
2226 for (i = 0; i < attempts; ++i)
2228 start_x = org_x + random() * delta_x;
2229 start_y = org_y + random() * delta_y;
2230 start_z = org_z + random() * delta_z;
2232 // rule 1: start inside world bounds, and outside
2233 // solid, and don't start from somewhere where you can
2234 // fall down to evil
2235 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2236 if (trace_fraction >= 1)
2238 if (trace_startsolid)
2240 if (trace_dphitcontents & badcontents)
2242 if (trace_dphitq3surfaceflags & badsurfaceflags)
2245 // rule 2: if we are too high, lower the point
2246 if (trace_fraction * delta_z > maxaboveground)
2247 start = trace_endpos + '0 0 1' * maxaboveground;
2248 enddown = trace_endpos;
2250 // rule 3: make sure we aren't outside the map. This only works
2251 // for somewhat well formed maps. A good rule of thumb is that
2252 // the map should have a convex outside hull.
2253 // these can be traceLINES as we already verified the starting box
2254 mstart = start + 0.5 * (e.mins + e.maxs);
2255 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2256 if (trace_fraction >= 1)
2258 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2259 if (trace_fraction >= 1)
2261 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2262 if (trace_fraction >= 1)
2264 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2265 if (trace_fraction >= 1)
2267 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2268 if (trace_fraction >= 1)
2271 // find a random vector to "look at"
2272 end_x = org_x + random() * delta_x;
2273 end_y = org_y + random() * delta_y;
2274 end_z = org_z + random() * delta_z;
2275 end = start + normalize(end - start) * vlen(delta);
2277 // rule 4: start TO end must not be too short
2278 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2279 if (trace_startsolid)
2281 if (trace_fraction < minviewdistance / vlen(delta))
2284 // rule 5: don't want to look at sky
2285 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2288 // rule 6: we must not end up in trigger_hurt
2289 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2291 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2298 e.dphitcontentsmask = m;
2302 setorigin(e, start);
2303 e.angles = vectoangles(end - start);
2304 dprint("Needed ", ftos(i + 1), " attempts\n");
2311 float zcurveparticles_effectno;
2312 vector zcurveparticles_start;
2313 float zcurveparticles_spd;
2315 void endzcurveparticles()
2317 if(zcurveparticles_effectno)
2320 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2322 zcurveparticles_effectno = 0;
2325 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2327 spd = bound(0, floor(spd / 16), 32767);
2328 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2330 endzcurveparticles();
2331 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2332 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2333 WriteShort(MSG_BROADCAST, effectno);
2334 WriteCoord(MSG_BROADCAST, start_x);
2335 WriteCoord(MSG_BROADCAST, start_y);
2336 WriteCoord(MSG_BROADCAST, start_z);
2337 zcurveparticles_effectno = effectno;
2338 zcurveparticles_start = start;
2341 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2342 WriteCoord(MSG_BROADCAST, end_x);
2343 WriteCoord(MSG_BROADCAST, end_y);
2344 WriteCoord(MSG_BROADCAST, end_z);
2345 WriteCoord(MSG_BROADCAST, end_dz);
2346 zcurveparticles_spd = spd;
2349 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2352 vector vecxy, velxy;
2354 vecxy = end - start;
2359 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2361 endzcurveparticles();
2362 trailparticles(world, effectno, start, end);
2366 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2367 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2370 string GetGametype(); // g_world.qc
2371 void write_recordmarker(entity pl, float tstart, float dt)
2373 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2375 // also write a marker into demo files for demotc-race-record-extractor to find
2378 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2379 " ", ftos(tstart), " ", ftos(dt), "\n"));
2382 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2384 switch(self.owner.cvar_cl_gunalign)
2395 if(allowcenter) // 2: allow center handedness
2408 if(allowcenter) // 2: allow center handedness
2424 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2429 if (cvar("g_shootfromeye"))
2433 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2441 else if (cvar("g_shootfromcenter"))
2445 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2453 else if (cvar("g_shootfromclient"))
2455 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2457 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2472 void attach_sameorigin(entity e, entity to, string tag)
2474 vector org, t_forward, t_left, t_up, e_forward, e_up;
2481 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2482 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2483 t_forward = v_forward * tagscale;
2484 t_left = v_right * -tagscale;
2485 t_up = v_up * tagscale;
2487 e.origin_x = org * t_forward;
2488 e.origin_y = org * t_left;
2489 e.origin_z = org * t_up;
2491 // current forward and up directions
2492 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2493 e.angles_x = -e.angles_x;
2494 fixedmakevectors(e.angles);
2496 // untransform forward, up!
2497 e_forward_x = v_forward * t_forward;
2498 e_forward_y = v_forward * t_left;
2499 e_forward_z = v_forward * t_up;
2500 e_up_x = v_up * t_forward;
2501 e_up_y = v_up * t_left;
2502 e_up_z = v_up * t_up;
2504 e.angles = fixedvectoangles2(e_forward, e_up);
2505 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2506 e.angles_x = -e.angles_x;
2508 setattachment(e, to, tag);
2509 setorigin(e, e.origin);
2512 void detach_sameorigin(entity e)
2515 org = gettaginfo(e, 0);
2516 e.angles = fixedvectoangles2(v_forward, v_up);
2517 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2518 e.angles_x = -e.angles_x;
2520 setattachment(e, world, "");
2521 setorigin(e, e.origin);
2524 void follow_sameorigin(entity e, entity to)
2526 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2527 e.aiment = to; // make the hole follow bmodel
2528 e.punchangle = to.angles; // the original angles of bmodel
2529 e.view_ofs = e.origin - to.origin; // relative origin
2530 e.v_angle = e.angles - to.angles; // relative angles
2533 void unfollow_sameorigin(entity e)
2535 e.movetype = MOVETYPE_NONE;
2538 entity gettaginfo_relative_ent;
2539 vector gettaginfo_relative(entity e, float tag)
2541 if (!gettaginfo_relative_ent)
2543 gettaginfo_relative_ent = spawn();
2544 gettaginfo_relative_ent.effects = EF_NODRAW;
2546 gettaginfo_relative_ent.model = e.model;
2547 gettaginfo_relative_ent.modelindex = e.modelindex;
2548 gettaginfo_relative_ent.frame = e.frame;
2549 return gettaginfo(gettaginfo_relative_ent, tag);
2552 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2556 if (pl.soundentity.cnt & p)
2558 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2559 pl.soundentity.cnt |= p;
2562 void SoundEntity_StopSound(entity pl, float chan)
2566 if (pl.soundentity.cnt & p)
2568 stopsoundto(MSG_ALL, pl.soundentity, chan);
2569 pl.soundentity.cnt &~= p;
2573 void SoundEntity_Attach(entity pl)
2575 pl.soundentity = spawn();
2576 pl.soundentity.classname = "soundentity";
2577 pl.soundentity.owner = pl;
2578 setattachment(pl.soundentity, pl, "");
2579 setmodel(pl.soundentity, "null");
2582 void SoundEntity_Detach(entity pl)
2585 for (i = 0; i <= 7; ++i)
2586 SoundEntity_StopSound(pl, i);
2590 float ParseCommandPlayerSlotTarget_firsttoken;
2591 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2599 ParseCommandPlayerSlotTarget_firsttoken = -1;
2603 if (substring(argv(idx), 0, 1) == "#")
2605 s = substring(argv(idx), 1, -1);
2613 ParseCommandPlayerSlotTarget_firsttoken = idx;
2614 if (s == ftos(stof(s)))
2616 e = edict_num(stof(s));
2617 if (e.flags & FL_CLIENT)
2623 // it must be a nick name
2626 ParseCommandPlayerSlotTarget_firsttoken = idx;
2629 FOR_EACH_CLIENT(head)
2630 if (head.netname == s)
2638 s = strdecolorize(s);
2640 FOR_EACH_CLIENT(head)
2641 if (strdecolorize(head.netname) == s)
2656 float modeleffect_SendEntity(entity to, float sf)
2659 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2662 if(self.velocity != '0 0 0')
2664 if(self.angles != '0 0 0')
2666 if(self.avelocity != '0 0 0')
2669 WriteByte(MSG_ENTITY, f);
2670 WriteShort(MSG_ENTITY, self.modelindex);
2671 WriteByte(MSG_ENTITY, self.skin);
2672 WriteByte(MSG_ENTITY, self.frame);
2673 WriteCoord(MSG_ENTITY, self.origin_x);
2674 WriteCoord(MSG_ENTITY, self.origin_y);
2675 WriteCoord(MSG_ENTITY, self.origin_z);
2678 WriteCoord(MSG_ENTITY, self.velocity_x);
2679 WriteCoord(MSG_ENTITY, self.velocity_y);
2680 WriteCoord(MSG_ENTITY, self.velocity_z);
2684 WriteCoord(MSG_ENTITY, self.angles_x);
2685 WriteCoord(MSG_ENTITY, self.angles_y);
2686 WriteCoord(MSG_ENTITY, self.angles_z);
2690 WriteCoord(MSG_ENTITY, self.avelocity_x);
2691 WriteCoord(MSG_ENTITY, self.avelocity_y);
2692 WriteCoord(MSG_ENTITY, self.avelocity_z);
2694 WriteShort(MSG_ENTITY, self.scale * 256.0);
2695 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2696 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2697 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2698 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2703 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)
2708 e.classname = "modeleffect";
2716 e.teleport_time = t1;
2720 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2724 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2727 sz = max(e.scale, e.scale2);
2728 setsize(e, e.mins * sz, e.maxs * sz);
2729 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2732 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2734 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2737 float randombit(float bits)
2739 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2748 for(f = 1; f <= bits; f *= 2)
2757 r = (r - 1) / (n - 1);
2764 float randombits(float bits, float k, float error_return)
2768 while(k > 0 && bits != r)
2770 r += randombit(bits - r);
2779 void randombit_test(float bits, float iter)
2783 print(ftos(randombit(bits)), "\n");
2788 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2790 if(halflifedist > 0)
2791 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2792 else if(halflifedist < 0)
2793 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2802 #define cvar_string_normal cvar_string_builtin
2803 #define cvar_normal cvar_builtin
2805 string cvar_string_normal(string n)
2807 if not(cvar_type(n) & 1)
2808 backtrace(strcat("Attempt to access undefined cvar: ", n));
2809 return cvar_string_builtin(n);
2812 float cvar_normal(string n)
2814 return stof(cvar_string_normal(n));
2817 #define cvar_set_normal cvar_set_builtin
2825 oself.think = SUB_Remove;
2826 oself.nextthink = time;
2832 Execute func() after time + fdelay.
2833 self when func is executed = self when defer is called
2835 void defer(float fdelay, void() func)
2842 e.think = defer_think;
2843 e.nextthink = time + fdelay;