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