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