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 & WEPSPAWNFLAG_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 & WEPSPAWNFLAG_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_cheats = cvar("sv_cheats");
1234 sv_gentle = cvar("sv_gentle");
1235 sv_foginterval = cvar("sv_foginterval");
1236 g_cloaked = cvar("g_cloaked");
1237 g_jump_grunt = cvar("g_jump_grunt");
1238 g_footsteps = cvar("g_footsteps");
1239 g_grappling_hook = cvar("g_grappling_hook");
1240 g_jetpack = cvar("g_jetpack");
1241 g_laserguided_missile = cvar("g_laserguided_missile");
1242 g_midair = cvar("g_midair");
1243 g_minstagib = cvar("g_minstagib");
1244 g_nixnex = cvar("g_nixnex");
1245 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1246 g_norecoil = cvar("g_norecoil");
1247 g_vampire = cvar("g_vampire");
1248 g_bloodloss = cvar("g_bloodloss");
1249 sv_maxidle = cvar("sv_maxidle");
1250 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1251 sv_pogostick = cvar("sv_pogostick");
1252 sv_doublejump = cvar("sv_doublejump");
1253 g_ctf_reverse = cvar("g_ctf_reverse");
1254 sv_autotaunt = cvar("sv_autotaunt");
1255 sv_taunt = cvar("sv_taunt");
1257 inWarmupStage = cvar("g_warmup");
1258 g_warmup_limit = cvar("g_warmup_limit");
1259 g_warmup_allguns = cvar("g_warmup_allguns");
1260 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1262 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1263 inWarmupStage = 0; // these modes cannot work together, sorry
1265 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1266 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1267 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1268 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1269 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1270 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1271 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1272 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1273 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1274 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1275 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1276 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1278 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1279 if (g_nixnex) g_weaponarena = 0;
1282 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1283 g_weaponratefactor = cvar("g_weaponratefactor");
1284 g_weapondamagefactor = cvar("g_weapondamagefactor");
1285 g_weaponforcefactor = cvar("g_weaponforcefactor");
1286 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1288 g_pickup_shells = cvar("g_pickup_shells");
1289 g_pickup_shells_max = cvar("g_pickup_shells_max");
1290 g_pickup_nails = cvar("g_pickup_nails");
1291 g_pickup_nails_max = cvar("g_pickup_nails_max");
1292 g_pickup_rockets = cvar("g_pickup_rockets");
1293 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1294 g_pickup_cells = cvar("g_pickup_cells");
1295 g_pickup_cells_max = cvar("g_pickup_cells_max");
1296 g_pickup_fuel = cvar("g_pickup_fuel");
1297 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1298 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1299 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1300 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1301 g_pickup_armormedium = cvar("g_pickup_armormedium");
1302 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1303 g_pickup_armorbig = cvar("g_pickup_armorbig");
1304 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1305 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1306 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1307 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1308 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1309 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1310 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1311 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1312 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1313 g_pickup_healthmega = cvar("g_pickup_healthmega");
1314 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1316 g_pinata = cvar("g_pinata");
1318 g_weapon_stay = cvar("g_weapon_stay");
1320 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1323 g_ghost_items = cvar("g_ghost_items");
1325 if(g_ghost_items >= 1)
1326 g_ghost_items = 0.13; // default alpha value
1328 if not(inWarmupStage && !g_ca)
1329 game_starttime = cvar("g_start_delay");
1331 sv_pitch_min = cvar("sv_pitch_min");
1332 sv_pitch_max = cvar("sv_pitch_max");
1333 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1335 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1337 readplayerstartcvars();
1341 // TODO sound pack system
1344 string precache_sound_builtin (string s) = #19;
1345 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1346 string precache_sound(string s)
1348 return precache_sound_builtin(strcat(soundpack, s));
1350 void play2(entity e, string filename)
1352 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1354 void sound(entity e, float chan, string samp, float vol, float atten)
1356 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1361 string precache_sound (string s) = #19;
1362 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1363 float precache_sound_index (string s) = #19;
1365 #define SND_VOLUME 1
1366 #define SND_ATTENUATION 2
1367 #define SND_LARGEENTITY 8
1368 #define SND_LARGESOUND 16
1370 float sound_allowed(float dest, entity e)
1372 // sounds from world may always pass
1375 if (e.classname == "body")
1377 if (e.owner && e.owner != e)
1382 // sounds to self may always pass
1383 if (dest == MSG_ONE)
1384 if (e == msg_entity)
1386 // sounds by players can be removed
1387 if (cvar("bot_sound_monopoly"))
1388 if (clienttype(e) == CLIENTTYPE_REAL)
1390 // anything else may pass
1394 void sound(entity e, float chan, string samp, float vol, float atten)
1396 if (!sound_allowed(MSG_BROADCAST, e))
1398 sound_builtin(e, chan, samp, vol, atten);
1400 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1404 if (!sound_allowed(dest, e))
1407 entno = num_for_edict(e);
1408 idx = precache_sound_index(samp);
1413 atten = floor(atten * 64);
1414 vol = floor(vol * 255);
1417 sflags |= SND_VOLUME;
1419 sflags |= SND_ATTENUATION;
1421 sflags |= SND_LARGEENTITY;
1423 sflags |= SND_LARGESOUND;
1425 WriteByte(dest, SVC_SOUND);
1426 WriteByte(dest, sflags);
1427 if (sflags & SND_VOLUME)
1428 WriteByte(dest, vol);
1429 if (sflags & SND_ATTENUATION)
1430 WriteByte(dest, atten);
1431 if (sflags & SND_LARGEENTITY)
1433 WriteShort(dest, entno);
1434 WriteByte(dest, chan);
1438 WriteShort(dest, entno * 8 + chan);
1440 if (sflags & SND_LARGESOUND)
1441 WriteShort(dest, idx);
1443 WriteByte(dest, idx);
1445 WriteCoord(dest, o_x);
1446 WriteCoord(dest, o_y);
1447 WriteCoord(dest, o_z);
1449 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1453 if (!sound_allowed(dest, e))
1456 o = e.origin + 0.5 * (e.mins + e.maxs);
1457 soundtoat(dest, e, o, chan, samp, vol, atten);
1459 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1461 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1463 void stopsoundto(float dest, entity e, float chan)
1467 if (!sound_allowed(dest, e))
1470 entno = num_for_edict(e);
1475 idx = precache_sound_index("misc/null.wav");
1476 sflags = SND_LARGEENTITY;
1478 sflags |= SND_LARGESOUND;
1479 WriteByte(dest, SVC_SOUND);
1480 WriteByte(dest, sflags);
1481 WriteShort(dest, entno);
1482 WriteByte(dest, chan);
1483 if (sflags & SND_LARGESOUND)
1484 WriteShort(dest, idx);
1486 WriteByte(dest, idx);
1487 WriteCoord(dest, e.origin_x);
1488 WriteCoord(dest, e.origin_y);
1489 WriteCoord(dest, e.origin_z);
1493 WriteByte(dest, SVC_STOPSOUND);
1494 WriteShort(dest, entno * 8 + chan);
1497 void stopsound(entity e, float chan)
1499 if (!sound_allowed(MSG_BROADCAST, e))
1502 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1503 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1506 void play2(entity e, string filename)
1508 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1510 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1513 .float announcetime;
1514 float announce(entity player, string msg)
1516 if (time > player.announcetime)
1517 if (clienttype(player) == CLIENTTYPE_REAL)
1519 player.announcetime = time + 0.8;
1525 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1526 float spamsound(entity e, float chan, string samp, float vol, float atten)
1528 if (!sound_allowed(MSG_BROADCAST, e))
1531 if (time > e.announcetime)
1533 e.announcetime = time;
1534 sound(e, chan, samp, vol, atten);
1540 void play2team(float t, string filename)
1544 if (cvar("bot_sound_monopoly"))
1547 FOR_EACH_REALPLAYER(head)
1550 play2(head, filename);
1554 void play2all(string samp)
1556 if (cvar("bot_sound_monopoly"))
1559 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1562 void PrecachePlayerSounds(string f);
1563 void precache_all_models(string pattern)
1565 float globhandle, i, n;
1568 globhandle = search_begin(pattern, TRUE, FALSE);
1571 n = search_getsize(globhandle);
1572 for (i = 0; i < n; ++i)
1574 //print(search_getfilename(globhandle, i), "\n");
1575 f = search_getfilename(globhandle, i);
1578 if(substring(f, -9,5) == "_lod1")
1580 if(substring(f, -9,5) == "_lod2")
1582 if(!sv_loddistance1)
1584 PrecachePlayerSounds(strcat(f, ".sounds"));
1586 search_end(globhandle);
1591 // gamemode related things
1592 precache_model ("models/misc/chatbubble.spr");
1593 precache_model ("models/misc/teambubble.spr");
1596 precache_model ("models/runematch/curse.mdl");
1597 precache_model ("models/runematch/rune.mdl");
1600 #ifdef TTURRETS_ENABLED
1601 if (cvar("g_turrets"))
1605 // Precache all player models if desired
1606 if (cvar("sv_precacheplayermodels"))
1608 PrecachePlayerSounds("sound/player/default.sounds");
1609 precache_all_models("models/player/*.zym");
1610 precache_all_models("models/player/*.dpm");
1611 precache_all_models("models/player/*.md3");
1612 precache_all_models("models/player/*.psk");
1613 //precache_model("models/player/carni.zym");
1614 //precache_model("models/player/crash.zym");
1615 //precache_model("models/player/grunt.zym");
1616 //precache_model("models/player/headhunter.zym");
1617 //precache_model("models/player/insurrectionist.zym");
1618 //precache_model("models/player/jeandarc.zym");
1619 //precache_model("models/player/lurk.zym");
1620 //precache_model("models/player/lycanthrope.zym");
1621 //precache_model("models/player/marine.zym");
1622 //precache_model("models/player/nexus.zym");
1623 //precache_model("models/player/pyria.zym");
1624 //precache_model("models/player/shock.zym");
1625 //precache_model("models/player/skadi.zym");
1626 //precache_model("models/player/specop.zym");
1627 //precache_model("models/player/visitant.zym");
1630 if (cvar("sv_defaultcharacter"))
1633 s = cvar_string("sv_defaultplayermodel_red");
1637 PrecachePlayerSounds(strcat(s, ".sounds"));
1639 s = cvar_string("sv_defaultplayermodel_blue");
1643 PrecachePlayerSounds(strcat(s, ".sounds"));
1645 s = cvar_string("sv_defaultplayermodel_yellow");
1649 PrecachePlayerSounds(strcat(s, ".sounds"));
1651 s = cvar_string("sv_defaultplayermodel_pink");
1655 PrecachePlayerSounds(strcat(s, ".sounds"));
1657 s = cvar_string("sv_defaultplayermodel");
1661 PrecachePlayerSounds(strcat(s, ".sounds"));
1667 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1668 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1671 // gore and miscellaneous sounds
1672 //precache_sound ("misc/h2ohit.wav");
1673 precache_model ("models/hook.md3");
1674 precache_sound ("misc/armorimpact.wav");
1675 precache_sound ("misc/bodyimpact1.wav");
1676 precache_sound ("misc/bodyimpact2.wav");
1677 precache_sound ("misc/gib.wav");
1678 precache_sound ("misc/gib_splat01.wav");
1679 precache_sound ("misc/gib_splat02.wav");
1680 precache_sound ("misc/gib_splat03.wav");
1681 precache_sound ("misc/gib_splat04.wav");
1682 precache_sound ("misc/hit.wav");
1683 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1684 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1685 precache_sound ("misc/null.wav");
1686 precache_sound ("misc/spawn.wav");
1687 precache_sound ("misc/talk.wav");
1688 precache_sound ("misc/teleport.wav");
1689 precache_sound ("misc/poweroff.wav");
1690 precache_sound ("player/lava.wav");
1691 precache_sound ("player/slime.wav");
1694 precache_sound ("misc/jetpack_fly.wav");
1696 // announcer sounds - male
1697 precache_sound ("announcer/male/electrobitch.wav");
1698 precache_sound ("announcer/male/airshot.wav");
1699 precache_sound ("announcer/male/03kills.wav");
1700 precache_sound ("announcer/male/05kills.wav");
1701 precache_sound ("announcer/male/10kills.wav");
1702 precache_sound ("announcer/male/15kills.wav");
1703 precache_sound ("announcer/male/20kills.wav");
1704 precache_sound ("announcer/male/25kills.wav");
1705 precache_sound ("announcer/male/30kills.wav");
1706 precache_sound ("announcer/male/botlike.wav");
1707 precache_sound ("announcer/male/yoda.wav");
1708 precache_sound ("announcer/male/amazing.wav");
1709 precache_sound ("announcer/male/awesome.wav");
1710 precache_sound ("announcer/male/headshot.wav");
1711 precache_sound ("announcer/male/impressive.wav");
1713 // announcer sounds - robotic
1714 precache_sound ("announcer/robotic/prepareforbattle.wav");
1715 precache_sound ("announcer/robotic/begin.wav");
1716 precache_sound ("announcer/robotic/timeoutcalled.wav");
1717 precache_sound ("announcer/robotic/1fragleft.wav");
1718 precache_sound ("announcer/robotic/2fragsleft.wav");
1719 precache_sound ("announcer/robotic/3fragsleft.wav");
1720 precache_sound ("announcer/robotic/terminated.wav");
1723 precache_sound ("announcer/robotic/lastsecond.wav");
1724 precache_sound ("announcer/robotic/narrowly.wav");
1727 precache_model ("models/sprites/0.spr32");
1728 precache_model ("models/sprites/1.spr32");
1729 precache_model ("models/sprites/2.spr32");
1730 precache_model ("models/sprites/3.spr32");
1731 precache_model ("models/sprites/4.spr32");
1732 precache_model ("models/sprites/5.spr32");
1733 precache_model ("models/sprites/6.spr32");
1734 precache_model ("models/sprites/7.spr32");
1735 precache_model ("models/sprites/8.spr32");
1736 precache_model ("models/sprites/9.spr32");
1737 precache_model ("models/sprites/10.spr32");
1738 precache_sound ("announcer/robotic/1.wav");
1739 precache_sound ("announcer/robotic/2.wav");
1740 precache_sound ("announcer/robotic/3.wav");
1741 precache_sound ("announcer/robotic/4.wav");
1742 precache_sound ("announcer/robotic/5.wav");
1743 precache_sound ("announcer/robotic/6.wav");
1744 precache_sound ("announcer/robotic/7.wav");
1745 precache_sound ("announcer/robotic/8.wav");
1746 precache_sound ("announcer/robotic/9.wav");
1747 precache_sound ("announcer/robotic/10.wav");
1749 // common weapon precaches
1750 precache_sound ("weapons/weapon_switch.wav");
1751 precache_sound ("weapons/weaponpickup.wav");
1752 precache_sound ("weapons/unavailable.wav");
1753 if (g_grappling_hook)
1755 precache_sound ("weapons/hook_fire.wav"); // hook
1756 precache_sound ("weapons/hook_impact.wav"); // hook
1759 if (cvar("sv_precacheweapons") || g_nixnex)
1761 //precache weapon models/sounds
1764 while (wep <= WEP_LAST)
1766 weapon_action(wep, WR_PRECACHE);
1771 precache_model("models/elaser.mdl");
1772 precache_model("models/laser.mdl");
1773 precache_model("models/ebomb.mdl");
1776 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1778 if (!self.noise && self.music) // quake 3 uses the music field
1779 self.noise = self.music;
1781 // plays music for the level if there is any
1784 precache_sound (self.noise);
1785 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1790 // sorry, but using \ in macros breaks line numbers
1791 #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
1792 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1793 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1795 vector ExactTriggerHit_mins;
1796 vector ExactTriggerHit_maxs;
1797 float ExactTriggerHit_Recurse()
1803 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1806 if (trace_ent == self)
1811 se.solid = SOLID_NOT;
1812 f = ExactTriggerHit_Recurse();
1818 float ExactTriggerHit()
1822 if not(self.modelindex)
1826 self.solid = SOLID_BSP;
1827 ExactTriggerHit_mins = other.absmin;
1828 ExactTriggerHit_maxs = other.absmax;
1829 f = ExactTriggerHit_Recurse();
1835 // WARNING: this kills the trace globals
1836 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1837 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1839 #define INITPRIO_FIRST 0
1840 #define INITPRIO_GAMETYPE 0
1841 #define INITPRIO_GAMETYPE_FALLBACK 1
1842 #define INITPRIO_CVARS 5
1843 #define INITPRIO_FINDTARGET 10
1844 #define INITPRIO_DROPTOFLOOR 20
1845 #define INITPRIO_SETLOCATION 90
1846 #define INITPRIO_LINKDOORS 91
1847 #define INITPRIO_LAST 99
1849 .void(void) initialize_entity;
1850 .float initialize_entity_order;
1851 .entity initialize_entity_next;
1852 entity initialize_entity_first;
1854 void make_safe_for_remove(entity e)
1856 if (e.initialize_entity)
1859 for (ent = initialize_entity_first; ent; )
1861 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1863 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1864 // skip it in linked list
1867 prev.initialize_entity_next = ent.initialize_entity_next;
1868 ent = prev.initialize_entity_next;
1872 initialize_entity_first = ent.initialize_entity_next;
1873 ent = initialize_entity_first;
1879 ent = ent.initialize_entity_next;
1885 void objerror(string s)
1887 make_safe_for_remove(self);
1888 objerror_builtin(s);
1891 void remove_unsafely(entity e)
1896 void remove_safely(entity e)
1898 make_safe_for_remove(e);
1902 void InitializeEntity(entity e, void(void) func, float order)
1906 if (!e || e.initialize_entity)
1908 // make a proxy initializer entity
1912 e.classname = "initialize_entity";
1916 e.initialize_entity = func;
1917 e.initialize_entity_order = order;
1919 cur = initialize_entity_first;
1922 if (!cur || cur.initialize_entity_order > order)
1924 // insert between prev and cur
1926 prev.initialize_entity_next = e;
1928 initialize_entity_first = e;
1929 e.initialize_entity_next = cur;
1933 cur = cur.initialize_entity_next;
1936 void InitializeEntitiesRun()
1939 startoflist = initialize_entity_first;
1940 initialize_entity_first = world;
1941 for (self = startoflist; self; )
1944 var void(void) func;
1945 e = self.initialize_entity_next;
1946 func = self.initialize_entity;
1947 self.initialize_entity_order = 0;
1948 self.initialize_entity = func_null;
1949 self.initialize_entity_next = world;
1950 if (self.classname == "initialize_entity")
1954 remove_builtin(self);
1957 //dprint("Delayed initialization: ", self.classname, "\n");
1963 .float uncustomizeentityforclient_set;
1964 .void(void) uncustomizeentityforclient;
1965 void(void) SUB_Nullpointer = #0;
1966 void UncustomizeEntitiesRun()
1970 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1971 self.uncustomizeentityforclient();
1974 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1976 e.customizeentityforclient = customizer;
1977 e.uncustomizeentityforclient = uncustomizer;
1978 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1982 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1985 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1989 if (e.classname == "")
1990 e.classname = "net_linked";
1992 if (e.model == "" || self.modelindex == 0)
1996 setmodel(e, "null");
2000 e.SendEntity = sendfunc;
2001 e.SendFlags = 0xFFFFFF;
2004 e.effects |= EF_NODEPTHTEST;
2008 e.nextthink = time + dt;
2009 e.think = SUB_Remove;
2013 void adaptor_think2touch()
2022 void adaptor_think2use()
2034 // deferred dropping
2035 void DropToFloor_Handler()
2037 droptofloor_builtin();
2038 self.dropped_origin = self.origin;
2043 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
2048 float trace_hits_box_a0, trace_hits_box_a1;
2050 float trace_hits_box_1d(float end, float thmi, float thma)
2054 // just check if x is in range
2062 // do the trace with respect to x
2063 // 0 -> end has to stay in thmi -> thma
2064 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
2065 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
2066 if (trace_hits_box_a0 > trace_hits_box_a1)
2072 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
2077 // now it is a trace from 0 to end
2079 trace_hits_box_a0 = 0;
2080 trace_hits_box_a1 = 1;
2082 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
2084 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
2086 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
2092 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
2094 return trace_hits_box(start, end, thmi - ma, thma - mi);
2097 float SUB_NoImpactCheck()
2099 // zero hitcontents = this is not the real impact, but either the
2100 // mirror-impact of something hitting the projectile instead of the
2101 // projectile hitting the something, or a touchareagrid one. Neither of
2102 // these stop the projectile from moving, so...
2103 if(trace_dphitcontents == 0)
2105 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
2108 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2110 if (other == world && self.size != '0 0 0')
2113 tic = self.velocity * sys_frametime;
2114 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2115 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2116 if (trace_fraction >= 1)
2118 dprint("Odd... did not hit...?\n");
2120 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2122 dprint("Detected and prevented the sky-grapple bug.\n");
2130 #define SUB_OwnerCheck() (other && (other == self.owner))
2132 #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)
2134 float MAX_IPBAN_URIS = 16;
2136 float URI_GET_DISCARD = 0;
2137 float URI_GET_IPBAN = 1;
2138 float URI_GET_IPBAN_END = 16;
2140 void URI_Get_Callback(float id, float status, string data)
2142 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2144 dprint("\nEnd of data.\n");
2146 if (id == URI_GET_DISCARD)
2150 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2153 OnlineBanList_URI_Get_Callback(id, status, data);
2157 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2161 void print_to(entity e, string s)
2164 sprint(e, strcat(s, "\n"));
2183 for (i = 0; i < MapInfo_count; ++i)
2185 if (MapInfo_Get_ByID(i))
2187 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2190 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2191 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2199 for (i = 0; i < MapInfo_count; ++i)
2201 if (MapInfo_Get_ByID(i))
2203 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2206 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2207 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2215 for (i = 0; i < MapInfo_count; ++i)
2217 if (MapInfo_Get_ByID(i))
2219 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2222 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2223 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2229 MapInfo_ClearTemps();
2232 return "No records are available on this server.\n";
2234 return strcat("Records on this server:\n", s);
2237 string getrankings()
2250 for (i = 1; i <= RANKINGS_CNT; ++i)
2252 t = race_GetTime(i);
2255 n = race_GetName(i);
2256 p = race_PlaceName(i);
2257 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2260 MapInfo_ClearTemps();
2263 return strcat("No records are available for the map: ", map, "\n");
2265 return strcat("Records for ", map, ":\n", s);
2268 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2271 vector start, org, delta, end, enddown, mstart;
2273 m = e.dphitcontentsmask;
2274 e.dphitcontentsmask = goodcontents | badcontents;
2277 delta = world.maxs - world.mins;
2279 for (i = 0; i < attempts; ++i)
2281 start_x = org_x + random() * delta_x;
2282 start_y = org_y + random() * delta_y;
2283 start_z = org_z + random() * delta_z;
2285 // rule 1: start inside world bounds, and outside
2286 // solid, and don't start from somewhere where you can
2287 // fall down to evil
2288 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2289 if (trace_fraction >= 1)
2291 if (trace_startsolid)
2293 if (trace_dphitcontents & badcontents)
2295 if (trace_dphitq3surfaceflags & badsurfaceflags)
2298 // rule 2: if we are too high, lower the point
2299 if (trace_fraction * delta_z > maxaboveground)
2300 start = trace_endpos + '0 0 1' * maxaboveground;
2301 enddown = trace_endpos;
2303 // rule 3: make sure we aren't outside the map. This only works
2304 // for somewhat well formed maps. A good rule of thumb is that
2305 // the map should have a convex outside hull.
2306 // these can be traceLINES as we already verified the starting box
2307 mstart = start + 0.5 * (e.mins + e.maxs);
2308 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2309 if (trace_fraction >= 1)
2311 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2312 if (trace_fraction >= 1)
2314 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2315 if (trace_fraction >= 1)
2317 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2318 if (trace_fraction >= 1)
2320 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2321 if (trace_fraction >= 1)
2324 // find a random vector to "look at"
2325 end_x = org_x + random() * delta_x;
2326 end_y = org_y + random() * delta_y;
2327 end_z = org_z + random() * delta_z;
2328 end = start + normalize(end - start) * vlen(delta);
2330 // rule 4: start TO end must not be too short
2331 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2332 if (trace_startsolid)
2334 if (trace_fraction < minviewdistance / vlen(delta))
2337 // rule 5: don't want to look at sky
2338 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2341 // rule 6: we must not end up in trigger_hurt
2342 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2344 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2351 e.dphitcontentsmask = m;
2355 setorigin(e, start);
2356 e.angles = vectoangles(end - start);
2357 dprint("Needed ", ftos(i + 1), " attempts\n");
2364 float zcurveparticles_effectno;
2365 vector zcurveparticles_start;
2366 float zcurveparticles_spd;
2368 void endzcurveparticles()
2370 if(zcurveparticles_effectno)
2373 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2375 zcurveparticles_effectno = 0;
2378 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2380 spd = bound(0, floor(spd / 16), 32767);
2381 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2383 endzcurveparticles();
2384 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2385 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2386 WriteShort(MSG_BROADCAST, effectno);
2387 WriteCoord(MSG_BROADCAST, start_x);
2388 WriteCoord(MSG_BROADCAST, start_y);
2389 WriteCoord(MSG_BROADCAST, start_z);
2390 zcurveparticles_effectno = effectno;
2391 zcurveparticles_start = start;
2394 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2395 WriteCoord(MSG_BROADCAST, end_x);
2396 WriteCoord(MSG_BROADCAST, end_y);
2397 WriteCoord(MSG_BROADCAST, end_z);
2398 WriteCoord(MSG_BROADCAST, end_dz);
2399 zcurveparticles_spd = spd;
2402 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2405 vector vecxy, velxy;
2407 vecxy = end - start;
2412 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2414 endzcurveparticles();
2415 trailparticles(world, effectno, start, end);
2419 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2420 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2423 string GetGametype(); // g_world.qc
2424 void write_recordmarker(entity pl, float tstart, float dt)
2426 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2428 // also write a marker into demo files for demotc-race-record-extractor to find
2431 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2432 " ", ftos(tstart), " ", ftos(dt), "\n"));
2435 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2437 switch(self.owner.cvar_cl_gunalign)
2448 if(allowcenter) // 2: allow center handedness
2461 if(allowcenter) // 2: allow center handedness
2477 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2482 if (cvar("g_shootfromeye"))
2486 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2494 else if (cvar("g_shootfromcenter"))
2498 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2506 else if (cvar("g_shootfromclient"))
2508 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2510 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2525 void attach_sameorigin(entity e, entity to, string tag)
2527 vector org, t_forward, t_left, t_up, e_forward, e_up;
2534 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2535 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2536 t_forward = v_forward * tagscale;
2537 t_left = v_right * -tagscale;
2538 t_up = v_up * tagscale;
2540 e.origin_x = org * t_forward;
2541 e.origin_y = org * t_left;
2542 e.origin_z = org * t_up;
2544 // current forward and up directions
2545 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2546 e.angles_x = -e.angles_x;
2547 fixedmakevectors(e.angles);
2549 // untransform forward, up!
2550 e_forward_x = v_forward * t_forward;
2551 e_forward_y = v_forward * t_left;
2552 e_forward_z = v_forward * t_up;
2553 e_up_x = v_up * t_forward;
2554 e_up_y = v_up * t_left;
2555 e_up_z = v_up * t_up;
2557 e.angles = fixedvectoangles2(e_forward, e_up);
2558 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2559 e.angles_x = -e.angles_x;
2561 setattachment(e, to, tag);
2562 setorigin(e, e.origin);
2565 void detach_sameorigin(entity e)
2568 org = gettaginfo(e, 0);
2569 e.angles = fixedvectoangles2(v_forward, v_up);
2570 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2571 e.angles_x = -e.angles_x;
2573 setattachment(e, world, "");
2574 setorigin(e, e.origin);
2577 void follow_sameorigin(entity e, entity to)
2579 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2580 e.aiment = to; // make the hole follow bmodel
2581 e.punchangle = to.angles; // the original angles of bmodel
2582 e.view_ofs = e.origin - to.origin; // relative origin
2583 e.v_angle = e.angles - to.angles; // relative angles
2586 void unfollow_sameorigin(entity e)
2588 e.movetype = MOVETYPE_NONE;
2591 entity gettaginfo_relative_ent;
2592 vector gettaginfo_relative(entity e, float tag)
2594 if (!gettaginfo_relative_ent)
2596 gettaginfo_relative_ent = spawn();
2597 gettaginfo_relative_ent.effects = EF_NODRAW;
2599 gettaginfo_relative_ent.model = e.model;
2600 gettaginfo_relative_ent.modelindex = e.modelindex;
2601 gettaginfo_relative_ent.frame = e.frame;
2602 return gettaginfo(gettaginfo_relative_ent, tag);
2605 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2609 if (pl.soundentity.cnt & p)
2611 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2612 pl.soundentity.cnt |= p;
2615 void SoundEntity_StopSound(entity pl, float chan)
2619 if (pl.soundentity.cnt & p)
2621 stopsoundto(MSG_ALL, pl.soundentity, chan);
2622 pl.soundentity.cnt &~= p;
2626 void SoundEntity_Attach(entity pl)
2628 pl.soundentity = spawn();
2629 pl.soundentity.classname = "soundentity";
2630 pl.soundentity.owner = pl;
2631 setattachment(pl.soundentity, pl, "");
2632 setmodel(pl.soundentity, "null");
2635 void SoundEntity_Detach(entity pl)
2638 for (i = 0; i <= 7; ++i)
2639 SoundEntity_StopSound(pl, i);
2643 float ParseCommandPlayerSlotTarget_firsttoken;
2644 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2652 ParseCommandPlayerSlotTarget_firsttoken = -1;
2656 if (substring(argv(idx), 0, 1) == "#")
2658 s = substring(argv(idx), 1, -1);
2666 ParseCommandPlayerSlotTarget_firsttoken = idx;
2667 if (s == ftos(stof(s)))
2669 e = edict_num(stof(s));
2670 if (e.flags & FL_CLIENT)
2676 // it must be a nick name
2679 ParseCommandPlayerSlotTarget_firsttoken = idx;
2682 FOR_EACH_CLIENT(head)
2683 if (head.netname == s)
2691 s = strdecolorize(s);
2693 FOR_EACH_CLIENT(head)
2694 if (strdecolorize(head.netname) == s)
2709 float modeleffect_SendEntity(entity to, float sf)
2712 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2715 if(self.velocity != '0 0 0')
2717 if(self.angles != '0 0 0')
2719 if(self.avelocity != '0 0 0')
2722 WriteByte(MSG_ENTITY, f);
2723 WriteShort(MSG_ENTITY, self.modelindex);
2724 WriteByte(MSG_ENTITY, self.skin);
2725 WriteByte(MSG_ENTITY, self.frame);
2726 WriteCoord(MSG_ENTITY, self.origin_x);
2727 WriteCoord(MSG_ENTITY, self.origin_y);
2728 WriteCoord(MSG_ENTITY, self.origin_z);
2731 WriteCoord(MSG_ENTITY, self.velocity_x);
2732 WriteCoord(MSG_ENTITY, self.velocity_y);
2733 WriteCoord(MSG_ENTITY, self.velocity_z);
2737 WriteCoord(MSG_ENTITY, self.angles_x);
2738 WriteCoord(MSG_ENTITY, self.angles_y);
2739 WriteCoord(MSG_ENTITY, self.angles_z);
2743 WriteCoord(MSG_ENTITY, self.avelocity_x);
2744 WriteCoord(MSG_ENTITY, self.avelocity_y);
2745 WriteCoord(MSG_ENTITY, self.avelocity_z);
2747 WriteShort(MSG_ENTITY, self.scale * 256.0);
2748 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2749 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2750 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2751 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2756 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)
2761 e.classname = "modeleffect";
2769 e.teleport_time = t1;
2773 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2777 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2780 sz = max(e.scale, e.scale2);
2781 setsize(e, e.mins * sz, e.maxs * sz);
2782 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2785 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2787 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2790 float randombit(float bits)
2792 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2801 for(f = 1; f <= bits; f *= 2)
2810 r = (r - 1) / (n - 1);
2817 float randombits(float bits, float k, float error_return)
2821 while(k > 0 && bits != r)
2823 r += randombit(bits - r);
2832 void randombit_test(float bits, float iter)
2836 print(ftos(randombit(bits)), "\n");
2841 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2843 if(halflifedist > 0)
2844 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2845 else if(halflifedist < 0)
2846 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2855 #define cvar_string_normal cvar_string_builtin
2856 #define cvar_normal cvar_builtin
2858 string cvar_string_normal(string n)
2860 if not(cvar_type(n) & 1)
2861 backtrace(strcat("Attempt to access undefined cvar: ", n));
2862 return cvar_string_builtin(n);
2865 float cvar_normal(string n)
2867 return stof(cvar_string_normal(n));
2870 #define cvar_set_normal cvar_set_builtin
2878 oself.think = SUB_Remove;
2879 oself.nextthink = time;
2885 Execute func() after time + fdelay.
2886 self when func is executed = self when defer is called
2888 void defer(float fdelay, void() func)
2895 e.think = defer_think;
2896 e.nextthink = time + fdelay;