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