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