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