1 var void remove(entity e);
2 void objerror(string s);
4 .vector dropped_origin;
6 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
8 string ColoredTeamName(float t);
10 string admin_name(void)
12 if(cvar_string("sv_adminnick") != "")
13 return cvar_string("sv_adminnick");
15 return "SERVER ADMIN";
18 float DistributeEvenly_amount;
19 float DistributeEvenly_totalweight;
20 void DistributeEvenly_Init(float amount, float totalweight)
22 if (DistributeEvenly_amount)
24 dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
25 dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
28 DistributeEvenly_amount = 0;
30 DistributeEvenly_amount = amount;
31 DistributeEvenly_totalweight = totalweight;
33 float DistributeEvenly_Get(float weight)
38 f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
39 DistributeEvenly_totalweight -= weight;
40 DistributeEvenly_amount -= f;
44 void move_out_of_solid_expand(entity e, vector by)
47 tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
50 if (trace_fraction < 1)
53 // adjust origin in the other direction...
54 setorigin(e,e.origin - by * (1 - trace_fraction));
58 float move_out_of_solid(entity e)
63 traceline(o, o, MOVE_WORLDONLY, e);
67 tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
68 if (!trace_startsolid)
75 move_out_of_solid_expand(e, '1 0 0' * m0_x);
77 move_out_of_solid_expand(e, '1 0 0' * m1_x);
79 move_out_of_solid_expand(e, '0 1 0' * m0_y);
81 move_out_of_solid_expand(e, '0 1 0' * m1_y);
83 move_out_of_solid_expand(e, '0 0 1' * m0_z);
85 move_out_of_solid_expand(e, '0 0 1' * m1_z);
87 setorigin(e, e.origin);
89 tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
99 string STR_PLAYER = "player";
100 string STR_SPECTATOR = "spectator";
101 string STR_OBSERVER = "observer";
104 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
105 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
106 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
107 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
109 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
110 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
111 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
112 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
113 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
116 // copies a string to a tempstring (so one can strunzone it)
117 string strcat1(string s) = #115; // FRIK_FILE
122 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
124 local float nPlayerHealth = rint(enPlayer.health);
125 local float nPlayerArmor = rint(enPlayer.armorvalue);
126 local float nPlayerHandicap = enPlayer.cvar_cl_handicap;
127 local float nPlayerPing = rint(enPlayer.ping);
128 local string strPlayerPingColor;
129 local string strMessage;
130 if(nPlayerPing >= 150)
131 strPlayerPingColor = "^1";
133 strPlayerPingColor = "^2";
135 if((cvar("sv_fragmessage_information_stats")) && (enPlayer.health >= 1))
136 strMessage = strcat(strMessage, "\n^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
138 if(cvar("sv_fragmessage_information_ping")) {
139 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
140 strMessage = strcat(strMessage, "\n^7(^2Bot");
142 strMessage = strcat(strMessage, "\n^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
143 if(cvar("sv_fragmessage_information_handicap"))
144 if(cvar("sv_fragmessage_information_handicap") == 2)
145 if(nPlayerHandicap <= 1)
146 strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
148 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
149 else if not(nPlayerHandicap <= 1)
150 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
152 strMessage = strcat(strMessage, "^7)");
153 } else if(cvar("sv_fragmessage_information_handicap")) {
154 if(cvar("sv_fragmessage_information_handicap") == 2)
155 if(nPlayerHandicap <= 1)
156 strMessage = strcat(strMessage, "\n^7(Handicap ^2Off^7)");
158 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
159 else if(nPlayerHandicap > 1)
160 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
164 void bcenterprint(string s)
166 // TODO replace by MSG_ALL (would show it to spectators too, though)?
168 FOR_EACH_PLAYER(head)
169 if (clienttype(head) == CLIENTTYPE_REAL)
170 centerprint(head, s);
173 void GameLogEcho(string s)
178 if (cvar("sv_eventlog_files"))
183 matches = cvar("sv_eventlog_files_counter") + 1;
184 cvar_set("sv_eventlog_files_counter", ftos(matches));
187 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
188 fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
189 logfile = fopen(fn, FILE_APPEND);
190 fputs(logfile, ":logversion:3\n");
194 if (cvar("sv_eventlog_files_timestamps"))
195 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
197 fputs(logfile, strcat(s, "\n"));
200 if (cvar("sv_eventlog_console"))
209 // will be opened later
214 if (logfile_open && logfile >= 0)
221 float spawnpoint_nag;
222 void relocate_spawnpoint()
224 // nudge off the floor
225 setorigin(self, self.origin + '0 0 1');
227 tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
228 if (trace_startsolid)
234 if (!move_out_of_solid(self))
235 objerror("could not get out of solid at all!");
236 print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
237 print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
238 print(" ", ftos(self.origin_y - o_y));
239 print(" ", ftos(self.origin_z - o_z), "'\n");
240 if (cvar("g_spawnpoints_auto_move_out_of_solid"))
243 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
249 self.mins = self.maxs = '0 0 0';
250 objerror("player spawn point in solid, mapper sucks!\n");
255 if (cvar("g_spawnpoints_autodrop"))
257 setsize(self, PL_MIN, PL_MAX);
261 self.use = spawnpoint_use;
262 self.team_saved = self.team;
266 if (have_team_spawns != 0)
268 have_team_spawns = 1;
270 if (cvar("r_showbboxes"))
272 // show where spawnpoints point at too
273 makevectors(self.angles);
276 e.classname = "info_player_foo";
277 setorigin(e, self.origin + v_forward * 24);
278 setsize(e, '-8 -8 -8', '8 8 8');
279 e.solid = SOLID_TRIGGER;
283 #define strstr strstrofs
285 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
286 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
287 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
288 // BE CONSTANT OR strzoneD!
289 float strstr(string haystack, string needle, float offset)
293 len = strlen(needle);
294 endpos = strlen(haystack) - len;
295 while(offset <= endpos)
297 found = substring(haystack, offset, len);
306 float NUM_NEAREST_ENTITIES = 4;
307 entity nearest_entity[NUM_NEAREST_ENTITIES];
308 float nearest_length[NUM_NEAREST_ENTITIES];
309 entity findnearest(vector point, .string field, string value, vector axismod)
320 localhead = find(world, field, value);
323 if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
324 dist = localhead.oldorigin;
326 dist = localhead.origin;
328 dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
331 for (i = 0; i < num_nearest; ++i)
333 if (len < nearest_length[i])
337 // now i tells us where to insert at
338 // INSERTION SORT! YOU'VE SEEN IT! RUN!
339 if (i < NUM_NEAREST_ENTITIES)
341 for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
343 nearest_length[j + 1] = nearest_length[j];
344 nearest_entity[j + 1] = nearest_entity[j];
346 nearest_length[i] = len;
347 nearest_entity[i] = localhead;
348 if (num_nearest < NUM_NEAREST_ENTITIES)
349 num_nearest = num_nearest + 1;
352 localhead = find(localhead, field, value);
355 // now use the first one from our list that we can see
356 for (i = 0; i < num_nearest; ++i)
358 traceline(point, nearest_entity[i].origin, TRUE, world);
359 if (trace_fraction == 1)
363 dprint("Nearest point (");
364 dprint(nearest_entity[0].netname);
365 dprint(") is not visible, using a visible one.\n");
367 return nearest_entity[i];
371 if (num_nearest == 0)
374 dprint("Not seeing any location point, using nearest as fallback.\n");
376 dprint("Candidates were: ");
377 for(j = 0; j < num_nearest; ++j)
381 dprint(nearest_entity[j].netname);
386 return nearest_entity[0];
389 void spawnfunc_target_location()
391 self.classname = "target_location";
392 // location name in netname
393 // eventually support: count, teamgame selectors, line of sight?
396 void spawnfunc_info_location()
398 self.classname = "target_location";
399 self.message = self.netname;
402 string NearestLocation(vector p)
407 loc = findnearest(p, classname, "target_location", '1 1 1');
414 loc = findnearest(p, target, "###item###", '1 1 4');
421 string formatmessage(string msg)
432 break; // too many replacements
434 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
435 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
445 replacement = substring(msg, p, 2);
446 escape = substring(msg, p + 1, 1);
449 else if (escape == "\\")
451 else if (escape == "n")
453 else if (escape == "a")
454 replacement = ftos(floor(self.armorvalue));
455 else if (escape == "h")
456 replacement = ftos(floor(self.health));
457 else if (escape == "l")
458 replacement = NearestLocation(self.origin);
459 else if (escape == "y")
460 replacement = NearestLocation(self.cursor_trace_endpos);
461 else if (escape == "d")
462 replacement = NearestLocation(self.death_origin);
463 else if (escape == "w")
468 wep = self.switchweapon;
471 replacement = W_Name(wep);
473 else if (escape == "W")
475 if (self.items & IT_SHELLS) replacement = "shells";
476 else if (self.items & IT_NAILS) replacement = "bullets";
477 else if (self.items & IT_ROCKETS) replacement = "rockets";
478 else if (self.items & IT_CELLS) replacement = "cells";
479 else replacement = "batteries"; // ;)
481 else if (escape == "x")
483 replacement = self.cursor_trace_ent.netname;
484 if (!replacement || !self.cursor_trace_ent)
485 replacement = "nothing";
487 else if (escape == "p")
489 if (self.last_selected_player)
490 replacement = self.last_selected_player.netname;
492 replacement = "(nobody)";
494 else if (escape == "s")
495 replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
496 else if (escape == "S")
497 replacement = ftos(vlen(self.velocity));
498 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
499 p = p + strlen(replacement);
505 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
506 return (value == 0) ? FALSE : TRUE;
515 >0: receives a cvar from name=argv(f) value=argv(f+1)
517 void GetCvars_handleString(string thisname, float f, .string field, string name)
522 strunzone(self.field);
523 self.field = string_null;
527 if (thisname == name)
530 strunzone(self.field);
531 self.field = strzone(argv(f + 1));
535 stuffcmd(self, strcat("sendcvar ", name, "\n"));
537 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
539 GetCvars_handleString(thisname, f, field, name);
540 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
541 if (thisname == name)
544 s = func(strcat1(self.field));
547 strunzone(self.field);
548 self.field = strzone(s);
552 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
559 if (thisname == name)
560 self.field = stof(argv(f + 1));
563 stuffcmd(self, strcat("sendcvar ", name, "\n"));
565 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
572 if (thisname == name)
576 self.field = stof(argv(f + 1));
585 stuffcmd(self, strcat("sendcvar ", name, "\n"));
588 string W_FixWeaponOrder_ForceComplete(string s);
589 string W_FixWeaponOrder_AllowIncomplete(string s);
590 float w_getbestweapon(entity e);
591 void GetCvars(float f)
595 s = strcat1(argv(f));
596 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
597 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
598 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
599 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
600 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
601 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
602 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
603 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
604 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
605 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
606 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
607 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
608 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
609 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
610 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
611 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
612 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
613 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
614 GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
615 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
616 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
617 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
618 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
619 GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
621 self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
622 self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
624 #ifdef ALLOW_FORCEMODELS
625 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
626 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
628 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
630 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
633 if (s == "cl_weaponpriority")
634 self.switchweapon = w_getbestweapon(self);
638 float fexists(string f)
641 fh = fopen(f, FILE_READ);
648 void backtrace(string msg)
651 dev = cvar("developer");
652 war = cvar("prvm_backtraceforwarnings");
653 cvar_set("developer", "1");
654 cvar_set("prvm_backtraceforwarnings", "1");
656 dprint("--- CUT HERE ---\nWARNING: ");
659 remove(world); // isn't there any better way to cause a backtrace?
660 dprint("\n--- CUT UNTIL HERE ---\n");
661 cvar_set("developer", ftos(dev));
662 cvar_set("prvm_backtraceforwarnings", ftos(war));
665 string Team_ColorCode(float teamid)
667 if (teamid == COLOR_TEAM1)
669 else if (teamid == COLOR_TEAM2)
671 else if (teamid == COLOR_TEAM3)
673 else if (teamid == COLOR_TEAM4)
679 string Team_ColorName(float t)
681 // fixme: Search for team entities and get their .netname's!
682 if (t == COLOR_TEAM1)
684 if (t == COLOR_TEAM2)
686 if (t == COLOR_TEAM3)
688 if (t == COLOR_TEAM4)
693 string Team_ColorNameLowerCase(float t)
695 // fixme: Search for team entities and get their .netname's!
696 if (t == COLOR_TEAM1)
698 if (t == COLOR_TEAM2)
700 if (t == COLOR_TEAM3)
702 if (t == COLOR_TEAM4)
707 #define CENTERPRIO_POINT 1
708 #define CENTERPRIO_SPAM 2
709 #define CENTERPRIO_VOTE 4
710 #define CENTERPRIO_NORMAL 5
711 #define CENTERPRIO_SHIELDING 7
712 #define CENTERPRIO_MAPVOTE 9
713 #define CENTERPRIO_IDLEKICK 50
714 #define CENTERPRIO_ADMIN 99
715 .float centerprint_priority;
716 .float centerprint_expires;
717 void centerprint_atprio(entity e, float prio, string s)
719 if (intermission_running)
720 if (prio < CENTERPRIO_MAPVOTE)
722 if (time > e.centerprint_expires)
723 e.centerprint_priority = 0;
724 if (prio >= e.centerprint_priority)
726 e.centerprint_priority = prio;
727 if (timeoutStatus == 2)
728 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
730 e.centerprint_expires = time + e.cvar_scr_centertime;
731 centerprint_builtin(e, s);
734 void centerprint_expire(entity e, float prio)
736 if (prio == e.centerprint_priority)
738 e.centerprint_priority = 0;
739 centerprint_builtin(e, "");
742 void centerprint(entity e, string s)
744 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
747 // decolorizes and team colors the player name when needed
748 string playername(entity p)
751 if (teams_matter && !intermission_running && p.classname == "player")
753 t = Team_ColorCode(p.team);
754 return strcat(t, strdecolorize(p.netname));
760 vector randompos(vector m1, vector m2)
764 v_x = m2_x * random() + m1_x;
765 v_y = m2_y * random() + m1_y;
766 v_z = m2_z * random() + m1_z;
770 float g_pickup_shells;
771 float g_pickup_shells_max;
772 float g_pickup_nails;
773 float g_pickup_nails_max;
774 float g_pickup_rockets;
775 float g_pickup_rockets_max;
776 float g_pickup_cells;
777 float g_pickup_cells_max;
779 float g_pickup_fuel_jetpack;
780 float g_pickup_fuel_max;
781 float g_pickup_armorsmall;
782 float g_pickup_armorsmall_max;
783 float g_pickup_armormedium;
784 float g_pickup_armormedium_max;
785 float g_pickup_armorbig;
786 float g_pickup_armorbig_max;
787 float g_pickup_armorlarge;
788 float g_pickup_armorlarge_max;
789 float g_pickup_healthsmall;
790 float g_pickup_healthsmall_max;
791 float g_pickup_healthmedium;
792 float g_pickup_healthmedium_max;
793 float g_pickup_healthlarge;
794 float g_pickup_healthlarge_max;
795 float g_pickup_healthmega;
796 float g_pickup_healthmega_max;
798 float g_weaponarena_random;
799 string g_weaponarena_list;
800 float g_weaponspeedfactor;
801 float g_weaponratefactor;
802 float g_weapondamagefactor;
803 float g_weaponforcefactor;
807 float start_ammo_shells;
808 float start_ammo_nails;
809 float start_ammo_rockets;
810 float start_ammo_cells;
811 float start_ammo_fuel;
813 float start_armorvalue;
814 float warmup_start_weapons;
815 float warmup_start_ammo_shells;
816 float warmup_start_ammo_nails;
817 float warmup_start_ammo_rockets;
818 float warmup_start_ammo_cells;
819 float warmup_start_ammo_fuel;
820 float warmup_start_health;
821 float warmup_start_armorvalue;
825 entity get_weaponinfo(float w);
827 float NixNex_CanChooseWeapon(float wpn);
828 void readplayerstartcvars()
834 // initialize starting values for players
837 start_ammo_shells = 0;
838 start_ammo_nails = 0;
839 start_ammo_rockets = 0;
840 start_ammo_cells = 0;
841 start_health = cvar("g_balance_health_start");
842 start_armorvalue = cvar("g_balance_armor_start");
845 s = cvar_string("g_weaponarena");
851 g_weaponarena_list = "All Weapons";
852 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
854 e = get_weaponinfo(j);
855 g_weaponarena |= e.weapons;
856 weapon_action(e.weapon, WR_PRECACHE);
859 else if (s == "most")
861 g_weaponarena_list = "Most Weapons";
862 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
864 e = get_weaponinfo(j);
865 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
867 g_weaponarena |= e.weapons;
868 weapon_action(e.weapon, WR_PRECACHE);
872 else if (s == "none")
874 g_weaponarena_list = "No Weapons";
875 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
879 t = tokenize_console(s);
880 g_weaponarena_list = "";
881 for (i = 0; i < t; ++i)
884 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
886 e = get_weaponinfo(j);
889 g_weaponarena |= e.weapons;
890 weapon_action(e.weapon, WR_PRECACHE);
891 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
897 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
900 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
904 g_weaponarena_random = cvar("g_weaponarena_random");
906 g_weaponarena_random = 0;
911 // will be done later
912 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
913 if (NixNex_CanChooseWeapon(i))
914 weapon_action(i, WR_PRECACHE);
915 if(!cvar("g_use_ammunition"))
916 start_items |= IT_UNLIMITED_AMMO;
918 else if (g_weaponarena)
920 start_weapons = g_weaponarena;
921 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
922 start_ammo_rockets = 999;
923 if (g_weaponarena & WEPBIT_SHOTGUN)
924 start_ammo_shells = 999;
925 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
926 start_ammo_cells = 999;
927 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
928 start_ammo_nails = 999;
929 if (g_weaponarena & WEPBIT_HOOK)
930 start_ammo_fuel = 999;
931 start_items |= IT_UNLIMITED_AMMO;
933 else if (g_minstagib)
936 start_armorvalue = 0;
937 start_weapons = WEPBIT_MINSTANEX;
938 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
939 start_ammo_cells = cvar("g_minstagib_ammo_start");
940 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
941 start_ammo_fuel = cvar("g_start_ammo_fuel");
943 if (g_minstagib_invis_alpha <= 0)
944 g_minstagib_invis_alpha = -1;
950 start_ammo_shells = cvar("g_lms_start_ammo_shells");
951 start_ammo_nails = cvar("g_lms_start_ammo_nails");
952 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
953 start_ammo_cells = cvar("g_lms_start_ammo_cells");
954 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
955 start_health = cvar("g_lms_start_health");
956 start_armorvalue = cvar("g_lms_start_armor");
958 else if (cvar("g_use_ammunition"))
960 start_ammo_shells = cvar("g_start_ammo_shells");
961 start_ammo_nails = cvar("g_start_ammo_nails");
962 start_ammo_rockets = cvar("g_start_ammo_rockets");
963 start_ammo_cells = cvar("g_start_ammo_cells");
964 start_ammo_fuel = cvar("g_start_ammo_fuel");
968 start_ammo_shells = cvar("g_pickup_shells_max");
969 start_ammo_nails = cvar("g_pickup_nails_max");
970 start_ammo_rockets = cvar("g_pickup_rockets_max");
971 start_ammo_cells = cvar("g_pickup_cells_max");
972 start_ammo_fuel = cvar("g_pickup_fuel_max");
973 start_items |= IT_UNLIMITED_AMMO;
976 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
978 e = get_weaponinfo(i);
982 t = cvar(strcat("g_start_weapon_", e.netname));
984 if (t < 0) // "default" weapon selection
987 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
988 else if (g_race || g_cts)
989 t = (i == WEP_LASER);
991 t = 0; // weapon is set a few lines later
993 t = (i == WEP_LASER || i == WEP_SHOTGUN);
994 if (g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
995 t += (i == WEP_HOOK);
998 if (g_nexball && i == WEP_PORTO)
1003 start_weapons |= e.weapons;
1004 weapon_action(e.weapon, WR_PRECACHE);
1011 warmup_start_ammo_shells = start_ammo_shells;
1012 warmup_start_ammo_nails = start_ammo_nails;
1013 warmup_start_ammo_rockets = start_ammo_rockets;
1014 warmup_start_ammo_cells = start_ammo_cells;
1015 warmup_start_ammo_fuel = start_ammo_fuel;
1016 warmup_start_health = start_health;
1017 warmup_start_armorvalue = start_armorvalue;
1018 warmup_start_weapons = start_weapons;
1020 if (!g_weaponarena && !g_nixnex && !g_minstagib)
1022 if (cvar("g_use_ammunition"))
1024 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1025 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1026 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1027 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1028 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1030 warmup_start_health = cvar("g_warmup_start_health");
1031 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1032 if (cvar("g_warmup_allguns"))
1034 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1036 e = get_weaponinfo(i);
1039 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
1041 warmup_start_weapons |= e.weapons;
1042 weapon_action(e.weapon, WR_PRECACHE);
1049 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1051 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1052 start_items |= IT_FUEL_REGEN;
1053 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1054 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1058 start_items |= IT_JETPACK;
1060 if (g_weapon_stay == 2)
1062 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1063 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1064 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1065 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1066 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1067 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1068 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1069 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1070 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1071 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1074 start_ammo_shells = max(0, start_ammo_shells);
1075 start_ammo_nails = max(0, start_ammo_nails);
1076 start_ammo_cells = max(0, start_ammo_cells);
1077 start_ammo_rockets = max(0, start_ammo_rockets);
1078 start_ammo_fuel = max(0, start_ammo_fuel);
1080 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1081 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1082 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1083 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1084 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1088 float g_bugrigs_planar_movement;
1089 float g_bugrigs_planar_movement_car_jumping;
1090 float g_bugrigs_reverse_spinning;
1091 float g_bugrigs_reverse_speeding;
1092 float g_bugrigs_reverse_stopping;
1093 float g_bugrigs_air_steering;
1094 float g_bugrigs_angle_smoothing;
1095 float g_bugrigs_friction_floor;
1096 float g_bugrigs_friction_brake;
1097 float g_bugrigs_friction_air;
1098 float g_bugrigs_accel;
1099 float g_bugrigs_speed_ref;
1100 float g_bugrigs_speed_pow;
1101 float g_bugrigs_steer;
1103 float g_touchexplode;
1104 float g_touchexplode_radius;
1105 float g_touchexplode_damage;
1106 float g_touchexplode_edgedamage;
1107 float g_touchexplode_force;
1114 float sv_pitch_fixyaw;
1116 float sv_accuracy_data_share;
1118 void readlevelcvars(void)
1120 g_bugrigs = cvar("g_bugrigs");
1121 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1122 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1123 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1124 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1125 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1126 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1127 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1128 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1129 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1130 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1131 g_bugrigs_accel = cvar("g_bugrigs_accel");
1132 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1133 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1134 g_bugrigs_steer = cvar("g_bugrigs_steer");
1136 g_touchexplode = cvar("g_touchexplode");
1137 g_touchexplode_radius = cvar("g_touchexplode_radius");
1138 g_touchexplode_damage = cvar("g_touchexplode_damage");
1139 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1140 g_touchexplode_force = cvar("g_touchexplode_force");
1142 #ifdef ALLOW_FORCEMODELS
1143 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1145 sv_loddistance1 = cvar("sv_loddistance1");
1146 sv_loddistance2 = cvar("sv_loddistance2");
1148 if(sv_loddistance2 <= sv_loddistance1)
1149 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1151 sv_clones = cvar("sv_clones");
1152 sv_cheats = cvar("sv_cheats");
1153 sv_gentle = cvar("sv_gentle");
1154 sv_foginterval = cvar("sv_foginterval");
1155 g_cloaked = cvar("g_cloaked");
1156 g_jump_grunt = cvar("g_jump_grunt");
1157 g_footsteps = cvar("g_footsteps");
1158 g_grappling_hook = cvar("g_grappling_hook");
1159 g_jetpack = cvar("g_jetpack");
1160 g_laserguided_missile = cvar("g_laserguided_missile");
1161 g_midair = cvar("g_midair");
1162 g_minstagib = cvar("g_minstagib");
1163 g_nixnex = cvar("g_nixnex");
1164 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1165 g_norecoil = cvar("g_norecoil");
1166 g_vampire = cvar("g_vampire");
1167 g_bloodloss = cvar("g_bloodloss");
1168 sv_maxidle = cvar("sv_maxidle");
1169 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1170 sv_pogostick = cvar("sv_pogostick");
1171 sv_doublejump = cvar("sv_doublejump");
1172 g_ctf_reverse = cvar("g_ctf_reverse");
1173 sv_autotaunt = cvar("sv_autotaunt");
1174 sv_taunt = cvar("sv_taunt");
1176 inWarmupStage = cvar("g_warmup");
1177 g_warmup_limit = cvar("g_warmup_limit");
1178 g_warmup_allguns = cvar("g_warmup_allguns");
1179 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1181 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1182 inWarmupStage = 0; // these modes cannot work together, sorry
1184 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1185 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1186 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1187 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1188 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1189 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1190 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1191 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1192 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1193 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1194 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1195 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1197 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1198 if (g_nixnex) g_weaponarena = 0;
1201 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1202 g_weaponratefactor = cvar("g_weaponratefactor");
1203 g_weapondamagefactor = cvar("g_weapondamagefactor");
1204 g_weaponforcefactor = cvar("g_weaponforcefactor");
1206 g_pickup_shells = cvar("g_pickup_shells");
1207 g_pickup_shells_max = cvar("g_pickup_shells_max");
1208 g_pickup_nails = cvar("g_pickup_nails");
1209 g_pickup_nails_max = cvar("g_pickup_nails_max");
1210 g_pickup_rockets = cvar("g_pickup_rockets");
1211 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1212 g_pickup_cells = cvar("g_pickup_cells");
1213 g_pickup_cells_max = cvar("g_pickup_cells_max");
1214 g_pickup_fuel = cvar("g_pickup_fuel");
1215 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1216 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1217 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1218 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1219 g_pickup_armormedium = cvar("g_pickup_armormedium");
1220 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1221 g_pickup_armorbig = cvar("g_pickup_armorbig");
1222 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1223 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1224 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1225 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1226 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1227 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1228 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1229 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1230 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1231 g_pickup_healthmega = cvar("g_pickup_healthmega");
1232 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1234 g_pinata = cvar("g_pinata");
1236 g_weapon_stay = cvar("g_weapon_stay");
1238 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1241 g_ghost_items = cvar("g_ghost_items");
1243 if(g_ghost_items >= 1)
1244 g_ghost_items = 0.13; // default alpha value
1246 if not(inWarmupStage)
1247 game_starttime = cvar("g_start_delay");
1249 sv_pitch_min = cvar("sv_pitch_min");
1250 sv_pitch_max = cvar("sv_pitch_max");
1251 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1253 sv_accuracy_data_share = boolean(cvar("sv_accuracy_data_share"));
1255 readplayerstartcvars();
1259 // TODO sound pack system
1262 string precache_sound_builtin (string s) = #19;
1263 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1264 string precache_sound(string s)
1266 return precache_sound_builtin(strcat(soundpack, s));
1268 void play2(entity e, string filename)
1270 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1272 void sound(entity e, float chan, string samp, float vol, float atten)
1274 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1279 string precache_sound (string s) = #19;
1280 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1281 float precache_sound_index (string s) = #19;
1283 #define SND_VOLUME 1
1284 #define SND_ATTENUATION 2
1285 #define SND_LARGEENTITY 8
1286 #define SND_LARGESOUND 16
1288 float sound_allowed(float dest, entity e)
1290 // sounds from world may always pass
1293 if (e.classname == "body")
1295 if (e.owner && e.owner != e)
1300 // sounds to self may always pass
1301 if (dest == MSG_ONE)
1302 if (e == msg_entity)
1304 // sounds by players can be removed
1305 if (cvar("bot_sound_monopoly"))
1306 if (clienttype(e) == CLIENTTYPE_REAL)
1308 // anything else may pass
1312 void sound(entity e, float chan, string samp, float vol, float atten)
1314 if (!sound_allowed(MSG_BROADCAST, e))
1316 sound_builtin(e, chan, samp, vol, atten);
1318 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1322 if (!sound_allowed(dest, e))
1325 entno = num_for_edict(e);
1326 idx = precache_sound_index(samp);
1331 atten = floor(atten * 64);
1332 vol = floor(vol * 255);
1335 sflags |= SND_VOLUME;
1337 sflags |= SND_ATTENUATION;
1339 sflags |= SND_LARGEENTITY;
1341 sflags |= SND_LARGESOUND;
1343 WriteByte(dest, SVC_SOUND);
1344 WriteByte(dest, sflags);
1345 if (sflags & SND_VOLUME)
1346 WriteByte(dest, vol);
1347 if (sflags & SND_ATTENUATION)
1348 WriteByte(dest, atten);
1349 if (sflags & SND_LARGEENTITY)
1351 WriteShort(dest, entno);
1352 WriteByte(dest, chan);
1356 WriteShort(dest, entno * 8 + chan);
1358 if (sflags & SND_LARGESOUND)
1359 WriteShort(dest, idx);
1361 WriteByte(dest, idx);
1363 WriteCoord(dest, o_x);
1364 WriteCoord(dest, o_y);
1365 WriteCoord(dest, o_z);
1367 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1371 if (!sound_allowed(dest, e))
1374 o = e.origin + 0.5 * (e.mins + e.maxs);
1375 soundtoat(dest, e, o, chan, samp, vol, atten);
1377 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1379 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1381 void stopsoundto(float dest, entity e, float chan)
1385 if (!sound_allowed(dest, e))
1388 entno = num_for_edict(e);
1393 idx = precache_sound_index("misc/null.wav");
1394 sflags = SND_LARGEENTITY;
1396 sflags |= SND_LARGESOUND;
1397 WriteByte(dest, SVC_SOUND);
1398 WriteByte(dest, sflags);
1399 WriteShort(dest, entno);
1400 WriteByte(dest, chan);
1401 if (sflags & SND_LARGESOUND)
1402 WriteShort(dest, idx);
1404 WriteByte(dest, idx);
1405 WriteCoord(dest, e.origin_x);
1406 WriteCoord(dest, e.origin_y);
1407 WriteCoord(dest, e.origin_z);
1411 WriteByte(dest, SVC_STOPSOUND);
1412 WriteShort(dest, entno * 8 + chan);
1415 void stopsound(entity e, float chan)
1417 if (!sound_allowed(MSG_BROADCAST, e))
1420 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1421 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1424 void play2(entity e, string filename)
1426 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1428 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1431 .float announcetime;
1432 float announce(entity player, string msg)
1434 if (time > player.announcetime)
1435 if (clienttype(player) == CLIENTTYPE_REAL)
1437 player.announcetime = time + 0.8;
1443 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1444 float spamsound(entity e, float chan, string samp, float vol, float atten)
1446 if (!sound_allowed(MSG_BROADCAST, e))
1449 if (time > e.announcetime)
1451 e.announcetime = time;
1452 sound(e, chan, samp, vol, atten);
1458 void play2team(float t, string filename)
1462 if (cvar("bot_sound_monopoly"))
1465 FOR_EACH_REALPLAYER(head)
1468 play2(head, filename);
1472 void play2all(string samp)
1474 if (cvar("bot_sound_monopoly"))
1477 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1480 void PrecachePlayerSounds(string f);
1481 void precache_all_models(string pattern)
1483 float globhandle, i, n;
1486 globhandle = search_begin(pattern, TRUE, FALSE);
1489 n = search_getsize(globhandle);
1490 for (i = 0; i < n; ++i)
1492 //print(search_getfilename(globhandle, i), "\n");
1493 f = search_getfilename(globhandle, i);
1496 if(substring(f, -9,5) == "_lod1")
1498 if(substring(f, -9,5) == "_lod2")
1500 if(!sv_loddistance1)
1502 PrecachePlayerSounds(strcat(f, ".sounds"));
1504 search_end(globhandle);
1509 // gamemode related things
1510 precache_model ("models/misc/chatbubble.spr");
1511 precache_model ("models/misc/teambubble.spr");
1514 precache_model ("models/runematch/curse.mdl");
1515 precache_model ("models/runematch/rune.mdl");
1518 #ifdef TTURRETS_ENABLED
1519 if (cvar("g_turrets"))
1523 // Precache all player models if desired
1524 if (cvar("sv_precacheplayermodels"))
1526 PrecachePlayerSounds("sound/player/default.sounds");
1527 precache_all_models("models/player/*.zym");
1528 precache_all_models("models/player/*.dpm");
1529 precache_all_models("models/player/*.md3");
1530 precache_all_models("models/player/*.psk");
1531 //precache_model("models/player/carni.zym");
1532 //precache_model("models/player/crash.zym");
1533 //precache_model("models/player/grunt.zym");
1534 //precache_model("models/player/headhunter.zym");
1535 //precache_model("models/player/insurrectionist.zym");
1536 //precache_model("models/player/jeandarc.zym");
1537 //precache_model("models/player/lurk.zym");
1538 //precache_model("models/player/lycanthrope.zym");
1539 //precache_model("models/player/marine.zym");
1540 //precache_model("models/player/nexus.zym");
1541 //precache_model("models/player/pyria.zym");
1542 //precache_model("models/player/shock.zym");
1543 //precache_model("models/player/skadi.zym");
1544 //precache_model("models/player/specop.zym");
1545 //precache_model("models/player/visitant.zym");
1548 if (cvar("sv_defaultcharacter"))
1551 s = cvar_string("sv_defaultplayermodel_red");
1555 PrecachePlayerSounds(strcat(s, ".sounds"));
1557 s = cvar_string("sv_defaultplayermodel_blue");
1561 PrecachePlayerSounds(strcat(s, ".sounds"));
1563 s = cvar_string("sv_defaultplayermodel_yellow");
1567 PrecachePlayerSounds(strcat(s, ".sounds"));
1569 s = cvar_string("sv_defaultplayermodel_pink");
1573 PrecachePlayerSounds(strcat(s, ".sounds"));
1575 s = cvar_string("sv_defaultplayermodel");
1579 PrecachePlayerSounds(strcat(s, ".sounds"));
1585 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1586 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1589 // gore and miscellaneous sounds
1590 //precache_sound ("misc/h2ohit.wav");
1591 precache_model ("models/hook.md3");
1592 precache_sound ("misc/armorimpact.wav");
1593 precache_sound ("misc/bodyimpact1.wav");
1594 precache_sound ("misc/bodyimpact2.wav");
1595 precache_sound ("misc/gib.wav");
1596 precache_sound ("misc/gib_splat01.wav");
1597 precache_sound ("misc/gib_splat02.wav");
1598 precache_sound ("misc/gib_splat03.wav");
1599 precache_sound ("misc/gib_splat04.wav");
1600 precache_sound ("misc/hit.wav");
1601 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1602 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1603 precache_sound ("misc/null.wav");
1604 precache_sound ("misc/spawn.wav");
1605 precache_sound ("misc/talk.wav");
1606 precache_sound ("misc/teleport.wav");
1607 precache_sound ("misc/poweroff.wav");
1608 precache_sound ("player/lava.wav");
1609 precache_sound ("player/slime.wav");
1612 precache_sound ("misc/jetpack_fly.wav");
1614 // announcer sounds - male
1615 precache_sound ("announcer/male/electrobitch.wav");
1616 precache_sound ("announcer/male/airshot.wav");
1617 precache_sound ("announcer/male/03kills.wav");
1618 precache_sound ("announcer/male/05kills.wav");
1619 precache_sound ("announcer/male/10kills.wav");
1620 precache_sound ("announcer/male/15kills.wav");
1621 precache_sound ("announcer/male/20kills.wav");
1622 precache_sound ("announcer/male/25kills.wav");
1623 precache_sound ("announcer/male/30kills.wav");
1624 precache_sound ("announcer/male/botlike.wav");
1625 precache_sound ("announcer/male/yoda.wav");
1626 precache_sound ("announcer/male/amazing.wav");
1627 precache_sound ("announcer/male/awesome.wav");
1628 precache_sound ("announcer/male/headshot.wav");
1629 precache_sound ("announcer/male/impressive.wav");
1631 // announcer sounds - robotic
1632 precache_sound ("announcer/robotic/prepareforbattle.wav");
1633 precache_sound ("announcer/robotic/begin.wav");
1634 precache_sound ("announcer/robotic/timeoutcalled.wav");
1635 precache_sound ("announcer/robotic/1fragleft.wav");
1636 precache_sound ("announcer/robotic/2fragsleft.wav");
1637 precache_sound ("announcer/robotic/3fragsleft.wav");
1638 precache_sound ("announcer/robotic/terminated.wav");
1641 precache_sound ("announcer/robotic/lastsecond.wav");
1642 precache_sound ("announcer/robotic/narrowly.wav");
1645 precache_model ("models/sprites/0.spr32");
1646 precache_model ("models/sprites/1.spr32");
1647 precache_model ("models/sprites/2.spr32");
1648 precache_model ("models/sprites/3.spr32");
1649 precache_model ("models/sprites/4.spr32");
1650 precache_model ("models/sprites/5.spr32");
1651 precache_model ("models/sprites/6.spr32");
1652 precache_model ("models/sprites/7.spr32");
1653 precache_model ("models/sprites/8.spr32");
1654 precache_model ("models/sprites/9.spr32");
1655 precache_model ("models/sprites/10.spr32");
1656 precache_sound ("announcer/robotic/1.wav");
1657 precache_sound ("announcer/robotic/2.wav");
1658 precache_sound ("announcer/robotic/3.wav");
1659 precache_sound ("announcer/robotic/4.wav");
1660 precache_sound ("announcer/robotic/5.wav");
1661 precache_sound ("announcer/robotic/6.wav");
1662 precache_sound ("announcer/robotic/7.wav");
1663 precache_sound ("announcer/robotic/8.wav");
1664 precache_sound ("announcer/robotic/9.wav");
1665 precache_sound ("announcer/robotic/10.wav");
1667 // common weapon precaches
1668 precache_sound ("weapons/weapon_switch.wav");
1669 precache_sound ("weapons/weaponpickup.wav");
1670 precache_sound ("weapons/unavailable.wav");
1671 if (g_grappling_hook)
1673 precache_sound ("weapons/hook_fire.wav"); // hook
1674 precache_sound ("weapons/hook_impact.wav"); // hook
1677 if (cvar("sv_precacheweapons") || g_nixnex)
1679 //precache weapon models/sounds
1682 while (wep <= WEP_LAST)
1684 weapon_action(wep, WR_PRECACHE);
1689 precache_model("models/elaser.mdl");
1690 precache_model("models/laser.mdl");
1691 precache_model("models/ebomb.mdl");
1694 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1696 if (!self.noise && self.music) // quake 3 uses the music field
1697 self.noise = self.music;
1699 // plays music for the level if there is any
1702 precache_sound (self.noise);
1703 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1708 // sorry, but using \ in macros breaks line numbers
1709 #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
1710 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1711 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1713 vector ExactTriggerHit_mins;
1714 vector ExactTriggerHit_maxs;
1715 float ExactTriggerHit_Recurse()
1721 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1724 if (trace_ent == self)
1729 se.solid = SOLID_NOT;
1730 f = ExactTriggerHit_Recurse();
1736 float ExactTriggerHit()
1740 if not(self.modelindex)
1744 self.solid = SOLID_BSP;
1745 ExactTriggerHit_mins = other.absmin;
1746 ExactTriggerHit_maxs = other.absmax;
1747 f = ExactTriggerHit_Recurse();
1753 // WARNING: this kills the trace globals
1754 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1755 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1757 #define INITPRIO_FIRST 0
1758 #define INITPRIO_GAMETYPE 0
1759 #define INITPRIO_GAMETYPE_FALLBACK 1
1760 #define INITPRIO_CVARS 5
1761 #define INITPRIO_FINDTARGET 10
1762 #define INITPRIO_DROPTOFLOOR 20
1763 #define INITPRIO_SETLOCATION 90
1764 #define INITPRIO_LINKDOORS 91
1765 #define INITPRIO_LAST 99
1767 .void(void) initialize_entity;
1768 .float initialize_entity_order;
1769 .entity initialize_entity_next;
1770 entity initialize_entity_first;
1772 void make_safe_for_remove(entity e)
1774 if (e.initialize_entity)
1777 for (ent = initialize_entity_first; ent; )
1779 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1781 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1782 // skip it in linked list
1785 prev.initialize_entity_next = ent.initialize_entity_next;
1786 ent = prev.initialize_entity_next;
1790 initialize_entity_first = ent.initialize_entity_next;
1791 ent = initialize_entity_first;
1797 ent = ent.initialize_entity_next;
1803 void objerror(string s)
1805 make_safe_for_remove(self);
1806 objerror_builtin(s);
1809 void remove_unsafely(entity e)
1814 void remove_safely(entity e)
1816 make_safe_for_remove(e);
1820 void InitializeEntity(entity e, void(void) func, float order)
1824 if (!e || e.initialize_entity)
1826 // make a proxy initializer entity
1830 e.classname = "initialize_entity";
1834 e.initialize_entity = func;
1835 e.initialize_entity_order = order;
1837 cur = initialize_entity_first;
1840 if (!cur || cur.initialize_entity_order > order)
1842 // insert between prev and cur
1844 prev.initialize_entity_next = e;
1846 initialize_entity_first = e;
1847 e.initialize_entity_next = cur;
1851 cur = cur.initialize_entity_next;
1854 void InitializeEntitiesRun()
1857 startoflist = initialize_entity_first;
1858 initialize_entity_first = world;
1859 for (self = startoflist; self; )
1862 var void(void) func;
1863 e = self.initialize_entity_next;
1864 func = self.initialize_entity;
1865 self.initialize_entity_order = 0;
1866 self.initialize_entity = func_null;
1867 self.initialize_entity_next = world;
1868 if (self.classname == "initialize_entity")
1872 remove_builtin(self);
1875 //dprint("Delayed initialization: ", self.classname, "\n");
1881 .float uncustomizeentityforclient_set;
1882 .void(void) uncustomizeentityforclient;
1883 void(void) SUB_Nullpointer = #0;
1884 void UncustomizeEntitiesRun()
1888 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1889 self.uncustomizeentityforclient();
1892 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1894 e.customizeentityforclient = customizer;
1895 e.uncustomizeentityforclient = uncustomizer;
1896 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1900 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1903 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1907 if (e.classname == "")
1908 e.classname = "net_linked";
1910 if (e.model == "" || self.modelindex == 0)
1914 setmodel(e, "null");
1918 e.SendEntity = sendfunc;
1919 e.SendFlags = 0xFFFFFF;
1922 e.effects |= EF_NODEPTHTEST;
1926 e.nextthink = time + dt;
1927 e.think = SUB_Remove;
1931 void adaptor_think2touch()
1940 void adaptor_think2use()
1952 // deferred dropping
1953 void DropToFloor_Handler()
1955 droptofloor_builtin();
1956 self.dropped_origin = self.origin;
1961 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1966 float trace_hits_box_a0, trace_hits_box_a1;
1968 float trace_hits_box_1d(float end, float thmi, float thma)
1972 // just check if x is in range
1980 // do the trace with respect to x
1981 // 0 -> end has to stay in thmi -> thma
1982 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1983 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1984 if (trace_hits_box_a0 > trace_hits_box_a1)
1990 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1995 // now it is a trace from 0 to end
1997 trace_hits_box_a0 = 0;
1998 trace_hits_box_a1 = 1;
2000 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
2002 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
2004 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
2010 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
2012 return trace_hits_box(start, end, thmi - ma, thma - mi);
2015 float SUB_NoImpactCheck()
2017 // zero hitcontents = this is not the real impact, but either the
2018 // mirror-impact of something hitting the projectile instead of the
2019 // projectile hitting the something, or a touchareagrid one. Neither of
2020 // these stop the projectile from moving, so...
2021 if(trace_dphitcontents == 0)
2023 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
2026 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2028 if (other == world && self.size != '0 0 0')
2031 tic = self.velocity * sys_ticrate;
2032 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2033 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2034 if (trace_fraction >= 1)
2036 dprint("Odd... did not hit...?\n");
2038 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2040 dprint("Detected and prevented the sky-grapple bug.\n");
2048 #define SUB_OwnerCheck() (other && (other == self.owner))
2050 #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)
2052 float MAX_IPBAN_URIS = 16;
2054 float URI_GET_DISCARD = 0;
2055 float URI_GET_IPBAN = 1;
2056 float URI_GET_IPBAN_END = 16;
2058 void URI_Get_Callback(float id, float status, string data)
2060 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2062 dprint("\nEnd of data.\n");
2064 if (id == URI_GET_DISCARD)
2068 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2071 OnlineBanList_URI_Get_Callback(id, status, data);
2075 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2079 void print_to(entity e, string s)
2082 sprint(e, strcat(s, "\n"));
2101 for (i = 0; i < MapInfo_count; ++i)
2103 if (MapInfo_Get_ByID(i))
2105 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2108 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2109 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2117 for (i = 0; i < MapInfo_count; ++i)
2119 if (MapInfo_Get_ByID(i))
2121 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2124 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2125 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2133 for (i = 0; i < MapInfo_count; ++i)
2135 if (MapInfo_Get_ByID(i))
2137 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2140 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2141 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2147 MapInfo_ClearTemps();
2150 return "No records are available on this server.\n";
2152 return strcat("Records on this server:\n", s);
2155 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2158 vector start, org, delta, end, enddown, mstart;
2160 m = e.dphitcontentsmask;
2161 e.dphitcontentsmask = goodcontents | badcontents;
2164 delta = world.maxs - world.mins;
2166 for (i = 0; i < attempts; ++i)
2168 start_x = org_x + random() * delta_x;
2169 start_y = org_y + random() * delta_y;
2170 start_z = org_z + random() * delta_z;
2172 // rule 1: start inside world bounds, and outside
2173 // solid, and don't start from somewhere where you can
2174 // fall down to evil
2175 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2176 if (trace_fraction >= 1)
2178 if (trace_startsolid)
2180 if (trace_dphitcontents & badcontents)
2182 if (trace_dphitq3surfaceflags & badsurfaceflags)
2185 // rule 2: if we are too high, lower the point
2186 if (trace_fraction * delta_z > maxaboveground)
2187 start = trace_endpos + '0 0 1' * maxaboveground;
2188 enddown = trace_endpos;
2190 // rule 3: make sure we aren't outside the map. This only works
2191 // for somewhat well formed maps. A good rule of thumb is that
2192 // the map should have a convex outside hull.
2193 // these can be traceLINES as we already verified the starting box
2194 mstart = start + 0.5 * (e.mins + e.maxs);
2195 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2196 if (trace_fraction >= 1)
2198 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2199 if (trace_fraction >= 1)
2201 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2202 if (trace_fraction >= 1)
2204 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2205 if (trace_fraction >= 1)
2207 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2208 if (trace_fraction >= 1)
2211 // find a random vector to "look at"
2212 end_x = org_x + random() * delta_x;
2213 end_y = org_y + random() * delta_y;
2214 end_z = org_z + random() * delta_z;
2215 end = start + normalize(end - start) * vlen(delta);
2217 // rule 4: start TO end must not be too short
2218 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2219 if (trace_startsolid)
2221 if (trace_fraction < minviewdistance / vlen(delta))
2224 // rule 5: don't want to look at sky
2225 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2228 // rule 6: we must not end up in trigger_hurt
2229 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2231 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2238 e.dphitcontentsmask = m;
2242 setorigin(e, start);
2243 e.angles = vectoangles(end - start);
2244 dprint("Needed ", ftos(i + 1), " attempts\n");
2251 float zcurveparticles_effectno;
2252 vector zcurveparticles_start;
2253 float zcurveparticles_spd;
2255 void endzcurveparticles()
2257 if(zcurveparticles_effectno)
2260 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2262 zcurveparticles_effectno = 0;
2265 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2267 spd = bound(0, floor(spd / 16), 32767);
2268 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2270 endzcurveparticles();
2271 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2272 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2273 WriteShort(MSG_BROADCAST, effectno);
2274 WriteCoord(MSG_BROADCAST, start_x);
2275 WriteCoord(MSG_BROADCAST, start_y);
2276 WriteCoord(MSG_BROADCAST, start_z);
2277 zcurveparticles_effectno = effectno;
2278 zcurveparticles_start = start;
2281 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2282 WriteCoord(MSG_BROADCAST, end_x);
2283 WriteCoord(MSG_BROADCAST, end_y);
2284 WriteCoord(MSG_BROADCAST, end_z);
2285 WriteCoord(MSG_BROADCAST, end_dz);
2286 zcurveparticles_spd = spd;
2289 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2292 vector vecxy, velxy;
2294 vecxy = end - start;
2299 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2301 endzcurveparticles();
2302 trailparticles(world, effectno, start, end);
2306 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2307 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2310 string GetGametype(); // g_world.qc
2311 void write_recordmarker(entity pl, float tstart, float dt)
2313 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2315 // also write a marker into demo files for demotc-race-record-extractor to find
2318 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2319 " ", ftos(tstart), " ", ftos(dt), "\n"));
2322 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2324 switch(self.owner.cvar_cl_gunalign)
2335 if(allowcenter) // 2: allow center handedness
2348 if(allowcenter) // 2: allow center handedness
2364 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2369 if (cvar("g_shootfromeye"))
2373 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2381 else if (cvar("g_shootfromcenter"))
2385 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2393 else if (cvar("g_shootfromclient"))
2395 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2397 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2412 void attach_sameorigin(entity e, entity to, string tag)
2414 vector org, t_forward, t_left, t_up, e_forward, e_up;
2421 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2422 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2423 t_forward = v_forward * tagscale;
2424 t_left = v_right * -tagscale;
2425 t_up = v_up * tagscale;
2427 e.origin_x = org * t_forward;
2428 e.origin_y = org * t_left;
2429 e.origin_z = org * t_up;
2431 // current forward and up directions
2432 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2433 e.angles_x = -e.angles_x;
2434 fixedmakevectors(e.angles);
2436 // untransform forward, up!
2437 e_forward_x = v_forward * t_forward;
2438 e_forward_y = v_forward * t_left;
2439 e_forward_z = v_forward * t_up;
2440 e_up_x = v_up * t_forward;
2441 e_up_y = v_up * t_left;
2442 e_up_z = v_up * t_up;
2444 e.angles = fixedvectoangles2(e_forward, e_up);
2445 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2446 e.angles_x = -e.angles_x;
2448 setattachment(e, to, tag);
2449 setorigin(e, e.origin);
2452 void detach_sameorigin(entity e)
2455 org = gettaginfo(e, 0);
2456 e.angles = fixedvectoangles2(v_forward, v_up);
2457 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2458 e.angles_x = -e.angles_x;
2460 setattachment(e, world, "");
2461 setorigin(e, e.origin);
2464 void follow_sameorigin(entity e, entity to)
2466 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2467 e.aiment = to; // make the hole follow bmodel
2468 e.punchangle = to.angles; // the original angles of bmodel
2469 e.view_ofs = e.origin - to.origin; // relative origin
2470 e.v_angle = e.angles - to.angles; // relative angles
2473 void unfollow_sameorigin(entity e)
2475 e.movetype = MOVETYPE_NONE;
2478 entity gettaginfo_relative_ent;
2479 vector gettaginfo_relative(entity e, float tag)
2481 if (!gettaginfo_relative_ent)
2483 gettaginfo_relative_ent = spawn();
2484 gettaginfo_relative_ent.effects = EF_NODRAW;
2486 gettaginfo_relative_ent.model = e.model;
2487 gettaginfo_relative_ent.modelindex = e.modelindex;
2488 gettaginfo_relative_ent.frame = e.frame;
2489 return gettaginfo(gettaginfo_relative_ent, tag);
2492 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2496 if (pl.soundentity.cnt & p)
2498 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2499 pl.soundentity.cnt |= p;
2502 void SoundEntity_StopSound(entity pl, float chan)
2506 if (pl.soundentity.cnt & p)
2508 stopsoundto(MSG_ALL, pl.soundentity, chan);
2509 pl.soundentity.cnt &~= p;
2513 void SoundEntity_Attach(entity pl)
2515 pl.soundentity = spawn();
2516 pl.soundentity.classname = "soundentity";
2517 pl.soundentity.owner = pl;
2518 setattachment(pl.soundentity, pl, "");
2519 setmodel(pl.soundentity, "null");
2522 void SoundEntity_Detach(entity pl)
2525 for (i = 0; i <= 7; ++i)
2526 SoundEntity_StopSound(pl, i);
2530 float ParseCommandPlayerSlotTarget_firsttoken;
2531 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2539 ParseCommandPlayerSlotTarget_firsttoken = -1;
2543 if (substring(argv(idx), 0, 1) == "#")
2545 s = substring(argv(idx), 1, -1);
2553 ParseCommandPlayerSlotTarget_firsttoken = idx;
2554 if (s == ftos(stof(s)))
2556 e = edict_num(stof(s));
2557 if (e.flags & FL_CLIENT)
2563 // it must be a nick name
2566 ParseCommandPlayerSlotTarget_firsttoken = idx;
2569 FOR_EACH_CLIENT(head)
2570 if (head.netname == s)
2578 s = strdecolorize(s);
2580 FOR_EACH_CLIENT(head)
2581 if (strdecolorize(head.netname) == s)
2596 float modeleffect_SendEntity(entity to, float sf)
2599 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2602 if(self.velocity != '0 0 0')
2604 if(self.angles != '0 0 0')
2606 if(self.avelocity != '0 0 0')
2609 WriteByte(MSG_ENTITY, f);
2610 WriteShort(MSG_ENTITY, self.modelindex);
2611 WriteByte(MSG_ENTITY, self.skin);
2612 WriteByte(MSG_ENTITY, self.frame);
2613 WriteCoord(MSG_ENTITY, self.origin_x);
2614 WriteCoord(MSG_ENTITY, self.origin_y);
2615 WriteCoord(MSG_ENTITY, self.origin_z);
2618 WriteCoord(MSG_ENTITY, self.velocity_x);
2619 WriteCoord(MSG_ENTITY, self.velocity_y);
2620 WriteCoord(MSG_ENTITY, self.velocity_z);
2624 WriteCoord(MSG_ENTITY, self.angles_x);
2625 WriteCoord(MSG_ENTITY, self.angles_y);
2626 WriteCoord(MSG_ENTITY, self.angles_z);
2630 WriteCoord(MSG_ENTITY, self.avelocity_x);
2631 WriteCoord(MSG_ENTITY, self.avelocity_y);
2632 WriteCoord(MSG_ENTITY, self.avelocity_z);
2634 WriteShort(MSG_ENTITY, self.scale * 256.0);
2635 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2636 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2637 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2638 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2643 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)
2648 e.classname = "modeleffect";
2656 e.teleport_time = t1;
2660 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2664 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2667 sz = max(e.scale, e.scale2);
2668 setsize(e, e.mins * sz, e.maxs * sz);
2669 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2672 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2674 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2677 float randombit(float bits)
2679 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2688 for(f = 1; f <= bits; f *= 2)
2697 r = (r - 1) / (n - 1);
2704 float randombits(float bits, float k, float error_return)
2708 while(k > 0 && bits != r)
2710 r += randombit(bits - r);
2719 void randombit_test(float bits, float iter)
2723 print(ftos(randombit(bits)), "\n");
2728 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2730 if(halflifedist > 0)
2731 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2732 else if(halflifedist < 0)
2733 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);