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