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