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