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