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 (g_ctf || g_assault || g_onslaught || g_domination || g_nexball)
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 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
495 p = p + strlen(replacement);
506 >0: receives a cvar from name=argv(f) value=argv(f+1)
508 void GetCvars_handleString(string thisname, float f, .string field, string name)
513 strunzone(self.field);
514 self.field = string_null;
518 if (thisname == name)
521 strunzone(self.field);
522 self.field = strzone(argv(f + 1));
526 stuffcmd(self, strcat("sendcvar ", name, "\n"));
528 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
530 GetCvars_handleString(thisname, f, field, name);
531 if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
532 if (thisname == name)
535 s = func(strcat1(self.field));
538 strunzone(self.field);
539 self.field = strzone(s);
543 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
550 if (thisname == name)
551 self.field = stof(argv(f + 1));
554 stuffcmd(self, strcat("sendcvar ", name, "\n"));
556 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
563 if (thisname == name)
567 self.field = stof(argv(f + 1));
576 stuffcmd(self, strcat("sendcvar ", name, "\n"));
579 string W_FixWeaponOrder_ForceComplete(string s);
580 string W_FixWeaponOrder_AllowIncomplete(string s);
581 float w_getbestweapon(entity e);
582 void GetCvars(float f)
586 s = strcat1(argv(f));
587 GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
588 GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
589 GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
590 GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
591 GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
592 GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
593 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
594 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
595 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
596 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
597 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
598 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
599 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
600 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
601 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
602 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
603 GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
604 GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
605 GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
606 GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
607 GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
608 #ifdef ALLOW_FORCEMODELS
609 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
610 GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
612 GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
613 GetCvars_handleFloatOnce(s, f, cvar_cl_noantilag, "cl_noantilag");
616 // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
619 if (s == "cl_weaponpriority")
620 self.switchweapon = w_getbestweapon(self);
624 float fexists(string f)
627 fh = fopen(f, FILE_READ);
634 void backtrace(string msg)
637 dev = cvar("developer");
638 war = cvar("prvm_backtraceforwarnings");
639 cvar_set("developer", "1");
640 cvar_set("prvm_backtraceforwarnings", "1");
642 dprint("--- CUT HERE ---\nWARNING: ");
645 remove(world); // isn't there any better way to cause a backtrace?
646 dprint("\n--- CUT UNTIL HERE ---\n");
647 cvar_set("developer", ftos(dev));
648 cvar_set("prvm_backtraceforwarnings", ftos(war));
651 string Team_ColorCode(float teamid)
653 if (teamid == COLOR_TEAM1)
655 else if (teamid == COLOR_TEAM2)
657 else if (teamid == COLOR_TEAM3)
659 else if (teamid == COLOR_TEAM4)
665 string Team_ColorName(float t)
667 // fixme: Search for team entities and get their .netname's!
668 if (t == COLOR_TEAM1)
670 if (t == COLOR_TEAM2)
672 if (t == COLOR_TEAM3)
674 if (t == COLOR_TEAM4)
679 string Team_ColorNameLowerCase(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 #define CENTERPRIO_POINT 1
694 #define CENTERPRIO_SPAM 2
695 #define CENTERPRIO_VOTE 4
696 #define CENTERPRIO_NORMAL 5
697 #define CENTERPRIO_SHIELDING 7
698 #define CENTERPRIO_MAPVOTE 9
699 #define CENTERPRIO_IDLEKICK 50
700 #define CENTERPRIO_ADMIN 99
701 .float centerprint_priority;
702 .float centerprint_expires;
703 void centerprint_atprio(entity e, float prio, string s)
705 if (intermission_running)
706 if (prio < CENTERPRIO_MAPVOTE)
708 if (time > e.centerprint_expires)
709 e.centerprint_priority = 0;
710 if (prio >= e.centerprint_priority)
712 e.centerprint_priority = prio;
713 if (timeoutStatus == 2)
714 e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
716 e.centerprint_expires = time + e.cvar_scr_centertime;
717 centerprint_builtin(e, s);
720 void centerprint_expire(entity e, float prio)
722 if (prio == e.centerprint_priority)
724 e.centerprint_priority = 0;
725 centerprint_builtin(e, "");
728 void centerprint(entity e, string s)
730 centerprint_atprio(e, CENTERPRIO_NORMAL, s);
733 // decolorizes and team colors the player name when needed
734 string playername(entity p)
737 if (teams_matter && !intermission_running && p.classname == "player")
739 t = Team_ColorCode(p.team);
740 return strcat(t, strdecolorize(p.netname));
746 vector randompos(vector m1, vector m2)
750 v_x = m2_x * random() + m1_x;
751 v_y = m2_y * random() + m1_y;
752 v_z = m2_z * random() + m1_z;
756 float g_pickup_shells;
757 float g_pickup_shells_max;
758 float g_pickup_nails;
759 float g_pickup_nails_max;
760 float g_pickup_rockets;
761 float g_pickup_rockets_max;
762 float g_pickup_cells;
763 float g_pickup_cells_max;
765 float g_pickup_fuel_jetpack;
766 float g_pickup_fuel_max;
767 float g_pickup_armorsmall;
768 float g_pickup_armorsmall_max;
769 float g_pickup_armormedium;
770 float g_pickup_armormedium_max;
771 float g_pickup_armorbig;
772 float g_pickup_armorbig_max;
773 float g_pickup_armorlarge;
774 float g_pickup_armorlarge_max;
775 float g_pickup_healthsmall;
776 float g_pickup_healthsmall_max;
777 float g_pickup_healthmedium;
778 float g_pickup_healthmedium_max;
779 float g_pickup_healthlarge;
780 float g_pickup_healthlarge_max;
781 float g_pickup_healthmega;
782 float g_pickup_healthmega_max;
784 float g_weaponarena_random;
785 string g_weaponarena_list;
786 float g_weaponspeedfactor;
787 float g_weaponratefactor;
788 float g_weapondamagefactor;
789 float g_weaponforcefactor;
793 float start_ammo_shells;
794 float start_ammo_nails;
795 float start_ammo_rockets;
796 float start_ammo_cells;
797 float start_ammo_fuel;
799 float start_armorvalue;
800 float warmup_start_weapons;
801 float warmup_start_ammo_shells;
802 float warmup_start_ammo_nails;
803 float warmup_start_ammo_rockets;
804 float warmup_start_ammo_cells;
805 float warmup_start_ammo_fuel;
806 float warmup_start_health;
807 float warmup_start_armorvalue;
811 entity get_weaponinfo(float w);
813 float NixNex_CanChooseWeapon(float wpn);
814 void readplayerstartcvars()
820 // initialize starting values for players
823 start_ammo_shells = 0;
824 start_ammo_nails = 0;
825 start_ammo_rockets = 0;
826 start_ammo_cells = 0;
827 start_health = cvar("g_balance_health_start");
828 start_armorvalue = cvar("g_balance_armor_start");
831 s = cvar_string("g_weaponarena");
837 g_weaponarena_list = "All Weapons";
838 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
840 e = get_weaponinfo(j);
841 g_weaponarena |= e.weapons;
842 weapon_action(e.weapon, WR_PRECACHE);
845 else if (s == "most")
847 g_weaponarena_list = "Most Weapons";
848 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
850 e = get_weaponinfo(j);
851 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
853 g_weaponarena |= e.weapons;
854 weapon_action(e.weapon, WR_PRECACHE);
858 else if (s == "none")
860 g_weaponarena_list = "No Weapons";
861 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
865 t = tokenize_console(s);
866 g_weaponarena_list = "";
867 for (i = 0; i < t; ++i)
870 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
872 e = get_weaponinfo(j);
875 g_weaponarena |= e.weapons;
876 weapon_action(e.weapon, WR_PRECACHE);
877 g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
883 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
886 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
890 g_weaponarena_random = cvar("g_weaponarena_random");
892 g_weaponarena_random = 0;
897 // will be done later
898 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
899 if (NixNex_CanChooseWeapon(i))
900 weapon_action(i, WR_PRECACHE);
901 if(!cvar("g_use_ammunition"))
902 start_items |= IT_UNLIMITED_AMMO;
904 else if (g_weaponarena)
906 start_weapons = g_weaponarena;
907 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
908 start_ammo_rockets = 999;
909 if (g_weaponarena & WEPBIT_SHOTGUN)
910 start_ammo_shells = 999;
911 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
912 start_ammo_cells = 999;
913 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
914 start_ammo_nails = 999;
915 if (g_weaponarena & WEPBIT_HOOK)
916 start_ammo_fuel = 999;
917 start_items |= IT_UNLIMITED_AMMO;
919 else if (g_minstagib)
922 start_armorvalue = 0;
923 start_weapons = WEPBIT_MINSTANEX;
924 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
925 start_ammo_cells = cvar("g_minstagib_ammo_start");
926 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
927 start_ammo_fuel = cvar("g_start_ammo_fuel");
929 if (g_minstagib_invis_alpha <= 0)
930 g_minstagib_invis_alpha = -1;
936 start_ammo_shells = cvar("g_lms_start_ammo_shells");
937 start_ammo_nails = cvar("g_lms_start_ammo_nails");
938 start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
939 start_ammo_cells = cvar("g_lms_start_ammo_cells");
940 start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
941 start_health = cvar("g_lms_start_health");
942 start_armorvalue = cvar("g_lms_start_armor");
944 else if (cvar("g_use_ammunition"))
946 start_ammo_shells = cvar("g_start_ammo_shells");
947 start_ammo_nails = cvar("g_start_ammo_nails");
948 start_ammo_rockets = cvar("g_start_ammo_rockets");
949 start_ammo_cells = cvar("g_start_ammo_cells");
950 start_ammo_fuel = cvar("g_start_ammo_fuel");
954 start_ammo_shells = cvar("g_pickup_shells_max");
955 start_ammo_nails = cvar("g_pickup_nails_max");
956 start_ammo_rockets = cvar("g_pickup_rockets_max");
957 start_ammo_cells = cvar("g_pickup_cells_max");
958 start_ammo_fuel = cvar("g_pickup_fuel_max");
959 start_items |= IT_UNLIMITED_AMMO;
962 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
964 e = get_weaponinfo(i);
968 t = cvar(strcat("g_start_weapon_", e.netname));
970 if (t < 0) // "default" weapon selection
973 t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
974 else if (g_race || g_cts)
975 t = (i == WEP_LASER);
977 t = 0; // weapon is set a few lines later
979 t = (i == WEP_LASER || i == WEP_SHOTGUN);
980 if (g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
981 t += (i == WEP_HOOK);
984 if (g_nexball && i == WEP_PORTO)
989 start_weapons |= e.weapons;
990 weapon_action(e.weapon, WR_PRECACHE);
997 warmup_start_ammo_shells = start_ammo_shells;
998 warmup_start_ammo_nails = start_ammo_nails;
999 warmup_start_ammo_rockets = start_ammo_rockets;
1000 warmup_start_ammo_cells = start_ammo_cells;
1001 warmup_start_ammo_fuel = start_ammo_fuel;
1002 warmup_start_health = start_health;
1003 warmup_start_armorvalue = start_armorvalue;
1004 warmup_start_weapons = start_weapons;
1006 if (!g_weaponarena && !g_nixnex && !g_minstagib)
1008 if (cvar("g_use_ammunition"))
1010 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1011 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1012 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1013 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1014 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1016 warmup_start_health = cvar("g_warmup_start_health");
1017 warmup_start_armorvalue = cvar("g_warmup_start_armor");
1018 if (cvar("g_warmup_allguns"))
1020 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1022 e = get_weaponinfo(i);
1025 if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
1027 warmup_start_weapons |= e.weapons;
1028 weapon_action(e.weapon, WR_PRECACHE);
1035 if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1037 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1038 start_items |= IT_FUEL_REGEN;
1039 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1040 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1044 start_items |= IT_JETPACK;
1046 if (g_weapon_stay == 2)
1048 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1049 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1050 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1051 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1052 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1053 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1054 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1055 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1056 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1057 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1060 start_ammo_shells = max(0, start_ammo_shells);
1061 start_ammo_nails = max(0, start_ammo_nails);
1062 start_ammo_cells = max(0, start_ammo_cells);
1063 start_ammo_rockets = max(0, start_ammo_rockets);
1064 start_ammo_fuel = max(0, start_ammo_fuel);
1066 warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1067 warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1068 warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1069 warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1070 warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1074 float g_bugrigs_planar_movement;
1075 float g_bugrigs_planar_movement_car_jumping;
1076 float g_bugrigs_reverse_spinning;
1077 float g_bugrigs_reverse_speeding;
1078 float g_bugrigs_reverse_stopping;
1079 float g_bugrigs_air_steering;
1080 float g_bugrigs_angle_smoothing;
1081 float g_bugrigs_friction_floor;
1082 float g_bugrigs_friction_brake;
1083 float g_bugrigs_friction_air;
1084 float g_bugrigs_accel;
1085 float g_bugrigs_speed_ref;
1086 float g_bugrigs_speed_pow;
1087 float g_bugrigs_steer;
1089 float g_touchexplode;
1090 float g_touchexplode_radius;
1091 float g_touchexplode_damage;
1092 float g_touchexplode_edgedamage;
1093 float g_touchexplode_force;
1100 float sv_pitch_fixyaw;
1102 void readlevelcvars(void)
1104 g_bugrigs = cvar("g_bugrigs");
1105 g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1106 g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1107 g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1108 g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1109 g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1110 g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1111 g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1112 g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1113 g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1114 g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1115 g_bugrigs_accel = cvar("g_bugrigs_accel");
1116 g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1117 g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1118 g_bugrigs_steer = cvar("g_bugrigs_steer");
1120 g_touchexplode = cvar("g_touchexplode");
1121 g_touchexplode_radius = cvar("g_touchexplode_radius");
1122 g_touchexplode_damage = cvar("g_touchexplode_damage");
1123 g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1124 g_touchexplode_force = cvar("g_touchexplode_force");
1126 #ifdef ALLOW_FORCEMODELS
1127 sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1129 sv_loddistance1 = cvar("sv_loddistance1");
1130 sv_loddistance2 = cvar("sv_loddistance2");
1131 if(sv_loddistance2 <= sv_loddistance1)
1132 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1133 sv_clones = cvar("sv_clones");
1134 sv_cheats = cvar("sv_cheats");
1135 sv_gentle = cvar("sv_gentle");
1136 sv_foginterval = cvar("sv_foginterval");
1137 g_cloaked = cvar("g_cloaked");
1138 g_jump_grunt = cvar("g_jump_grunt");
1139 g_footsteps = cvar("g_footsteps");
1140 g_grappling_hook = cvar("g_grappling_hook");
1141 g_jetpack = cvar("g_jetpack");
1142 g_laserguided_missile = cvar("g_laserguided_missile");
1143 g_midair = cvar("g_midair");
1144 g_minstagib = cvar("g_minstagib");
1145 g_nixnex = cvar("g_nixnex");
1146 g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1147 g_norecoil = cvar("g_norecoil");
1148 g_vampire = cvar("g_vampire");
1149 g_bloodloss = cvar("g_bloodloss");
1150 sv_maxidle = cvar("sv_maxidle");
1151 sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1152 sv_pogostick = cvar("sv_pogostick");
1153 sv_doublejump = cvar("sv_doublejump");
1154 g_ctf_reverse = cvar("g_ctf_reverse");
1155 sv_autotaunt = cvar("sv_autotaunt");
1156 sv_taunt = cvar("sv_taunt");
1158 inWarmupStage = cvar("g_warmup");
1159 g_warmup_limit = cvar("g_warmup_limit");
1160 g_warmup_allguns = cvar("g_warmup_allguns");
1161 g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1163 if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1164 inWarmupStage = 0; // these modes cannot work together, sorry
1166 g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1167 g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1168 g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1169 g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1170 g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1171 g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1172 g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1173 g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1174 g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1175 g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1176 g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1177 g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1179 if (g_minstagib) g_nixnex = g_weaponarena = 0;
1180 if (g_nixnex) g_weaponarena = 0;
1183 g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1184 g_weaponratefactor = cvar("g_weaponratefactor");
1185 g_weapondamagefactor = cvar("g_weapondamagefactor");
1186 g_weaponforcefactor = cvar("g_weaponforcefactor");
1188 g_pickup_shells = cvar("g_pickup_shells");
1189 g_pickup_shells_max = cvar("g_pickup_shells_max");
1190 g_pickup_nails = cvar("g_pickup_nails");
1191 g_pickup_nails_max = cvar("g_pickup_nails_max");
1192 g_pickup_rockets = cvar("g_pickup_rockets");
1193 g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1194 g_pickup_cells = cvar("g_pickup_cells");
1195 g_pickup_cells_max = cvar("g_pickup_cells_max");
1196 g_pickup_fuel = cvar("g_pickup_fuel");
1197 g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1198 g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1199 g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1200 g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1201 g_pickup_armormedium = cvar("g_pickup_armormedium");
1202 g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1203 g_pickup_armorbig = cvar("g_pickup_armorbig");
1204 g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1205 g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1206 g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1207 g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1208 g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1209 g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1210 g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1211 g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1212 g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1213 g_pickup_healthmega = cvar("g_pickup_healthmega");
1214 g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1216 g_pinata = cvar("g_pinata");
1218 g_weapon_stay = cvar("g_weapon_stay");
1219 if (!g_weapon_stay && (cvar("deathmatch") == 2))
1221 g_ghost_items = cvar("g_ghost_items");
1222 if(g_ghost_items >= 1)
1223 g_ghost_items = 0.13; // default alpha value
1225 if not(inWarmupStage)
1226 game_starttime = cvar("g_start_delay");
1228 sv_pitch_min = cvar("sv_pitch_min");
1229 sv_pitch_max = cvar("sv_pitch_max");
1230 sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1232 readplayerstartcvars();
1236 // TODO sound pack system
1239 string precache_sound_builtin (string s) = #19;
1240 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1241 string precache_sound(string s)
1243 return precache_sound_builtin(strcat(soundpack, s));
1245 void play2(entity e, string filename)
1247 stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1249 void sound(entity e, float chan, string samp, float vol, float atten)
1251 sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1256 string precache_sound (string s) = #19;
1257 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1258 float precache_sound_index (string s) = #19;
1260 #define SND_VOLUME 1
1261 #define SND_ATTENUATION 2
1262 #define SND_LARGEENTITY 8
1263 #define SND_LARGESOUND 16
1265 float sound_allowed(float dest, entity e)
1267 // sounds from world may always pass
1270 if (e.classname == "body")
1272 if (e.owner && e.owner != e)
1277 // sounds to self may always pass
1278 if (dest == MSG_ONE)
1279 if (e == msg_entity)
1281 // sounds by players can be removed
1282 if (cvar("bot_sound_monopoly"))
1283 if (clienttype(e) == CLIENTTYPE_REAL)
1285 // anything else may pass
1289 void sound(entity e, float chan, string samp, float vol, float atten)
1291 if (!sound_allowed(MSG_BROADCAST, e))
1293 sound_builtin(e, chan, samp, vol, atten);
1295 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1299 if (!sound_allowed(dest, e))
1302 entno = num_for_edict(e);
1303 idx = precache_sound_index(samp);
1308 atten = floor(atten * 64);
1309 vol = floor(vol * 255);
1312 sflags |= SND_VOLUME;
1314 sflags |= SND_ATTENUATION;
1316 sflags |= SND_LARGEENTITY;
1318 sflags |= SND_LARGESOUND;
1320 WriteByte(dest, SVC_SOUND);
1321 WriteByte(dest, sflags);
1322 if (sflags & SND_VOLUME)
1323 WriteByte(dest, vol);
1324 if (sflags & SND_ATTENUATION)
1325 WriteByte(dest, atten);
1326 if (sflags & SND_LARGEENTITY)
1328 WriteShort(dest, entno);
1329 WriteByte(dest, chan);
1333 WriteShort(dest, entno * 8 + chan);
1335 if (sflags & SND_LARGESOUND)
1336 WriteShort(dest, idx);
1338 WriteByte(dest, idx);
1340 WriteCoord(dest, o_x);
1341 WriteCoord(dest, o_y);
1342 WriteCoord(dest, o_z);
1344 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1348 if (!sound_allowed(dest, e))
1351 o = e.origin + 0.5 * (e.mins + e.maxs);
1352 soundtoat(dest, e, o, chan, samp, vol, atten);
1354 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1356 soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1358 void stopsoundto(float dest, entity e, float chan)
1362 if (!sound_allowed(dest, e))
1365 entno = num_for_edict(e);
1370 idx = precache_sound_index("misc/null.wav");
1371 sflags = SND_LARGEENTITY;
1373 sflags |= SND_LARGESOUND;
1374 WriteByte(dest, SVC_SOUND);
1375 WriteByte(dest, sflags);
1376 WriteShort(dest, entno);
1377 WriteByte(dest, chan);
1378 if (sflags & SND_LARGESOUND)
1379 WriteShort(dest, idx);
1381 WriteByte(dest, idx);
1382 WriteCoord(dest, e.origin_x);
1383 WriteCoord(dest, e.origin_y);
1384 WriteCoord(dest, e.origin_z);
1388 WriteByte(dest, SVC_STOPSOUND);
1389 WriteShort(dest, entno * 8 + chan);
1392 void stopsound(entity e, float chan)
1394 if (!sound_allowed(MSG_BROADCAST, e))
1397 stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1398 stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1401 void play2(entity e, string filename)
1403 //stuffcmd(e, strcat("play2 ", filename, "\n"));
1405 soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1408 .float announcetime;
1409 float announce(entity player, string msg)
1411 if (time > player.announcetime)
1412 if (clienttype(player) == CLIENTTYPE_REAL)
1414 player.announcetime = time + 0.8;
1420 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1421 float spamsound(entity e, float chan, string samp, float vol, float atten)
1423 if (!sound_allowed(MSG_BROADCAST, e))
1426 if (time > e.announcetime)
1428 e.announcetime = time;
1429 sound(e, chan, samp, vol, atten);
1435 void play2team(float t, string filename)
1439 if (cvar("bot_sound_monopoly"))
1442 FOR_EACH_REALPLAYER(head)
1445 play2(head, filename);
1449 void play2all(string samp)
1451 if (cvar("bot_sound_monopoly"))
1454 sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1457 void PrecachePlayerSounds(string f);
1458 void precache_all_models(string pattern)
1460 float globhandle, i, n;
1463 globhandle = search_begin(pattern, TRUE, FALSE);
1466 n = search_getsize(globhandle);
1467 for (i = 0; i < n; ++i)
1469 //print(search_getfilename(globhandle, i), "\n");
1470 f = search_getfilename(globhandle, i);
1473 if(substring(f, -9,5) == "_lod1")
1475 if(substring(f, -9,5) == "_lod2")
1477 if(!sv_loddistance1)
1479 PrecachePlayerSounds(strcat(f, ".sounds"));
1481 search_end(globhandle);
1486 // gamemode related things
1487 precache_model ("models/misc/chatbubble.spr");
1488 precache_model ("models/misc/teambubble.spr");
1491 precache_model ("models/runematch/curse.mdl");
1492 precache_model ("models/runematch/rune.mdl");
1495 #ifdef TTURRETS_ENABLED
1496 if (cvar("g_turrets"))
1500 // Precache all player models if desired
1501 if (cvar("sv_precacheplayermodels"))
1503 PrecachePlayerSounds("sound/player/default.sounds");
1504 precache_all_models("models/player/*.zym");
1505 precache_all_models("models/player/*.dpm");
1506 precache_all_models("models/player/*.md3");
1507 precache_all_models("models/player/*.psk");
1508 //precache_model("models/player/carni.zym");
1509 //precache_model("models/player/crash.zym");
1510 //precache_model("models/player/grunt.zym");
1511 //precache_model("models/player/headhunter.zym");
1512 //precache_model("models/player/insurrectionist.zym");
1513 //precache_model("models/player/jeandarc.zym");
1514 //precache_model("models/player/lurk.zym");
1515 //precache_model("models/player/lycanthrope.zym");
1516 //precache_model("models/player/marine.zym");
1517 //precache_model("models/player/nexus.zym");
1518 //precache_model("models/player/pyria.zym");
1519 //precache_model("models/player/shock.zym");
1520 //precache_model("models/player/skadi.zym");
1521 //precache_model("models/player/specop.zym");
1522 //precache_model("models/player/visitant.zym");
1525 if (cvar("sv_defaultcharacter"))
1528 s = cvar_string("sv_defaultplayermodel_red");
1532 PrecachePlayerSounds(strcat(s, ".sounds"));
1534 s = cvar_string("sv_defaultplayermodel_blue");
1538 PrecachePlayerSounds(strcat(s, ".sounds"));
1540 s = cvar_string("sv_defaultplayermodel_yellow");
1544 PrecachePlayerSounds(strcat(s, ".sounds"));
1546 s = cvar_string("sv_defaultplayermodel_pink");
1550 PrecachePlayerSounds(strcat(s, ".sounds"));
1552 s = cvar_string("sv_defaultplayermodel");
1556 PrecachePlayerSounds(strcat(s, ".sounds"));
1562 PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1563 PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1566 // gore and miscellaneous sounds
1567 //precache_sound ("misc/h2ohit.wav");
1568 precache_model ("models/hook.md3");
1569 precache_sound ("misc/armorimpact.wav");
1570 precache_sound ("misc/bodyimpact1.wav");
1571 precache_sound ("misc/bodyimpact2.wav");
1572 precache_sound ("misc/gib.wav");
1573 precache_sound ("misc/gib_splat01.wav");
1574 precache_sound ("misc/gib_splat02.wav");
1575 precache_sound ("misc/gib_splat03.wav");
1576 precache_sound ("misc/gib_splat04.wav");
1577 precache_sound ("misc/hit.wav");
1578 PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1579 PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1580 precache_sound ("misc/null.wav");
1581 precache_sound ("misc/spawn.wav");
1582 precache_sound ("misc/talk.wav");
1583 precache_sound ("misc/teleport.wav");
1584 precache_sound ("misc/poweroff.wav");
1585 precache_sound ("player/lava.wav");
1586 precache_sound ("player/slime.wav");
1589 precache_sound ("misc/jetpack_fly.wav");
1591 // announcer sounds - male
1592 precache_sound ("announcer/male/electrobitch.wav");
1593 precache_sound ("announcer/male/airshot.wav");
1594 precache_sound ("announcer/male/03kills.wav");
1595 precache_sound ("announcer/male/05kills.wav");
1596 precache_sound ("announcer/male/10kills.wav");
1597 precache_sound ("announcer/male/15kills.wav");
1598 precache_sound ("announcer/male/20kills.wav");
1599 precache_sound ("announcer/male/25kills.wav");
1600 precache_sound ("announcer/male/30kills.wav");
1601 precache_sound ("announcer/male/botlike.wav");
1602 precache_sound ("announcer/male/yoda.wav");
1603 precache_sound ("announcer/male/amazing.wav");
1604 precache_sound ("announcer/male/awesome.wav");
1605 precache_sound ("announcer/male/headshot.wav");
1606 precache_sound ("announcer/male/impressive.wav");
1608 // announcer sounds - robotic
1609 precache_sound ("announcer/robotic/prepareforbattle.wav");
1610 precache_sound ("announcer/robotic/begin.wav");
1611 precache_sound ("announcer/robotic/timeoutcalled.wav");
1612 precache_sound ("announcer/robotic/1fragleft.wav");
1613 precache_sound ("announcer/robotic/2fragsleft.wav");
1614 precache_sound ("announcer/robotic/3fragsleft.wav");
1615 precache_sound ("announcer/robotic/terminated.wav");
1618 precache_sound ("announcer/robotic/lastsecond.wav");
1619 precache_sound ("announcer/robotic/narrowly.wav");
1622 precache_model ("models/sprites/0.spr32");
1623 precache_model ("models/sprites/1.spr32");
1624 precache_model ("models/sprites/2.spr32");
1625 precache_model ("models/sprites/3.spr32");
1626 precache_model ("models/sprites/4.spr32");
1627 precache_model ("models/sprites/5.spr32");
1628 precache_model ("models/sprites/6.spr32");
1629 precache_model ("models/sprites/7.spr32");
1630 precache_model ("models/sprites/8.spr32");
1631 precache_model ("models/sprites/9.spr32");
1632 precache_model ("models/sprites/10.spr32");
1633 precache_sound ("announcer/robotic/1.wav");
1634 precache_sound ("announcer/robotic/2.wav");
1635 precache_sound ("announcer/robotic/3.wav");
1636 precache_sound ("announcer/robotic/4.wav");
1637 precache_sound ("announcer/robotic/5.wav");
1638 precache_sound ("announcer/robotic/6.wav");
1639 precache_sound ("announcer/robotic/7.wav");
1640 precache_sound ("announcer/robotic/8.wav");
1641 precache_sound ("announcer/robotic/9.wav");
1642 precache_sound ("announcer/robotic/10.wav");
1644 // common weapon precaches
1645 precache_sound ("weapons/weapon_switch.wav");
1646 precache_sound ("weapons/weaponpickup.wav");
1647 precache_sound ("weapons/unavailable.wav");
1648 if (g_grappling_hook)
1650 precache_sound ("weapons/hook_fire.wav"); // hook
1651 precache_sound ("weapons/hook_impact.wav"); // hook
1654 if (cvar("sv_precacheweapons") || g_nixnex)
1656 //precache weapon models/sounds
1659 while (wep <= WEP_LAST)
1661 weapon_action(wep, WR_PRECACHE);
1666 precache_model("models/elaser.mdl");
1667 precache_model("models/laser.mdl");
1668 precache_model("models/ebomb.mdl");
1671 // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1673 if (!self.noise && self.music) // quake 3 uses the music field
1674 self.noise = self.music;
1676 // plays music for the level if there is any
1679 precache_sound (self.noise);
1680 ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1685 // sorry, but using \ in macros breaks line numbers
1686 #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
1687 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1688 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1690 vector ExactTriggerHit_mins;
1691 vector ExactTriggerHit_maxs;
1692 float ExactTriggerHit_Recurse()
1698 tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1701 if (trace_ent == self)
1706 se.solid = SOLID_NOT;
1707 f = ExactTriggerHit_Recurse();
1713 float ExactTriggerHit()
1717 if not(self.modelindex)
1721 self.solid = SOLID_BSP;
1722 ExactTriggerHit_mins = other.absmin;
1723 ExactTriggerHit_maxs = other.absmax;
1724 f = ExactTriggerHit_Recurse();
1730 // WARNING: this kills the trace globals
1731 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1732 #define EXACTTRIGGER_INIT InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1734 #define INITPRIO_FIRST 0
1735 #define INITPRIO_GAMETYPE 0
1736 #define INITPRIO_GAMETYPE_FALLBACK 1
1737 #define INITPRIO_CVARS 5
1738 #define INITPRIO_FINDTARGET 10
1739 #define INITPRIO_DROPTOFLOOR 20
1740 #define INITPRIO_SETLOCATION 90
1741 #define INITPRIO_LINKDOORS 91
1742 #define INITPRIO_LAST 99
1744 .void(void) initialize_entity;
1745 .float initialize_entity_order;
1746 .entity initialize_entity_next;
1747 entity initialize_entity_first;
1749 void make_safe_for_remove(entity e)
1751 if (e.initialize_entity)
1754 for (ent = initialize_entity_first; ent; )
1756 if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1758 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1759 // skip it in linked list
1762 prev.initialize_entity_next = ent.initialize_entity_next;
1763 ent = prev.initialize_entity_next;
1767 initialize_entity_first = ent.initialize_entity_next;
1768 ent = initialize_entity_first;
1774 ent = ent.initialize_entity_next;
1780 void objerror(string s)
1782 make_safe_for_remove(self);
1783 objerror_builtin(s);
1786 void remove_unsafely(entity e)
1791 void remove_safely(entity e)
1793 make_safe_for_remove(e);
1797 void InitializeEntity(entity e, void(void) func, float order)
1801 if (!e || e.initialize_entity)
1803 // make a proxy initializer entity
1807 e.classname = "initialize_entity";
1811 e.initialize_entity = func;
1812 e.initialize_entity_order = order;
1814 cur = initialize_entity_first;
1817 if (!cur || cur.initialize_entity_order > order)
1819 // insert between prev and cur
1821 prev.initialize_entity_next = e;
1823 initialize_entity_first = e;
1824 e.initialize_entity_next = cur;
1828 cur = cur.initialize_entity_next;
1831 void InitializeEntitiesRun()
1834 startoflist = initialize_entity_first;
1835 initialize_entity_first = world;
1836 for (self = startoflist; self; )
1839 var void(void) func;
1840 e = self.initialize_entity_next;
1841 func = self.initialize_entity;
1842 self.initialize_entity_order = 0;
1843 self.initialize_entity = func_null;
1844 self.initialize_entity_next = world;
1845 if (self.classname == "initialize_entity")
1849 remove_builtin(self);
1852 //dprint("Delayed initialization: ", self.classname, "\n");
1858 .float uncustomizeentityforclient_set;
1859 .void(void) uncustomizeentityforclient;
1860 void(void) SUB_Nullpointer = #0;
1861 void UncustomizeEntitiesRun()
1865 for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1866 self.uncustomizeentityforclient();
1869 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1871 e.customizeentityforclient = customizer;
1872 e.uncustomizeentityforclient = uncustomizer;
1873 e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1877 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1880 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1884 if (e.classname == "")
1885 e.classname = "net_linked";
1887 if (e.model == "" || self.modelindex == 0)
1891 setmodel(e, "null");
1895 e.SendEntity = sendfunc;
1896 e.SendFlags = 0xFFFFFF;
1899 e.effects |= EF_NODEPTHTEST;
1903 e.nextthink = time + dt;
1904 e.think = SUB_Remove;
1908 void adaptor_think2touch()
1917 void adaptor_think2use()
1929 // deferred dropping
1930 void DropToFloor_Handler()
1932 droptofloor_builtin();
1933 self.dropped_origin = self.origin;
1938 InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1943 float trace_hits_box_a0, trace_hits_box_a1;
1945 float trace_hits_box_1d(float end, float thmi, float thma)
1949 // just check if x is in range
1957 // do the trace with respect to x
1958 // 0 -> end has to stay in thmi -> thma
1959 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1960 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1961 if (trace_hits_box_a0 > trace_hits_box_a1)
1967 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1972 // now it is a trace from 0 to end
1974 trace_hits_box_a0 = 0;
1975 trace_hits_box_a1 = 1;
1977 if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1979 if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1981 if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1987 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1989 return trace_hits_box(start, end, thmi - ma, thma - mi);
1992 float SUB_NoImpactCheck()
1994 // zero hitcontents = this is not the real impact, but either the
1995 // mirror-impact of something hitting the projectile instead of the
1996 // projectile hitting the something, or a touchareagrid one. Neither of
1997 // these stop the projectile from moving, so...
1998 if(trace_dphitcontents == 0)
2000 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
2003 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2005 if (other == world && self.size != '0 0 0')
2008 tic = self.velocity * sys_ticrate;
2009 tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2010 traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2011 if (trace_fraction >= 1)
2013 dprint("Odd... did not hit...?\n");
2015 else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2017 dprint("Detected and prevented the sky-grapple bug.\n");
2025 #define SUB_OwnerCheck() (other && (other == self.owner))
2027 #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)
2029 float MAX_IPBAN_URIS = 16;
2031 float URI_GET_DISCARD = 0;
2032 float URI_GET_IPBAN = 1;
2033 float URI_GET_IPBAN_END = 16;
2035 void URI_Get_Callback(float id, float status, string data)
2037 dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2039 dprint("\nEnd of data.\n");
2041 if (id == URI_GET_DISCARD)
2045 else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2048 OnlineBanList_URI_Get_Callback(id, status, data);
2052 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2056 void print_to(entity e, string s)
2059 sprint(e, strcat(s, "\n"));
2078 for (i = 0; i < MapInfo_count; ++i)
2080 if (MapInfo_Get_ByID(i))
2082 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2085 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2086 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2094 for (i = 0; i < MapInfo_count; ++i)
2096 if (MapInfo_Get_ByID(i))
2098 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2101 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2102 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2110 for (i = 0; i < MapInfo_count; ++i)
2112 if (MapInfo_Get_ByID(i))
2114 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2117 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2118 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2124 MapInfo_ClearTemps();
2127 return "No records are available on this server.\n";
2129 return strcat("Records on this server:\n", s);
2132 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2135 vector start, org, delta, end, enddown, mstart;
2137 m = e.dphitcontentsmask;
2138 e.dphitcontentsmask = goodcontents | badcontents;
2141 delta = world.maxs - world.mins;
2143 for (i = 0; i < attempts; ++i)
2145 start_x = org_x + random() * delta_x;
2146 start_y = org_y + random() * delta_y;
2147 start_z = org_z + random() * delta_z;
2149 // rule 1: start inside world bounds, and outside
2150 // solid, and don't start from somewhere where you can
2151 // fall down to evil
2152 tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2153 if (trace_fraction >= 1)
2155 if (trace_startsolid)
2157 if (trace_dphitcontents & badcontents)
2159 if (trace_dphitq3surfaceflags & badsurfaceflags)
2162 // rule 2: if we are too high, lower the point
2163 if (trace_fraction * delta_z > maxaboveground)
2164 start = trace_endpos + '0 0 1' * maxaboveground;
2165 enddown = trace_endpos;
2167 // rule 3: make sure we aren't outside the map. This only works
2168 // for somewhat well formed maps. A good rule of thumb is that
2169 // the map should have a convex outside hull.
2170 // these can be traceLINES as we already verified the starting box
2171 mstart = start + 0.5 * (e.mins + e.maxs);
2172 traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2173 if (trace_fraction >= 1)
2175 traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2176 if (trace_fraction >= 1)
2178 traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2179 if (trace_fraction >= 1)
2181 traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2182 if (trace_fraction >= 1)
2184 traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2185 if (trace_fraction >= 1)
2188 // find a random vector to "look at"
2189 end_x = org_x + random() * delta_x;
2190 end_y = org_y + random() * delta_y;
2191 end_z = org_z + random() * delta_z;
2192 end = start + normalize(end - start) * vlen(delta);
2194 // rule 4: start TO end must not be too short
2195 tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2196 if (trace_startsolid)
2198 if (trace_fraction < minviewdistance / vlen(delta))
2201 // rule 5: don't want to look at sky
2202 if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2205 // rule 6: we must not end up in trigger_hurt
2206 if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2208 dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2215 e.dphitcontentsmask = m;
2219 setorigin(e, start);
2220 e.angles = vectoangles(end - start);
2221 dprint("Needed ", ftos(i + 1), " attempts\n");
2228 float zcurveparticles_effectno;
2229 vector zcurveparticles_start;
2230 float zcurveparticles_spd;
2232 void endzcurveparticles()
2234 if(zcurveparticles_effectno)
2237 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2239 zcurveparticles_effectno = 0;
2242 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2244 spd = bound(0, floor(spd / 16), 32767);
2245 if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2247 endzcurveparticles();
2248 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2249 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2250 WriteShort(MSG_BROADCAST, effectno);
2251 WriteCoord(MSG_BROADCAST, start_x);
2252 WriteCoord(MSG_BROADCAST, start_y);
2253 WriteCoord(MSG_BROADCAST, start_z);
2254 zcurveparticles_effectno = effectno;
2255 zcurveparticles_start = start;
2258 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2259 WriteCoord(MSG_BROADCAST, end_x);
2260 WriteCoord(MSG_BROADCAST, end_y);
2261 WriteCoord(MSG_BROADCAST, end_z);
2262 WriteCoord(MSG_BROADCAST, end_dz);
2263 zcurveparticles_spd = spd;
2266 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2269 vector vecxy, velxy;
2271 vecxy = end - start;
2276 if (vlen(velxy) < 0.000001 * fabs(vel_z))
2278 endzcurveparticles();
2279 trailparticles(world, effectno, start, end);
2283 end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2284 zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2287 string GetGametype(); // g_world.qc
2288 void write_recordmarker(entity pl, float tstart, float dt)
2290 GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2292 // also write a marker into demo files for demotc-race-record-extractor to find
2295 strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2296 " ", ftos(tstart), " ", ftos(dt), "\n"));
2299 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2301 switch(self.owner.cvar_cl_gunalign)
2312 if(allowcenter) // 2: allow center handedness
2325 if(allowcenter) // 2: allow center handedness
2341 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2346 if (cvar("g_shootfromeye"))
2350 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2358 else if (cvar("g_shootfromcenter"))
2362 vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2370 else if (cvar("g_shootfromclient"))
2372 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2374 else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2389 void attach_sameorigin(entity e, entity to, string tag)
2391 vector org, t_forward, t_left, t_up, e_forward, e_up;
2398 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2399 tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2400 t_forward = v_forward * tagscale;
2401 t_left = v_right * -tagscale;
2402 t_up = v_up * tagscale;
2404 e.origin_x = org * t_forward;
2405 e.origin_y = org * t_left;
2406 e.origin_z = org * t_up;
2408 // current forward and up directions
2409 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2410 e.angles_x = -e.angles_x;
2411 fixedmakevectors(e.angles);
2413 // untransform forward, up!
2414 e_forward_x = v_forward * t_forward;
2415 e_forward_y = v_forward * t_left;
2416 e_forward_z = v_forward * t_up;
2417 e_up_x = v_up * t_forward;
2418 e_up_y = v_up * t_left;
2419 e_up_z = v_up * t_up;
2421 e.angles = fixedvectoangles2(e_forward, e_up);
2422 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2423 e.angles_x = -e.angles_x;
2425 setattachment(e, to, tag);
2426 setorigin(e, e.origin);
2429 void detach_sameorigin(entity e)
2432 org = gettaginfo(e, 0);
2433 e.angles = fixedvectoangles2(v_forward, v_up);
2434 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2435 e.angles_x = -e.angles_x;
2437 setattachment(e, world, "");
2438 setorigin(e, e.origin);
2441 void follow_sameorigin(entity e, entity to)
2443 e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2444 e.aiment = to; // make the hole follow bmodel
2445 e.punchangle = to.angles; // the original angles of bmodel
2446 e.view_ofs = e.origin - to.origin; // relative origin
2447 e.v_angle = e.angles - to.angles; // relative angles
2450 void unfollow_sameorigin(entity e)
2452 e.movetype = MOVETYPE_NONE;
2455 entity gettaginfo_relative_ent;
2456 vector gettaginfo_relative(entity e, float tag)
2458 if (!gettaginfo_relative_ent)
2460 gettaginfo_relative_ent = spawn();
2461 gettaginfo_relative_ent.effects = EF_NODRAW;
2463 gettaginfo_relative_ent.model = e.model;
2464 gettaginfo_relative_ent.modelindex = e.modelindex;
2465 gettaginfo_relative_ent.frame = e.frame;
2466 return gettaginfo(gettaginfo_relative_ent, tag);
2469 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2473 if (pl.soundentity.cnt & p)
2475 soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2476 pl.soundentity.cnt |= p;
2479 void SoundEntity_StopSound(entity pl, float chan)
2483 if (pl.soundentity.cnt & p)
2485 stopsoundto(MSG_ALL, pl.soundentity, chan);
2486 pl.soundentity.cnt &~= p;
2490 void SoundEntity_Attach(entity pl)
2492 pl.soundentity = spawn();
2493 pl.soundentity.classname = "soundentity";
2494 pl.soundentity.owner = pl;
2495 setattachment(pl.soundentity, pl, "");
2496 setmodel(pl.soundentity, "null");
2499 void SoundEntity_Detach(entity pl)
2502 for (i = 0; i <= 7; ++i)
2503 SoundEntity_StopSound(pl, i);
2507 float ParseCommandPlayerSlotTarget_firsttoken;
2508 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2516 ParseCommandPlayerSlotTarget_firsttoken = -1;
2520 if (substring(argv(idx), 0, 1) == "#")
2522 s = substring(argv(idx), 1, -1);
2530 ParseCommandPlayerSlotTarget_firsttoken = idx;
2531 if (s == ftos(stof(s)))
2533 e = edict_num(stof(s));
2534 if (e.flags & FL_CLIENT)
2540 // it must be a nick name
2543 ParseCommandPlayerSlotTarget_firsttoken = idx;
2546 FOR_EACH_CLIENT(head)
2547 if (head.netname == s)
2555 s = strdecolorize(s);
2557 FOR_EACH_CLIENT(head)
2558 if (strdecolorize(head.netname) == s)
2573 float modeleffect_SendEntity(entity to, float sf)
2576 WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2579 if(self.velocity != '0 0 0')
2581 if(self.angles != '0 0 0')
2583 if(self.avelocity != '0 0 0')
2586 WriteByte(MSG_ENTITY, f);
2587 WriteShort(MSG_ENTITY, self.modelindex);
2588 WriteByte(MSG_ENTITY, self.skin);
2589 WriteByte(MSG_ENTITY, self.frame);
2590 WriteCoord(MSG_ENTITY, self.origin_x);
2591 WriteCoord(MSG_ENTITY, self.origin_y);
2592 WriteCoord(MSG_ENTITY, self.origin_z);
2595 WriteCoord(MSG_ENTITY, self.velocity_x);
2596 WriteCoord(MSG_ENTITY, self.velocity_y);
2597 WriteCoord(MSG_ENTITY, self.velocity_z);
2601 WriteCoord(MSG_ENTITY, self.angles_x);
2602 WriteCoord(MSG_ENTITY, self.angles_y);
2603 WriteCoord(MSG_ENTITY, self.angles_z);
2607 WriteCoord(MSG_ENTITY, self.avelocity_x);
2608 WriteCoord(MSG_ENTITY, self.avelocity_y);
2609 WriteCoord(MSG_ENTITY, self.avelocity_z);
2611 WriteShort(MSG_ENTITY, self.scale * 256.0);
2612 WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2613 WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2614 WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2615 WriteByte(MSG_ENTITY, self.alpha * 255.0);
2620 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)
2625 e.classname = "modeleffect";
2633 e.teleport_time = t1;
2637 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2641 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2644 sz = max(e.scale, e.scale2);
2645 setsize(e, e.mins * sz, e.maxs * sz);
2646 Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2649 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2651 return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2654 float randombit(float bits)
2656 if not(bits & (bits-1)) // this ONLY holds for powers of two!
2665 for(f = 1; f <= bits; f *= 2)
2674 r = (r - 1) / (n - 1);
2681 float randombits(float bits, float k, float error_return)
2685 while(k > 0 && bits != r)
2687 r += randombit(bits - r);
2696 void randombit_test(float bits, float iter)
2700 print(ftos(randombit(bits)), "\n");
2705 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2707 if(halflifedist > 0)
2708 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2709 else if(halflifedist < 0)
2710 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);