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