]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/miscfunctions.qc
do not rot/regen fuel when using unlimited ammo
[divverent/nexuiz.git] / data / qcsrc / server / miscfunctions.qc
1 var void remove(entity e);
2 void objerror(string s);
3 void droptofloor();
4 .vector dropped_origin;
5
6 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
7 void() spawnpoint_use;
8 string ColoredTeamName(float t);
9
10 float DistributeEvenly_amount;
11 float DistributeEvenly_totalweight;
12 void DistributeEvenly_Init(float amount, float totalweight)
13 {
14     if (DistributeEvenly_amount)
15     {
16         dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
17         dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
18     }
19     if (totalweight == 0)
20         DistributeEvenly_amount = 0;
21     else
22         DistributeEvenly_amount = amount;
23     DistributeEvenly_totalweight = totalweight;
24 }
25 float DistributeEvenly_Get(float weight)
26 {
27     float f;
28     if (weight <= 0)
29         return 0;
30     f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
31     DistributeEvenly_totalweight -= weight;
32     DistributeEvenly_amount -= f;
33     return f;
34 }
35
36 void move_out_of_solid_expand(entity e, vector by)
37 {
38     float eps = 0.0625;
39     tracebox(e.origin, e.mins - '1 1 1' * eps, e.maxs + '1 1 1' * eps, e.origin + by, MOVE_WORLDONLY, e);
40     if (trace_startsolid)
41         return;
42     if (trace_fraction < 1)
43     {
44         // hit something
45         // adjust origin in the other direction...
46         e.origin = e.origin - by * (1 - trace_fraction);
47     }
48 }
49
50 float move_out_of_solid(entity e)
51 {
52     vector o, m0, m1;
53
54     o = e.origin;
55     traceline(o, o, MOVE_WORLDONLY, e);
56     if (trace_startsolid)
57         return 0;
58
59     tracebox(o, e.mins, e.maxs, o, MOVE_WORLDONLY, e);
60     if (!trace_startsolid)
61         return 1;
62
63     m0 = e.mins;
64     m1 = e.maxs;
65     e.mins = '0 0 0';
66     e.maxs = '0 0 0';
67     move_out_of_solid_expand(e, '1 0 0' * m0_x);
68     e.mins_x = m0_x;
69     move_out_of_solid_expand(e, '1 0 0' * m1_x);
70     e.maxs_x = m1_x;
71     move_out_of_solid_expand(e, '0 1 0' * m0_y);
72     e.mins_y = m0_y;
73     move_out_of_solid_expand(e, '0 1 0' * m1_y);
74     e.maxs_y = m1_y;
75     move_out_of_solid_expand(e, '0 0 1' * m0_z);
76     e.mins_z = m0_z;
77     move_out_of_solid_expand(e, '0 0 1' * m1_z);
78     e.maxs_z = m1_z;
79     setorigin(e, e.origin);
80
81     tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
82     if (trace_startsolid)
83     {
84         setorigin(e, o);
85         return 0;
86     }
87
88     return 1;
89 }
90
91 string STR_PLAYER = "player";
92 string STR_SPECTATOR = "spectator";
93 string STR_OBSERVER = "observer";
94
95 #if 0
96 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
97 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
98 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
99 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
100 #else
101 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
102 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
103 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
104 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
105 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
106 #endif
107
108 // copies a string to a tempstring (so one can strunzone it)
109 string strcat1(string s) = #115; // FRIK_FILE
110
111 float logfile_open;
112 float logfile;
113
114 void bcenterprint(string s)
115 {
116     // TODO replace by MSG_ALL (would show it to spectators too, though)?
117     entity head;
118     FOR_EACH_PLAYER(head)
119     if (clienttype(head) == CLIENTTYPE_REAL)
120         centerprint(head, s);
121 }
122
123 void GameLogEcho(string s)
124 {
125     string fn;
126     float matches;
127
128     if (cvar("sv_eventlog_files"))
129     {
130         if (!logfile_open)
131         {
132             logfile_open = TRUE;
133             matches = cvar("sv_eventlog_files_counter") + 1;
134             cvar_set("sv_eventlog_files_counter", ftos(matches));
135             fn = ftos(matches);
136             if (strlen(fn) < 8)
137                 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
138             fn = strcat(cvar_string("sv_eventlog_files_nameprefix"), fn, cvar_string("sv_eventlog_files_namesuffix"));
139             logfile = fopen(fn, FILE_APPEND);
140             fputs(logfile, ":logversion:3\n");
141         }
142         if (logfile >= 0)
143         {
144             if (cvar("sv_eventlog_files_timestamps"))
145                 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
146             else
147                 fputs(logfile, strcat(s, "\n"));
148         }
149     }
150     if (cvar("sv_eventlog_console"))
151     {
152         print(s, "\n");
153     }
154 }
155
156 void GameLogInit()
157 {
158     logfile_open = 0;
159     // will be opened later
160 }
161
162 void GameLogClose()
163 {
164     if (logfile_open && logfile >= 0)
165     {
166         fclose(logfile);
167         logfile = -1;
168     }
169 }
170
171 float spawnpoint_nag;
172 void relocate_spawnpoint()
173 {
174     // nudge off the floor
175     setorigin(self, self.origin + '0 0 1');
176
177     tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
178     if (trace_startsolid)
179     {
180         vector o;
181         o = self.origin;
182         self.mins = PL_MIN;
183         self.maxs = PL_MAX;
184         if (!move_out_of_solid(self))
185             objerror("could not get out of solid at all!");
186         print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
187         print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
188         print(" ", ftos(self.origin_y - o_y));
189         print(" ", ftos(self.origin_z - o_z), "'\n");
190         if (cvar("g_spawnpoints_auto_move_out_of_solid"))
191         {
192             if (!spawnpoint_nag)
193                 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
194             spawnpoint_nag = 1;
195         }
196         else
197         {
198             self.origin = o;
199             self.mins = self.maxs = '0 0 0';
200             objerror("player spawn point in solid, mapper sucks!\n");
201             return;
202         }
203     }
204
205     if (cvar("g_spawnpoints_autodrop"))
206     {
207         setsize(self, PL_MIN, PL_MAX);
208         droptofloor();
209     }
210
211     self.use = spawnpoint_use;
212     self.team_saved = self.team;
213     if (!self.cnt)
214         self.cnt = 1;
215
216     if (g_ctf || g_assault || g_onslaught || g_domination || g_nexball)
217         if (self.team)
218             have_team_spawns = 1;
219
220     if (cvar("r_showbboxes"))
221     {
222         // show where spawnpoints point at too
223         makevectors(self.angles);
224         entity e;
225         e = spawn();
226         e.classname = "info_player_foo";
227         setorigin(e, self.origin + v_forward * 24);
228         setsize(e, '-8 -8 -8', '8 8 8');
229         e.solid = SOLID_TRIGGER;
230     }
231 }
232
233 #define strstr strstrofs
234 /*
235 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
236 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
237 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
238 // BE CONSTANT OR strzoneD!
239 float strstr(string haystack, string needle, float offset)
240 {
241         float len, endpos;
242         string found;
243         len = strlen(needle);
244         endpos = strlen(haystack) - len;
245         while(offset <= endpos)
246         {
247                 found = substring(haystack, offset, len);
248                 if(found == needle)
249                         return offset;
250                 offset = offset + 1;
251         }
252         return -1;
253 }
254 */
255
256 float NUM_NEAREST_ENTITIES = 4;
257 entity nearest_entity[NUM_NEAREST_ENTITIES];
258 float nearest_length[NUM_NEAREST_ENTITIES];
259 entity findnearest(vector point, .string field, string value, vector axismod)
260 {
261     entity localhead;
262     float i;
263     float j;
264     float len;
265     vector dist;
266
267     float num_nearest;
268     num_nearest = 0;
269
270     localhead = find(world, field, value);
271     while (localhead)
272     {
273         if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
274             dist = localhead.oldorigin;
275         else
276             dist = localhead.origin;
277         dist = dist - point;
278         dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
279         len = vlen(dist);
280
281         for (i = 0; i < num_nearest; ++i)
282         {
283             if (len < nearest_length[i])
284                 break;
285         }
286
287         // now i tells us where to insert at
288         //   INSERTION SORT! YOU'VE SEEN IT! RUN!
289         if (i < NUM_NEAREST_ENTITIES)
290         {
291             for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
292             {
293                 nearest_length[j + 1] = nearest_length[j];
294                 nearest_entity[j + 1] = nearest_entity[j];
295             }
296             nearest_length[i] = len;
297             nearest_entity[i] = localhead;
298             if (num_nearest < NUM_NEAREST_ENTITIES)
299                 num_nearest = num_nearest + 1;
300         }
301
302         localhead = find(localhead, field, value);
303     }
304
305     // now use the first one from our list that we can see
306     for (i = 0; i < num_nearest; ++i)
307     {
308         traceline(point, nearest_entity[i].origin, TRUE, world);
309         if (trace_fraction == 1)
310         {
311             if (i != 0)
312             {
313                 dprint("Nearest point (");
314                 dprint(nearest_entity[0].netname);
315                 dprint(") is not visible, using a visible one.\n");
316             }
317             return nearest_entity[i];
318         }
319     }
320
321     if (num_nearest == 0)
322         return world;
323
324     dprint("Not seeing any location point, using nearest as fallback.\n");
325     /* DEBUGGING CODE:
326     dprint("Candidates were: ");
327     for(j = 0; j < num_nearest; ++j)
328     {
329         if(j != 0)
330                 dprint(", ");
331         dprint(nearest_entity[j].netname);
332     }
333     dprint("\n");
334     */
335
336     return nearest_entity[0];
337 }
338
339 void spawnfunc_target_location()
340 {
341     self.classname = "target_location";
342     // location name in netname
343     // eventually support: count, teamgame selectors, line of sight?
344 };
345
346 void spawnfunc_info_location()
347 {
348     self.classname = "target_location";
349     self.message = self.netname;
350 };
351
352 string NearestLocation(vector p)
353 {
354     entity loc;
355     string ret;
356     ret = "somewhere";
357     loc = findnearest(p, classname, "target_location", '1 1 1');
358     if (loc)
359     {
360         ret = loc.message;
361     }
362     else
363     {
364         loc = findnearest(p, target, "###item###", '1 1 4');
365         if (loc)
366             ret = loc.netname;
367     }
368     return ret;
369 }
370
371 string formatmessage(string msg)
372 {
373     float p, p1, p2;
374     float n;
375     string escape;
376     string replacement;
377     p = 0;
378     n = 7;
379     while (1)
380     {
381         if (n < 1)
382             break; // too many replacements
383         n = n - 1;
384         p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
385         p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
386
387         if (p1 < 0)
388             p1 = p2;
389         if (p2 < 0)
390             p2 = p1;
391         p = min(p1, p2);
392
393         if (p < 0)
394             break;
395         replacement = substring(msg, p, 2);
396         escape = substring(msg, p + 1, 1);
397         if (escape == "%")
398             replacement = "%";
399         else if (escape == "\\")
400             replacement = "\\";
401         else if (escape == "n")
402             replacement = "\n";
403         else if (escape == "a")
404             replacement = ftos(floor(self.armorvalue));
405         else if (escape == "h")
406             replacement = ftos(floor(self.health));
407         else if (escape == "l")
408             replacement = NearestLocation(self.origin);
409         else if (escape == "y")
410             replacement = NearestLocation(self.cursor_trace_endpos);
411         else if (escape == "d")
412             replacement = NearestLocation(self.death_origin);
413         else if (escape == "w")
414         {
415             float wep;
416             wep = self.weapon;
417             if (!wep)
418                 wep = self.switchweapon;
419             if (!wep)
420                 wep = self.cnt;
421             replacement = W_Name(wep);
422         }
423         else if (escape == "W")
424         {
425             if (self.items & IT_SHELLS) replacement = "shells";
426             else if (self.items & IT_NAILS) replacement = "bullets";
427             else if (self.items & IT_ROCKETS) replacement = "rockets";
428             else if (self.items & IT_CELLS) replacement = "cells";
429             else replacement = "batteries"; // ;)
430         }
431         else if (escape == "x")
432         {
433             replacement = self.cursor_trace_ent.netname;
434             if (!replacement || !self.cursor_trace_ent)
435                 replacement = "nothing";
436         }
437         else if (escape == "p")
438         {
439             if (self.last_selected_player)
440                 replacement = self.last_selected_player.netname;
441             else
442                 replacement = "(nobody)";
443         }
444         msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
445         p = p + strlen(replacement);
446     }
447     return msg;
448 }
449
450 /*
451 =============
452 GetCvars
453 =============
454 Called with:
455   0:  sends the request
456   >0: receives a cvar from name=argv(f) value=argv(f+1)
457 */
458 void GetCvars_handleString(string thisname, float f, .string field, string name)
459 {
460         if (f < 0)
461         {
462                 if (self.field)
463                         strunzone(self.field);
464                 self.field = string_null;
465         }
466         else if (f > 0)
467         {
468                 if (thisname == name)
469                 {
470                         if (self.field)
471                                 strunzone(self.field);
472                         self.field = strzone(argv(f + 1));
473                 }
474         }
475         else
476                 stuffcmd(self, strcat("sendcvar ", name, "\n"));
477 }
478 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
479 {
480         GetCvars_handleString(thisname, f, field, name);
481         if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
482                 if (thisname == name)
483                 {
484                         string s;
485                         s = func(strcat1(self.field));
486                         if (s != self.field)
487                         {
488                                 strunzone(self.field);
489                                 self.field = strzone(s);
490                         }
491                 }
492 }
493 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
494 {
495         if (f < 0)
496         {
497         }
498         else if (f > 0)
499         {
500                 if (thisname == name)
501                         self.field = stof(argv(f + 1));
502         }
503         else
504                 stuffcmd(self, strcat("sendcvar ", name, "\n"));
505 }
506 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
507 {
508         if (f < 0)
509         {
510         }
511         else if (f > 0)
512         {
513                 if (thisname == name)
514                 {
515                         if(!self.field)
516                         {
517                                 self.field = stof(argv(f + 1));
518                                 if(!self.field)
519                                         self.field = -1;
520                         }
521                 }
522         }
523         else
524         {
525                 if(!self.field)
526                         stuffcmd(self, strcat("sendcvar ", name, "\n"));
527         }
528 }
529 string W_FixWeaponOrder_ForceComplete(string s);
530 string W_FixWeaponOrder_AllowIncomplete(string s);
531 float w_getbestweapon(entity e);
532 void GetCvars(float f)
533 {
534         string s;
535         if (f > 0)
536                 s = strcat1(argv(f));
537         GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
538         GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
539         GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
540         GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
541         GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
542         GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
543         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
544         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
545         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
546         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
547         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
548         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
549         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
550         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
551         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
552         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
553         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
554         GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
555         GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
556         GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
557         GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
558 #ifdef ALLOW_FORCEMODELS
559         GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
560         GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
561 #endif
562         GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
563
564
565         // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
566         if (f > 0)
567         {
568                 if (s == "cl_weaponpriority")
569                         self.switchweapon = w_getbestweapon(self);
570         }
571 }
572
573 float fexists(string f)
574 {
575     float fh;
576     fh = fopen(f, FILE_READ);
577     if (fh < 0)
578         return FALSE;
579     fclose(fh);
580     return TRUE;
581 }
582
583 void backtrace(string msg)
584 {
585     float dev;
586     dev = cvar("developer");
587     cvar_set("developer", "1");
588     dprint("\n");
589     dprint("--- CUT HERE ---\nWARNING: ");
590     dprint(msg);
591     dprint("\n");
592     remove(world); // isn't there any better way to cause a backtrace?
593     dprint("\n--- CUT UNTIL HERE ---\n");
594     cvar_set("developer", ftos(dev));
595 }
596
597 string Team_ColorCode(float teamid)
598 {
599     if (teamid == COLOR_TEAM1)
600         return "^1";
601     else if (teamid == COLOR_TEAM2)
602         return "^4";
603     else if (teamid == COLOR_TEAM3)
604         return "^3";
605     else if (teamid == COLOR_TEAM4)
606         return "^6";
607     else
608         return "^7";
609 }
610 string Team_ColorName(float t)
611 {
612     // fixme: Search for team entities and get their .netname's!
613     if (t == COLOR_TEAM1)
614         return "Red";
615     if (t == COLOR_TEAM2)
616         return "Blue";
617     if (t == COLOR_TEAM3)
618         return "Yellow";
619     if (t == COLOR_TEAM4)
620         return "Pink";
621     return "Neutral";
622 }
623 string Team_ColorNameLowerCase(float t)
624 {
625     // fixme: Search for team entities and get their .netname's!
626     if (t == COLOR_TEAM1)
627         return "red";
628     if (t == COLOR_TEAM2)
629         return "blue";
630     if (t == COLOR_TEAM3)
631         return "yellow";
632     if (t == COLOR_TEAM4)
633         return "pink";
634     return "neutral";
635 }
636
637 #define CENTERPRIO_POINT 1
638 #define CENTERPRIO_SPAM 2
639 #define CENTERPRIO_VOTE 4
640 #define CENTERPRIO_NORMAL 5
641 #define CENTERPRIO_SHIELDING 7
642 #define CENTERPRIO_MAPVOTE 9
643 #define CENTERPRIO_IDLEKICK 50
644 #define CENTERPRIO_ADMIN 99
645 .float centerprint_priority;
646 .float centerprint_expires;
647 void centerprint_atprio(entity e, float prio, string s)
648 {
649     if (intermission_running)
650         if (prio < CENTERPRIO_MAPVOTE)
651             return;
652     if (time > e.centerprint_expires)
653         e.centerprint_priority = 0;
654     if (prio >= e.centerprint_priority)
655     {
656         e.centerprint_priority = prio;
657         if (timeoutStatus == 2)
658             e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
659         else
660             e.centerprint_expires = time + e.cvar_scr_centertime;
661         centerprint_builtin(e, s);
662     }
663 }
664 void centerprint_expire(entity e, float prio)
665 {
666     if (prio == e.centerprint_priority)
667     {
668         e.centerprint_priority = 0;
669         centerprint_builtin(e, "");
670     }
671 }
672 void centerprint(entity e, string s)
673 {
674     centerprint_atprio(e, CENTERPRIO_NORMAL, s);
675 }
676
677 // decolorizes and team colors the player name when needed
678 string playername(entity p)
679 {
680     string t;
681     if (teams_matter && !intermission_running && p.classname == "player")
682     {
683         t = Team_ColorCode(p.team);
684         return strcat(t, strdecolorize(p.netname));
685     }
686     else
687         return p.netname;
688 }
689
690 vector randompos(vector m1, vector m2)
691 {
692     local vector v;
693     m2 = m2 - m1;
694     v_x = m2_x * random() + m1_x;
695     v_y = m2_y * random() + m1_y;
696     v_z = m2_z * random() + m1_z;
697     return  v;
698 };
699
700 float g_pickup_shells;
701 float g_pickup_shells_max;
702 float g_pickup_nails;
703 float g_pickup_nails_max;
704 float g_pickup_rockets;
705 float g_pickup_rockets_max;
706 float g_pickup_cells;
707 float g_pickup_cells_max;
708 float g_pickup_fuel;
709 float g_pickup_fuel_jetpack;
710 float g_pickup_fuel_max;
711 float g_pickup_armorsmall;
712 float g_pickup_armorsmall_max;
713 float g_pickup_armormedium;
714 float g_pickup_armormedium_max;
715 float g_pickup_armorbig;
716 float g_pickup_armorbig_max;
717 float g_pickup_armorlarge;
718 float g_pickup_armorlarge_max;
719 float g_pickup_healthsmall;
720 float g_pickup_healthsmall_max;
721 float g_pickup_healthmedium;
722 float g_pickup_healthmedium_max;
723 float g_pickup_healthlarge;
724 float g_pickup_healthlarge_max;
725 float g_pickup_healthmega;
726 float g_pickup_healthmega_max;
727 float g_weaponarena;
728 string g_weaponarena_list;
729 float g_weaponspeedfactor;
730 float g_weaponratefactor;
731 float g_weapondamagefactor;
732 float g_weaponforcefactor;
733
734 float start_weapons;
735 float start_items;
736 float start_ammo_shells;
737 float start_ammo_nails;
738 float start_ammo_rockets;
739 float start_ammo_cells;
740 float start_ammo_fuel;
741 float start_health;
742 float start_armorvalue;
743 float warmup_start_weapons;
744 float warmup_start_ammo_shells;
745 float warmup_start_ammo_nails;
746 float warmup_start_ammo_rockets;
747 float warmup_start_ammo_cells;
748 float warmup_start_ammo_fuel;
749 float warmup_start_health;
750 float warmup_start_armorvalue;
751 float g_weapon_stay;
752
753 entity get_weaponinfo(float w);
754
755 float NixNex_CanChooseWeapon(float wpn);
756 void readplayerstartcvars()
757 {
758     entity e;
759     float i, j, t;
760     string s;
761
762     // initialize starting values for players
763     start_weapons = 0;
764     start_items = 0;
765     start_ammo_shells = 0;
766     start_ammo_nails = 0;
767     start_ammo_rockets = 0;
768     start_ammo_cells = 0;
769     start_health = cvar("g_balance_health_start");
770     start_armorvalue = cvar("g_balance_armor_start");
771
772     g_weaponarena = 0;
773     s = cvar_string("g_weaponarena");
774     if (s == "0")
775     {
776     }
777     else if (s == "all")
778     {
779         g_weaponarena_list = "All Weapons";
780         for (j = WEP_FIRST; j <= WEP_LAST; ++j)
781         {
782             e = get_weaponinfo(j);
783             g_weaponarena |= e.weapons;
784             weapon_action(e.weapon, WR_PRECACHE);
785         }
786     }
787     else if (s == "most")
788     {
789         g_weaponarena_list = "Most Weapons";
790         for (j = WEP_FIRST; j <= WEP_LAST; ++j)
791         {
792             e = get_weaponinfo(j);
793             if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
794             {
795                 g_weaponarena |= e.weapons;
796                 weapon_action(e.weapon, WR_PRECACHE);
797             }
798         }
799     }
800     else if (s == "none")
801     {
802         g_weaponarena_list = "No Weapons";
803         g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
804     }
805     else
806     {
807         t = tokenize_console(s);
808         g_weaponarena_list = "";
809         for (i = 0; i < t; ++i)
810         {
811             s = argv(i);
812             for (j = WEP_FIRST; j <= WEP_LAST; ++j)
813             {
814                 e = get_weaponinfo(j);
815                 if (e.netname == s)
816                 {
817                     g_weaponarena |= e.weapons;
818                     weapon_action(e.weapon, WR_PRECACHE);
819                     g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
820                     break;
821                 }
822             }
823             if (j > WEP_LAST)
824             {
825                 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
826             }
827         }
828         g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
829     }
830
831     if (g_nixnex)
832     {
833         start_weapons = 0;
834         // will be done later
835         for (i = WEP_FIRST; i <= WEP_LAST; ++i)
836             if (NixNex_CanChooseWeapon(i))
837                 weapon_action(i, WR_PRECACHE);
838         if(!cvar("g_use_ammunition"))
839                 start_items |= IT_UNLIMITED_AMMO;
840     }
841     else if (g_weaponarena)
842     {
843         start_weapons = g_weaponarena;
844                 if (g_weaponarena & (WEPBIT_GRENADE_LAUNCHER | WEPBIT_HAGAR | WEPBIT_ROCKET_LAUNCHER))
845                         start_ammo_rockets = 999;
846                 if (g_weaponarena & WEPBIT_SHOTGUN)
847                         start_ammo_shells = 999;
848                 if (g_weaponarena & (WEPBIT_ELECTRO | WEPBIT_CRYLINK | WEPBIT_NEX | WEPBIT_MINSTANEX | WEPBIT_HLAC | WEPBIT_HOOK))
849                         start_ammo_cells = 999;
850                 if (g_weaponarena & (WEPBIT_UZI | WEPBIT_CAMPINGRIFLE))
851                         start_ammo_nails = 999;
852                 if (g_weaponarena & WEPBIT_HOOK)
853                         start_ammo_fuel = 999;
854         start_items |= IT_UNLIMITED_AMMO;
855     }
856     else if (g_minstagib)
857     {
858         start_health = 100;
859         start_armorvalue = 0;
860         start_weapons = WEPBIT_MINSTANEX;
861         weapon_action(WEP_MINSTANEX, WR_PRECACHE);
862         start_ammo_cells = cvar("g_minstagib_ammo_start");
863         g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
864         start_ammo_fuel = cvar("g_start_ammo_fuel");
865
866         if (g_minstagib_invis_alpha <= 0)
867             g_minstagib_invis_alpha = -1;
868     }
869     else
870     {
871         if (g_lms)
872         {
873             start_ammo_shells = cvar("g_lms_start_ammo_shells");
874             start_ammo_nails = cvar("g_lms_start_ammo_nails");
875             start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
876             start_ammo_cells = cvar("g_lms_start_ammo_cells");
877             start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
878             start_health = cvar("g_lms_start_health");
879             start_armorvalue = cvar("g_lms_start_armor");
880         }
881         else if (cvar("g_use_ammunition"))
882         {
883             start_ammo_shells = cvar("g_start_ammo_shells");
884             start_ammo_nails = cvar("g_start_ammo_nails");
885             start_ammo_rockets = cvar("g_start_ammo_rockets");
886             start_ammo_cells = cvar("g_start_ammo_cells");
887             start_ammo_fuel = cvar("g_start_ammo_fuel");
888         }
889         else
890         {
891             start_ammo_shells = cvar("g_pickup_shells_max");
892             start_ammo_nails = cvar("g_pickup_nails_max");
893             start_ammo_rockets = cvar("g_pickup_rockets_max");
894             start_ammo_cells = cvar("g_pickup_cells_max");
895             start_ammo_fuel = cvar("g_pickup_fuel_max");
896             start_items |= IT_UNLIMITED_AMMO;
897         }
898
899         for (i = WEP_FIRST; i <= WEP_LAST; ++i)
900         {
901             e = get_weaponinfo(i);
902             if (!(e.weapon))
903                 continue;
904
905             t = cvar(strcat("g_start_weapon_", e.netname));
906
907             if (t < 0) // "default" weapon selection
908             {
909                 if (g_lms)
910                     t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
911                 else if (g_race || g_cts)
912                     t = (i == WEP_LASER);
913                 else if (g_nexball)
914                     t = 0; // weapon is set a few lines later
915                 else
916                     t = (i == WEP_LASER || i == WEP_SHOTGUN);
917                 if (g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
918                     t += (i == WEP_HOOK);
919             }
920
921             if (g_nexball && i == WEP_PORTO)
922                 t=1;
923
924             if (t)
925             {
926                 start_weapons |= e.weapons;
927                 weapon_action(e.weapon, WR_PRECACHE);
928             }
929         }
930     }
931
932     if (inWarmupStage)
933     {
934         warmup_start_ammo_shells = start_ammo_shells;
935         warmup_start_ammo_nails = start_ammo_nails;
936         warmup_start_ammo_rockets = start_ammo_rockets;
937         warmup_start_ammo_cells = start_ammo_cells;
938         warmup_start_ammo_fuel = start_ammo_fuel;
939         warmup_start_health = start_health;
940         warmup_start_armorvalue = start_armorvalue;
941         warmup_start_weapons = start_weapons;
942
943         if (!g_weaponarena && !g_nixnex && !g_minstagib)
944         {
945             if (cvar("g_use_ammunition"))
946             {
947                 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
948                 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
949                 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
950                 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
951                 warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
952             }
953             warmup_start_health = cvar("g_warmup_start_health");
954             warmup_start_armorvalue = cvar("g_warmup_start_armor");
955             if (cvar("g_warmup_allguns"))
956             {
957                 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
958                 {
959                     e = get_weaponinfo(i);
960                     if (!(e.weapon))
961                         continue;
962                     if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
963                     {
964                         warmup_start_weapons |= e.weapons;
965                         weapon_action(e.weapon, WR_PRECACHE);
966                     }
967                 }
968             }
969         }
970     }
971
972     if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
973     {
974         g_grappling_hook = 0; // these two can't coexist, as they use the same button
975         start_items |= IT_FUEL_REGEN;
976         start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
977         warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
978     }
979
980     if (g_jetpack)
981         start_items |= IT_JETPACK;
982
983     if (g_weapon_stay == 2)
984     {
985         if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
986         if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
987         if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
988         if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
989         if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
990         if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
991         if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
992         if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
993         if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
994         if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
995     }
996
997     start_ammo_shells = max(0, start_ammo_shells);
998     start_ammo_nails = max(0, start_ammo_nails);
999     start_ammo_cells = max(0, start_ammo_cells);
1000     start_ammo_rockets = max(0, start_ammo_rockets);
1001     start_ammo_fuel = max(0, start_ammo_fuel);
1002
1003     warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1004     warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1005     warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1006     warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1007     warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1008 }
1009
1010 float g_bugrigs;
1011 float g_bugrigs_planar_movement;
1012 float g_bugrigs_planar_movement_car_jumping;
1013 float g_bugrigs_reverse_spinning;
1014 float g_bugrigs_reverse_speeding;
1015 float g_bugrigs_reverse_stopping;
1016 float g_bugrigs_air_steering;
1017 float g_bugrigs_angle_smoothing;
1018 float g_bugrigs_friction_floor;
1019 float g_bugrigs_friction_brake;
1020 float g_bugrigs_friction_air;
1021 float g_bugrigs_accel;
1022 float g_bugrigs_speed_ref;
1023 float g_bugrigs_speed_pow;
1024 float g_bugrigs_steer;
1025
1026 float g_touchexplode;
1027 float g_touchexplode_radius;
1028 float g_touchexplode_damage;
1029 float g_touchexplode_edgedamage;
1030 float g_touchexplode_force;
1031
1032 float sv_autotaunt;
1033 float sv_taunt;
1034
1035 void readlevelcvars(void)
1036 {
1037     g_bugrigs = cvar("g_bugrigs");
1038     g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1039     g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1040     g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1041     g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1042     g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1043     g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1044     g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1045     g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1046     g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1047     g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1048     g_bugrigs_accel = cvar("g_bugrigs_accel");
1049     g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1050     g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1051     g_bugrigs_steer = cvar("g_bugrigs_steer");
1052
1053     g_touchexplode = cvar("g_touchexplode");
1054     g_touchexplode_radius = cvar("g_touchexplode_radius");
1055     g_touchexplode_damage = cvar("g_touchexplode_damage");
1056     g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1057     g_touchexplode_force = cvar("g_touchexplode_force");
1058
1059 #ifdef ALLOW_FORCEMODELS
1060     sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1061 #endif
1062     sv_loddistance1 = cvar("sv_loddistance1");
1063     sv_loddistance2 = cvar("sv_loddistance2");
1064         if(sv_loddistance2 <= sv_loddistance1)
1065                 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1066     sv_clones = cvar("sv_clones");
1067     sv_cheats = cvar("sv_cheats");
1068     sv_gentle = cvar("sv_gentle");
1069     sv_foginterval = cvar("sv_foginterval");
1070     g_cloaked = cvar("g_cloaked");
1071     g_jump_grunt = cvar("g_jump_grunt");
1072     g_footsteps = cvar("g_footsteps");
1073     g_grappling_hook = cvar("g_grappling_hook");
1074     g_jetpack = cvar("g_jetpack");
1075     g_laserguided_missile = cvar("g_laserguided_missile");
1076     g_midair = cvar("g_midair");
1077     g_minstagib = cvar("g_minstagib");
1078     g_nixnex = cvar("g_nixnex");
1079     g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1080     g_norecoil = cvar("g_norecoil");
1081     g_vampire = cvar("g_vampire");
1082     g_bloodloss = cvar("g_bloodloss");
1083     sv_maxidle = cvar("sv_maxidle");
1084     sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1085     sv_pogostick = cvar("sv_pogostick");
1086     sv_doublejump = cvar("sv_doublejump");
1087     g_ctf_reverse = cvar("g_ctf_reverse");
1088         sv_autotaunt = cvar("sv_autotaunt");
1089         sv_taunt = cvar("sv_taunt");
1090
1091     inWarmupStage = cvar("g_warmup");
1092     g_warmup_limit = cvar("g_warmup_limit");
1093     g_warmup_allguns = cvar("g_warmup_allguns");
1094     g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1095
1096     if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1097         inWarmupStage = 0; // these modes cannot work together, sorry
1098
1099     g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1100     g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1101     g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1102     g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1103     g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1104     g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1105     g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1106     g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1107     g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1108     g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1109     g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1110     g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1111
1112     if (g_minstagib) g_nixnex = g_weaponarena = 0;
1113     if (g_nixnex) g_weaponarena = 0;
1114     g_weaponarena = 0;
1115
1116     g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1117     g_weaponratefactor = cvar("g_weaponratefactor");
1118     g_weapondamagefactor = cvar("g_weapondamagefactor");
1119     g_weaponforcefactor = cvar("g_weaponforcefactor");
1120
1121     g_pickup_shells                    = cvar("g_pickup_shells");
1122     g_pickup_shells_max                = cvar("g_pickup_shells_max");
1123     g_pickup_nails                     = cvar("g_pickup_nails");
1124     g_pickup_nails_max                 = cvar("g_pickup_nails_max");
1125     g_pickup_rockets                   = cvar("g_pickup_rockets");
1126     g_pickup_rockets_max               = cvar("g_pickup_rockets_max");
1127     g_pickup_cells                     = cvar("g_pickup_cells");
1128     g_pickup_cells_max                 = cvar("g_pickup_cells_max");
1129     g_pickup_fuel                     = cvar("g_pickup_fuel");
1130     g_pickup_fuel_jetpack             = cvar("g_pickup_fuel_jetpack");
1131     g_pickup_fuel_max                 = cvar("g_pickup_fuel_max");
1132     g_pickup_armorsmall                = cvar("g_pickup_armorsmall");
1133     g_pickup_armorsmall_max            = cvar("g_pickup_armorsmall_max");
1134     g_pickup_armormedium               = cvar("g_pickup_armormedium");
1135     g_pickup_armormedium_max           = cvar("g_pickup_armormedium_max");
1136     g_pickup_armorbig                  = cvar("g_pickup_armorbig");
1137     g_pickup_armorbig_max              = cvar("g_pickup_armorbig_max");
1138     g_pickup_armorlarge                = cvar("g_pickup_armorlarge");
1139     g_pickup_armorlarge_max            = cvar("g_pickup_armorlarge_max");
1140     g_pickup_healthsmall               = cvar("g_pickup_healthsmall");
1141     g_pickup_healthsmall_max           = cvar("g_pickup_healthsmall_max");
1142     g_pickup_healthmedium              = cvar("g_pickup_healthmedium");
1143     g_pickup_healthmedium_max          = cvar("g_pickup_healthmedium_max");
1144     g_pickup_healthlarge               = cvar("g_pickup_healthlarge");
1145     g_pickup_healthlarge_max           = cvar("g_pickup_healthlarge_max");
1146     g_pickup_healthmega                = cvar("g_pickup_healthmega");
1147     g_pickup_healthmega_max            = cvar("g_pickup_healthmega_max");
1148
1149     g_pinata = cvar("g_pinata");
1150
1151     g_weapon_stay = cvar("g_weapon_stay");
1152     if (!g_weapon_stay && (cvar("deathmatch") == 2))
1153         g_weapon_stay = 1;
1154
1155     if not(inWarmupStage)
1156         game_starttime                 = cvar("g_start_delay");
1157
1158     readplayerstartcvars();
1159 }
1160
1161 /*
1162 // TODO sound pack system
1163 string soundpack;
1164
1165 string precache_sound_builtin (string s) = #19;
1166 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1167 string precache_sound(string s)
1168 {
1169         return precache_sound_builtin(strcat(soundpack, s));
1170 }
1171 void play2(entity e, string filename)
1172 {
1173         stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1174 }
1175 void sound(entity e, float chan, string samp, float vol, float atten)
1176 {
1177         sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1178 }
1179 */
1180
1181 // Sound functions
1182 string precache_sound (string s) = #19;
1183 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1184 float precache_sound_index (string s) = #19;
1185
1186 #define SND_VOLUME      1
1187 #define SND_ATTENUATION 2
1188 #define SND_LARGEENTITY 8
1189 #define SND_LARGESOUND  16
1190
1191 float sound_allowed(float dest, entity e)
1192 {
1193     // sounds from world may always pass
1194     for (;;)
1195     {
1196         if (e.classname == "body")
1197             e = e.enemy;
1198         if (e.owner && e.owner != e)
1199             e = e.owner;
1200         else
1201             break;
1202     }
1203     // sounds to self may always pass
1204     if (dest == MSG_ONE)
1205         if (e == msg_entity)
1206             return TRUE;
1207     // sounds by players can be removed
1208     if (cvar("bot_sound_monopoly"))
1209         if (clienttype(e) == CLIENTTYPE_REAL)
1210             return FALSE;
1211     // anything else may pass
1212     return TRUE;
1213 }
1214
1215 void sound(entity e, float chan, string samp, float vol, float atten)
1216 {
1217     if (!sound_allowed(MSG_BROADCAST, e))
1218         return;
1219     sound_builtin(e, chan, samp, vol, atten);
1220 }
1221 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1222 {
1223     float entno, idx;
1224
1225     if (!sound_allowed(dest, e))
1226         return;
1227
1228     entno = num_for_edict(e);
1229     idx = precache_sound_index(samp);
1230
1231     float sflags;
1232     sflags = 0;
1233
1234     atten = floor(atten * 64);
1235     vol = floor(vol * 255);
1236
1237     if (vol != 255)
1238         sflags |= SND_VOLUME;
1239     if (atten != 64)
1240         sflags |= SND_ATTENUATION;
1241     if (entno >= 8192)
1242         sflags |= SND_LARGEENTITY;
1243     if (idx >= 256)
1244         sflags |= SND_LARGESOUND;
1245
1246     WriteByte(dest, SVC_SOUND);
1247     WriteByte(dest, sflags);
1248     if (sflags & SND_VOLUME)
1249         WriteByte(dest, vol);
1250     if (sflags & SND_ATTENUATION)
1251         WriteByte(dest, atten);
1252     if (sflags & SND_LARGEENTITY)
1253     {
1254         WriteShort(dest, entno);
1255         WriteByte(dest, chan);
1256     }
1257     else
1258     {
1259         WriteShort(dest, entno * 8 + chan);
1260     }
1261     if (sflags & SND_LARGESOUND)
1262         WriteShort(dest, idx);
1263     else
1264         WriteByte(dest, idx);
1265
1266     WriteCoord(dest, o_x);
1267     WriteCoord(dest, o_y);
1268     WriteCoord(dest, o_z);
1269 }
1270 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1271 {
1272     vector o;
1273
1274     if (!sound_allowed(dest, e))
1275         return;
1276
1277     o = e.origin + 0.5 * (e.mins + e.maxs);
1278     soundtoat(dest, e, o, chan, samp, vol, atten);
1279 }
1280 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1281 {
1282     soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1283 }
1284 void stopsoundto(float dest, entity e, float chan)
1285 {
1286     float entno;
1287
1288     if (!sound_allowed(dest, e))
1289         return;
1290
1291     entno = num_for_edict(e);
1292
1293     if (entno >= 8192)
1294     {
1295         float idx, sflags;
1296         idx = precache_sound_index("misc/null.wav");
1297         sflags = SND_LARGEENTITY;
1298         if (idx >= 256)
1299             sflags |= SND_LARGESOUND;
1300         WriteByte(dest, SVC_SOUND);
1301         WriteByte(dest, sflags);
1302         WriteShort(dest, entno);
1303         WriteByte(dest, chan);
1304         if (sflags & SND_LARGESOUND)
1305             WriteShort(dest, idx);
1306         else
1307             WriteByte(dest, idx);
1308         WriteCoord(dest, e.origin_x);
1309         WriteCoord(dest, e.origin_y);
1310         WriteCoord(dest, e.origin_z);
1311     }
1312     else
1313     {
1314         WriteByte(dest, SVC_STOPSOUND);
1315         WriteShort(dest, entno * 8 + chan);
1316     }
1317 }
1318 void stopsound(entity e, float chan)
1319 {
1320     if (!sound_allowed(MSG_BROADCAST, e))
1321         return;
1322
1323     stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1324     stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1325 }
1326
1327 void play2(entity e, string filename)
1328 {
1329     //stuffcmd(e, strcat("play2 ", filename, "\n"));
1330     msg_entity = e;
1331     soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1332 }
1333
1334 .float announcetime;
1335 float announce(entity player, string msg)
1336 {
1337     if (time > player.announcetime)
1338         if (clienttype(player) == CLIENTTYPE_REAL)
1339         {
1340             player.announcetime = time + 0.8;
1341             play2(player, msg);
1342             return TRUE;
1343         }
1344     return FALSE;
1345 }
1346 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1347 float spamsound(entity e, float chan, string samp, float vol, float atten)
1348 {
1349     if (!sound_allowed(MSG_BROADCAST, e))
1350         return FALSE;
1351
1352     if (time > e.announcetime)
1353     {
1354         e.announcetime = time;
1355         sound(e, chan, samp, vol, atten);
1356         return TRUE;
1357     }
1358     return FALSE;
1359 }
1360
1361 void play2team(float t, string filename)
1362 {
1363     local entity head;
1364
1365     if (cvar("bot_sound_monopoly"))
1366         return;
1367
1368     FOR_EACH_REALPLAYER(head)
1369     {
1370         if (head.team == t)
1371             play2(head, filename);
1372     }
1373 }
1374
1375 void play2all(string samp)
1376 {
1377     if (cvar("bot_sound_monopoly"))
1378         return;
1379
1380     sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1381 }
1382
1383 void PrecachePlayerSounds(string f);
1384 void precache_all_models(string pattern)
1385 {
1386     float globhandle, i, n;
1387     string f;
1388
1389     globhandle = search_begin(pattern, TRUE, FALSE);
1390     if (globhandle < 0)
1391         return;
1392     n = search_getsize(globhandle);
1393     for (i = 0; i < n; ++i)
1394     {
1395                 //print(search_getfilename(globhandle, i), "\n");
1396                 f = search_getfilename(globhandle, i);
1397                 if(sv_loddistance1)
1398                         precache_model(f);
1399                 if(substring(f, -9,5) == "_lod1")
1400                         continue;
1401                 if(substring(f, -9,5) == "_lod2")
1402                         continue;
1403                 if(!sv_loddistance1)
1404                         precache_model(f);
1405                 PrecachePlayerSounds(strcat(f, ".sounds"));
1406     }
1407     search_end(globhandle);
1408 }
1409
1410 void precache()
1411 {
1412     // gamemode related things
1413     precache_model ("models/misc/chatbubble.spr");
1414     precache_model ("models/misc/teambubble.spr");
1415     if (g_runematch)
1416     {
1417         precache_model ("models/runematch/curse.mdl");
1418         precache_model ("models/runematch/rune.mdl");
1419     }
1420
1421 #ifdef TTURRETS_ENABLED
1422     if (cvar("g_turrets"))
1423         turrets_precash();
1424 #endif
1425
1426     // Precache all player models if desired
1427     if (cvar("sv_precacheplayermodels"))
1428     {
1429         PrecachePlayerSounds("sound/player/default.sounds");
1430         precache_all_models("models/player/*.zym");
1431         precache_all_models("models/player/*.dpm");
1432         precache_all_models("models/player/*.md3");
1433         precache_all_models("models/player/*.psk");
1434         //precache_model("models/player/carni.zym");
1435         //precache_model("models/player/crash.zym");
1436         //precache_model("models/player/grunt.zym");
1437         //precache_model("models/player/headhunter.zym");
1438         //precache_model("models/player/insurrectionist.zym");
1439         //precache_model("models/player/jeandarc.zym");
1440         //precache_model("models/player/lurk.zym");
1441         //precache_model("models/player/lycanthrope.zym");
1442         //precache_model("models/player/marine.zym");
1443         //precache_model("models/player/nexus.zym");
1444         //precache_model("models/player/pyria.zym");
1445         //precache_model("models/player/shock.zym");
1446         //precache_model("models/player/skadi.zym");
1447         //precache_model("models/player/specop.zym");
1448         //precache_model("models/player/visitant.zym");
1449     }
1450
1451     if (cvar("sv_defaultcharacter"))
1452     {
1453         string s;
1454         s = cvar_string("sv_defaultplayermodel_red");
1455         if (s != "")
1456         {
1457             precache_model(s);
1458             PrecachePlayerSounds(strcat(s, ".sounds"));
1459         }
1460         s = cvar_string("sv_defaultplayermodel_blue");
1461         if (s != "")
1462         {
1463             precache_model(s);
1464             PrecachePlayerSounds(strcat(s, ".sounds"));
1465         }
1466         s = cvar_string("sv_defaultplayermodel_yellow");
1467         if (s != "")
1468         {
1469             precache_model(s);
1470             PrecachePlayerSounds(strcat(s, ".sounds"));
1471         }
1472         s = cvar_string("sv_defaultplayermodel_pink");
1473         if (s != "")
1474         {
1475             precache_model(s);
1476             PrecachePlayerSounds(strcat(s, ".sounds"));
1477         }
1478         s = cvar_string("sv_defaultplayermodel");
1479         if (s != "")
1480         {
1481             precache_model(s);
1482             PrecachePlayerSounds(strcat(s, ".sounds"));
1483         }
1484     }
1485
1486     if (g_footsteps)
1487     {
1488         PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1489         PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1490     }
1491
1492     // gore and miscellaneous sounds
1493     //precache_sound ("misc/h2ohit.wav");
1494     precache_model ("models/hook.md3");
1495     precache_sound ("misc/armorimpact.wav");
1496     precache_sound ("misc/bodyimpact1.wav");
1497     precache_sound ("misc/bodyimpact2.wav");
1498     precache_sound ("misc/gib.wav");
1499     precache_sound ("misc/gib_splat01.wav");
1500     precache_sound ("misc/gib_splat02.wav");
1501     precache_sound ("misc/gib_splat03.wav");
1502     precache_sound ("misc/gib_splat04.wav");
1503     precache_sound ("misc/hit.wav");
1504     PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1505     PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1506     precache_sound ("misc/null.wav");
1507     precache_sound ("misc/spawn.wav");
1508     precache_sound ("misc/talk.wav");
1509     precache_sound ("misc/teleport.wav");
1510     precache_sound ("misc/poweroff.wav");
1511     precache_sound ("player/lava.wav");
1512     precache_sound ("player/slime.wav");
1513
1514     if (g_jetpack)
1515         precache_sound ("misc/jetpack_fly.wav");
1516
1517     // announcer sounds - male
1518     precache_sound ("announcer/male/electrobitch.wav");
1519     precache_sound ("announcer/male/airshot.wav");
1520     precache_sound ("announcer/male/03kills.wav");
1521     precache_sound ("announcer/male/05kills.wav");
1522     precache_sound ("announcer/male/10kills.wav");
1523     precache_sound ("announcer/male/15kills.wav");
1524     precache_sound ("announcer/male/20kills.wav");
1525     precache_sound ("announcer/male/25kills.wav");
1526     precache_sound ("announcer/male/30kills.wav");
1527     precache_sound ("announcer/male/botlike.wav");
1528     precache_sound ("announcer/male/yoda.wav");
1529     precache_sound ("announcer/male/amazing.wav");
1530     precache_sound ("announcer/male/awesome.wav");
1531     precache_sound ("announcer/male/headshot.wav");
1532     precache_sound ("announcer/male/impressive.wav");
1533
1534     // announcer sounds - robotic
1535     precache_sound ("announcer/robotic/prepareforbattle.wav");
1536     precache_sound ("announcer/robotic/begin.wav");
1537     precache_sound ("announcer/robotic/timeoutcalled.wav");
1538     precache_sound ("announcer/robotic/1fragleft.wav");
1539     precache_sound ("announcer/robotic/2fragsleft.wav");
1540     precache_sound ("announcer/robotic/3fragsleft.wav");
1541     precache_sound ("announcer/robotic/terminated.wav");
1542     if (g_minstagib)
1543     {
1544         precache_sound ("announcer/robotic/lastsecond.wav");
1545         precache_sound ("announcer/robotic/narrowly.wav");
1546     }
1547
1548     precache_model ("models/sprites/0.spr32");
1549     precache_model ("models/sprites/1.spr32");
1550     precache_model ("models/sprites/2.spr32");
1551     precache_model ("models/sprites/3.spr32");
1552     precache_model ("models/sprites/4.spr32");
1553     precache_model ("models/sprites/5.spr32");
1554     precache_model ("models/sprites/6.spr32");
1555     precache_model ("models/sprites/7.spr32");
1556     precache_model ("models/sprites/8.spr32");
1557     precache_model ("models/sprites/9.spr32");
1558     precache_model ("models/sprites/10.spr32");
1559     precache_sound ("announcer/robotic/1.wav");
1560     precache_sound ("announcer/robotic/2.wav");
1561     precache_sound ("announcer/robotic/3.wav");
1562     precache_sound ("announcer/robotic/4.wav");
1563     precache_sound ("announcer/robotic/5.wav");
1564     precache_sound ("announcer/robotic/6.wav");
1565     precache_sound ("announcer/robotic/7.wav");
1566     precache_sound ("announcer/robotic/8.wav");
1567     precache_sound ("announcer/robotic/9.wav");
1568     precache_sound ("announcer/robotic/10.wav");
1569
1570     // common weapon precaches
1571     precache_sound ("weapons/weapon_switch.wav");
1572     precache_sound ("weapons/weaponpickup.wav");
1573     precache_sound ("weapons/unavailable.wav");
1574     if (g_grappling_hook)
1575     {
1576         precache_sound ("weapons/hook_fire.wav"); // hook
1577         precache_sound ("weapons/hook_impact.wav"); // hook
1578     }
1579
1580     if (cvar("sv_precacheweapons") || g_nixnex)
1581     {
1582         //precache weapon models/sounds
1583         local float wep;
1584         wep = WEP_FIRST;
1585         while (wep <= WEP_LAST)
1586         {
1587             weapon_action(wep, WR_PRECACHE);
1588             wep = wep + 1;
1589         }
1590     }
1591
1592     precache_model("models/elaser.mdl");
1593     precache_model("models/laser.mdl");
1594     precache_model("models/ebomb.mdl");
1595
1596 #if 0
1597     // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1598
1599     if (!self.noise && self.music) // quake 3 uses the music field
1600         self.noise = self.music;
1601
1602     // plays music for the level if there is any
1603     if (self.noise)
1604     {
1605         precache_sound (self.noise);
1606         ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1607     }
1608 #endif
1609 }
1610
1611 // sorry, but using \ in macros breaks line numbers
1612 #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
1613 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1614 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1615
1616 vector ExactTriggerHit_mins;
1617 vector ExactTriggerHit_maxs;
1618 float ExactTriggerHit_Recurse()
1619 {
1620     float s;
1621     entity se;
1622     float f;
1623
1624     tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1625     if not(trace_ent)
1626         return 0;
1627     if (trace_ent == self)
1628         return 1;
1629
1630     se = trace_ent;
1631     s = se.solid;
1632     se.solid = SOLID_NOT;
1633     f = ExactTriggerHit_Recurse();
1634     se.solid = s;
1635
1636     return f;
1637 }
1638
1639 float ExactTriggerHit()
1640 {
1641     float f, s;
1642
1643     if not(self.modelindex)
1644         return 1;
1645
1646     s = self.solid;
1647     self.solid = SOLID_BSP;
1648     ExactTriggerHit_mins = other.absmin;
1649     ExactTriggerHit_maxs = other.absmax;
1650     f = ExactTriggerHit_Recurse();
1651     self.solid = s;
1652
1653     return f;
1654 }
1655
1656 // WARNING: this kills the trace globals
1657 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1658 #define EXACTTRIGGER_INIT  InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1659
1660 #define INITPRIO_FIRST              0
1661 #define INITPRIO_GAMETYPE           0
1662 #define INITPRIO_GAMETYPE_FALLBACK  1
1663 #define INITPRIO_CVARS              5
1664 #define INITPRIO_FINDTARGET        10
1665 #define INITPRIO_DROPTOFLOOR       20
1666 #define INITPRIO_SETLOCATION       90
1667 #define INITPRIO_LINKDOORS         91
1668 #define INITPRIO_LAST              99
1669
1670 .void(void) initialize_entity;
1671 .float initialize_entity_order;
1672 .entity initialize_entity_next;
1673 entity initialize_entity_first;
1674
1675 void make_safe_for_remove(entity e)
1676 {
1677     if (e.initialize_entity)
1678     {
1679         entity ent, prev;
1680         for (ent = initialize_entity_first; ent; )
1681         {
1682             if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1683             {
1684                 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1685                 // skip it in linked list
1686                 if (prev)
1687                 {
1688                     prev.initialize_entity_next = ent.initialize_entity_next;
1689                     ent = prev.initialize_entity_next;
1690                 }
1691                 else
1692                 {
1693                     initialize_entity_first = ent.initialize_entity_next;
1694                     ent = initialize_entity_first;
1695                 }
1696             }
1697             else
1698             {
1699                 prev = ent;
1700                 ent = ent.initialize_entity_next;
1701             }
1702         }
1703     }
1704 }
1705
1706 void objerror(string s)
1707 {
1708     make_safe_for_remove(self);
1709     objerror_builtin(s);
1710 }
1711
1712 void remove_unsafely(entity e)
1713 {
1714     remove_builtin(e);
1715 }
1716
1717 void remove_safely(entity e)
1718 {
1719     make_safe_for_remove(e);
1720     remove_builtin(e);
1721 }
1722
1723 void InitializeEntity(entity e, void(void) func, float order)
1724 {
1725     entity prev, cur;
1726
1727     if (!e || e.initialize_entity)
1728     {
1729         // make a proxy initializer entity
1730         entity e_old;
1731         e_old = e;
1732         e = spawn();
1733         e.classname = "initialize_entity";
1734         e.enemy = e_old;
1735     }
1736
1737     e.initialize_entity = func;
1738     e.initialize_entity_order = order;
1739
1740     cur = initialize_entity_first;
1741     for (;;)
1742     {
1743         if (!cur || cur.initialize_entity_order > order)
1744         {
1745             // insert between prev and cur
1746             if (prev)
1747                 prev.initialize_entity_next = e;
1748             else
1749                 initialize_entity_first = e;
1750             e.initialize_entity_next = cur;
1751             return;
1752         }
1753         prev = cur;
1754         cur = cur.initialize_entity_next;
1755     }
1756 }
1757 void InitializeEntitiesRun()
1758 {
1759     entity startoflist;
1760     startoflist = initialize_entity_first;
1761     initialize_entity_first = world;
1762     for (self = startoflist; self; )
1763     {
1764         entity e;
1765         var void(void) func;
1766         e = self.initialize_entity_next;
1767         func = self.initialize_entity;
1768         self.initialize_entity_order = 0;
1769         self.initialize_entity = func_null;
1770         self.initialize_entity_next = world;
1771         if (self.classname == "initialize_entity")
1772         {
1773             entity e_old;
1774             e_old = self.enemy;
1775             remove_builtin(self);
1776             self = e_old;
1777         }
1778         //dprint("Delayed initialization: ", self.classname, "\n");
1779         func();
1780         self = e;
1781     }
1782 }
1783
1784 .float uncustomizeentityforclient_set;
1785 .void(void) uncustomizeentityforclient;
1786 void(void) SUB_Nullpointer = #0;
1787 void UncustomizeEntitiesRun()
1788 {
1789     entity oldself;
1790     oldself = self;
1791     for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1792         self.uncustomizeentityforclient();
1793     self = oldself;
1794 }
1795 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1796 {
1797     e.customizeentityforclient = customizer;
1798     e.uncustomizeentityforclient = uncustomizer;
1799     e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1800 }
1801
1802 .float nottargeted;
1803 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1804
1805 void() SUB_Remove;
1806 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1807 {
1808     vector mi, ma;
1809
1810     if (e.classname == "")
1811         e.classname = "net_linked";
1812
1813     if (e.model == "" || self.modelindex == 0)
1814     {
1815         mi = e.mins;
1816         ma = e.maxs;
1817         setmodel(e, "null");
1818         setsize(e, mi, ma);
1819     }
1820
1821     e.SendEntity = sendfunc;
1822     e.SendFlags = 0xFFFFFF;
1823
1824     if (!docull)
1825         e.effects |= EF_NODEPTHTEST;
1826
1827     if (dt)
1828     {
1829         e.nextthink = time + dt;
1830         e.think = SUB_Remove;
1831     }
1832 }
1833
1834 void adaptor_think2touch()
1835 {
1836     entity o;
1837     o = other;
1838     other = world;
1839     self.touch();
1840     other = o;
1841 }
1842
1843 void adaptor_think2use()
1844 {
1845     entity o, a;
1846     o = other;
1847     a = activator;
1848     activator = world;
1849     other = world;
1850     self.use();
1851     other = o;
1852     activator = a;
1853 }
1854
1855 // deferred dropping
1856 void DropToFloor_Handler()
1857 {
1858     droptofloor_builtin();
1859     self.dropped_origin = self.origin;
1860 }
1861
1862 void droptofloor()
1863 {
1864     InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1865 }
1866
1867
1868
1869 float trace_hits_box_a0, trace_hits_box_a1;
1870
1871 float trace_hits_box_1d(float end, float thmi, float thma)
1872 {
1873     if (end == 0)
1874     {
1875         // just check if x is in range
1876         if (0 < thmi)
1877             return FALSE;
1878         if (0 > thma)
1879             return FALSE;
1880     }
1881     else
1882     {
1883         // do the trace with respect to x
1884         // 0 -> end has to stay in thmi -> thma
1885         trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1886         trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1887         if (trace_hits_box_a0 > trace_hits_box_a1)
1888             return FALSE;
1889     }
1890     return TRUE;
1891 }
1892
1893 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1894 {
1895     end -= start;
1896     thmi -= start;
1897     thma -= start;
1898     // now it is a trace from 0 to end
1899
1900     trace_hits_box_a0 = 0;
1901     trace_hits_box_a1 = 1;
1902
1903     if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1904         return FALSE;
1905     if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1906         return FALSE;
1907     if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1908         return FALSE;
1909
1910     return TRUE;
1911 }
1912
1913 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1914 {
1915     return trace_hits_box(start, end, thmi - ma, thma - mi);
1916 }
1917
1918 float SUB_NoImpactCheck()
1919 {
1920         // zero hitcontents = this is not the real impact, but either the
1921         // mirror-impact of something hitting the projectile instead of the
1922         // projectile hitting the something, or a touchareagrid one. Neither of
1923         // these stop the projectile from moving, so...
1924         if(trace_dphitcontents == 0)
1925         {
1926                 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1927                 checkclient();
1928         }
1929     if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1930         return 1;
1931     if (other == world && self.size != '0 0 0')
1932     {
1933         vector tic;
1934         tic = self.velocity * sys_ticrate;
1935         tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1936         traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1937         if (trace_fraction >= 1)
1938         {
1939             dprint("Odd... did not hit...?\n");
1940         }
1941         else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1942         {
1943             dprint("Detected and prevented the sky-grapple bug.\n");
1944             return 1;
1945         }
1946     }
1947
1948     return 0;
1949 }
1950
1951 #define SUB_OwnerCheck() (other && (other == self.owner))
1952
1953 #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)
1954
1955 float MAX_IPBAN_URIS = 16;
1956
1957 float URI_GET_DISCARD   = 0;
1958 float URI_GET_IPBAN     = 1;
1959 float URI_GET_IPBAN_END = 16;
1960
1961 void URI_Get_Callback(float id, float status, string data)
1962 {
1963     dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1964     dprint(data);
1965     dprint("\nEnd of data.\n");
1966
1967     if (id == URI_GET_DISCARD)
1968     {
1969         // discard
1970     }
1971     else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1972     {
1973         // online ban list
1974         OnlineBanList_URI_Get_Callback(id, status, data);
1975     }
1976     else
1977     {
1978         print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1979     }
1980 }
1981
1982 void print_to(entity e, string s)
1983 {
1984     if (e)
1985         sprint(e, strcat(s, "\n"));
1986     else
1987         print(s, "\n");
1988 }
1989
1990 string getrecords()
1991 {
1992     float rec;
1993     string h;
1994     float r;
1995     float i;
1996     string s;
1997
1998     rec = 0;
1999
2000     s = "";
2001
2002     if (g_ctf)
2003     {
2004         for (i = 0; i < MapInfo_count; ++i)
2005         {
2006             if (MapInfo_Get_ByID(i))
2007             {
2008                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2009                 if (r == 0)
2010                     continue;
2011                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2012                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2013                 ++rec;
2014             }
2015         }
2016     }
2017
2018     if (g_race)
2019     {
2020         for (i = 0; i < MapInfo_count; ++i)
2021         {
2022             if (MapInfo_Get_ByID(i))
2023             {
2024                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
2025                 if (r == 0)
2026                     continue;
2027                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
2028                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2029                 ++rec;
2030             }
2031         }
2032     }
2033
2034     if (g_cts)
2035     {
2036         for (i = 0; i < MapInfo_count; ++i)
2037         {
2038             if (MapInfo_Get_ByID(i))
2039             {
2040                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/time")));
2041                 if (r == 0)
2042                     continue;
2043                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/netname"));
2044                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2045                 ++rec;
2046             }
2047         }
2048     }
2049
2050     MapInfo_ClearTemps();
2051
2052     if (s == "")
2053         return "No records are available on this server.\n";
2054     else
2055         return strcat("Records on this server:\n", s);
2056 }
2057
2058 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2059 {
2060     float m, i;
2061     vector start, org, delta, end, enddown, mstart;
2062
2063     m = e.dphitcontentsmask;
2064     e.dphitcontentsmask = goodcontents | badcontents;
2065
2066     org = world.mins;
2067     delta = world.maxs - world.mins;
2068
2069     for (i = 0; i < attempts; ++i)
2070     {
2071         start_x = org_x + random() * delta_x;
2072         start_y = org_y + random() * delta_y;
2073         start_z = org_z + random() * delta_z;
2074
2075         // rule 1: start inside world bounds, and outside
2076         // solid, and don't start from somewhere where you can
2077         // fall down to evil
2078         tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2079         if (trace_fraction >= 1)
2080             continue;
2081         if (trace_startsolid)
2082             continue;
2083         if (trace_dphitcontents & badcontents)
2084             continue;
2085         if (trace_dphitq3surfaceflags & badsurfaceflags)
2086             continue;
2087
2088         // rule 2: if we are too high, lower the point
2089         if (trace_fraction * delta_z > maxaboveground)
2090             start = trace_endpos + '0 0 1' * maxaboveground;
2091         enddown = trace_endpos;
2092
2093         // rule 3: make sure we aren't outside the map. This only works
2094         // for somewhat well formed maps. A good rule of thumb is that
2095         // the map should have a convex outside hull.
2096         // these can be traceLINES as we already verified the starting box
2097         mstart = start + 0.5 * (e.mins + e.maxs);
2098         traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2099         if (trace_fraction >= 1)
2100             continue;
2101         traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2102         if (trace_fraction >= 1)
2103             continue;
2104         traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2105         if (trace_fraction >= 1)
2106             continue;
2107         traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2108         if (trace_fraction >= 1)
2109             continue;
2110         traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2111         if (trace_fraction >= 1)
2112             continue;
2113
2114         // find a random vector to "look at"
2115         end_x = org_x + random() * delta_x;
2116         end_y = org_y + random() * delta_y;
2117         end_z = org_z + random() * delta_z;
2118         end = start + normalize(end - start) * vlen(delta);
2119
2120         // rule 4: start TO end must not be too short
2121         tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2122         if (trace_startsolid)
2123             continue;
2124         if (trace_fraction < minviewdistance / vlen(delta))
2125             continue;
2126
2127         // rule 5: don't want to look at sky
2128         if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2129             continue;
2130
2131         // rule 6: we must not end up in trigger_hurt
2132         if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2133         {
2134             dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2135             continue;
2136         }
2137
2138         break;
2139     }
2140
2141     e.dphitcontentsmask = m;
2142
2143     if (i < attempts)
2144     {
2145         setorigin(e, start);
2146         e.angles = vectoangles(end - start);
2147         dprint("Needed ", ftos(i + 1), " attempts\n");
2148         return TRUE;
2149     }
2150     else
2151         return FALSE;
2152 }
2153
2154 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2155 {
2156     WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2157     WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2158     WriteShort(MSG_BROADCAST, effectno);
2159     WriteCoord(MSG_BROADCAST, start_x);
2160     WriteCoord(MSG_BROADCAST, start_y);
2161     WriteCoord(MSG_BROADCAST, start_z);
2162     WriteCoord(MSG_BROADCAST, end_x);
2163     WriteCoord(MSG_BROADCAST, end_y);
2164     WriteCoord(MSG_BROADCAST, end_z);
2165     WriteCoord(MSG_BROADCAST, end_dz);
2166     WriteShort(MSG_BROADCAST, spd / 16);
2167 }
2168
2169 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2170 {
2171     float end_dz;
2172     vector vecxy, velxy;
2173
2174     vecxy = end - start;
2175     vecxy_z = 0;
2176     velxy = vel;
2177     velxy_z = 0;
2178
2179     if (vlen(velxy) < 0.000001 * fabs(vel_z))
2180     {
2181         trailparticles(world, effectno, start, end);
2182         return;
2183     }
2184
2185     end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2186     zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2187 }
2188
2189 string GetGametype(); // g_world.qc
2190 void write_recordmarker(entity pl, float tstart, float dt)
2191 {
2192     GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2193
2194     // also write a marker into demo files for demotc-race-record-extractor to find
2195     stuffcmd(pl,
2196              strcat(
2197                  strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2198                  " ", ftos(tstart), " ", ftos(dt), "\n"));
2199 }
2200
2201 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2202 {
2203         switch(self.owner.cvar_cl_gunalign)
2204         {
2205                 case 1: // right
2206                         break;
2207
2208                 case 2: // left
2209                         vecs_y = -vecs_y;
2210                         break;
2211
2212                 default:
2213                 case 3:
2214                         if(allowcenter) // 2: allow center handedness
2215                         {
2216                                 // center
2217                                 vecs_y = 0;
2218                                 vecs_z -= 4;
2219                         }
2220                         else
2221                         {
2222                                 // right
2223                         }
2224                         break;
2225
2226                 case 4:
2227                         if(allowcenter) // 2: allow center handedness
2228                         {
2229                                 // center
2230                                 vecs_y = 0;
2231                                 vecs_z -= 4;
2232                         }
2233                         else
2234                         {
2235                                 // left
2236                                 vecs_y = -vecs_y;
2237                         }
2238                         break;
2239         }
2240         return vecs;
2241 }
2242
2243 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2244 {
2245         string s;
2246         vector v;
2247
2248         if (cvar("g_shootfromeye"))
2249         {
2250                 if (visual)
2251                 {
2252                         vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2253                 }
2254                 else
2255                 {
2256                         vecs_y = 0;
2257                         vecs_z = 0;
2258                 }
2259         }
2260         else if (cvar("g_shootfromcenter"))
2261         {
2262                 if (visual)
2263                 {
2264                         vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2265                 }
2266                 else
2267                 {
2268                         vecs_y = 0;
2269                         vecs_z -= 4;
2270                 }
2271         }
2272         else if (cvar("g_shootfromclient"))
2273         {
2274                 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2275         }
2276         else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2277         {
2278                 v = stov(s);
2279                 if (y_is_right)
2280                         v_y = -v_y;
2281                 if (v_x != 0)
2282                         vecs_x = v_x;
2283                 vecs_y = v_y;
2284                 vecs_z = v_z;
2285         }
2286         return vecs;
2287 }
2288
2289
2290
2291 void attach_sameorigin(entity e, entity to, string tag)
2292 {
2293     vector org, t_forward, t_left, t_up, e_forward, e_up;
2294     vector org0, ang0;
2295     float tagscale;
2296
2297     ang0 = e.angles;
2298     org0 = e.origin;
2299
2300     org = e.origin - gettaginfo(to, gettagindex(to, tag));
2301     tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2302     t_forward = v_forward * tagscale;
2303     t_left = v_right * -tagscale;
2304     t_up = v_up * tagscale;
2305
2306     e.origin_x = org * t_forward;
2307     e.origin_y = org * t_left;
2308     e.origin_z = org * t_up;
2309
2310     // current forward and up directions
2311     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2312         e.angles_x = -e.angles_x;
2313     fixedmakevectors(e.angles);
2314
2315     // untransform forward, up!
2316     e_forward_x = v_forward * t_forward;
2317     e_forward_y = v_forward * t_left;
2318     e_forward_z = v_forward * t_up;
2319     e_up_x = v_up * t_forward;
2320     e_up_y = v_up * t_left;
2321     e_up_z = v_up * t_up;
2322
2323     e.angles = fixedvectoangles2(e_forward, e_up);
2324     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2325         e.angles_x = -e.angles_x;
2326
2327     setattachment(e, to, tag);
2328     setorigin(e, e.origin);
2329 }
2330
2331 void detach_sameorigin(entity e)
2332 {
2333     vector org;
2334     org = gettaginfo(e, 0);
2335     e.angles = fixedvectoangles2(v_forward, v_up);
2336     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2337         e.angles_x = -e.angles_x;
2338     e.origin = org;
2339     setattachment(e, world, "");
2340     setorigin(e, e.origin);
2341 }
2342
2343 void follow_sameorigin(entity e, entity to)
2344 {
2345     e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2346     e.aiment = to; // make the hole follow bmodel
2347     e.punchangle = to.angles; // the original angles of bmodel
2348     e.view_ofs = e.origin - to.origin; // relative origin
2349     e.v_angle = e.angles - to.angles; // relative angles
2350 }
2351
2352 void unfollow_sameorigin(entity e)
2353 {
2354     e.movetype = MOVETYPE_NONE;
2355 }
2356
2357 entity gettaginfo_relative_ent;
2358 vector gettaginfo_relative(entity e, float tag)
2359 {
2360     if (!gettaginfo_relative_ent)
2361     {
2362         gettaginfo_relative_ent = spawn();
2363         gettaginfo_relative_ent.effects = EF_NODRAW;
2364     }
2365     gettaginfo_relative_ent.model = e.model;
2366     gettaginfo_relative_ent.modelindex = e.modelindex;
2367     gettaginfo_relative_ent.frame = e.frame;
2368     return gettaginfo(gettaginfo_relative_ent, tag);
2369 }
2370
2371 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2372 {
2373     float p;
2374     p = pow(2, chan);
2375     if (pl.soundentity.cnt & p)
2376         return;
2377     soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2378     pl.soundentity.cnt |= p;
2379 }
2380
2381 void SoundEntity_StopSound(entity pl, float chan)
2382 {
2383     float p;
2384     p = pow(2, chan);
2385     if (pl.soundentity.cnt & p)
2386     {
2387         stopsoundto(MSG_ALL, pl.soundentity, chan);
2388         pl.soundentity.cnt &~= p;
2389     }
2390 }
2391
2392 void SoundEntity_Attach(entity pl)
2393 {
2394     pl.soundentity = spawn();
2395     pl.soundentity.classname = "soundentity";
2396     pl.soundentity.owner = pl;
2397     setattachment(pl.soundentity, pl, "");
2398     setmodel(pl.soundentity, "null");
2399 }
2400
2401 void SoundEntity_Detach(entity pl)
2402 {
2403     float i;
2404     for (i = 0; i <= 7; ++i)
2405         SoundEntity_StopSound(pl, i);
2406 }
2407
2408
2409 float ParseCommandPlayerSlotTarget_firsttoken;
2410 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2411 {
2412     string s;
2413     entity e, head;
2414     float n;
2415
2416     s = string_null;
2417
2418     ParseCommandPlayerSlotTarget_firsttoken = -1;
2419
2420     if (tokens > idx)
2421     {
2422         if (substring(argv(idx), 0, 1) == "#")
2423         {
2424             s = substring(argv(idx), 1, -1);
2425             ++idx;
2426             if (s == "")
2427                 if (tokens > idx)
2428                 {
2429                     s = argv(idx);
2430                     ++idx;
2431                 }
2432                         ParseCommandPlayerSlotTarget_firsttoken = idx;
2433             if (s == ftos(stof(s)))
2434             {
2435                 e = edict_num(stof(s));
2436                 if (e.flags & FL_CLIENT)
2437                     return e;
2438             }
2439         }
2440         else
2441         {
2442             // it must be a nick name
2443             s = argv(idx);
2444             ++idx;
2445                         ParseCommandPlayerSlotTarget_firsttoken = idx;
2446
2447             n = 0;
2448             FOR_EACH_CLIENT(head)
2449             if (head.netname == s)
2450             {
2451                 e = head;
2452                 ++n;
2453             }
2454             if (n == 1)
2455                 return e;
2456
2457             s = strdecolorize(s);
2458             n = 0;
2459             FOR_EACH_CLIENT(head)
2460             if (strdecolorize(head.netname) == s)
2461             {
2462                 e = head;
2463                 ++n;
2464             }
2465             if (n == 1)
2466                 return e;
2467         }
2468     }
2469
2470     return world;
2471 }
2472
2473 .float scale2;
2474
2475 float modeleffect_SendEntity(entity to, float sf)
2476 {
2477         float f;
2478         WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2479
2480         f = 0;
2481         if(self.velocity != '0 0 0')
2482                 f |= 1;
2483         if(self.angles != '0 0 0')
2484                 f |= 2;
2485         if(self.avelocity != '0 0 0')
2486                 f |= 4;
2487         
2488         WriteByte(MSG_ENTITY, f);
2489         WriteShort(MSG_ENTITY, self.modelindex);
2490         WriteByte(MSG_ENTITY, self.skin);
2491         WriteByte(MSG_ENTITY, self.frame);
2492         WriteCoord(MSG_ENTITY, self.origin_x);
2493         WriteCoord(MSG_ENTITY, self.origin_y);
2494         WriteCoord(MSG_ENTITY, self.origin_z);
2495         if(f & 1)
2496         {
2497                 WriteCoord(MSG_ENTITY, self.velocity_x);
2498                 WriteCoord(MSG_ENTITY, self.velocity_y);
2499                 WriteCoord(MSG_ENTITY, self.velocity_z);
2500         }
2501         if(f & 2)
2502         {
2503                 WriteCoord(MSG_ENTITY, self.angles_x);
2504                 WriteCoord(MSG_ENTITY, self.angles_y);
2505                 WriteCoord(MSG_ENTITY, self.angles_z);
2506         }
2507         if(f & 4)
2508         {
2509                 WriteCoord(MSG_ENTITY, self.avelocity_x);
2510                 WriteCoord(MSG_ENTITY, self.avelocity_y);
2511                 WriteCoord(MSG_ENTITY, self.avelocity_z);
2512         }
2513         WriteShort(MSG_ENTITY, self.scale * 256.0);
2514         WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2515         WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2516         WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2517         WriteByte(MSG_ENTITY, self.alpha * 255.0);
2518
2519         return TRUE;
2520 }
2521
2522 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)
2523 {
2524         entity e;
2525         float sz;
2526         e = spawn();
2527         e.classname = "modeleffect";
2528         setmodel(e, m);
2529         e.frame = f;
2530         setorigin(e, o);
2531         e.velocity = v;
2532         e.angles = ang;
2533         e.avelocity = angv;
2534         e.alpha = a;
2535         e.teleport_time = t1;
2536         e.fade_time = t2;
2537         e.skin = s;
2538         if(s0 >= 0)
2539                 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2540         else
2541                 e.scale = -s0;
2542         if(s2 >= 0)
2543                 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2544         else
2545                 e.scale2 = -s2;
2546         sz = max(e.scale, e.scale2);
2547         setsize(e, e.mins * sz, e.maxs * sz);
2548         Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2549 }
2550
2551 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2552 {
2553         return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2554 }