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