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