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