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