]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/miscfunctions.qc
turn off warmup in runematch too (warmup code SUCKS)
[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_health = start_health;
939         warmup_start_armorvalue = start_armorvalue;
940         warmup_start_weapons = start_weapons;
941
942         if (!g_weaponarena && !g_nixnex && !g_minstagib)
943         {
944             if (cvar("g_use_ammunition"))
945             {
946                 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
947                 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
948                 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
949                 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
950             }
951             warmup_start_health = cvar("g_warmup_start_health");
952             warmup_start_armorvalue = cvar("g_warmup_start_armor");
953             if (cvar("g_warmup_allguns"))
954             {
955                 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
956                 {
957                     e = get_weaponinfo(i);
958                     if (!(e.weapon))
959                         continue;
960                     if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
961                     {
962                         warmup_start_weapons |= e.weapons;
963                         weapon_action(e.weapon, WR_PRECACHE);
964                     }
965                 }
966             }
967         }
968     }
969
970     if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
971     {
972         g_grappling_hook = 0; // these two can't coexist, as they use the same button
973         start_items |= IT_FUEL_REGEN;
974         start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
975     }
976
977     if (g_jetpack)
978         start_items |= IT_JETPACK;
979
980     if (g_weapon_stay == 2)
981     {
982         if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
983         if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
984         if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
985         if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
986         if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
987         if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
988         if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
989         if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
990         if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
991         if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
992     }
993
994     start_ammo_shells = max(0, start_ammo_shells);
995     start_ammo_nails = max(0, start_ammo_nails);
996     start_ammo_cells = max(0, start_ammo_cells);
997     start_ammo_rockets = max(0, start_ammo_rockets);
998     start_ammo_fuel = max(0, start_ammo_fuel);
999
1000     warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1001     warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1002     warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1003     warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1004     warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1005 }
1006
1007 float g_bugrigs;
1008 float g_bugrigs_planar_movement;
1009 float g_bugrigs_planar_movement_car_jumping;
1010 float g_bugrigs_reverse_spinning;
1011 float g_bugrigs_reverse_speeding;
1012 float g_bugrigs_reverse_stopping;
1013 float g_bugrigs_air_steering;
1014 float g_bugrigs_angle_smoothing;
1015 float g_bugrigs_friction_floor;
1016 float g_bugrigs_friction_brake;
1017 float g_bugrigs_friction_air;
1018 float g_bugrigs_accel;
1019 float g_bugrigs_speed_ref;
1020 float g_bugrigs_speed_pow;
1021 float g_bugrigs_steer;
1022
1023 float g_touchexplode;
1024 float g_touchexplode_radius;
1025 float g_touchexplode_damage;
1026 float g_touchexplode_edgedamage;
1027 float g_touchexplode_force;
1028
1029 float sv_autotaunt;
1030 float sv_taunt;
1031
1032 void readlevelcvars(void)
1033 {
1034     g_bugrigs = cvar("g_bugrigs");
1035     g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1036     g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1037     g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1038     g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1039     g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1040     g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1041     g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1042     g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1043     g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1044     g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1045     g_bugrigs_accel = cvar("g_bugrigs_accel");
1046     g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1047     g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1048     g_bugrigs_steer = cvar("g_bugrigs_steer");
1049
1050     g_touchexplode = cvar("g_touchexplode");
1051     g_touchexplode_radius = cvar("g_touchexplode_radius");
1052     g_touchexplode_damage = cvar("g_touchexplode_damage");
1053     g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1054     g_touchexplode_force = cvar("g_touchexplode_force");
1055
1056 #ifdef ALLOW_FORCEMODELS
1057     sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1058 #endif
1059     sv_loddistance1 = cvar("sv_loddistance1");
1060     sv_loddistance2 = cvar("sv_loddistance2");
1061         if(sv_loddistance2 <= sv_loddistance1)
1062                 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1063     sv_clones = cvar("sv_clones");
1064     sv_cheats = cvar("sv_cheats");
1065     sv_gentle = cvar("sv_gentle");
1066     sv_foginterval = cvar("sv_foginterval");
1067     g_cloaked = cvar("g_cloaked");
1068     g_jump_grunt = cvar("g_jump_grunt");
1069     g_footsteps = cvar("g_footsteps");
1070     g_grappling_hook = cvar("g_grappling_hook");
1071     g_jetpack = cvar("g_jetpack");
1072     g_laserguided_missile = cvar("g_laserguided_missile");
1073     g_midair = cvar("g_midair");
1074     g_minstagib = cvar("g_minstagib");
1075     g_nixnex = cvar("g_nixnex");
1076     g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1077     g_norecoil = cvar("g_norecoil");
1078     g_vampire = cvar("g_vampire");
1079     g_bloodloss = cvar("g_bloodloss");
1080     sv_maxidle = cvar("sv_maxidle");
1081     sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1082     sv_pogostick = cvar("sv_pogostick");
1083     sv_doublejump = cvar("sv_doublejump");
1084     g_ctf_reverse = cvar("g_ctf_reverse");
1085         sv_autotaunt = cvar("sv_autotaunt");
1086         sv_taunt = cvar("sv_taunt");
1087
1088     inWarmupStage = cvar("g_warmup");
1089     g_warmup_limit = cvar("g_warmup_limit");
1090     g_warmup_allguns = cvar("g_warmup_allguns");
1091     g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1092
1093     if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1094         inWarmupStage = 0; // these modes cannot work together, sorry
1095
1096     g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1097     g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1098     g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1099     g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1100     g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1101     g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1102     g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1103     g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1104     g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1105     g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1106     g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1107     g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1108
1109     if (g_minstagib) g_nixnex = g_weaponarena = 0;
1110     if (g_nixnex) g_weaponarena = 0;
1111     g_weaponarena = 0;
1112
1113     g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1114     g_weaponratefactor = cvar("g_weaponratefactor");
1115     g_weapondamagefactor = cvar("g_weapondamagefactor");
1116     g_weaponforcefactor = cvar("g_weaponforcefactor");
1117
1118     g_pickup_shells                    = cvar("g_pickup_shells");
1119     g_pickup_shells_max                = cvar("g_pickup_shells_max");
1120     g_pickup_nails                     = cvar("g_pickup_nails");
1121     g_pickup_nails_max                 = cvar("g_pickup_nails_max");
1122     g_pickup_rockets                   = cvar("g_pickup_rockets");
1123     g_pickup_rockets_max               = cvar("g_pickup_rockets_max");
1124     g_pickup_cells                     = cvar("g_pickup_cells");
1125     g_pickup_cells_max                 = cvar("g_pickup_cells_max");
1126     g_pickup_fuel                     = cvar("g_pickup_fuel");
1127     g_pickup_fuel_jetpack             = cvar("g_pickup_fuel_jetpack");
1128     g_pickup_fuel_max                 = cvar("g_pickup_fuel_max");
1129     g_pickup_armorsmall                = cvar("g_pickup_armorsmall");
1130     g_pickup_armorsmall_max            = cvar("g_pickup_armorsmall_max");
1131     g_pickup_armormedium               = cvar("g_pickup_armormedium");
1132     g_pickup_armormedium_max           = cvar("g_pickup_armormedium_max");
1133     g_pickup_armorbig                  = cvar("g_pickup_armorbig");
1134     g_pickup_armorbig_max              = cvar("g_pickup_armorbig_max");
1135     g_pickup_armorlarge                = cvar("g_pickup_armorlarge");
1136     g_pickup_armorlarge_max            = cvar("g_pickup_armorlarge_max");
1137     g_pickup_healthsmall               = cvar("g_pickup_healthsmall");
1138     g_pickup_healthsmall_max           = cvar("g_pickup_healthsmall_max");
1139     g_pickup_healthmedium              = cvar("g_pickup_healthmedium");
1140     g_pickup_healthmedium_max          = cvar("g_pickup_healthmedium_max");
1141     g_pickup_healthlarge               = cvar("g_pickup_healthlarge");
1142     g_pickup_healthlarge_max           = cvar("g_pickup_healthlarge_max");
1143     g_pickup_healthmega                = cvar("g_pickup_healthmega");
1144     g_pickup_healthmega_max            = cvar("g_pickup_healthmega_max");
1145
1146     g_pinata = cvar("g_pinata");
1147
1148     g_weapon_stay = cvar("g_weapon_stay");
1149     if (!g_weapon_stay && (cvar("deathmatch") == 2))
1150         g_weapon_stay = 1;
1151
1152     if not(inWarmupStage)
1153         game_starttime                 = cvar("g_start_delay");
1154
1155     readplayerstartcvars();
1156 }
1157
1158 /*
1159 // TODO sound pack system
1160 string soundpack;
1161
1162 string precache_sound_builtin (string s) = #19;
1163 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1164 string precache_sound(string s)
1165 {
1166         return precache_sound_builtin(strcat(soundpack, s));
1167 }
1168 void play2(entity e, string filename)
1169 {
1170         stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1171 }
1172 void sound(entity e, float chan, string samp, float vol, float atten)
1173 {
1174         sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1175 }
1176 */
1177
1178 // Sound functions
1179 string precache_sound (string s) = #19;
1180 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1181 float precache_sound_index (string s) = #19;
1182
1183 #define SND_VOLUME      1
1184 #define SND_ATTENUATION 2
1185 #define SND_LARGEENTITY 8
1186 #define SND_LARGESOUND  16
1187
1188 float sound_allowed(float dest, entity e)
1189 {
1190     // sounds from world may always pass
1191     for (;;)
1192     {
1193         if (e.classname == "body")
1194             e = e.enemy;
1195         if (e.owner && e.owner != e)
1196             e = e.owner;
1197         else
1198             break;
1199     }
1200     // sounds to self may always pass
1201     if (dest == MSG_ONE)
1202         if (e == msg_entity)
1203             return TRUE;
1204     // sounds by players can be removed
1205     if (cvar("bot_sound_monopoly"))
1206         if (clienttype(e) == CLIENTTYPE_REAL)
1207             return FALSE;
1208     // anything else may pass
1209     return TRUE;
1210 }
1211
1212 void sound(entity e, float chan, string samp, float vol, float atten)
1213 {
1214     if (!sound_allowed(MSG_BROADCAST, e))
1215         return;
1216     sound_builtin(e, chan, samp, vol, atten);
1217 }
1218 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1219 {
1220     float entno, idx;
1221
1222     if (!sound_allowed(dest, e))
1223         return;
1224
1225     entno = num_for_edict(e);
1226     idx = precache_sound_index(samp);
1227
1228     float sflags;
1229     sflags = 0;
1230
1231     atten = floor(atten * 64);
1232     vol = floor(vol * 255);
1233
1234     if (vol != 255)
1235         sflags |= SND_VOLUME;
1236     if (atten != 64)
1237         sflags |= SND_ATTENUATION;
1238     if (entno >= 8192)
1239         sflags |= SND_LARGEENTITY;
1240     if (idx >= 256)
1241         sflags |= SND_LARGESOUND;
1242
1243     WriteByte(dest, SVC_SOUND);
1244     WriteByte(dest, sflags);
1245     if (sflags & SND_VOLUME)
1246         WriteByte(dest, vol);
1247     if (sflags & SND_ATTENUATION)
1248         WriteByte(dest, atten);
1249     if (sflags & SND_LARGEENTITY)
1250     {
1251         WriteShort(dest, entno);
1252         WriteByte(dest, chan);
1253     }
1254     else
1255     {
1256         WriteShort(dest, entno * 8 + chan);
1257     }
1258     if (sflags & SND_LARGESOUND)
1259         WriteShort(dest, idx);
1260     else
1261         WriteByte(dest, idx);
1262
1263     WriteCoord(dest, o_x);
1264     WriteCoord(dest, o_y);
1265     WriteCoord(dest, o_z);
1266 }
1267 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1268 {
1269     vector o;
1270
1271     if (!sound_allowed(dest, e))
1272         return;
1273
1274     o = e.origin + 0.5 * (e.mins + e.maxs);
1275     soundtoat(dest, e, o, chan, samp, vol, atten);
1276 }
1277 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1278 {
1279     soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1280 }
1281 void stopsoundto(float dest, entity e, float chan)
1282 {
1283     float entno;
1284
1285     if (!sound_allowed(dest, e))
1286         return;
1287
1288     entno = num_for_edict(e);
1289
1290     if (entno >= 8192)
1291     {
1292         float idx, sflags;
1293         idx = precache_sound_index("misc/null.wav");
1294         sflags = SND_LARGEENTITY;
1295         if (idx >= 256)
1296             sflags |= SND_LARGESOUND;
1297         WriteByte(dest, SVC_SOUND);
1298         WriteByte(dest, sflags);
1299         WriteShort(dest, entno);
1300         WriteByte(dest, chan);
1301         if (sflags & SND_LARGESOUND)
1302             WriteShort(dest, idx);
1303         else
1304             WriteByte(dest, idx);
1305         WriteCoord(dest, e.origin_x);
1306         WriteCoord(dest, e.origin_y);
1307         WriteCoord(dest, e.origin_z);
1308     }
1309     else
1310     {
1311         WriteByte(dest, SVC_STOPSOUND);
1312         WriteShort(dest, entno * 8 + chan);
1313     }
1314 }
1315 void stopsound(entity e, float chan)
1316 {
1317     if (!sound_allowed(MSG_BROADCAST, e))
1318         return;
1319
1320     stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1321     stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1322 }
1323
1324 void play2(entity e, string filename)
1325 {
1326     //stuffcmd(e, strcat("play2 ", filename, "\n"));
1327     msg_entity = e;
1328     soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1329 }
1330
1331 .float announcetime;
1332 float announce(entity player, string msg)
1333 {
1334     if (time > player.announcetime)
1335         if (clienttype(player) == CLIENTTYPE_REAL)
1336         {
1337             player.announcetime = time + 0.8;
1338             play2(player, msg);
1339             return TRUE;
1340         }
1341     return FALSE;
1342 }
1343 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1344 float spamsound(entity e, float chan, string samp, float vol, float atten)
1345 {
1346     if (!sound_allowed(MSG_BROADCAST, e))
1347         return FALSE;
1348
1349     if (time > e.announcetime)
1350     {
1351         e.announcetime = time;
1352         sound(e, chan, samp, vol, atten);
1353         return TRUE;
1354     }
1355     return FALSE;
1356 }
1357
1358 void play2team(float t, string filename)
1359 {
1360     local entity head;
1361
1362     if (cvar("bot_sound_monopoly"))
1363         return;
1364
1365     FOR_EACH_REALPLAYER(head)
1366     {
1367         if (head.team == t)
1368             play2(head, filename);
1369     }
1370 }
1371
1372 void play2all(string samp)
1373 {
1374     if (cvar("bot_sound_monopoly"))
1375         return;
1376
1377     sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1378 }
1379
1380 void PrecachePlayerSounds(string f);
1381 void precache_all_models(string pattern)
1382 {
1383     float globhandle, i, n;
1384     string f;
1385
1386     globhandle = search_begin(pattern, TRUE, FALSE);
1387     if (globhandle < 0)
1388         return;
1389     n = search_getsize(globhandle);
1390     for (i = 0; i < n; ++i)
1391     {
1392                 //print(search_getfilename(globhandle, i), "\n");
1393                 f = search_getfilename(globhandle, i);
1394                 if(sv_loddistance1)
1395                         precache_model(f);
1396                 if(substring(f, -9,5) == "_lod1")
1397                         continue;
1398                 if(substring(f, -9,5) == "_lod2")
1399                         continue;
1400                 if(!sv_loddistance1)
1401                         precache_model(f);
1402                 PrecachePlayerSounds(strcat(f, ".sounds"));
1403     }
1404     search_end(globhandle);
1405 }
1406
1407 void precache()
1408 {
1409     // gamemode related things
1410     precache_model ("models/misc/chatbubble.spr");
1411     precache_model ("models/misc/teambubble.spr");
1412     if (g_runematch)
1413     {
1414         precache_model ("models/runematch/curse.mdl");
1415         precache_model ("models/runematch/rune.mdl");
1416     }
1417
1418 #ifdef TTURRETS_ENABLED
1419     if (cvar("g_turrets"))
1420         turrets_precash();
1421 #endif
1422
1423     // Precache all player models if desired
1424     if (cvar("sv_precacheplayermodels"))
1425     {
1426         PrecachePlayerSounds("sound/player/default.sounds");
1427         precache_all_models("models/player/*.zym");
1428         precache_all_models("models/player/*.dpm");
1429         precache_all_models("models/player/*.md3");
1430         precache_all_models("models/player/*.psk");
1431         //precache_model("models/player/carni.zym");
1432         //precache_model("models/player/crash.zym");
1433         //precache_model("models/player/grunt.zym");
1434         //precache_model("models/player/headhunter.zym");
1435         //precache_model("models/player/insurrectionist.zym");
1436         //precache_model("models/player/jeandarc.zym");
1437         //precache_model("models/player/lurk.zym");
1438         //precache_model("models/player/lycanthrope.zym");
1439         //precache_model("models/player/marine.zym");
1440         //precache_model("models/player/nexus.zym");
1441         //precache_model("models/player/pyria.zym");
1442         //precache_model("models/player/shock.zym");
1443         //precache_model("models/player/skadi.zym");
1444         //precache_model("models/player/specop.zym");
1445         //precache_model("models/player/visitant.zym");
1446     }
1447
1448     if (cvar("sv_defaultcharacter"))
1449     {
1450         string s;
1451         s = cvar_string("sv_defaultplayermodel_red");
1452         if (s != "")
1453         {
1454             precache_model(s);
1455             PrecachePlayerSounds(strcat(s, ".sounds"));
1456         }
1457         s = cvar_string("sv_defaultplayermodel_blue");
1458         if (s != "")
1459         {
1460             precache_model(s);
1461             PrecachePlayerSounds(strcat(s, ".sounds"));
1462         }
1463         s = cvar_string("sv_defaultplayermodel_yellow");
1464         if (s != "")
1465         {
1466             precache_model(s);
1467             PrecachePlayerSounds(strcat(s, ".sounds"));
1468         }
1469         s = cvar_string("sv_defaultplayermodel_pink");
1470         if (s != "")
1471         {
1472             precache_model(s);
1473             PrecachePlayerSounds(strcat(s, ".sounds"));
1474         }
1475         s = cvar_string("sv_defaultplayermodel");
1476         if (s != "")
1477         {
1478             precache_model(s);
1479             PrecachePlayerSounds(strcat(s, ".sounds"));
1480         }
1481     }
1482
1483     if (g_footsteps)
1484     {
1485         PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1486         PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1487     }
1488
1489     // gore and miscellaneous sounds
1490     //precache_sound ("misc/h2ohit.wav");
1491     precache_model ("models/hook.md3");
1492     precache_sound ("misc/armorimpact.wav");
1493     precache_sound ("misc/bodyimpact1.wav");
1494     precache_sound ("misc/bodyimpact2.wav");
1495     precache_sound ("misc/gib.wav");
1496     precache_sound ("misc/gib_splat01.wav");
1497     precache_sound ("misc/gib_splat02.wav");
1498     precache_sound ("misc/gib_splat03.wav");
1499     precache_sound ("misc/gib_splat04.wav");
1500     precache_sound ("misc/hit.wav");
1501     PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1502     PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1503     precache_sound ("misc/null.wav");
1504     precache_sound ("misc/spawn.wav");
1505     precache_sound ("misc/talk.wav");
1506     precache_sound ("misc/teleport.wav");
1507     precache_sound ("misc/poweroff.wav");
1508     precache_sound ("player/lava.wav");
1509     precache_sound ("player/slime.wav");
1510
1511     if (g_jetpack)
1512         precache_sound ("misc/jetpack_fly.wav");
1513
1514     // announcer sounds - male
1515     precache_sound ("announcer/male/electrobitch.wav");
1516     precache_sound ("announcer/male/airshot.wav");
1517     precache_sound ("announcer/male/03kills.wav");
1518     precache_sound ("announcer/male/05kills.wav");
1519     precache_sound ("announcer/male/10kills.wav");
1520     precache_sound ("announcer/male/15kills.wav");
1521     precache_sound ("announcer/male/20kills.wav");
1522     precache_sound ("announcer/male/25kills.wav");
1523     precache_sound ("announcer/male/30kills.wav");
1524     precache_sound ("announcer/male/botlike.wav");
1525     precache_sound ("announcer/male/yoda.wav");
1526     precache_sound ("announcer/male/amazing.wav");
1527     precache_sound ("announcer/male/awesome.wav");
1528     precache_sound ("announcer/male/headshot.wav");
1529     precache_sound ("announcer/male/impressive.wav");
1530
1531     // announcer sounds - robotic
1532     precache_sound ("announcer/robotic/prepareforbattle.wav");
1533     precache_sound ("announcer/robotic/begin.wav");
1534     precache_sound ("announcer/robotic/timeoutcalled.wav");
1535     precache_sound ("announcer/robotic/1fragleft.wav");
1536     precache_sound ("announcer/robotic/2fragsleft.wav");
1537     precache_sound ("announcer/robotic/3fragsleft.wav");
1538     precache_sound ("announcer/robotic/terminated.wav");
1539     if (g_minstagib)
1540     {
1541         precache_sound ("announcer/robotic/lastsecond.wav");
1542         precache_sound ("announcer/robotic/narrowly.wav");
1543     }
1544
1545     precache_model ("models/sprites/0.spr32");
1546     precache_model ("models/sprites/1.spr32");
1547     precache_model ("models/sprites/2.spr32");
1548     precache_model ("models/sprites/3.spr32");
1549     precache_model ("models/sprites/4.spr32");
1550     precache_model ("models/sprites/5.spr32");
1551     precache_model ("models/sprites/6.spr32");
1552     precache_model ("models/sprites/7.spr32");
1553     precache_model ("models/sprites/8.spr32");
1554     precache_model ("models/sprites/9.spr32");
1555     precache_model ("models/sprites/10.spr32");
1556     precache_sound ("announcer/robotic/1.wav");
1557     precache_sound ("announcer/robotic/2.wav");
1558     precache_sound ("announcer/robotic/3.wav");
1559     precache_sound ("announcer/robotic/4.wav");
1560     precache_sound ("announcer/robotic/5.wav");
1561     precache_sound ("announcer/robotic/6.wav");
1562     precache_sound ("announcer/robotic/7.wav");
1563     precache_sound ("announcer/robotic/8.wav");
1564     precache_sound ("announcer/robotic/9.wav");
1565     precache_sound ("announcer/robotic/10.wav");
1566
1567     // common weapon precaches
1568     precache_sound ("weapons/weapon_switch.wav");
1569     precache_sound ("weapons/weaponpickup.wav");
1570     precache_sound ("weapons/unavailable.wav");
1571     if (g_grappling_hook)
1572     {
1573         precache_sound ("weapons/hook_fire.wav"); // hook
1574         precache_sound ("weapons/hook_impact.wav"); // hook
1575     }
1576
1577     if (cvar("sv_precacheweapons") || g_nixnex)
1578     {
1579         //precache weapon models/sounds
1580         local float wep;
1581         wep = WEP_FIRST;
1582         while (wep <= WEP_LAST)
1583         {
1584             weapon_action(wep, WR_PRECACHE);
1585             wep = wep + 1;
1586         }
1587     }
1588
1589     precache_model("models/elaser.mdl");
1590     precache_model("models/laser.mdl");
1591     precache_model("models/ebomb.mdl");
1592
1593 #if 0
1594     // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1595
1596     if (!self.noise && self.music) // quake 3 uses the music field
1597         self.noise = self.music;
1598
1599     // plays music for the level if there is any
1600     if (self.noise)
1601     {
1602         precache_sound (self.noise);
1603         ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1604     }
1605 #endif
1606 }
1607
1608 // sorry, but using \ in macros breaks line numbers
1609 #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
1610 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1611 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1612
1613 vector ExactTriggerHit_mins;
1614 vector ExactTriggerHit_maxs;
1615 float ExactTriggerHit_Recurse()
1616 {
1617     float s;
1618     entity se;
1619     float f;
1620
1621     tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1622     if not(trace_ent)
1623         return 0;
1624     if (trace_ent == self)
1625         return 1;
1626
1627     se = trace_ent;
1628     s = se.solid;
1629     se.solid = SOLID_NOT;
1630     f = ExactTriggerHit_Recurse();
1631     se.solid = s;
1632
1633     return f;
1634 }
1635
1636 float ExactTriggerHit()
1637 {
1638     float f, s;
1639
1640     if not(self.modelindex)
1641         return 1;
1642
1643     s = self.solid;
1644     self.solid = SOLID_BSP;
1645     ExactTriggerHit_mins = other.absmin;
1646     ExactTriggerHit_maxs = other.absmax;
1647     f = ExactTriggerHit_Recurse();
1648     self.solid = s;
1649
1650     return f;
1651 }
1652
1653 // WARNING: this kills the trace globals
1654 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1655 #define EXACTTRIGGER_INIT  InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1656
1657 #define INITPRIO_FIRST              0
1658 #define INITPRIO_GAMETYPE           0
1659 #define INITPRIO_GAMETYPE_FALLBACK  1
1660 #define INITPRIO_CVARS              5
1661 #define INITPRIO_FINDTARGET        10
1662 #define INITPRIO_DROPTOFLOOR       20
1663 #define INITPRIO_SETLOCATION       90
1664 #define INITPRIO_LINKDOORS         91
1665 #define INITPRIO_LAST              99
1666
1667 .void(void) initialize_entity;
1668 .float initialize_entity_order;
1669 .entity initialize_entity_next;
1670 entity initialize_entity_first;
1671
1672 void make_safe_for_remove(entity e)
1673 {
1674     if (e.initialize_entity)
1675     {
1676         entity ent, prev;
1677         for (ent = initialize_entity_first; ent; )
1678         {
1679             if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1680             {
1681                 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1682                 // skip it in linked list
1683                 if (prev)
1684                 {
1685                     prev.initialize_entity_next = ent.initialize_entity_next;
1686                     ent = prev.initialize_entity_next;
1687                 }
1688                 else
1689                 {
1690                     initialize_entity_first = ent.initialize_entity_next;
1691                     ent = initialize_entity_first;
1692                 }
1693             }
1694             else
1695             {
1696                 prev = ent;
1697                 ent = ent.initialize_entity_next;
1698             }
1699         }
1700     }
1701 }
1702
1703 void objerror(string s)
1704 {
1705     make_safe_for_remove(self);
1706     objerror_builtin(s);
1707 }
1708
1709 void remove_unsafely(entity e)
1710 {
1711     remove_builtin(e);
1712 }
1713
1714 void remove_safely(entity e)
1715 {
1716     make_safe_for_remove(e);
1717     remove_builtin(e);
1718 }
1719
1720 void InitializeEntity(entity e, void(void) func, float order)
1721 {
1722     entity prev, cur;
1723
1724     if (!e || e.initialize_entity)
1725     {
1726         // make a proxy initializer entity
1727         entity e_old;
1728         e_old = e;
1729         e = spawn();
1730         e.classname = "initialize_entity";
1731         e.enemy = e_old;
1732     }
1733
1734     e.initialize_entity = func;
1735     e.initialize_entity_order = order;
1736
1737     cur = initialize_entity_first;
1738     for (;;)
1739     {
1740         if (!cur || cur.initialize_entity_order > order)
1741         {
1742             // insert between prev and cur
1743             if (prev)
1744                 prev.initialize_entity_next = e;
1745             else
1746                 initialize_entity_first = e;
1747             e.initialize_entity_next = cur;
1748             return;
1749         }
1750         prev = cur;
1751         cur = cur.initialize_entity_next;
1752     }
1753 }
1754 void InitializeEntitiesRun()
1755 {
1756     entity startoflist;
1757     startoflist = initialize_entity_first;
1758     initialize_entity_first = world;
1759     for (self = startoflist; self; )
1760     {
1761         entity e;
1762         var void(void) func;
1763         e = self.initialize_entity_next;
1764         func = self.initialize_entity;
1765         self.initialize_entity_order = 0;
1766         self.initialize_entity = func_null;
1767         self.initialize_entity_next = world;
1768         if (self.classname == "initialize_entity")
1769         {
1770             entity e_old;
1771             e_old = self.enemy;
1772             remove_builtin(self);
1773             self = e_old;
1774         }
1775         //dprint("Delayed initialization: ", self.classname, "\n");
1776         func();
1777         self = e;
1778     }
1779 }
1780
1781 .float uncustomizeentityforclient_set;
1782 .void(void) uncustomizeentityforclient;
1783 void(void) SUB_Nullpointer = #0;
1784 void UncustomizeEntitiesRun()
1785 {
1786     entity oldself;
1787     oldself = self;
1788     for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1789         self.uncustomizeentityforclient();
1790     self = oldself;
1791 }
1792 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1793 {
1794     e.customizeentityforclient = customizer;
1795     e.uncustomizeentityforclient = uncustomizer;
1796     e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1797 }
1798
1799 .float nottargeted;
1800 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1801
1802 void() SUB_Remove;
1803 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1804 {
1805     vector mi, ma;
1806
1807     if (e.classname == "")
1808         e.classname = "net_linked";
1809
1810     if (e.model == "" || self.modelindex == 0)
1811     {
1812         mi = e.mins;
1813         ma = e.maxs;
1814         setmodel(e, "null");
1815         setsize(e, mi, ma);
1816     }
1817
1818     e.SendEntity = sendfunc;
1819     e.SendFlags = 0xFFFFFF;
1820
1821     if (!docull)
1822         e.effects |= EF_NODEPTHTEST;
1823
1824     if (dt)
1825     {
1826         e.nextthink = time + dt;
1827         e.think = SUB_Remove;
1828     }
1829 }
1830
1831 void adaptor_think2touch()
1832 {
1833     entity o;
1834     o = other;
1835     other = world;
1836     self.touch();
1837     other = o;
1838 }
1839
1840 void adaptor_think2use()
1841 {
1842     entity o, a;
1843     o = other;
1844     a = activator;
1845     activator = world;
1846     other = world;
1847     self.use();
1848     other = o;
1849     activator = a;
1850 }
1851
1852 // deferred dropping
1853 void DropToFloor_Handler()
1854 {
1855     droptofloor_builtin();
1856     self.dropped_origin = self.origin;
1857 }
1858
1859 void droptofloor()
1860 {
1861     InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1862 }
1863
1864
1865
1866 float trace_hits_box_a0, trace_hits_box_a1;
1867
1868 float trace_hits_box_1d(float end, float thmi, float thma)
1869 {
1870     if (end == 0)
1871     {
1872         // just check if x is in range
1873         if (0 < thmi)
1874             return FALSE;
1875         if (0 > thma)
1876             return FALSE;
1877     }
1878     else
1879     {
1880         // do the trace with respect to x
1881         // 0 -> end has to stay in thmi -> thma
1882         trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1883         trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1884         if (trace_hits_box_a0 > trace_hits_box_a1)
1885             return FALSE;
1886     }
1887     return TRUE;
1888 }
1889
1890 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1891 {
1892     end -= start;
1893     thmi -= start;
1894     thma -= start;
1895     // now it is a trace from 0 to end
1896
1897     trace_hits_box_a0 = 0;
1898     trace_hits_box_a1 = 1;
1899
1900     if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1901         return FALSE;
1902     if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1903         return FALSE;
1904     if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1905         return FALSE;
1906
1907     return TRUE;
1908 }
1909
1910 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1911 {
1912     return trace_hits_box(start, end, thmi - ma, thma - mi);
1913 }
1914
1915 float SUB_NoImpactCheck()
1916 {
1917         // zero hitcontents = this is not the real impact, but either the
1918         // mirror-impact of something hitting the projectile instead of the
1919         // projectile hitting the something, or a touchareagrid one. Neither of
1920         // these stop the projectile from moving, so...
1921         if(trace_dphitcontents == 0)
1922         {
1923                 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1924                 checkclient();
1925         }
1926     if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1927         return 1;
1928     if (other == world && self.size != '0 0 0')
1929     {
1930         vector tic;
1931         tic = self.velocity * sys_ticrate;
1932         tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1933         traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1934         if (trace_fraction >= 1)
1935         {
1936             dprint("Odd... did not hit...?\n");
1937         }
1938         else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1939         {
1940             dprint("Detected and prevented the sky-grapple bug.\n");
1941             return 1;
1942         }
1943     }
1944
1945     return 0;
1946 }
1947
1948 #define SUB_OwnerCheck() (other && (other == self.owner))
1949
1950 #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)
1951
1952 float MAX_IPBAN_URIS = 16;
1953
1954 float URI_GET_DISCARD   = 0;
1955 float URI_GET_IPBAN     = 1;
1956 float URI_GET_IPBAN_END = 16;
1957
1958 void URI_Get_Callback(float id, float status, string data)
1959 {
1960     dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1961     dprint(data);
1962     dprint("\nEnd of data.\n");
1963
1964     if (id == URI_GET_DISCARD)
1965     {
1966         // discard
1967     }
1968     else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1969     {
1970         // online ban list
1971         OnlineBanList_URI_Get_Callback(id, status, data);
1972     }
1973     else
1974     {
1975         print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1976     }
1977 }
1978
1979 void print_to(entity e, string s)
1980 {
1981     if (e)
1982         sprint(e, strcat(s, "\n"));
1983     else
1984         print(s, "\n");
1985 }
1986
1987 string getrecords()
1988 {
1989     float rec;
1990     string h;
1991     float r;
1992     float i;
1993     string s;
1994
1995     rec = 0;
1996
1997     s = "";
1998
1999     if (g_ctf)
2000     {
2001         for (i = 0; i < MapInfo_count; ++i)
2002         {
2003             if (MapInfo_Get_ByID(i))
2004             {
2005                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2006                 if (r == 0)
2007                     continue;
2008                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2009                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2010                 ++rec;
2011             }
2012         }
2013     }
2014
2015     if (g_race)
2016     {
2017         for (i = 0; i < MapInfo_count; ++i)
2018         {
2019             if (MapInfo_Get_ByID(i))
2020             {
2021                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
2022                 if (r == 0)
2023                     continue;
2024                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
2025                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2026                 ++rec;
2027             }
2028         }
2029     }
2030
2031     if (g_cts)
2032     {
2033         for (i = 0; i < MapInfo_count; ++i)
2034         {
2035             if (MapInfo_Get_ByID(i))
2036             {
2037                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/time")));
2038                 if (r == 0)
2039                     continue;
2040                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/netname"));
2041                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2042                 ++rec;
2043             }
2044         }
2045     }
2046
2047     MapInfo_ClearTemps();
2048
2049     if (s == "")
2050         return "No records are available on this server.\n";
2051     else
2052         return strcat("Records on this server:\n", s);
2053 }
2054
2055 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2056 {
2057     float m, i;
2058     vector start, org, delta, end, enddown, mstart;
2059
2060     m = e.dphitcontentsmask;
2061     e.dphitcontentsmask = goodcontents | badcontents;
2062
2063     org = world.mins;
2064     delta = world.maxs - world.mins;
2065
2066     for (i = 0; i < attempts; ++i)
2067     {
2068         start_x = org_x + random() * delta_x;
2069         start_y = org_y + random() * delta_y;
2070         start_z = org_z + random() * delta_z;
2071
2072         // rule 1: start inside world bounds, and outside
2073         // solid, and don't start from somewhere where you can
2074         // fall down to evil
2075         tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2076         if (trace_fraction >= 1)
2077             continue;
2078         if (trace_startsolid)
2079             continue;
2080         if (trace_dphitcontents & badcontents)
2081             continue;
2082         if (trace_dphitq3surfaceflags & badsurfaceflags)
2083             continue;
2084
2085         // rule 2: if we are too high, lower the point
2086         if (trace_fraction * delta_z > maxaboveground)
2087             start = trace_endpos + '0 0 1' * maxaboveground;
2088         enddown = trace_endpos;
2089
2090         // rule 3: make sure we aren't outside the map. This only works
2091         // for somewhat well formed maps. A good rule of thumb is that
2092         // the map should have a convex outside hull.
2093         // these can be traceLINES as we already verified the starting box
2094         mstart = start + 0.5 * (e.mins + e.maxs);
2095         traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2096         if (trace_fraction >= 1)
2097             continue;
2098         traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2099         if (trace_fraction >= 1)
2100             continue;
2101         traceline(mstart, mstart + '0 1 0' * delta_y, 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 0 1' * delta_z, MOVE_NORMAL, e);
2108         if (trace_fraction >= 1)
2109             continue;
2110
2111         // find a random vector to "look at"
2112         end_x = org_x + random() * delta_x;
2113         end_y = org_y + random() * delta_y;
2114         end_z = org_z + random() * delta_z;
2115         end = start + normalize(end - start) * vlen(delta);
2116
2117         // rule 4: start TO end must not be too short
2118         tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2119         if (trace_startsolid)
2120             continue;
2121         if (trace_fraction < minviewdistance / vlen(delta))
2122             continue;
2123
2124         // rule 5: don't want to look at sky
2125         if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2126             continue;
2127
2128         // rule 6: we must not end up in trigger_hurt
2129         if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2130         {
2131             dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2132             continue;
2133         }
2134
2135         break;
2136     }
2137
2138     e.dphitcontentsmask = m;
2139
2140     if (i < attempts)
2141     {
2142         setorigin(e, start);
2143         e.angles = vectoangles(end - start);
2144         dprint("Needed ", ftos(i + 1), " attempts\n");
2145         return TRUE;
2146     }
2147     else
2148         return FALSE;
2149 }
2150
2151 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2152 {
2153     WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2154     WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2155     WriteShort(MSG_BROADCAST, effectno);
2156     WriteCoord(MSG_BROADCAST, start_x);
2157     WriteCoord(MSG_BROADCAST, start_y);
2158     WriteCoord(MSG_BROADCAST, start_z);
2159     WriteCoord(MSG_BROADCAST, end_x);
2160     WriteCoord(MSG_BROADCAST, end_y);
2161     WriteCoord(MSG_BROADCAST, end_z);
2162     WriteCoord(MSG_BROADCAST, end_dz);
2163     WriteShort(MSG_BROADCAST, spd / 16);
2164 }
2165
2166 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2167 {
2168     float end_dz;
2169     vector vecxy, velxy;
2170
2171     vecxy = end - start;
2172     vecxy_z = 0;
2173     velxy = vel;
2174     velxy_z = 0;
2175
2176     if (vlen(velxy) < 0.000001 * fabs(vel_z))
2177     {
2178         trailparticles(world, effectno, start, end);
2179         return;
2180     }
2181
2182     end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2183     zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2184 }
2185
2186 string GetGametype(); // g_world.qc
2187 void write_recordmarker(entity pl, float tstart, float dt)
2188 {
2189     GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2190
2191     // also write a marker into demo files for demotc-race-record-extractor to find
2192     stuffcmd(pl,
2193              strcat(
2194                  strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2195                  " ", ftos(tstart), " ", ftos(dt), "\n"));
2196 }
2197
2198 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2199 {
2200         switch(self.owner.cvar_cl_gunalign)
2201         {
2202                 case 1: // right
2203                         break;
2204
2205                 case 2: // left
2206                         vecs_y = -vecs_y;
2207                         break;
2208
2209                 default:
2210                 case 3:
2211                         if(allowcenter) // 2: allow center handedness
2212                         {
2213                                 // center
2214                                 vecs_y = 0;
2215                                 vecs_z -= 4;
2216                         }
2217                         else
2218                         {
2219                                 // right
2220                         }
2221                         break;
2222
2223                 case 4:
2224                         if(allowcenter) // 2: allow center handedness
2225                         {
2226                                 // center
2227                                 vecs_y = 0;
2228                                 vecs_z -= 4;
2229                         }
2230                         else
2231                         {
2232                                 // left
2233                                 vecs_y = -vecs_y;
2234                         }
2235                         break;
2236         }
2237         return vecs;
2238 }
2239
2240 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2241 {
2242         string s;
2243         vector v;
2244
2245         if (cvar("g_shootfromeye"))
2246         {
2247                 if (visual)
2248                 {
2249                         vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2250                 }
2251                 else
2252                 {
2253                         vecs_y = 0;
2254                         vecs_z = 0;
2255                 }
2256         }
2257         else if (cvar("g_shootfromcenter"))
2258         {
2259                 if (visual)
2260                 {
2261                         vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2262                 }
2263                 else
2264                 {
2265                         vecs_y = 0;
2266                         vecs_z -= 4;
2267                 }
2268         }
2269         else if (cvar("g_shootfromclient"))
2270         {
2271                 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2272         }
2273         else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2274         {
2275                 v = stov(s);
2276                 if (y_is_right)
2277                         v_y = -v_y;
2278                 if (v_x != 0)
2279                         vecs_x = v_x;
2280                 vecs_y = v_y;
2281                 vecs_z = v_z;
2282         }
2283         return vecs;
2284 }
2285
2286
2287
2288 void attach_sameorigin(entity e, entity to, string tag)
2289 {
2290     vector org, t_forward, t_left, t_up, e_forward, e_up;
2291     vector org0, ang0;
2292     float tagscale;
2293
2294     ang0 = e.angles;
2295     org0 = e.origin;
2296
2297     org = e.origin - gettaginfo(to, gettagindex(to, tag));
2298     tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2299     t_forward = v_forward * tagscale;
2300     t_left = v_right * -tagscale;
2301     t_up = v_up * tagscale;
2302
2303     e.origin_x = org * t_forward;
2304     e.origin_y = org * t_left;
2305     e.origin_z = org * t_up;
2306
2307     // current forward and up directions
2308     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2309         e.angles_x = -e.angles_x;
2310     fixedmakevectors(e.angles);
2311
2312     // untransform forward, up!
2313     e_forward_x = v_forward * t_forward;
2314     e_forward_y = v_forward * t_left;
2315     e_forward_z = v_forward * t_up;
2316     e_up_x = v_up * t_forward;
2317     e_up_y = v_up * t_left;
2318     e_up_z = v_up * t_up;
2319
2320     e.angles = fixedvectoangles2(e_forward, e_up);
2321     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2322         e.angles_x = -e.angles_x;
2323
2324     setattachment(e, to, tag);
2325     setorigin(e, e.origin);
2326 }
2327
2328 void detach_sameorigin(entity e)
2329 {
2330     vector org;
2331     org = gettaginfo(e, 0);
2332     e.angles = fixedvectoangles2(v_forward, v_up);
2333     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2334         e.angles_x = -e.angles_x;
2335     e.origin = org;
2336     setattachment(e, world, "");
2337     setorigin(e, e.origin);
2338 }
2339
2340 void follow_sameorigin(entity e, entity to)
2341 {
2342     e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2343     e.aiment = to; // make the hole follow bmodel
2344     e.punchangle = to.angles; // the original angles of bmodel
2345     e.view_ofs = e.origin - to.origin; // relative origin
2346     e.v_angle = e.angles - to.angles; // relative angles
2347 }
2348
2349 void unfollow_sameorigin(entity e)
2350 {
2351     e.movetype = MOVETYPE_NONE;
2352 }
2353
2354 entity gettaginfo_relative_ent;
2355 vector gettaginfo_relative(entity e, float tag)
2356 {
2357     if (!gettaginfo_relative_ent)
2358     {
2359         gettaginfo_relative_ent = spawn();
2360         gettaginfo_relative_ent.effects = EF_NODRAW;
2361     }
2362     gettaginfo_relative_ent.model = e.model;
2363     gettaginfo_relative_ent.modelindex = e.modelindex;
2364     gettaginfo_relative_ent.frame = e.frame;
2365     return gettaginfo(gettaginfo_relative_ent, tag);
2366 }
2367
2368 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2369 {
2370     float p;
2371     p = pow(2, chan);
2372     if (pl.soundentity.cnt & p)
2373         return;
2374     soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2375     pl.soundentity.cnt |= p;
2376 }
2377
2378 void SoundEntity_StopSound(entity pl, float chan)
2379 {
2380     float p;
2381     p = pow(2, chan);
2382     if (pl.soundentity.cnt & p)
2383     {
2384         stopsoundto(MSG_ALL, pl.soundentity, chan);
2385         pl.soundentity.cnt &~= p;
2386     }
2387 }
2388
2389 void SoundEntity_Attach(entity pl)
2390 {
2391     pl.soundentity = spawn();
2392     pl.soundentity.classname = "soundentity";
2393     pl.soundentity.owner = pl;
2394     setattachment(pl.soundentity, pl, "");
2395     setmodel(pl.soundentity, "null");
2396 }
2397
2398 void SoundEntity_Detach(entity pl)
2399 {
2400     float i;
2401     for (i = 0; i <= 7; ++i)
2402         SoundEntity_StopSound(pl, i);
2403 }
2404
2405
2406 float ParseCommandPlayerSlotTarget_firsttoken;
2407 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2408 {
2409     string s;
2410     entity e, head;
2411     float n;
2412
2413     s = string_null;
2414
2415     ParseCommandPlayerSlotTarget_firsttoken = -1;
2416
2417     if (tokens > idx)
2418     {
2419         if (substring(argv(idx), 0, 1) == "#")
2420         {
2421             s = substring(argv(idx), 1, -1);
2422             ++idx;
2423             if (s == "")
2424                 if (tokens > idx)
2425                 {
2426                     s = argv(idx);
2427                     ++idx;
2428                 }
2429                         ParseCommandPlayerSlotTarget_firsttoken = idx;
2430             if (s == ftos(stof(s)))
2431             {
2432                 e = edict_num(stof(s));
2433                 if (e.flags & FL_CLIENT)
2434                     return e;
2435             }
2436         }
2437         else
2438         {
2439             // it must be a nick name
2440             s = argv(idx);
2441             ++idx;
2442                         ParseCommandPlayerSlotTarget_firsttoken = idx;
2443
2444             n = 0;
2445             FOR_EACH_CLIENT(head)
2446             if (head.netname == s)
2447             {
2448                 e = head;
2449                 ++n;
2450             }
2451             if (n == 1)
2452                 return e;
2453
2454             s = strdecolorize(s);
2455             n = 0;
2456             FOR_EACH_CLIENT(head)
2457             if (strdecolorize(head.netname) == s)
2458             {
2459                 e = head;
2460                 ++n;
2461             }
2462             if (n == 1)
2463                 return e;
2464         }
2465     }
2466
2467     return world;
2468 }
2469
2470 .float scale2;
2471
2472 float modeleffect_SendEntity(entity to, float sf)
2473 {
2474         float f;
2475         WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2476
2477         f = 0;
2478         if(self.velocity != '0 0 0')
2479                 f |= 1;
2480         if(self.angles != '0 0 0')
2481                 f |= 2;
2482         if(self.avelocity != '0 0 0')
2483                 f |= 4;
2484         
2485         WriteByte(MSG_ENTITY, f);
2486         WriteShort(MSG_ENTITY, self.modelindex);
2487         WriteByte(MSG_ENTITY, self.frame);
2488         WriteCoord(MSG_ENTITY, self.origin_x);
2489         WriteCoord(MSG_ENTITY, self.origin_y);
2490         WriteCoord(MSG_ENTITY, self.origin_z);
2491         if(f & 1)
2492         {
2493                 WriteCoord(MSG_ENTITY, self.velocity_x);
2494                 WriteCoord(MSG_ENTITY, self.velocity_y);
2495                 WriteCoord(MSG_ENTITY, self.velocity_z);
2496         }
2497         if(f & 2)
2498         {
2499                 WriteCoord(MSG_ENTITY, self.angles_x);
2500                 WriteCoord(MSG_ENTITY, self.angles_y);
2501                 WriteCoord(MSG_ENTITY, self.angles_z);
2502         }
2503         if(f & 4)
2504         {
2505                 WriteCoord(MSG_ENTITY, self.avelocity_x);
2506                 WriteCoord(MSG_ENTITY, self.avelocity_y);
2507                 WriteCoord(MSG_ENTITY, self.avelocity_z);
2508         }
2509         WriteShort(MSG_ENTITY, self.scale * 256.0);
2510         WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2511         WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2512         WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2513         WriteByte(MSG_ENTITY, self.alpha * 255.0);
2514
2515         return TRUE;
2516 }
2517
2518 void modeleffect_spawn(string m, float f, vector o, vector v, vector ang, vector angv, float s0, float s2, float a, float t1, float t2)
2519 {
2520         entity e;
2521         float sz;
2522         e = spawn();
2523         e.classname = "modeleffect";
2524         setmodel(e, m);
2525         e.frame = f;
2526         setorigin(e, o);
2527         e.velocity = v;
2528         e.angles = ang;
2529         e.avelocity = angv;
2530         e.alpha = a;
2531         e.teleport_time = t1;
2532         e.fade_time = t2;
2533         sz = max(s0, s2);
2534         if(s0 >= 0)
2535                 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2536         else
2537                 e.scale = -s0;
2538         if(s2 >= 0)
2539                 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2540         else
2541                 e.scale2 = -s2;
2542         setsize(e, e.mins * sz, e.maxs * sz);
2543         Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2544 }
2545
2546 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2547 {
2548         return modeleffect_spawn(m, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2549 }