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