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