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