]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/miscfunctions.qc
Change typefrag fragmessage to be on by default + small fixes
[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 void readlevelcvars(void)
1089 {
1090     g_bugrigs = cvar("g_bugrigs");
1091     g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1092     g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1093     g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1094     g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1095     g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1096     g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1097     g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1098     g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1099     g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1100     g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1101     g_bugrigs_accel = cvar("g_bugrigs_accel");
1102     g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1103     g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1104     g_bugrigs_steer = cvar("g_bugrigs_steer");
1105
1106     g_touchexplode = cvar("g_touchexplode");
1107     g_touchexplode_radius = cvar("g_touchexplode_radius");
1108     g_touchexplode_damage = cvar("g_touchexplode_damage");
1109     g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1110     g_touchexplode_force = cvar("g_touchexplode_force");
1111
1112 #ifdef ALLOW_FORCEMODELS
1113     sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1114 #endif
1115     sv_loddistance1 = cvar("sv_loddistance1");
1116     sv_loddistance2 = cvar("sv_loddistance2");
1117         if(sv_loddistance2 <= sv_loddistance1)
1118                 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1119     sv_clones = cvar("sv_clones");
1120     sv_cheats = cvar("sv_cheats");
1121     sv_gentle = cvar("sv_gentle");
1122     sv_foginterval = cvar("sv_foginterval");
1123     g_cloaked = cvar("g_cloaked");
1124     g_jump_grunt = cvar("g_jump_grunt");
1125     g_footsteps = cvar("g_footsteps");
1126     g_grappling_hook = cvar("g_grappling_hook");
1127     g_jetpack = cvar("g_jetpack");
1128     g_laserguided_missile = cvar("g_laserguided_missile");
1129     g_midair = cvar("g_midair");
1130     g_minstagib = cvar("g_minstagib");
1131     g_nixnex = cvar("g_nixnex");
1132     g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1133     g_norecoil = cvar("g_norecoil");
1134     g_vampire = cvar("g_vampire");
1135     g_bloodloss = cvar("g_bloodloss");
1136     sv_maxidle = cvar("sv_maxidle");
1137     sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1138     sv_pogostick = cvar("sv_pogostick");
1139     sv_doublejump = cvar("sv_doublejump");
1140     g_ctf_reverse = cvar("g_ctf_reverse");
1141         sv_autotaunt = cvar("sv_autotaunt");
1142         sv_taunt = cvar("sv_taunt");
1143
1144     inWarmupStage = cvar("g_warmup");
1145     g_warmup_limit = cvar("g_warmup_limit");
1146     g_warmup_allguns = cvar("g_warmup_allguns");
1147     g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1148
1149     if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1150         inWarmupStage = 0; // these modes cannot work together, sorry
1151
1152     g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1153     g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1154     g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1155     g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1156     g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1157     g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1158     g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1159     g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1160     g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1161     g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1162     g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1163     g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1164
1165     if (g_minstagib) g_nixnex = g_weaponarena = 0;
1166     if (g_nixnex) g_weaponarena = 0;
1167     g_weaponarena = 0;
1168
1169     g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1170     g_weaponratefactor = cvar("g_weaponratefactor");
1171     g_weapondamagefactor = cvar("g_weapondamagefactor");
1172     g_weaponforcefactor = cvar("g_weaponforcefactor");
1173
1174     g_pickup_shells                    = cvar("g_pickup_shells");
1175     g_pickup_shells_max                = cvar("g_pickup_shells_max");
1176     g_pickup_nails                     = cvar("g_pickup_nails");
1177     g_pickup_nails_max                 = cvar("g_pickup_nails_max");
1178     g_pickup_rockets                   = cvar("g_pickup_rockets");
1179     g_pickup_rockets_max               = cvar("g_pickup_rockets_max");
1180     g_pickup_cells                     = cvar("g_pickup_cells");
1181     g_pickup_cells_max                 = cvar("g_pickup_cells_max");
1182     g_pickup_fuel                     = cvar("g_pickup_fuel");
1183     g_pickup_fuel_jetpack             = cvar("g_pickup_fuel_jetpack");
1184     g_pickup_fuel_max                 = cvar("g_pickup_fuel_max");
1185     g_pickup_armorsmall                = cvar("g_pickup_armorsmall");
1186     g_pickup_armorsmall_max            = cvar("g_pickup_armorsmall_max");
1187     g_pickup_armormedium               = cvar("g_pickup_armormedium");
1188     g_pickup_armormedium_max           = cvar("g_pickup_armormedium_max");
1189     g_pickup_armorbig                  = cvar("g_pickup_armorbig");
1190     g_pickup_armorbig_max              = cvar("g_pickup_armorbig_max");
1191     g_pickup_armorlarge                = cvar("g_pickup_armorlarge");
1192     g_pickup_armorlarge_max            = cvar("g_pickup_armorlarge_max");
1193     g_pickup_healthsmall               = cvar("g_pickup_healthsmall");
1194     g_pickup_healthsmall_max           = cvar("g_pickup_healthsmall_max");
1195     g_pickup_healthmedium              = cvar("g_pickup_healthmedium");
1196     g_pickup_healthmedium_max          = cvar("g_pickup_healthmedium_max");
1197     g_pickup_healthlarge               = cvar("g_pickup_healthlarge");
1198     g_pickup_healthlarge_max           = cvar("g_pickup_healthlarge_max");
1199     g_pickup_healthmega                = cvar("g_pickup_healthmega");
1200     g_pickup_healthmega_max            = cvar("g_pickup_healthmega_max");
1201
1202     g_pinata = cvar("g_pinata");
1203
1204     g_weapon_stay = cvar("g_weapon_stay");
1205     if (!g_weapon_stay && (cvar("deathmatch") == 2))
1206         g_weapon_stay = 1;
1207         g_ghost_items = cvar("g_ghost_items");
1208         if(g_ghost_items >= 1)
1209                 g_ghost_items = 0.13; // default alpha value
1210
1211     if not(inWarmupStage)
1212         game_starttime                 = cvar("g_start_delay");
1213
1214     readplayerstartcvars();
1215 }
1216
1217 /*
1218 // TODO sound pack system
1219 string soundpack;
1220
1221 string precache_sound_builtin (string s) = #19;
1222 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1223 string precache_sound(string s)
1224 {
1225         return precache_sound_builtin(strcat(soundpack, s));
1226 }
1227 void play2(entity e, string filename)
1228 {
1229         stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1230 }
1231 void sound(entity e, float chan, string samp, float vol, float atten)
1232 {
1233         sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1234 }
1235 */
1236
1237 // Sound functions
1238 string precache_sound (string s) = #19;
1239 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1240 float precache_sound_index (string s) = #19;
1241
1242 #define SND_VOLUME      1
1243 #define SND_ATTENUATION 2
1244 #define SND_LARGEENTITY 8
1245 #define SND_LARGESOUND  16
1246
1247 float sound_allowed(float dest, entity e)
1248 {
1249     // sounds from world may always pass
1250     for (;;)
1251     {
1252         if (e.classname == "body")
1253             e = e.enemy;
1254         if (e.owner && e.owner != e)
1255             e = e.owner;
1256         else
1257             break;
1258     }
1259     // sounds to self may always pass
1260     if (dest == MSG_ONE)
1261         if (e == msg_entity)
1262             return TRUE;
1263     // sounds by players can be removed
1264     if (cvar("bot_sound_monopoly"))
1265         if (clienttype(e) == CLIENTTYPE_REAL)
1266             return FALSE;
1267     // anything else may pass
1268     return TRUE;
1269 }
1270
1271 void sound(entity e, float chan, string samp, float vol, float atten)
1272 {
1273     if (!sound_allowed(MSG_BROADCAST, e))
1274         return;
1275     sound_builtin(e, chan, samp, vol, atten);
1276 }
1277 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1278 {
1279     float entno, idx;
1280
1281     if (!sound_allowed(dest, e))
1282         return;
1283
1284     entno = num_for_edict(e);
1285     idx = precache_sound_index(samp);
1286
1287     float sflags;
1288     sflags = 0;
1289
1290     atten = floor(atten * 64);
1291     vol = floor(vol * 255);
1292
1293     if (vol != 255)
1294         sflags |= SND_VOLUME;
1295     if (atten != 64)
1296         sflags |= SND_ATTENUATION;
1297     if (entno >= 8192)
1298         sflags |= SND_LARGEENTITY;
1299     if (idx >= 256)
1300         sflags |= SND_LARGESOUND;
1301
1302     WriteByte(dest, SVC_SOUND);
1303     WriteByte(dest, sflags);
1304     if (sflags & SND_VOLUME)
1305         WriteByte(dest, vol);
1306     if (sflags & SND_ATTENUATION)
1307         WriteByte(dest, atten);
1308     if (sflags & SND_LARGEENTITY)
1309     {
1310         WriteShort(dest, entno);
1311         WriteByte(dest, chan);
1312     }
1313     else
1314     {
1315         WriteShort(dest, entno * 8 + chan);
1316     }
1317     if (sflags & SND_LARGESOUND)
1318         WriteShort(dest, idx);
1319     else
1320         WriteByte(dest, idx);
1321
1322     WriteCoord(dest, o_x);
1323     WriteCoord(dest, o_y);
1324     WriteCoord(dest, o_z);
1325 }
1326 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1327 {
1328     vector o;
1329
1330     if (!sound_allowed(dest, e))
1331         return;
1332
1333     o = e.origin + 0.5 * (e.mins + e.maxs);
1334     soundtoat(dest, e, o, chan, samp, vol, atten);
1335 }
1336 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1337 {
1338     soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1339 }
1340 void stopsoundto(float dest, entity e, float chan)
1341 {
1342     float entno;
1343
1344     if (!sound_allowed(dest, e))
1345         return;
1346
1347     entno = num_for_edict(e);
1348
1349     if (entno >= 8192)
1350     {
1351         float idx, sflags;
1352         idx = precache_sound_index("misc/null.wav");
1353         sflags = SND_LARGEENTITY;
1354         if (idx >= 256)
1355             sflags |= SND_LARGESOUND;
1356         WriteByte(dest, SVC_SOUND);
1357         WriteByte(dest, sflags);
1358         WriteShort(dest, entno);
1359         WriteByte(dest, chan);
1360         if (sflags & SND_LARGESOUND)
1361             WriteShort(dest, idx);
1362         else
1363             WriteByte(dest, idx);
1364         WriteCoord(dest, e.origin_x);
1365         WriteCoord(dest, e.origin_y);
1366         WriteCoord(dest, e.origin_z);
1367     }
1368     else
1369     {
1370         WriteByte(dest, SVC_STOPSOUND);
1371         WriteShort(dest, entno * 8 + chan);
1372     }
1373 }
1374 void stopsound(entity e, float chan)
1375 {
1376     if (!sound_allowed(MSG_BROADCAST, e))
1377         return;
1378
1379     stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1380     stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1381 }
1382
1383 void play2(entity e, string filename)
1384 {
1385     //stuffcmd(e, strcat("play2 ", filename, "\n"));
1386     msg_entity = e;
1387     soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1388 }
1389
1390 .float announcetime;
1391 float announce(entity player, string msg)
1392 {
1393     if (time > player.announcetime)
1394         if (clienttype(player) == CLIENTTYPE_REAL)
1395         {
1396             player.announcetime = time + 0.8;
1397             play2(player, msg);
1398             return TRUE;
1399         }
1400     return FALSE;
1401 }
1402 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1403 float spamsound(entity e, float chan, string samp, float vol, float atten)
1404 {
1405     if (!sound_allowed(MSG_BROADCAST, e))
1406         return FALSE;
1407
1408     if (time > e.announcetime)
1409     {
1410         e.announcetime = time;
1411         sound(e, chan, samp, vol, atten);
1412         return TRUE;
1413     }
1414     return FALSE;
1415 }
1416
1417 void play2team(float t, string filename)
1418 {
1419     local entity head;
1420
1421     if (cvar("bot_sound_monopoly"))
1422         return;
1423
1424     FOR_EACH_REALPLAYER(head)
1425     {
1426         if (head.team == t)
1427             play2(head, filename);
1428     }
1429 }
1430
1431 void play2all(string samp)
1432 {
1433     if (cvar("bot_sound_monopoly"))
1434         return;
1435
1436     sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1437 }
1438
1439 void PrecachePlayerSounds(string f);
1440 void precache_all_models(string pattern)
1441 {
1442     float globhandle, i, n;
1443     string f;
1444
1445     globhandle = search_begin(pattern, TRUE, FALSE);
1446     if (globhandle < 0)
1447         return;
1448     n = search_getsize(globhandle);
1449     for (i = 0; i < n; ++i)
1450     {
1451                 //print(search_getfilename(globhandle, i), "\n");
1452                 f = search_getfilename(globhandle, i);
1453                 if(sv_loddistance1)
1454                         precache_model(f);
1455                 if(substring(f, -9,5) == "_lod1")
1456                         continue;
1457                 if(substring(f, -9,5) == "_lod2")
1458                         continue;
1459                 if(!sv_loddistance1)
1460                         precache_model(f);
1461                 PrecachePlayerSounds(strcat(f, ".sounds"));
1462     }
1463     search_end(globhandle);
1464 }
1465
1466 void precache()
1467 {
1468     // gamemode related things
1469     precache_model ("models/misc/chatbubble.spr");
1470     precache_model ("models/misc/teambubble.spr");
1471     if (g_runematch)
1472     {
1473         precache_model ("models/runematch/curse.mdl");
1474         precache_model ("models/runematch/rune.mdl");
1475     }
1476
1477 #ifdef TTURRETS_ENABLED
1478     if (cvar("g_turrets"))
1479         turrets_precash();
1480 #endif
1481
1482     // Precache all player models if desired
1483     if (cvar("sv_precacheplayermodels"))
1484     {
1485         PrecachePlayerSounds("sound/player/default.sounds");
1486         precache_all_models("models/player/*.zym");
1487         precache_all_models("models/player/*.dpm");
1488         precache_all_models("models/player/*.md3");
1489         precache_all_models("models/player/*.psk");
1490         //precache_model("models/player/carni.zym");
1491         //precache_model("models/player/crash.zym");
1492         //precache_model("models/player/grunt.zym");
1493         //precache_model("models/player/headhunter.zym");
1494         //precache_model("models/player/insurrectionist.zym");
1495         //precache_model("models/player/jeandarc.zym");
1496         //precache_model("models/player/lurk.zym");
1497         //precache_model("models/player/lycanthrope.zym");
1498         //precache_model("models/player/marine.zym");
1499         //precache_model("models/player/nexus.zym");
1500         //precache_model("models/player/pyria.zym");
1501         //precache_model("models/player/shock.zym");
1502         //precache_model("models/player/skadi.zym");
1503         //precache_model("models/player/specop.zym");
1504         //precache_model("models/player/visitant.zym");
1505     }
1506
1507     if (cvar("sv_defaultcharacter"))
1508     {
1509         string s;
1510         s = cvar_string("sv_defaultplayermodel_red");
1511         if (s != "")
1512         {
1513             precache_model(s);
1514             PrecachePlayerSounds(strcat(s, ".sounds"));
1515         }
1516         s = cvar_string("sv_defaultplayermodel_blue");
1517         if (s != "")
1518         {
1519             precache_model(s);
1520             PrecachePlayerSounds(strcat(s, ".sounds"));
1521         }
1522         s = cvar_string("sv_defaultplayermodel_yellow");
1523         if (s != "")
1524         {
1525             precache_model(s);
1526             PrecachePlayerSounds(strcat(s, ".sounds"));
1527         }
1528         s = cvar_string("sv_defaultplayermodel_pink");
1529         if (s != "")
1530         {
1531             precache_model(s);
1532             PrecachePlayerSounds(strcat(s, ".sounds"));
1533         }
1534         s = cvar_string("sv_defaultplayermodel");
1535         if (s != "")
1536         {
1537             precache_model(s);
1538             PrecachePlayerSounds(strcat(s, ".sounds"));
1539         }
1540     }
1541
1542     if (g_footsteps)
1543     {
1544         PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1545         PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1546     }
1547
1548     // gore and miscellaneous sounds
1549     //precache_sound ("misc/h2ohit.wav");
1550     precache_model ("models/hook.md3");
1551     precache_sound ("misc/armorimpact.wav");
1552     precache_sound ("misc/bodyimpact1.wav");
1553     precache_sound ("misc/bodyimpact2.wav");
1554     precache_sound ("misc/gib.wav");
1555     precache_sound ("misc/gib_splat01.wav");
1556     precache_sound ("misc/gib_splat02.wav");
1557     precache_sound ("misc/gib_splat03.wav");
1558     precache_sound ("misc/gib_splat04.wav");
1559     precache_sound ("misc/hit.wav");
1560     PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1561     PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1562     precache_sound ("misc/null.wav");
1563     precache_sound ("misc/spawn.wav");
1564     precache_sound ("misc/talk.wav");
1565     precache_sound ("misc/teleport.wav");
1566     precache_sound ("misc/poweroff.wav");
1567     precache_sound ("player/lava.wav");
1568     precache_sound ("player/slime.wav");
1569
1570     if (g_jetpack)
1571         precache_sound ("misc/jetpack_fly.wav");
1572
1573     // announcer sounds - male
1574     precache_sound ("announcer/male/electrobitch.wav");
1575     precache_sound ("announcer/male/airshot.wav");
1576     precache_sound ("announcer/male/03kills.wav");
1577     precache_sound ("announcer/male/05kills.wav");
1578     precache_sound ("announcer/male/10kills.wav");
1579     precache_sound ("announcer/male/15kills.wav");
1580     precache_sound ("announcer/male/20kills.wav");
1581     precache_sound ("announcer/male/25kills.wav");
1582     precache_sound ("announcer/male/30kills.wav");
1583     precache_sound ("announcer/male/botlike.wav");
1584     precache_sound ("announcer/male/yoda.wav");
1585     precache_sound ("announcer/male/amazing.wav");
1586     precache_sound ("announcer/male/awesome.wav");
1587     precache_sound ("announcer/male/headshot.wav");
1588     precache_sound ("announcer/male/impressive.wav");
1589
1590     // announcer sounds - robotic
1591     precache_sound ("announcer/robotic/prepareforbattle.wav");
1592     precache_sound ("announcer/robotic/begin.wav");
1593     precache_sound ("announcer/robotic/timeoutcalled.wav");
1594     precache_sound ("announcer/robotic/1fragleft.wav");
1595     precache_sound ("announcer/robotic/2fragsleft.wav");
1596     precache_sound ("announcer/robotic/3fragsleft.wav");
1597     precache_sound ("announcer/robotic/terminated.wav");
1598     if (g_minstagib)
1599     {
1600         precache_sound ("announcer/robotic/lastsecond.wav");
1601         precache_sound ("announcer/robotic/narrowly.wav");
1602     }
1603
1604     precache_model ("models/sprites/0.spr32");
1605     precache_model ("models/sprites/1.spr32");
1606     precache_model ("models/sprites/2.spr32");
1607     precache_model ("models/sprites/3.spr32");
1608     precache_model ("models/sprites/4.spr32");
1609     precache_model ("models/sprites/5.spr32");
1610     precache_model ("models/sprites/6.spr32");
1611     precache_model ("models/sprites/7.spr32");
1612     precache_model ("models/sprites/8.spr32");
1613     precache_model ("models/sprites/9.spr32");
1614     precache_model ("models/sprites/10.spr32");
1615     precache_sound ("announcer/robotic/1.wav");
1616     precache_sound ("announcer/robotic/2.wav");
1617     precache_sound ("announcer/robotic/3.wav");
1618     precache_sound ("announcer/robotic/4.wav");
1619     precache_sound ("announcer/robotic/5.wav");
1620     precache_sound ("announcer/robotic/6.wav");
1621     precache_sound ("announcer/robotic/7.wav");
1622     precache_sound ("announcer/robotic/8.wav");
1623     precache_sound ("announcer/robotic/9.wav");
1624     precache_sound ("announcer/robotic/10.wav");
1625
1626     // common weapon precaches
1627     precache_sound ("weapons/weapon_switch.wav");
1628     precache_sound ("weapons/weaponpickup.wav");
1629     precache_sound ("weapons/unavailable.wav");
1630     if (g_grappling_hook)
1631     {
1632         precache_sound ("weapons/hook_fire.wav"); // hook
1633         precache_sound ("weapons/hook_impact.wav"); // hook
1634     }
1635
1636     if (cvar("sv_precacheweapons") || g_nixnex)
1637     {
1638         //precache weapon models/sounds
1639         local float wep;
1640         wep = WEP_FIRST;
1641         while (wep <= WEP_LAST)
1642         {
1643             weapon_action(wep, WR_PRECACHE);
1644             wep = wep + 1;
1645         }
1646     }
1647
1648     precache_model("models/elaser.mdl");
1649     precache_model("models/laser.mdl");
1650     precache_model("models/ebomb.mdl");
1651
1652 #if 0
1653     // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1654
1655     if (!self.noise && self.music) // quake 3 uses the music field
1656         self.noise = self.music;
1657
1658     // plays music for the level if there is any
1659     if (self.noise)
1660     {
1661         precache_sound (self.noise);
1662         ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1663     }
1664 #endif
1665 }
1666
1667 // sorry, but using \ in macros breaks line numbers
1668 #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
1669 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1670 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1671
1672 vector ExactTriggerHit_mins;
1673 vector ExactTriggerHit_maxs;
1674 float ExactTriggerHit_Recurse()
1675 {
1676     float s;
1677     entity se;
1678     float f;
1679
1680     tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1681     if not(trace_ent)
1682         return 0;
1683     if (trace_ent == self)
1684         return 1;
1685
1686     se = trace_ent;
1687     s = se.solid;
1688     se.solid = SOLID_NOT;
1689     f = ExactTriggerHit_Recurse();
1690     se.solid = s;
1691
1692     return f;
1693 }
1694
1695 float ExactTriggerHit()
1696 {
1697     float f, s;
1698
1699     if not(self.modelindex)
1700         return 1;
1701
1702     s = self.solid;
1703     self.solid = SOLID_BSP;
1704     ExactTriggerHit_mins = other.absmin;
1705     ExactTriggerHit_maxs = other.absmax;
1706     f = ExactTriggerHit_Recurse();
1707     self.solid = s;
1708
1709     return f;
1710 }
1711
1712 // WARNING: this kills the trace globals
1713 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1714 #define EXACTTRIGGER_INIT  InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1715
1716 #define INITPRIO_FIRST              0
1717 #define INITPRIO_GAMETYPE           0
1718 #define INITPRIO_GAMETYPE_FALLBACK  1
1719 #define INITPRIO_CVARS              5
1720 #define INITPRIO_FINDTARGET        10
1721 #define INITPRIO_DROPTOFLOOR       20
1722 #define INITPRIO_SETLOCATION       90
1723 #define INITPRIO_LINKDOORS         91
1724 #define INITPRIO_LAST              99
1725
1726 .void(void) initialize_entity;
1727 .float initialize_entity_order;
1728 .entity initialize_entity_next;
1729 entity initialize_entity_first;
1730
1731 void make_safe_for_remove(entity e)
1732 {
1733     if (e.initialize_entity)
1734     {
1735         entity ent, prev;
1736         for (ent = initialize_entity_first; ent; )
1737         {
1738             if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1739             {
1740                 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1741                 // skip it in linked list
1742                 if (prev)
1743                 {
1744                     prev.initialize_entity_next = ent.initialize_entity_next;
1745                     ent = prev.initialize_entity_next;
1746                 }
1747                 else
1748                 {
1749                     initialize_entity_first = ent.initialize_entity_next;
1750                     ent = initialize_entity_first;
1751                 }
1752             }
1753             else
1754             {
1755                 prev = ent;
1756                 ent = ent.initialize_entity_next;
1757             }
1758         }
1759     }
1760 }
1761
1762 void objerror(string s)
1763 {
1764     make_safe_for_remove(self);
1765     objerror_builtin(s);
1766 }
1767
1768 void remove_unsafely(entity e)
1769 {
1770     remove_builtin(e);
1771 }
1772
1773 void remove_safely(entity e)
1774 {
1775     make_safe_for_remove(e);
1776     remove_builtin(e);
1777 }
1778
1779 void InitializeEntity(entity e, void(void) func, float order)
1780 {
1781     entity prev, cur;
1782
1783     if (!e || e.initialize_entity)
1784     {
1785         // make a proxy initializer entity
1786         entity e_old;
1787         e_old = e;
1788         e = spawn();
1789         e.classname = "initialize_entity";
1790         e.enemy = e_old;
1791     }
1792
1793     e.initialize_entity = func;
1794     e.initialize_entity_order = order;
1795
1796     cur = initialize_entity_first;
1797     for (;;)
1798     {
1799         if (!cur || cur.initialize_entity_order > order)
1800         {
1801             // insert between prev and cur
1802             if (prev)
1803                 prev.initialize_entity_next = e;
1804             else
1805                 initialize_entity_first = e;
1806             e.initialize_entity_next = cur;
1807             return;
1808         }
1809         prev = cur;
1810         cur = cur.initialize_entity_next;
1811     }
1812 }
1813 void InitializeEntitiesRun()
1814 {
1815     entity startoflist;
1816     startoflist = initialize_entity_first;
1817     initialize_entity_first = world;
1818     for (self = startoflist; self; )
1819     {
1820         entity e;
1821         var void(void) func;
1822         e = self.initialize_entity_next;
1823         func = self.initialize_entity;
1824         self.initialize_entity_order = 0;
1825         self.initialize_entity = func_null;
1826         self.initialize_entity_next = world;
1827         if (self.classname == "initialize_entity")
1828         {
1829             entity e_old;
1830             e_old = self.enemy;
1831             remove_builtin(self);
1832             self = e_old;
1833         }
1834         //dprint("Delayed initialization: ", self.classname, "\n");
1835         func();
1836         self = e;
1837     }
1838 }
1839
1840 .float uncustomizeentityforclient_set;
1841 .void(void) uncustomizeentityforclient;
1842 void(void) SUB_Nullpointer = #0;
1843 void UncustomizeEntitiesRun()
1844 {
1845     entity oldself;
1846     oldself = self;
1847     for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1848         self.uncustomizeentityforclient();
1849     self = oldself;
1850 }
1851 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1852 {
1853     e.customizeentityforclient = customizer;
1854     e.uncustomizeentityforclient = uncustomizer;
1855     e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1856 }
1857
1858 .float nottargeted;
1859 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1860
1861 void() SUB_Remove;
1862 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1863 {
1864     vector mi, ma;
1865
1866     if (e.classname == "")
1867         e.classname = "net_linked";
1868
1869     if (e.model == "" || self.modelindex == 0)
1870     {
1871         mi = e.mins;
1872         ma = e.maxs;
1873         setmodel(e, "null");
1874         setsize(e, mi, ma);
1875     }
1876
1877     e.SendEntity = sendfunc;
1878     e.SendFlags = 0xFFFFFF;
1879
1880     if (!docull)
1881         e.effects |= EF_NODEPTHTEST;
1882
1883     if (dt)
1884     {
1885         e.nextthink = time + dt;
1886         e.think = SUB_Remove;
1887     }
1888 }
1889
1890 void adaptor_think2touch()
1891 {
1892     entity o;
1893     o = other;
1894     other = world;
1895     self.touch();
1896     other = o;
1897 }
1898
1899 void adaptor_think2use()
1900 {
1901     entity o, a;
1902     o = other;
1903     a = activator;
1904     activator = world;
1905     other = world;
1906     self.use();
1907     other = o;
1908     activator = a;
1909 }
1910
1911 // deferred dropping
1912 void DropToFloor_Handler()
1913 {
1914     droptofloor_builtin();
1915     self.dropped_origin = self.origin;
1916 }
1917
1918 void droptofloor()
1919 {
1920     InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1921 }
1922
1923
1924
1925 float trace_hits_box_a0, trace_hits_box_a1;
1926
1927 float trace_hits_box_1d(float end, float thmi, float thma)
1928 {
1929     if (end == 0)
1930     {
1931         // just check if x is in range
1932         if (0 < thmi)
1933             return FALSE;
1934         if (0 > thma)
1935             return FALSE;
1936     }
1937     else
1938     {
1939         // do the trace with respect to x
1940         // 0 -> end has to stay in thmi -> thma
1941         trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1942         trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1943         if (trace_hits_box_a0 > trace_hits_box_a1)
1944             return FALSE;
1945     }
1946     return TRUE;
1947 }
1948
1949 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1950 {
1951     end -= start;
1952     thmi -= start;
1953     thma -= start;
1954     // now it is a trace from 0 to end
1955
1956     trace_hits_box_a0 = 0;
1957     trace_hits_box_a1 = 1;
1958
1959     if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1960         return FALSE;
1961     if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1962         return FALSE;
1963     if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1964         return FALSE;
1965
1966     return TRUE;
1967 }
1968
1969 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1970 {
1971     return trace_hits_box(start, end, thmi - ma, thma - mi);
1972 }
1973
1974 float SUB_NoImpactCheck()
1975 {
1976         // zero hitcontents = this is not the real impact, but either the
1977         // mirror-impact of something hitting the projectile instead of the
1978         // projectile hitting the something, or a touchareagrid one. Neither of
1979         // these stop the projectile from moving, so...
1980         if(trace_dphitcontents == 0)
1981         {
1982                 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1983                 checkclient();
1984         }
1985     if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1986         return 1;
1987     if (other == world && self.size != '0 0 0')
1988     {
1989         vector tic;
1990         tic = self.velocity * sys_ticrate;
1991         tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1992         traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1993         if (trace_fraction >= 1)
1994         {
1995             dprint("Odd... did not hit...?\n");
1996         }
1997         else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1998         {
1999             dprint("Detected and prevented the sky-grapple bug.\n");
2000             return 1;
2001         }
2002     }
2003
2004     return 0;
2005 }
2006
2007 #define SUB_OwnerCheck() (other && (other == self.owner))
2008
2009 #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)
2010
2011 float MAX_IPBAN_URIS = 16;
2012
2013 float URI_GET_DISCARD   = 0;
2014 float URI_GET_IPBAN     = 1;
2015 float URI_GET_IPBAN_END = 16;
2016
2017 void URI_Get_Callback(float id, float status, string data)
2018 {
2019     dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2020     dprint(data);
2021     dprint("\nEnd of data.\n");
2022
2023     if (id == URI_GET_DISCARD)
2024     {
2025         // discard
2026     }
2027     else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2028     {
2029         // online ban list
2030         OnlineBanList_URI_Get_Callback(id, status, data);
2031     }
2032     else
2033     {
2034         print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2035     }
2036 }
2037
2038 void print_to(entity e, string s)
2039 {
2040     if (e)
2041         sprint(e, strcat(s, "\n"));
2042     else
2043         print(s, "\n");
2044 }
2045
2046 string getrecords()
2047 {
2048     float rec;
2049     string h;
2050     float r;
2051     float i;
2052     string s;
2053
2054     rec = 0;
2055
2056     s = "";
2057
2058     if (g_ctf)
2059     {
2060         for (i = 0; i < MapInfo_count; ++i)
2061         {
2062             if (MapInfo_Get_ByID(i))
2063             {
2064                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2065                 if (r == 0)
2066                     continue;
2067                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2068                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2069                 ++rec;
2070             }
2071         }
2072     }
2073
2074     if (g_race)
2075     {
2076         for (i = 0; i < MapInfo_count; ++i)
2077         {
2078             if (MapInfo_Get_ByID(i))
2079             {
2080                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")));
2081                 if (r == 0)
2082                     continue;
2083                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "netname"));
2084                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2085                 ++rec;
2086             }
2087         }
2088     }
2089
2090     if (g_cts)
2091     {
2092         for (i = 0; i < MapInfo_count; ++i)
2093         {
2094             if (MapInfo_Get_ByID(i))
2095             {
2096                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "time")));
2097                 if (r == 0)
2098                     continue;
2099                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, CTS_RECORD, "netname"));
2100                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2101                 ++rec;
2102             }
2103         }
2104     }
2105
2106     MapInfo_ClearTemps();
2107
2108     if (s == "")
2109         return "No records are available on this server.\n";
2110     else
2111         return strcat("Records on this server:\n", s);
2112 }
2113
2114 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2115 {
2116     float m, i;
2117     vector start, org, delta, end, enddown, mstart;
2118
2119     m = e.dphitcontentsmask;
2120     e.dphitcontentsmask = goodcontents | badcontents;
2121
2122     org = world.mins;
2123     delta = world.maxs - world.mins;
2124
2125     for (i = 0; i < attempts; ++i)
2126     {
2127         start_x = org_x + random() * delta_x;
2128         start_y = org_y + random() * delta_y;
2129         start_z = org_z + random() * delta_z;
2130
2131         // rule 1: start inside world bounds, and outside
2132         // solid, and don't start from somewhere where you can
2133         // fall down to evil
2134         tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2135         if (trace_fraction >= 1)
2136             continue;
2137         if (trace_startsolid)
2138             continue;
2139         if (trace_dphitcontents & badcontents)
2140             continue;
2141         if (trace_dphitq3surfaceflags & badsurfaceflags)
2142             continue;
2143
2144         // rule 2: if we are too high, lower the point
2145         if (trace_fraction * delta_z > maxaboveground)
2146             start = trace_endpos + '0 0 1' * maxaboveground;
2147         enddown = trace_endpos;
2148
2149         // rule 3: make sure we aren't outside the map. This only works
2150         // for somewhat well formed maps. A good rule of thumb is that
2151         // the map should have a convex outside hull.
2152         // these can be traceLINES as we already verified the starting box
2153         mstart = start + 0.5 * (e.mins + e.maxs);
2154         traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2155         if (trace_fraction >= 1)
2156             continue;
2157         traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2158         if (trace_fraction >= 1)
2159             continue;
2160         traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2161         if (trace_fraction >= 1)
2162             continue;
2163         traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2164         if (trace_fraction >= 1)
2165             continue;
2166         traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2167         if (trace_fraction >= 1)
2168             continue;
2169
2170         // find a random vector to "look at"
2171         end_x = org_x + random() * delta_x;
2172         end_y = org_y + random() * delta_y;
2173         end_z = org_z + random() * delta_z;
2174         end = start + normalize(end - start) * vlen(delta);
2175
2176         // rule 4: start TO end must not be too short
2177         tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2178         if (trace_startsolid)
2179             continue;
2180         if (trace_fraction < minviewdistance / vlen(delta))
2181             continue;
2182
2183         // rule 5: don't want to look at sky
2184         if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2185             continue;
2186
2187         // rule 6: we must not end up in trigger_hurt
2188         if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2189         {
2190             dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2191             continue;
2192         }
2193
2194         break;
2195     }
2196
2197     e.dphitcontentsmask = m;
2198
2199     if (i < attempts)
2200     {
2201         setorigin(e, start);
2202         e.angles = vectoangles(end - start);
2203         dprint("Needed ", ftos(i + 1), " attempts\n");
2204         return TRUE;
2205     }
2206     else
2207         return FALSE;
2208 }
2209
2210 float zcurveparticles_effectno;
2211 vector zcurveparticles_start;
2212 float zcurveparticles_spd;
2213
2214 void endzcurveparticles()
2215 {
2216         if(zcurveparticles_effectno)
2217         {
2218                 // terminator
2219                 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2220         }
2221         zcurveparticles_effectno = 0;
2222 }
2223
2224 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2225 {
2226         spd = bound(0, floor(spd / 16), 32767);
2227         if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2228         {
2229                 endzcurveparticles();
2230                 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2231                 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2232                 WriteShort(MSG_BROADCAST, effectno);
2233                 WriteCoord(MSG_BROADCAST, start_x);
2234                 WriteCoord(MSG_BROADCAST, start_y);
2235                 WriteCoord(MSG_BROADCAST, start_z);
2236                 zcurveparticles_effectno = effectno;
2237                 zcurveparticles_start = start;
2238         }
2239         else
2240                 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2241         WriteCoord(MSG_BROADCAST, end_x);
2242         WriteCoord(MSG_BROADCAST, end_y);
2243         WriteCoord(MSG_BROADCAST, end_z);
2244         WriteCoord(MSG_BROADCAST, end_dz);
2245         zcurveparticles_spd = spd;
2246 }
2247
2248 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2249 {
2250         float end_dz;
2251         vector vecxy, velxy;
2252
2253         vecxy = end - start;
2254         vecxy_z = 0;
2255         velxy = vel;
2256         velxy_z = 0;
2257
2258         if (vlen(velxy) < 0.000001 * fabs(vel_z))
2259         {
2260                 endzcurveparticles();
2261                 trailparticles(world, effectno, start, end);
2262                 return;
2263         }
2264
2265         end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2266         zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2267 }
2268
2269 string GetGametype(); // g_world.qc
2270 void write_recordmarker(entity pl, float tstart, float dt)
2271 {
2272     GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2273
2274     // also write a marker into demo files for demotc-race-record-extractor to find
2275     stuffcmd(pl,
2276              strcat(
2277                  strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2278                  " ", ftos(tstart), " ", ftos(dt), "\n"));
2279 }
2280
2281 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2282 {
2283         switch(self.owner.cvar_cl_gunalign)
2284         {
2285                 case 1: // right
2286                         break;
2287
2288                 case 2: // left
2289                         vecs_y = -vecs_y;
2290                         break;
2291
2292                 default:
2293                 case 3:
2294                         if(allowcenter) // 2: allow center handedness
2295                         {
2296                                 // center
2297                                 vecs_y = 0;
2298                                 vecs_z -= 4;
2299                         }
2300                         else
2301                         {
2302                                 // right
2303                         }
2304                         break;
2305
2306                 case 4:
2307                         if(allowcenter) // 2: allow center handedness
2308                         {
2309                                 // center
2310                                 vecs_y = 0;
2311                                 vecs_z -= 4;
2312                         }
2313                         else
2314                         {
2315                                 // left
2316                                 vecs_y = -vecs_y;
2317                         }
2318                         break;
2319         }
2320         return vecs;
2321 }
2322
2323 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2324 {
2325         string s;
2326         vector v;
2327
2328         if (cvar("g_shootfromeye"))
2329         {
2330                 if (visual)
2331                 {
2332                         vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2333                 }
2334                 else
2335                 {
2336                         vecs_y = 0;
2337                         vecs_z = 0;
2338                 }
2339         }
2340         else if (cvar("g_shootfromcenter"))
2341         {
2342                 if (visual)
2343                 {
2344                         vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2345                 }
2346                 else
2347                 {
2348                         vecs_y = 0;
2349                         vecs_z -= 4;
2350                 }
2351         }
2352         else if (cvar("g_shootfromclient"))
2353         {
2354                 vecs = shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2355         }
2356         else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2357         {
2358                 v = stov(s);
2359                 if (y_is_right)
2360                         v_y = -v_y;
2361                 if (v_x != 0)
2362                         vecs_x = v_x;
2363                 vecs_y = v_y;
2364                 vecs_z = v_z;
2365         }
2366         return vecs;
2367 }
2368
2369
2370
2371 void attach_sameorigin(entity e, entity to, string tag)
2372 {
2373     vector org, t_forward, t_left, t_up, e_forward, e_up;
2374     vector org0, ang0;
2375     float tagscale;
2376
2377     ang0 = e.angles;
2378     org0 = e.origin;
2379
2380     org = e.origin - gettaginfo(to, gettagindex(to, tag));
2381     tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2382     t_forward = v_forward * tagscale;
2383     t_left = v_right * -tagscale;
2384     t_up = v_up * tagscale;
2385
2386     e.origin_x = org * t_forward;
2387     e.origin_y = org * t_left;
2388     e.origin_z = org * t_up;
2389
2390     // current forward and up directions
2391     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2392         e.angles_x = -e.angles_x;
2393     fixedmakevectors(e.angles);
2394
2395     // untransform forward, up!
2396     e_forward_x = v_forward * t_forward;
2397     e_forward_y = v_forward * t_left;
2398     e_forward_z = v_forward * t_up;
2399     e_up_x = v_up * t_forward;
2400     e_up_y = v_up * t_left;
2401     e_up_z = v_up * t_up;
2402
2403     e.angles = fixedvectoangles2(e_forward, e_up);
2404     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2405         e.angles_x = -e.angles_x;
2406
2407     setattachment(e, to, tag);
2408     setorigin(e, e.origin);
2409 }
2410
2411 void detach_sameorigin(entity e)
2412 {
2413     vector org;
2414     org = gettaginfo(e, 0);
2415     e.angles = fixedvectoangles2(v_forward, v_up);
2416     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2417         e.angles_x = -e.angles_x;
2418     setorigin(e, org);
2419     setattachment(e, world, "");
2420     setorigin(e, e.origin);
2421 }
2422
2423 void follow_sameorigin(entity e, entity to)
2424 {
2425     e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2426     e.aiment = to; // make the hole follow bmodel
2427     e.punchangle = to.angles; // the original angles of bmodel
2428     e.view_ofs = e.origin - to.origin; // relative origin
2429     e.v_angle = e.angles - to.angles; // relative angles
2430 }
2431
2432 void unfollow_sameorigin(entity e)
2433 {
2434     e.movetype = MOVETYPE_NONE;
2435 }
2436
2437 entity gettaginfo_relative_ent;
2438 vector gettaginfo_relative(entity e, float tag)
2439 {
2440     if (!gettaginfo_relative_ent)
2441     {
2442         gettaginfo_relative_ent = spawn();
2443         gettaginfo_relative_ent.effects = EF_NODRAW;
2444     }
2445     gettaginfo_relative_ent.model = e.model;
2446     gettaginfo_relative_ent.modelindex = e.modelindex;
2447     gettaginfo_relative_ent.frame = e.frame;
2448     return gettaginfo(gettaginfo_relative_ent, tag);
2449 }
2450
2451 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2452 {
2453     float p;
2454     p = pow(2, chan);
2455     if (pl.soundentity.cnt & p)
2456         return;
2457     soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2458     pl.soundentity.cnt |= p;
2459 }
2460
2461 void SoundEntity_StopSound(entity pl, float chan)
2462 {
2463     float p;
2464     p = pow(2, chan);
2465     if (pl.soundentity.cnt & p)
2466     {
2467         stopsoundto(MSG_ALL, pl.soundentity, chan);
2468         pl.soundentity.cnt &~= p;
2469     }
2470 }
2471
2472 void SoundEntity_Attach(entity pl)
2473 {
2474     pl.soundentity = spawn();
2475     pl.soundentity.classname = "soundentity";
2476     pl.soundentity.owner = pl;
2477     setattachment(pl.soundentity, pl, "");
2478     setmodel(pl.soundentity, "null");
2479 }
2480
2481 void SoundEntity_Detach(entity pl)
2482 {
2483     float i;
2484     for (i = 0; i <= 7; ++i)
2485         SoundEntity_StopSound(pl, i);
2486 }
2487
2488
2489 float ParseCommandPlayerSlotTarget_firsttoken;
2490 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2491 {
2492     string s;
2493     entity e, head;
2494     float n;
2495
2496     s = string_null;
2497
2498     ParseCommandPlayerSlotTarget_firsttoken = -1;
2499
2500     if (tokens > idx)
2501     {
2502         if (substring(argv(idx), 0, 1) == "#")
2503         {
2504             s = substring(argv(idx), 1, -1);
2505             ++idx;
2506             if (s == "")
2507                 if (tokens > idx)
2508                 {
2509                     s = argv(idx);
2510                     ++idx;
2511                 }
2512                         ParseCommandPlayerSlotTarget_firsttoken = idx;
2513             if (s == ftos(stof(s)))
2514             {
2515                 e = edict_num(stof(s));
2516                 if (e.flags & FL_CLIENT)
2517                     return e;
2518             }
2519         }
2520         else
2521         {
2522             // it must be a nick name
2523             s = argv(idx);
2524             ++idx;
2525                         ParseCommandPlayerSlotTarget_firsttoken = idx;
2526
2527             n = 0;
2528             FOR_EACH_CLIENT(head)
2529             if (head.netname == s)
2530             {
2531                 e = head;
2532                 ++n;
2533             }
2534             if (n == 1)
2535                 return e;
2536
2537             s = strdecolorize(s);
2538             n = 0;
2539             FOR_EACH_CLIENT(head)
2540             if (strdecolorize(head.netname) == s)
2541             {
2542                 e = head;
2543                 ++n;
2544             }
2545             if (n == 1)
2546                 return e;
2547         }
2548     }
2549
2550     return world;
2551 }
2552
2553 .float scale2;
2554
2555 float modeleffect_SendEntity(entity to, float sf)
2556 {
2557         float f;
2558         WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2559
2560         f = 0;
2561         if(self.velocity != '0 0 0')
2562                 f |= 1;
2563         if(self.angles != '0 0 0')
2564                 f |= 2;
2565         if(self.avelocity != '0 0 0')
2566                 f |= 4;
2567
2568         WriteByte(MSG_ENTITY, f);
2569         WriteShort(MSG_ENTITY, self.modelindex);
2570         WriteByte(MSG_ENTITY, self.skin);
2571         WriteByte(MSG_ENTITY, self.frame);
2572         WriteCoord(MSG_ENTITY, self.origin_x);
2573         WriteCoord(MSG_ENTITY, self.origin_y);
2574         WriteCoord(MSG_ENTITY, self.origin_z);
2575         if(f & 1)
2576         {
2577                 WriteCoord(MSG_ENTITY, self.velocity_x);
2578                 WriteCoord(MSG_ENTITY, self.velocity_y);
2579                 WriteCoord(MSG_ENTITY, self.velocity_z);
2580         }
2581         if(f & 2)
2582         {
2583                 WriteCoord(MSG_ENTITY, self.angles_x);
2584                 WriteCoord(MSG_ENTITY, self.angles_y);
2585                 WriteCoord(MSG_ENTITY, self.angles_z);
2586         }
2587         if(f & 4)
2588         {
2589                 WriteCoord(MSG_ENTITY, self.avelocity_x);
2590                 WriteCoord(MSG_ENTITY, self.avelocity_y);
2591                 WriteCoord(MSG_ENTITY, self.avelocity_z);
2592         }
2593         WriteShort(MSG_ENTITY, self.scale * 256.0);
2594         WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2595         WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2596         WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2597         WriteByte(MSG_ENTITY, self.alpha * 255.0);
2598
2599         return TRUE;
2600 }
2601
2602 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)
2603 {
2604         entity e;
2605         float sz;
2606         e = spawn();
2607         e.classname = "modeleffect";
2608         setmodel(e, m);
2609         e.frame = f;
2610         setorigin(e, o);
2611         e.velocity = v;
2612         e.angles = ang;
2613         e.avelocity = angv;
2614         e.alpha = a;
2615         e.teleport_time = t1;
2616         e.fade_time = t2;
2617         e.skin = s;
2618         if(s0 >= 0)
2619                 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2620         else
2621                 e.scale = -s0;
2622         if(s2 >= 0)
2623                 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2624         else
2625                 e.scale2 = -s2;
2626         sz = max(e.scale, e.scale2);
2627         setsize(e, e.mins * sz, e.maxs * sz);
2628         Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2629 }
2630
2631 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2632 {
2633         return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2634 }
2635
2636 float randombit(float bits)
2637 {
2638         if not(bits & (bits-1)) // this ONLY holds for powers of two!
2639                 return bits;
2640
2641         float n, f, b, r;
2642
2643         r = random();
2644         b = 0;
2645         n = 0;
2646
2647         for(f = 1; f <= bits; f *= 2)
2648         {
2649                 if(bits & f)
2650                 {
2651                         ++n;
2652                         r *= n;
2653                         if(r <= 1)
2654                                 b = f;
2655                         else
2656                                 r = (r - 1) / (n - 1);
2657                 }
2658         }
2659
2660         return b;
2661 }
2662
2663 float randombits(float bits, float k, float error_return)
2664 {
2665         float r;
2666         r = 0;
2667         while(k > 0 && bits != r)
2668         {
2669                 r += randombit(bits - r);
2670                 --k;
2671         }
2672         if(error_return)
2673                 if(k > 0)
2674                         return -1; // all
2675         return r;
2676 }
2677
2678 void randombit_test(float bits, float iter)
2679 {
2680         while(iter > 0)
2681         {
2682                 print(ftos(randombit(bits)), "\n");
2683                 --iter;
2684         }
2685 }
2686
2687 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
2688 {
2689         if(halflifedist > 0)
2690                 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
2691         else if(halflifedist < 0)
2692                 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
2693         else
2694                 return 1;
2695 }