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