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 //#NO AUTOCVARS START
846 float g_pickup_shells;
847 float g_pickup_shells_max;
848 float g_pickup_nails;
849 float g_pickup_nails_max;
850 float g_pickup_rockets;
851 float g_pickup_rockets_max;
852 float g_pickup_cells;
853 float g_pickup_cells_max;
855 float g_pickup_fuel_jetpack;
856 float g_pickup_fuel_max;
857 float g_pickup_armorsmall;
858 float g_pickup_armorsmall_max;
859 float g_pickup_armormedium;
860 float g_pickup_armormedium_max;
861 float g_pickup_armorbig;
862 float g_pickup_armorbig_max;
863 float g_pickup_armorlarge;
864 float g_pickup_armorlarge_max;
865 float g_pickup_healthsmall;
866 float g_pickup_healthsmall_max;
867 float g_pickup_healthmedium;
868 float g_pickup_healthmedium_max;
869 float g_pickup_healthlarge;
870 float g_pickup_healthlarge_max;
871 float g_pickup_healthmega;
872 float g_pickup_healthmega_max;
874 float g_weaponarena_random;
875 string g_weaponarena_list;
876 float g_weaponspeedfactor;
877 float g_weaponratefactor;
878 float g_weapondamagefactor;
879 float g_weaponforcefactor;
880 float g_weaponspreadfactor;
884 float start_ammo_shells;
885 float start_ammo_nails;
886 float start_ammo_rockets;
887 float start_ammo_cells;
888 float start_ammo_fuel;
890 float start_armorvalue;
891 float warmup_start_weapons;
892 float warmup_start_ammo_shells;
893 float warmup_start_ammo_nails;
894 float warmup_start_ammo_rockets;
895 float warmup_start_ammo_cells;
896 float warmup_start_ammo_fuel;
897 float warmup_start_health;
898 float warmup_start_armorvalue;
902 entity get_weaponinfo(float w);
904 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
906 var float i = weaponinfo.weapon;
911 var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
913 if (t < 0) // "default" weapon selection
915 if (g_lms || g_ca || allguns)
916 t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
919 else if (g_race || g_cts)
920 t = (i == WEP_LASER);
922 t = 0; // weapon is set a few lines later
924 t = (i == WEP_LASER || i == WEP_SHOTGUN);
925 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
926 t |= (i == WEP_HOOK);
929 // we cannot disable porto in Nexball, we must force it
930 if(g_nexball && i == WEP_PORTO)
936 float NixNex_CanChooseWeapon(float wpn);
937 void readplayerstartcvars()
943 // initialize starting values for players
946 start_ammo_shells = 0;
947 start_ammo_nails = 0;
948 start_ammo_rockets = 0;
949 start_ammo_cells = 0;
950 start_health = cvar("g_balance_health_start");
951 start_armorvalue = cvar("g_balance_armor_start");
954 s = cvar_string("g_weaponarena");
960 g_weaponarena_list = "All Weapons";
961 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
963 e = get_weaponinfo(j);
964 g_weaponarena |= e.weapons;
965 weapon_action(e.weapon, WR_PRECACHE);
968 else if (s == "most")
970 g_weaponarena_list = "Most Weapons";
971 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
973 e = get_weaponinfo(j);
974 if (e.spawnflags & WEP_FLAG_NORMAL)
976 g_weaponarena |= e.weapons;
977 weapon_action(e.weapon, WR_PRECACHE);
981 else if (s == "none")
983 g_weaponarena_list = "No Weapons";
984 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
988 t = tokenize_console(s);
989 g_weaponarena_list = "";
990 for (i = 0; i < t; ++i)
993 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
995 e = get_weaponinfo(j);
998 g_weaponarena |= e.weapons;
999 weapon_action(e.weapon, WR_PRECACHE);
1000 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
1006 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
1009 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
1013 g_weaponarena_random = cvar("g_weaponarena_random");
1015 g_weaponarena_random = 0;
1020 // will be done later
1021 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1022 if (NixNex_CanChooseWeapon(i))
1023 weapon_action(i, WR_PRECACHE);
1024 if(!cvar("g_use_ammunition"))
1025 start_items |= IT_UNLIMITED_AMMO;
1027 else if (g_weaponarena)
1029 start_weapons = g_weaponarena;
1030 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
1031 start_ammo_rockets = 999;
1032 if (g_weaponarena & WEPBIT_SHOTGUN)
1033 start_ammo_shells = 999;
1034 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
1035 start_ammo_cells = 999;
1036 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
1037 start_ammo_nails = 999;
1038 if (g_weaponarena & WEPBIT_HOOK)
1039 start_ammo_fuel = 999;
1040 start_items |= IT_UNLIMITED_AMMO;
1042 else if (g_minstagib)
1045 start_armorvalue = 0;
1046 start_weapons = WEPBIT_MINSTANEX;
1047 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
1048 start_ammo_cells = cvar("g_minstagib_ammo_start");
1049 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
1050 start_ammo_fuel = cvar("g_start_ammo_fuel");
1052 if (g_minstagib_invis_alpha <= 0)
1053 g_minstagib_invis_alpha = -1;
1059 start_ammo_shells = cvar("g_lms_start_ammo_shells");
1060 start_ammo_nails = cvar("g_lms_start_ammo_nails");
1061 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1062 start_ammo_cells = cvar("g_lms_start_ammo_cells");
1063 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1064 start_health = cvar("g_lms_start_health");
1065 start_armorvalue = cvar("g_lms_start_armor");
1067 else if (cvar("g_use_ammunition"))
1069 start_ammo_shells = cvar("g_start_ammo_shells");
1070 start_ammo_nails = cvar("g_start_ammo_nails");
1071 start_ammo_rockets = cvar("g_start_ammo_rockets");
1072 start_ammo_cells = cvar("g_start_ammo_cells");
1073 start_ammo_fuel = cvar("g_start_ammo_fuel");
1077 start_ammo_shells = cvar("g_pickup_shells_max");
1078 start_ammo_nails = cvar("g_pickup_nails_max");
1079 start_ammo_rockets = cvar("g_pickup_rockets_max");
1080 start_ammo_cells = cvar("g_pickup_cells_max");
1081 start_ammo_fuel = cvar("g_pickup_fuel_max");
1082 start_items |= IT_UNLIMITED_AMMO;
1085 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1087 e = get_weaponinfo(i);
1088 if(want_weapon("g_start_weapon_", e, FALSE))
1090 start_weapons |= e.weapons;
1091 weapon_action(e.weapon, WR_PRECACHE);
1098 warmup_start_ammo_shells = start_ammo_shells;
1099 warmup_start_ammo_nails = start_ammo_nails;
1100 warmup_start_ammo_rockets = start_ammo_rockets;
1101 warmup_start_ammo_cells = start_ammo_cells;
1102 warmup_start_ammo_fuel = start_ammo_fuel;
1103 warmup_start_health = start_health;
1104 warmup_start_armorvalue = start_armorvalue;
1105 warmup_start_weapons = start_weapons;
1107 if (!g_weaponarena && !g_nixnex && !g_minstagib && !g_ca)
1109 if (cvar("g_use_ammunition"))
1111 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1112 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1113 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1114 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1115 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1117 warmup_start_health = cvar("g_warmup_start_health");
1118 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1119 warmup_start_weapons = 0;
1120 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1122 e = get_weaponinfo(i);
1123 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1125 warmup_start_weapons |= e.weapons;
1126 weapon_action(e.weapon, WR_PRECACHE);
1132 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1134 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1135 start_items |= IT_FUEL_REGEN;
1136 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1137 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1141 start_items |= IT_JETPACK;
1143 if (g_weapon_stay == 2)
1145 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1146 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1147 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1148 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1149 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1150 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1151 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1152 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1153 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1154 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1157 start_ammo_shells = max(0, start_ammo_shells);
1158 start_ammo_nails = max(0, start_ammo_nails);
1159 start_ammo_cells = max(0, start_ammo_cells);
1160 start_ammo_rockets = max(0, start_ammo_rockets);
1161 start_ammo_fuel = max(0, start_ammo_fuel);
1163 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1164 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1165 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1166 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1167 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1171 float g_bugrigs_planar_movement;
1172 float g_bugrigs_planar_movement_car_jumping;
1173 float g_bugrigs_reverse_spinning;
1174 float g_bugrigs_reverse_speeding;
1175 float g_bugrigs_reverse_stopping;
1176 float g_bugrigs_air_steering;
1177 float g_bugrigs_angle_smoothing;
1178 float g_bugrigs_friction_floor;
1179 float g_bugrigs_friction_brake;
1180 float g_bugrigs_friction_air;
1181 float g_bugrigs_accel;
1182 float g_bugrigs_speed_ref;
1183 float g_bugrigs_speed_pow;
1184 float g_bugrigs_steer;
1186 float g_touchexplode;
1187 float g_touchexplode_radius;
1188 float g_touchexplode_damage;
1189 float g_touchexplode_edgedamage;
1190 float g_touchexplode_force;
1197 float sv_pitch_fixyaw;
1199 float sv_accuracy_data_share;
1201 void readlevelcvars(void)
1203 g_bugrigs = cvar("g_bugrigs");
1204 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1205 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1206 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1207 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1208 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1209 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1210 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1211 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1212 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1213 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1214 g_bugrigs_accel = cvar("g_bugrigs_accel");
1215 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1216 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1217 g_bugrigs_steer = cvar("g_bugrigs_steer");
1219 g_touchexplode = cvar("g_touchexplode");
1220 g_touchexplode_radius = cvar("g_touchexplode_radius");
1221 g_touchexplode_damage = cvar("g_touchexplode_damage");
1222 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1223 g_touchexplode_force = cvar("g_touchexplode_force");
1225 #ifdef ALLOW_FORCEMODELS
1226 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1228 sv_loddistance1 = cvar("sv_loddistance1");
1229 sv_loddistance2 = cvar("sv_loddistance2");
1231 if(sv_loddistance2 <= sv_loddistance1)
1232 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1234 sv_clones = cvar("sv_clones");
1235 sv_gentle = cvar("sv_gentle");
1236 sv_foginterval = cvar("sv_foginterval");
1237 g_cloaked = cvar("g_cloaked");
1238 g_jump_grunt = cvar("g_jump_grunt");
1239 g_footsteps = cvar("g_footsteps");
1240 g_grappling_hook = cvar("g_grappling_hook");
1241 g_jetpack = cvar("g_jetpack");
1242 g_laserguided_missile = cvar("g_laserguided_missile");
1243 g_midair = cvar("g_midair");
1244 g_minstagib = cvar("g_minstagib");
1245 g_nixnex = cvar("g_nixnex");
1246 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1247 g_norecoil = cvar("g_norecoil");
1248 g_vampire = cvar("g_vampire");
1249 g_bloodloss = cvar("g_bloodloss");
1250 sv_maxidle = cvar("sv_maxidle");
1251 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1252 sv_pogostick = cvar("sv_pogostick");
1253 sv_doublejump = cvar("sv_doublejump");
1254 g_ctf_reverse = cvar("g_ctf_reverse");
1255 sv_autotaunt = cvar("sv_autotaunt");
1256 sv_taunt = cvar("sv_taunt");
1258 inWarmupStage = cvar("g_warmup");
1259 g_warmup_limit = cvar("g_warmup_limit");
1260 g_warmup_allguns = cvar("g_warmup_allguns");
1261 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1263 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1264 inWarmupStage = 0; // these modes cannot work together, sorry
1266 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1267 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1268 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1269 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1270 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1271 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1272 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1273 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1274 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1275 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1276 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1277 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1279 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1280 if (g_nixnex) g_weaponarena = 0;
1283 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1284 g_weaponratefactor = cvar("g_weaponratefactor");
1285 g_weapondamagefactor = cvar("g_weapondamagefactor");
1286 g_weaponforcefactor = cvar("g_weaponforcefactor");
1287 g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1289 g_pickup_shells = cvar("g_pickup_shells");
1290 g_pickup_shells_max = cvar("g_pickup_shells_max");
1291 g_pickup_nails = cvar("g_pickup_nails");
1292 g_pickup_nails_max = cvar("g_pickup_nails_max");
1293 g_pickup_rockets = cvar("g_pickup_rockets");
1294 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1295 g_pickup_cells = cvar("g_pickup_cells");
1296 g_pickup_cells_max = cvar("g_pickup_cells_max");
1297 g_pickup_fuel = cvar("g_pickup_fuel");
1298 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1299 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1300 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1301 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1302 g_pickup_armormedium = cvar("g_pickup_armormedium");
1303 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1304 g_pickup_armorbig = cvar("g_pickup_armorbig");
1305 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1306 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1307 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1308 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1309 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1310 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1311 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1312 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1313 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1314 g_pickup_healthmega = cvar("g_pickup_healthmega");
1315 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1317 g_pinata = cvar("g_pinata");
1319 g_weapon_stay = cvar("g_weapon_stay");
1321 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1324 g_ghost_items = cvar("g_ghost_items");
1326 if(g_ghost_items >= 1)
1327 g_ghost_items = 0.13; // default alpha value
1329 if not(inWarmupStage && !g_ca)
1330 game_starttime = cvar("g_start_delay");
1332 sv_pitch_min = cvar("sv_pitch_min");
1333 sv_pitch_max = cvar("sv_pitch_max");
1334 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1336 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1338 readplayerstartcvars();
1344 string precache_sound (string s) = #19;
1345 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1346 float precache_sound_index (string s) = #19;
1348 #define SND_VOLUME 1
1349 #define SND_ATTENUATION 2
1350 #define SND_LARGEENTITY 8
1351 #define SND_LARGESOUND 16
1353 float sound_allowed(float dest, entity e)
1355 // sounds from world may always pass
1358 if (e.classname == "body")
1360 if (e.owner && e.owner != e)
1365 // sounds to self may always pass
1366 if (dest == MSG_ONE)
1367 if (e == msg_entity)
1369 // sounds by players can be removed
1370 if (cvar("bot_sound_monopoly"))
1371 if (clienttype(e) == CLIENTTYPE_REAL)
1373 // anything else may pass
1377 void sound(entity e, float chan, string samp, float vol, float atten)
1379 if (!sound_allowed(MSG_BROADCAST, e))
1381 sound_builtin(e, chan, samp, vol, atten);
1383 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1387 if (!sound_allowed(dest, e))
1390 entno = num_for_edict(e);
1391 idx = precache_sound_index(samp);
1396 atten = floor(atten * 64);
1397 vol = floor(vol * 255);
1400 sflags |= SND_VOLUME;
1402 sflags |= SND_ATTENUATION;
1404 sflags |= SND_LARGEENTITY;
1406 sflags |= SND_LARGESOUND;
1408 WriteByte(dest, SVC_SOUND);
1409 WriteByte(dest, sflags);
1410 if (sflags & SND_VOLUME)
1411 WriteByte(dest, vol);
1412 if (sflags & SND_ATTENUATION)
1413 WriteByte(dest, atten);
1414 if (sflags & SND_LARGEENTITY)
1416 WriteShort(dest, entno);
1417 WriteByte(dest, chan);
1421 WriteShort(dest, entno * 8 + chan);
1423 if (sflags & SND_LARGESOUND)
1424 WriteShort(dest, idx);
1426 WriteByte(dest, idx);
1428 WriteCoord(dest, o_x);
1429 WriteCoord(dest, o_y);
1430 WriteCoord(dest, o_z);
1432 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1436 if (!sound_allowed(dest, e))
1439 o = e.origin + 0.5 * (e.mins + e.maxs);
1440 soundtoat(dest, e, o, chan, samp, vol, atten);
1442 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1444 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1446 void stopsoundto(float dest, entity e, float chan)
1450 if (!sound_allowed(dest, e))
1453 entno = num_for_edict(e);
1458 idx = precache_sound_index("misc/null.wav");
1459 sflags = SND_LARGEENTITY;
1461 sflags |= SND_LARGESOUND;
1462 WriteByte(dest, SVC_SOUND);
1463 WriteByte(dest, sflags);
1464 WriteShort(dest, entno);
1465 WriteByte(dest, chan);
1466 if (sflags & SND_LARGESOUND)
1467 WriteShort(dest, idx);
1469 WriteByte(dest, idx);
1470 WriteCoord(dest, e.origin_x);
1471 WriteCoord(dest, e.origin_y);
1472 WriteCoord(dest, e.origin_z);
1476 WriteByte(dest, SVC_STOPSOUND);
1477 WriteShort(dest, entno * 8 + chan);
1480 void stopsound(entity e, float chan)
1482 if (!sound_allowed(MSG_BROADCAST, e))
1485 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1486 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1489 void play2(entity e, string filename)
1491 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1493 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1496 .float announcetime;
1497 float announce(entity player, string msg)
1499 if (time > player.announcetime)
1500 if (clienttype(player) == CLIENTTYPE_REAL)
1502 player.announcetime = time + 0.8;
1508 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1509 float spamsound(entity e, float chan, string samp, float vol, float atten)
1511 if (!sound_allowed(MSG_BROADCAST, e))
1514 if (time > e.announcetime)
1516 e.announcetime = time;
1517 sound(e, chan, samp, vol, atten);
1523 void play2team(float t, string filename)
1527 if (cvar("bot_sound_monopoly"))
1530 FOR_EACH_REALPLAYER(head)
1533 play2(head, filename);
1537 void play2all(string samp)
1539 if (cvar("bot_sound_monopoly"))
1542 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1545 void PrecachePlayerSounds(string f);
1546 void precache_all_models(string pattern)
1548 float globhandle, i, n;
1551 globhandle = search_begin(pattern, TRUE, FALSE);
1554 n = search_getsize(globhandle);
1555 for (i = 0; i < n; ++i)
1557 //print(search_getfilename(globhandle, i), "\n");
1558 f = search_getfilename(globhandle, i);
1561 if(substring(f, -9,5) == "_lod1")
1563 if(substring(f, -9,5) == "_lod2")
1565 if(!sv_loddistance1)
1567 PrecachePlayerSounds(strcat(f, ".sounds"));
1569 search_end(globhandle);
1574 // gamemode related things
1575 precache_model ("models/misc/chatbubble.spr");
1576 precache_model ("models/misc/teambubble.spr");
1579 precache_model ("models/runematch/curse.mdl");
1580 precache_model ("models/runematch/rune.mdl");
1583 #ifdef TTURRETS_ENABLED
1584 if (cvar("g_turrets"))
1588 // Precache all player models if desired
1589 if (cvar("sv_precacheplayermodels"))
1591 PrecachePlayerSounds("sound/player/default.sounds");
1592 precache_all_models("models/player/*.zym");
1593 precache_all_models("models/player/*.dpm");
1594 precache_all_models("models/player/*.md3");
1595 precache_all_models("models/player/*.psk");
1596 //precache_model("models/player/carni.zym");
1597 //precache_model("models/player/crash.zym");
1598 //precache_model("models/player/grunt.zym");
1599 //precache_model("models/player/headhunter.zym");
1600 //precache_model("models/player/insurrectionist.zym");
1601 //precache_model("models/player/jeandarc.zym");
1602 //precache_model("models/player/lurk.zym");
1603 //precache_model("models/player/lycanthrope.zym");
1604 //precache_model("models/player/marine.zym");
1605 //precache_model("models/player/nexus.zym");
1606 //precache_model("models/player/pyria.zym");
1607 //precache_model("models/player/shock.zym");
1608 //precache_model("models/player/skadi.zym");
1609 //precache_model("models/player/specop.zym");
1610 //precache_model("models/player/visitant.zym");
1613 if (cvar("sv_defaultcharacter"))
1616 s = cvar_string("sv_defaultplayermodel_red");
1620 PrecachePlayerSounds(strcat(s, ".sounds"));
1622 s = cvar_string("sv_defaultplayermodel_blue");
1626 PrecachePlayerSounds(strcat(s, ".sounds"));
1628 s = cvar_string("sv_defaultplayermodel_yellow");
1632 PrecachePlayerSounds(strcat(s, ".sounds"));
1634 s = cvar_string("sv_defaultplayermodel_pink");
1638 PrecachePlayerSounds(strcat(s, ".sounds"));
1640 s = cvar_string("sv_defaultplayermodel");
1644 PrecachePlayerSounds(strcat(s, ".sounds"));
1650 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1651 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1654 // gore and miscellaneous sounds
1655 //precache_sound ("misc/h2ohit.wav");
1656 precache_model ("models/hook.md3");
1657 precache_sound ("misc/armorimpact.wav");
1658 precache_sound ("misc/bodyimpact1.wav");
1659 precache_sound ("misc/bodyimpact2.wav");
1660 precache_sound ("misc/gib.wav");
1661 precache_sound ("misc/gib_splat01.wav");
1662 precache_sound ("misc/gib_splat02.wav");
1663 precache_sound ("misc/gib_splat03.wav");
1664 precache_sound ("misc/gib_splat04.wav");
1665 precache_sound ("misc/hit.wav");
1666 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1667 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1668 precache_sound ("misc/null.wav");
1669 precache_sound ("misc/spawn.wav");
1670 precache_sound ("misc/talk.wav");
1671 precache_sound ("misc/teleport.wav");
1672 precache_sound ("misc/poweroff.wav");
1673 precache_sound ("player/lava.wav");
1674 precache_sound ("player/slime.wav");
1677 precache_sound ("misc/jetpack_fly.wav");
1679 // announcer sounds - male
1680 precache_sound ("announcer/male/electrobitch.wav");
1681 precache_sound ("announcer/male/airshot.wav");
1682 precache_sound ("announcer/male/03kills.wav");
1683 precache_sound ("announcer/male/05kills.wav");
1684 precache_sound ("announcer/male/10kills.wav");
1685 precache_sound ("announcer/male/15kills.wav");
1686 precache_sound ("announcer/male/20kills.wav");
1687 precache_sound ("announcer/male/25kills.wav");
1688 precache_sound ("announcer/male/30kills.wav");
1689 precache_sound ("announcer/male/botlike.wav");
1690 precache_sound ("announcer/male/yoda.wav");
1691 precache_sound ("announcer/male/amazing.wav");
1692 precache_sound ("announcer/male/awesome.wav");
1693 precache_sound ("announcer/male/headshot.wav");
1694 precache_sound ("announcer/male/impressive.wav");
1696 // announcer sounds - robotic
1697 precache_sound ("announcer/robotic/prepareforbattle.wav");
1698 precache_sound ("announcer/robotic/begin.wav");
1699 precache_sound ("announcer/robotic/timeoutcalled.wav");
1700 precache_sound ("announcer/robotic/1fragleft.wav");
1701 precache_sound ("announcer/robotic/2fragsleft.wav");
1702 precache_sound ("announcer/robotic/3fragsleft.wav");
1703 precache_sound ("announcer/robotic/terminated.wav");
1706 precache_sound ("announcer/robotic/lastsecond.wav");
1707 precache_sound ("announcer/robotic/narrowly.wav");
1710 precache_model ("models/sprites/0.spr32");
1711 precache_model ("models/sprites/1.spr32");
1712 precache_model ("models/sprites/2.spr32");
1713 precache_model ("models/sprites/3.spr32");
1714 precache_model ("models/sprites/4.spr32");
1715 precache_model ("models/sprites/5.spr32");
1716 precache_model ("models/sprites/6.spr32");
1717 precache_model ("models/sprites/7.spr32");
1718 precache_model ("models/sprites/8.spr32");
1719 precache_model ("models/sprites/9.spr32");
1720 precache_model ("models/sprites/10.spr32");
1721 precache_sound ("announcer/robotic/1.wav");
1722 precache_sound ("announcer/robotic/2.wav");
1723 precache_sound ("announcer/robotic/3.wav");
1724 precache_sound ("announcer/robotic/4.wav");
1725 precache_sound ("announcer/robotic/5.wav");
1726 precache_sound ("announcer/robotic/6.wav");
1727 precache_sound ("announcer/robotic/7.wav");
1728 precache_sound ("announcer/robotic/8.wav");
1729 precache_sound ("announcer/robotic/9.wav");
1730 precache_sound ("announcer/robotic/10.wav");
1732 // common weapon precaches
1733 precache_sound ("weapons/weapon_switch.wav");
1734 precache_sound ("weapons/weaponpickup.wav");
1735 precache_sound ("weapons/unavailable.wav");
1736 if (g_grappling_hook)
1738 precache_sound ("weapons/hook_fire.wav"); // hook
1739 precache_sound ("weapons/hook_impact.wav"); // hook
1742 if (cvar("sv_precacheweapons") || g_nixnex)
1744 //precache weapon models/sounds
1747 while (wep <= WEP_LAST)
1749 weapon_action(wep, WR_PRECACHE);
1754 precache_model("models/elaser.mdl");
1755 precache_model("models/laser.mdl");
1756 precache_model("models/ebomb.mdl");
1759 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1761 if (!self.noise && self.music) // quake 3 uses the music field
1762 self.noise = self.music;
1764 // plays music for the level if there is any
1767 precache_sound (self.noise);
1768 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1773 // sorry, but using \ in macros breaks line numbers
1774 #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
1775 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1776 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1778 vector ExactTriggerHit_mins;
1779 vector ExactTriggerHit_maxs;
1780 float ExactTriggerHit_Recurse()
1786 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1789 if (trace_ent == self)
1794 se.solid = SOLID_NOT;
1795 f = ExactTriggerHit_Recurse();
1801 float ExactTriggerHit()
1805 if not(self.modelindex)
1809 self.solid = SOLID_BSP;
1810 ExactTriggerHit_mins = other.absmin;
1811 ExactTriggerHit_maxs = other.absmax;
1812 f = ExactTriggerHit_Recurse();
1818 // WARNING: this kills the trace globals
1819 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1820 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1822 #define INITPRIO_FIRST 0
1823 #define INITPRIO_GAMETYPE 0
1824 #define INITPRIO_GAMETYPE_FALLBACK 1
1825 #define INITPRIO_CVARS 5
1826 #define INITPRIO_FINDTARGET 10
1827 #define INITPRIO_DROPTOFLOOR 20
1828 #define INITPRIO_SETLOCATION 90
1829 #define INITPRIO_LINKDOORS 91
1830 #define INITPRIO_LAST 99
1832 .void(void) initialize_entity;
1833 .float initialize_entity_order;
1834 .entity initialize_entity_next;
1835 entity initialize_entity_first;
1837 void make_safe_for_remove(entity e)
1839 if (e.initialize_entity)
1842 for (ent = initialize_entity_first; ent; )
1844 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1846 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1847 // skip it in linked list
1850 prev.initialize_entity_next = ent.initialize_entity_next;
1851 ent = prev.initialize_entity_next;
1855 initialize_entity_first = ent.initialize_entity_next;
1856 ent = initialize_entity_first;
1862 ent = ent.initialize_entity_next;
1868 void objerror(string s)
1870 make_safe_for_remove(self);
1871 objerror_builtin(s);
1874 void remove_unsafely(entity e)
1879 void remove_safely(entity e)
1881 make_safe_for_remove(e);
1885 void InitializeEntity(entity e, void(void) func, float order)
1889 if (!e || e.initialize_entity)
1891 // make a proxy initializer entity
1895 e.classname = "initialize_entity";
1899 e.initialize_entity = func;
1900 e.initialize_entity_order = order;
1902 cur = initialize_entity_first;
1905 if (!cur || cur.initialize_entity_order > order)
1907 // insert between prev and cur
1909 prev.initialize_entity_next = e;
1911 initialize_entity_first = e;
1912 e.initialize_entity_next = cur;
1916 cur = cur.initialize_entity_next;
1919 void InitializeEntitiesRun()
1922 startoflist = initialize_entity_first;
1923 initialize_entity_first = world;
1924 for (self = startoflist; self; )
1927 var void(void) func;
1928 e = self.initialize_entity_next;
1929 func = self.initialize_entity;
1930 self.initialize_entity_order = 0;
1931 self.initialize_entity = func_null;
1932 self.initialize_entity_next = world;
1933 if (self.classname == "initialize_entity")
1937 remove_builtin(self);
1940 //dprint("Delayed initialization: ", self.classname, "\n");
1946 .float uncustomizeentityforclient_set;
1947 .void(void) uncustomizeentityforclient;
1948 void(void) SUB_Nullpointer = #0;
1949 void UncustomizeEntitiesRun()
1953 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1954 self.uncustomizeentityforclient();
1957 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1959 e.customizeentityforclient = customizer;
1960 e.uncustomizeentityforclient = uncustomizer;
1961 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1965 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1968 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1972 if (e.classname == "")
1973 e.classname = "net_linked";
1975 if (e.model == "" || self.modelindex == 0)
1979 setmodel(e, "null");
1983 e.SendEntity = sendfunc;
1984 e.SendFlags = 0xFFFFFF;
1987 e.effects |= EF_NODEPTHTEST;
1991 e.nextthink = time + dt;
1992 e.think = SUB_Remove;
1996 void adaptor_think2touch()
2005 void adaptor_think2use()
2017 // deferred dropping
2018 void DropToFloor_Handler()
2020 droptofloor_builtin();
2021 self.dropped_origin = self.origin;
2026 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
2031 float trace_hits_box_a0, trace_hits_box_a1;
2033 float trace_hits_box_1d(float end, float thmi, float thma)
2037 // just check if x is in range
2045 // do the trace with respect to x
2046 // 0 -> end has to stay in thmi -> thma
2047 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
2048 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
2049 if (trace_hits_box_a0 > trace_hits_box_a1)
2055 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
2060 // now it is a trace from 0 to end
2062 trace_hits_box_a0 = 0;
2063 trace_hits_box_a1 = 1;
2065 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
2067 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
2069 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
2075 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
2077 return trace_hits_box(start, end, thmi - ma, thma - mi);
2080 float SUB_NoImpactCheck()
2082 // zero hitcontents = this is not the real impact, but either the
2083 // mirror-impact of something hitting the projectile instead of the
2084 // projectile hitting the something, or a touchareagrid one. Neither of
2085 // these stop the projectile from moving, so...
2086 if(trace_dphitcontents == 0)
2088 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
2091 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2093 if (other == world && self.size != '0 0 0')
2096 tic = self.velocity * sys_frametime;
2097 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2098 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2099 if (trace_fraction >= 1)
2101 dprint("Odd... did not hit...?\n");
2103 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2105 dprint("Detected and prevented the sky-grapple bug.\n");
2113 #define SUB_OwnerCheck() (other && (other == self.owner))
2115 #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)
2117 float MAX_IPBAN_URIS = 16;
2119 float URI_GET_DISCARD = 0;
2120 float URI_GET_IPBAN = 1;
2121 float URI_GET_IPBAN_END = 16;
2123 void URI_Get_Callback(float id, float status, string data)
2125 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2127 dprint("\nEnd of data.\n");
2129 if (id == URI_GET_DISCARD)
2133 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2136 OnlineBanList_URI_Get_Callback(id, status, data);
2140 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2144 void print_to(entity e, string s)
2147 sprint(e, strcat(s, "\n"));
2166 for (i = 0; i < MapInfo_count; ++i)
2168 if (MapInfo_Get_ByID(i))
2170 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2173 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2174 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2182 for (i = 0; i < MapInfo_count; ++i)
2184 if (MapInfo_Get_ByID(i))
2186 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2189 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2190 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2198 for (i = 0; i < MapInfo_count; ++i)
2200 if (MapInfo_Get_ByID(i))
2202 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2205 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2206 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2212 MapInfo_ClearTemps();
2215 return "No records are available on this server.\n";
2217 return strcat("Records on this server:\n", s);
2220 string getrankings()
2233 for (i = 1; i <= RANKINGS_CNT; ++i)
2235 t = race_GetTime(i);
2238 n = race_GetName(i);
2239 p = race_PlaceName(i);
2240 s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2243 MapInfo_ClearTemps();
2246 return strcat("No records are available for the map: ", map, "\n");
2248 return strcat("Records for ", map, ":\n", s);
2251 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2254 vector start, org, delta, end, enddown, mstart;
2256 m = e.dphitcontentsmask;
2257 e.dphitcontentsmask = goodcontents | badcontents;
2260 delta = world.maxs - world.mins;
2262 for (i = 0; i < attempts; ++i)
2264 start_x = org_x + random() * delta_x;
2265 start_y = org_y + random() * delta_y;
2266 start_z = org_z + random() * delta_z;
2268 // rule 1: start inside world bounds, and outside
2269 // solid, and don't start from somewhere where you can
2270 // fall down to evil
2271 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2272 if (trace_fraction >= 1)
2274 if (trace_startsolid)
2276 if (trace_dphitcontents & badcontents)
2278 if (trace_dphitq3surfaceflags & badsurfaceflags)
2281 // rule 2: if we are too high, lower the point
2282 if (trace_fraction * delta_z > maxaboveground)
2283 start = trace_endpos + '0 0 1' * maxaboveground;
2284 enddown = trace_endpos;
2286 // rule 3: make sure we aren't outside the map. This only works
2287 // for somewhat well formed maps. A good rule of thumb is that
2288 // the map should have a convex outside hull.
2289 // these can be traceLINES as we already verified the starting box
2290 mstart = start + 0.5 * (e.mins + e.maxs);
2291 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2292 if (trace_fraction >= 1)
2294 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2295 if (trace_fraction >= 1)
2297 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2298 if (trace_fraction >= 1)
2300 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2301 if (trace_fraction >= 1)
2303 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2304 if (trace_fraction >= 1)
2307 // find a random vector to "look at"
2308 end_x = org_x + random() * delta_x;
2309 end_y = org_y + random() * delta_y;
2310 end_z = org_z + random() * delta_z;
2311 end = start + normalize(end - start) * vlen(delta);
2313 // rule 4: start TO end must not be too short
2314 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2315 if (trace_startsolid)
2317 if (trace_fraction < minviewdistance / vlen(delta))
2320 // rule 5: don't want to look at sky
2321 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2324 // rule 6: we must not end up in trigger_hurt
2325 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2327 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2334 e.dphitcontentsmask = m;
2338 setorigin(e, start);
2339 e.angles = vectoangles(end - start);
2340 dprint("Needed ", ftos(i + 1), " attempts\n");
2347 float zcurveparticles_effectno;
2348 vector zcurveparticles_start;
2349 float zcurveparticles_spd;
2351 void endzcurveparticles()
2353 if(zcurveparticles_effectno)
2356 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2358 zcurveparticles_effectno = 0;
2361 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2363 spd = bound(0, floor(spd / 16), 32767);
2364 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2366 endzcurveparticles();
2367 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2368 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2369 WriteShort(MSG_BROADCAST, effectno);
2370 WriteCoord(MSG_BROADCAST, start_x);
2371 WriteCoord(MSG_BROADCAST, start_y);
2372 WriteCoord(MSG_BROADCAST, start_z);
2373 zcurveparticles_effectno = effectno;
2374 zcurveparticles_start = start;
2377 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2378 WriteCoord(MSG_BROADCAST, end_x);
2379 WriteCoord(MSG_BROADCAST, end_y);
2380 WriteCoord(MSG_BROADCAST, end_z);
2381 WriteCoord(MSG_BROADCAST, end_dz);
2382 zcurveparticles_spd = spd;
2385 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2388 vector vecxy, velxy;
2390 vecxy = end - start;
2395 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2397 endzcurveparticles();
2398 trailparticles(world, effectno, start, end);
2402 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2403 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2406 string GetGametype(); // g_world.qc
2407 void write_recordmarker(entity pl, float tstart, float dt)
2409 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2411 // also write a marker into demo files for demotc-race-record-extractor to find
2414 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2415 " ", ftos(tstart), " ", ftos(dt), "\n"));
2418 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2420 switch(self.owner.cvar_cl_gunalign)
2431 if(allowcenter) // 2: allow center handedness
2444 if(allowcenter) // 2: allow center handedness
2460 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2465 if (cvar("g_shootfromeye"))
2469 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2477 else if (cvar("g_shootfromcenter"))
2481 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2489 else if (cvar("g_shootfromclient"))
2491 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2493 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2508 void attach_sameorigin(entity e, entity to, string tag)
2510 vector org, t_forward, t_left, t_up, e_forward, e_up;
2517 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2518 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2519 t_forward = v_forward * tagscale;
2520 t_left = v_right * -tagscale;
2521 t_up = v_up * tagscale;
2523 e.origin_x = org * t_forward;
2524 e.origin_y = org * t_left;
2525 e.origin_z = org * t_up;
2527 // current forward and up directions
2528 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2529 e.angles_x = -e.angles_x;
2530 fixedmakevectors(e.angles);
2532 // untransform forward, up!
2533 e_forward_x = v_forward * t_forward;
2534 e_forward_y = v_forward * t_left;
2535 e_forward_z = v_forward * t_up;
2536 e_up_x = v_up * t_forward;
2537 e_up_y = v_up * t_left;
2538 e_up_z = v_up * t_up;
2540 e.angles = fixedvectoangles2(e_forward, e_up);
2541 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2542 e.angles_x = -e.angles_x;
2544 setattachment(e, to, tag);
2545 setorigin(e, e.origin);
2548 void detach_sameorigin(entity e)
2551 org = gettaginfo(e, 0);
2552 e.angles = fixedvectoangles2(v_forward, v_up);
2553 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2554 e.angles_x = -e.angles_x;
2556 setattachment(e, world, "");
2557 setorigin(e, e.origin);
2560 void follow_sameorigin(entity e, entity to)
2562 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2563 e.aiment = to; // make the hole follow bmodel
2564 e.punchangle = to.angles; // the original angles of bmodel
2565 e.view_ofs = e.origin - to.origin; // relative origin
2566 e.v_angle = e.angles - to.angles; // relative angles
2569 void unfollow_sameorigin(entity e)
2571 e.movetype = MOVETYPE_NONE;
2574 entity gettaginfo_relative_ent;
2575 vector gettaginfo_relative(entity e, float tag)
2577 if (!gettaginfo_relative_ent)
2579 gettaginfo_relative_ent = spawn();
2580 gettaginfo_relative_ent.effects = EF_NODRAW;
2582 gettaginfo_relative_ent.model = e.model;
2583 gettaginfo_relative_ent.modelindex = e.modelindex;
2584 gettaginfo_relative_ent.frame = e.frame;
2585 return gettaginfo(gettaginfo_relative_ent, tag);
2588 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2592 if (pl.soundentity.cnt & p)
2594 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2595 pl.soundentity.cnt |= p;
2598 void SoundEntity_StopSound(entity pl, float chan)
2602 if (pl.soundentity.cnt & p)
2604 stopsoundto(MSG_ALL, pl.soundentity, chan);
2605 pl.soundentity.cnt &~= p;
2609 void SoundEntity_Attach(entity pl)
2611 pl.soundentity = spawn();
2612 pl.soundentity.classname = "soundentity";
2613 pl.soundentity.owner = pl;
2614 setattachment(pl.soundentity, pl, "");
2615 setmodel(pl.soundentity, "null");
2618 void SoundEntity_Detach(entity pl)
2621 for (i = 0; i <= 7; ++i)
2622 SoundEntity_StopSound(pl, i);
2626 float ParseCommandPlayerSlotTarget_firsttoken;
2627 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2635 ParseCommandPlayerSlotTarget_firsttoken = -1;
2639 if (substring(argv(idx), 0, 1) == "#")
2641 s = substring(argv(idx), 1, -1);
2649 ParseCommandPlayerSlotTarget_firsttoken = idx;
2650 if (s == ftos(stof(s)))
2652 e = edict_num(stof(s));
2653 if (e.flags & FL_CLIENT)
2659 // it must be a nick name
2662 ParseCommandPlayerSlotTarget_firsttoken = idx;
2665 FOR_EACH_CLIENT(head)
2666 if (head.netname == s)
2674 s = strdecolorize(s);
2676 FOR_EACH_CLIENT(head)
2677 if (strdecolorize(head.netname) == s)
2692 float modeleffect_SendEntity(entity to, float sf)
2695 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2698 if(self.velocity != '0 0 0')
2700 if(self.angles != '0 0 0')
2702 if(self.avelocity != '0 0 0')
2705 WriteByte(MSG_ENTITY, f);
2706 WriteShort(MSG_ENTITY, self.modelindex);
2707 WriteByte(MSG_ENTITY, self.skin);
2708 WriteByte(MSG_ENTITY, self.frame);
2709 WriteCoord(MSG_ENTITY, self.origin_x);
2710 WriteCoord(MSG_ENTITY, self.origin_y);
2711 WriteCoord(MSG_ENTITY, self.origin_z);
2714 WriteCoord(MSG_ENTITY, self.velocity_x);
2715 WriteCoord(MSG_ENTITY, self.velocity_y);
2716 WriteCoord(MSG_ENTITY, self.velocity_z);
2720 WriteCoord(MSG_ENTITY, self.angles_x);
2721 WriteCoord(MSG_ENTITY, self.angles_y);
2722 WriteCoord(MSG_ENTITY, self.angles_z);
2726 WriteCoord(MSG_ENTITY, self.avelocity_x);
2727 WriteCoord(MSG_ENTITY, self.avelocity_y);
2728 WriteCoord(MSG_ENTITY, self.avelocity_z);
2730 WriteShort(MSG_ENTITY, self.scale * 256.0);
2731 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2732 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2733 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2734 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2739 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)
2744 e.classname = "modeleffect";
2752 e.teleport_time = t1;
2756 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2760 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2763 sz = max(e.scale, e.scale2);
2764 setsize(e, e.mins * sz, e.maxs * sz);
2765 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2768 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2770 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2773 float randombit(float bits)
2775 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2784 for(f = 1; f <= bits; f *= 2)
2793 r = (r - 1) / (n - 1);
2800 float randombits(float bits, float k, float error_return)
2804 while(k > 0 && bits != r)
2806 r += randombit(bits - r);
2815 void randombit_test(float bits, float iter)
2819 print(ftos(randombit(bits)), "\n");
2824 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2826 if(halflifedist > 0)
2827 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2828 else if(halflifedist < 0)
2829 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2838 #define cvar_string_normal cvar_string_builtin
2839 #define cvar_normal cvar_builtin
2841 string cvar_string_normal(string n)
2843 if not(cvar_type(n) & 1)
2844 backtrace(strcat("Attempt to access undefined cvar: ", n));
2845 return cvar_string_builtin(n);
2848 float cvar_normal(string n)
2850 return stof(cvar_string_normal(n));
2853 #define cvar_set_normal cvar_set_builtin
2861 oself.think = SUB_Remove;
2862 oself.nextthink = time;
2868 Execute func() after time + fdelay.
2869 self when func is executed = self when defer is called
2871 void defer(float fdelay, void() func)
2878 e.think = defer_think;
2879 e.nextthink = time + fdelay;