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