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