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