]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/server/miscfunctions.qc
allow cl_gunalign settings for VISUAL-ONLY shot origin in shootfromcenter and shootfr...
[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 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
507 {
508         if (f < 0)
509         {
510         }
511         else if (f > 0)
512         {
513                 if (thisname == name)
514                 {
515                         if(!self.field)
516                         {
517                                 self.field = stof(argv(f + 1));
518                                 if(!self.field)
519                                         self.field = -1;
520                         }
521                 }
522         }
523         else
524         {
525                 if(!self.field)
526                         stuffcmd(self, strcat("sendcvar ", name, "\n"));
527         }
528 }
529 string W_FixWeaponOrder_ForceComplete(string s);
530 string W_FixWeaponOrder_AllowIncomplete(string s);
531 float w_getbestweapon(entity e);
532 void GetCvars(float f)
533 {
534         string s;
535         if (f > 0)
536                 s = strcat1(argv(f));
537         GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
538         GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
539         GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
540         GetCvars_handleFloat(s, f, cvar_cl_shownames, "cl_shownames");
541         GetCvars_handleString(s, f, cvar_g_nexuizversion, "g_nexuizversion");
542         GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
543         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete);
544         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
545         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
546         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
547         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
548         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
549         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
550         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
551         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
552         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
553         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
554         GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
555         GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
556         GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
557         GetCvars_handleFloat(s, f, cvar_cl_hitsound, "cl_hitsound");
558 #ifdef ALLOW_FORCEMODELS
559         GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
560         GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromnexuiz, "cl_forceplayermodelsfromnexuiz");
561 #endif
562         GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
563
564
565         // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
566         if (f > 0)
567         {
568                 if (s == "cl_weaponpriority")
569                         self.switchweapon = w_getbestweapon(self);
570         }
571 }
572
573 float fexists(string f)
574 {
575     float fh;
576     fh = fopen(f, FILE_READ);
577     if (fh < 0)
578         return FALSE;
579     fclose(fh);
580     return TRUE;
581 }
582
583 void backtrace(string msg)
584 {
585     float dev;
586     dev = cvar("developer");
587     cvar_set("developer", "1");
588     dprint("\n");
589     dprint("--- CUT HERE ---\nWARNING: ");
590     dprint(msg);
591     dprint("\n");
592     remove(world); // isn't there any better way to cause a backtrace?
593     dprint("\n--- CUT UNTIL HERE ---\n");
594     cvar_set("developer", ftos(dev));
595 }
596
597 string Team_ColorCode(float teamid)
598 {
599     if (teamid == COLOR_TEAM1)
600         return "^1";
601     else if (teamid == COLOR_TEAM2)
602         return "^4";
603     else if (teamid == COLOR_TEAM3)
604         return "^3";
605     else if (teamid == COLOR_TEAM4)
606         return "^6";
607     else
608         return "^7";
609 }
610 string Team_ColorName(float t)
611 {
612     // fixme: Search for team entities and get their .netname's!
613     if (t == COLOR_TEAM1)
614         return "Red";
615     if (t == COLOR_TEAM2)
616         return "Blue";
617     if (t == COLOR_TEAM3)
618         return "Yellow";
619     if (t == COLOR_TEAM4)
620         return "Pink";
621     return "Neutral";
622 }
623 string Team_ColorNameLowerCase(float t)
624 {
625     // fixme: Search for team entities and get their .netname's!
626     if (t == COLOR_TEAM1)
627         return "red";
628     if (t == COLOR_TEAM2)
629         return "blue";
630     if (t == COLOR_TEAM3)
631         return "yellow";
632     if (t == COLOR_TEAM4)
633         return "pink";
634     return "neutral";
635 }
636
637 #define CENTERPRIO_POINT 1
638 #define CENTERPRIO_SPAM 2
639 #define CENTERPRIO_VOTE 4
640 #define CENTERPRIO_NORMAL 5
641 #define CENTERPRIO_SHIELDING 7
642 #define CENTERPRIO_MAPVOTE 9
643 #define CENTERPRIO_IDLEKICK 50
644 #define CENTERPRIO_ADMIN 99
645 .float centerprint_priority;
646 .float centerprint_expires;
647 void centerprint_atprio(entity e, float prio, string s)
648 {
649     if (intermission_running)
650         if (prio < CENTERPRIO_MAPVOTE)
651             return;
652     if (time > e.centerprint_expires)
653         e.centerprint_priority = 0;
654     if (prio >= e.centerprint_priority)
655     {
656         e.centerprint_priority = prio;
657         if (timeoutStatus == 2)
658             e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
659         else
660             e.centerprint_expires = time + e.cvar_scr_centertime;
661         centerprint_builtin(e, s);
662     }
663 }
664 void centerprint_expire(entity e, float prio)
665 {
666     if (prio == e.centerprint_priority)
667     {
668         e.centerprint_priority = 0;
669         centerprint_builtin(e, "");
670     }
671 }
672 void centerprint(entity e, string s)
673 {
674     centerprint_atprio(e, CENTERPRIO_NORMAL, s);
675 }
676
677 // decolorizes and team colors the player name when needed
678 string playername(entity p)
679 {
680     string t;
681     if (teams_matter && !intermission_running && p.classname == "player")
682     {
683         t = Team_ColorCode(p.team);
684         return strcat(t, strdecolorize(p.netname));
685     }
686     else
687         return p.netname;
688 }
689
690 vector randompos(vector m1, vector m2)
691 {
692     local vector v;
693     m2 = m2 - m1;
694     v_x = m2_x * random() + m1_x;
695     v_y = m2_y * random() + m1_y;
696     v_z = m2_z * random() + m1_z;
697     return  v;
698 };
699
700 float g_pickup_shells;
701 float g_pickup_shells_max;
702 float g_pickup_nails;
703 float g_pickup_nails_max;
704 float g_pickup_rockets;
705 float g_pickup_rockets_max;
706 float g_pickup_cells;
707 float g_pickup_cells_max;
708 float g_pickup_fuel;
709 float g_pickup_fuel_jetpack;
710 float g_pickup_fuel_max;
711 float g_pickup_armorsmall;
712 float g_pickup_armorsmall_max;
713 float g_pickup_armormedium;
714 float g_pickup_armormedium_max;
715 float g_pickup_armorbig;
716 float g_pickup_armorbig_max;
717 float g_pickup_armorlarge;
718 float g_pickup_armorlarge_max;
719 float g_pickup_healthsmall;
720 float g_pickup_healthsmall_max;
721 float g_pickup_healthmedium;
722 float g_pickup_healthmedium_max;
723 float g_pickup_healthlarge;
724 float g_pickup_healthlarge_max;
725 float g_pickup_healthmega;
726 float g_pickup_healthmega_max;
727 float g_weaponarena;
728 string g_weaponarena_list;
729 float g_weaponspeedfactor;
730 float g_weaponratefactor;
731 float g_weapondamagefactor;
732 float g_weaponforcefactor;
733
734 float start_weapons;
735 float start_items;
736 float start_ammo_shells;
737 float start_ammo_nails;
738 float start_ammo_rockets;
739 float start_ammo_cells;
740 float start_ammo_fuel;
741 float start_health;
742 float start_armorvalue;
743 float warmup_start_weapons;
744 float warmup_start_ammo_shells;
745 float warmup_start_ammo_nails;
746 float warmup_start_ammo_rockets;
747 float warmup_start_ammo_cells;
748 float warmup_start_ammo_fuel;
749 float warmup_start_health;
750 float warmup_start_armorvalue;
751 float g_weapon_stay;
752
753 entity get_weaponinfo(float w);
754
755 float NixNex_CanChooseWeapon(float wpn);
756 void readplayerstartcvars()
757 {
758     entity e;
759     float i, j, t;
760     string s;
761
762     // initialize starting values for players
763     start_weapons = 0;
764     start_items = 0;
765     start_ammo_shells = 0;
766     start_ammo_nails = 0;
767     start_ammo_rockets = 0;
768     start_ammo_cells = 0;
769     start_health = cvar("g_balance_health_start");
770     start_armorvalue = cvar("g_balance_armor_start");
771
772     g_weaponarena = 0;
773     s = cvar_string("g_weaponarena");
774     if (s == "0")
775     {
776     }
777     else if (s == "all")
778     {
779         g_weaponarena_list = "All Weapons";
780         for (j = WEP_FIRST; j <= WEP_LAST; ++j)
781         {
782             e = get_weaponinfo(j);
783             g_weaponarena |= e.weapons;
784             weapon_action(e.weapon, WR_PRECACHE);
785         }
786     }
787     else if (s == "most")
788     {
789         g_weaponarena_list = "Most Weapons";
790         for (j = WEP_FIRST; j <= WEP_LAST; ++j)
791         {
792             e = get_weaponinfo(j);
793             if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
794             {
795                 g_weaponarena |= e.weapons;
796                 weapon_action(e.weapon, WR_PRECACHE);
797             }
798         }
799     }
800     else if (s == "none")
801     {
802         g_weaponarena_list = "No Weapons";
803         g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
804     }
805     else
806     {
807         t = tokenize_console(s);
808         g_weaponarena_list = "";
809         for (i = 0; i < t; ++i)
810         {
811             s = argv(i);
812             for (j = WEP_FIRST; j <= WEP_LAST; ++j)
813             {
814                 e = get_weaponinfo(j);
815                 if (e.netname == s)
816                 {
817                     g_weaponarena |= e.weapons;
818                     weapon_action(e.weapon, WR_PRECACHE);
819                     g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
820                     break;
821                 }
822             }
823             if (j > WEP_LAST)
824             {
825                 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
826             }
827         }
828         g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
829     }
830
831     if (g_nixnex)
832     {
833         start_weapons = 0;
834         // will be done later
835         for (i = WEP_FIRST; i <= WEP_LAST; ++i)
836             if (NixNex_CanChooseWeapon(i))
837                 weapon_action(i, WR_PRECACHE);
838         if(!cvar("g_use_ammunition"))
839                 start_items |= IT_UNLIMITED_AMMO;
840     }
841     else if (g_weaponarena)
842     {
843         start_weapons = g_weaponarena;
844         start_ammo_rockets = 999;
845         start_ammo_shells = 999;
846         start_ammo_cells = 999;
847         start_ammo_nails = 999;
848         start_ammo_fuel = 999;
849         start_items |= IT_UNLIMITED_AMMO;
850     }
851     else if (g_minstagib)
852     {
853         start_health = 100;
854         start_armorvalue = 0;
855         start_weapons = WEPBIT_MINSTANEX;
856         weapon_action(WEP_MINSTANEX, WR_PRECACHE);
857         start_ammo_cells = cvar("g_minstagib_ammo_start");
858         g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
859         start_ammo_fuel = cvar("g_start_ammo_fuel");
860
861         if (g_minstagib_invis_alpha <= 0)
862             g_minstagib_invis_alpha = -1;
863     }
864     else
865     {
866         if (g_lms)
867         {
868             start_ammo_shells = cvar("g_lms_start_ammo_shells");
869             start_ammo_nails = cvar("g_lms_start_ammo_nails");
870             start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
871             start_ammo_cells = cvar("g_lms_start_ammo_cells");
872             start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
873             start_health = cvar("g_lms_start_health");
874             start_armorvalue = cvar("g_lms_start_armor");
875         }
876         else if (cvar("g_use_ammunition"))
877         {
878             start_ammo_shells = cvar("g_start_ammo_shells");
879             start_ammo_nails = cvar("g_start_ammo_nails");
880             start_ammo_rockets = cvar("g_start_ammo_rockets");
881             start_ammo_cells = cvar("g_start_ammo_cells");
882             start_ammo_fuel = cvar("g_start_ammo_fuel");
883         }
884         else
885         {
886             start_ammo_shells = cvar("g_pickup_shells_max");
887             start_ammo_nails = cvar("g_pickup_nails_max");
888             start_ammo_rockets = cvar("g_pickup_rockets_max");
889             start_ammo_cells = cvar("g_pickup_cells_max");
890             start_ammo_fuel = cvar("g_pickup_fuel_max");
891             start_items |= IT_UNLIMITED_AMMO;
892         }
893
894         for (i = WEP_FIRST; i <= WEP_LAST; ++i)
895         {
896             e = get_weaponinfo(i);
897             if (!(e.weapon))
898                 continue;
899
900             t = cvar(strcat("g_start_weapon_", e.netname));
901
902             if (t < 0) // "default" weapon selection
903             {
904                 if (g_lms)
905                     t = (e.spawnflags & WEPSPAWNFLAG_NORMAL);
906                 else if (g_race || g_cts)
907                     t = (i == WEP_LASER);
908                 else if (g_nexball)
909                     t = 0; // weapon is set a few lines later
910                 else
911                     t = (i == WEP_LASER || i == WEP_SHOTGUN);
912                 if (g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
913                     t += (i == WEP_HOOK);
914             }
915
916             if (g_nexball && i == WEP_PORTO)
917                 t=1;
918
919             if (t)
920             {
921                 start_weapons |= e.weapons;
922                 weapon_action(e.weapon, WR_PRECACHE);
923             }
924         }
925     }
926
927     if (inWarmupStage)
928     {
929         warmup_start_ammo_shells = start_ammo_shells;
930         warmup_start_ammo_nails = start_ammo_nails;
931         warmup_start_ammo_rockets = start_ammo_rockets;
932         warmup_start_ammo_cells = start_ammo_cells;
933         warmup_start_health = start_health;
934         warmup_start_armorvalue = start_armorvalue;
935         warmup_start_weapons = start_weapons;
936
937         if (!g_weaponarena && !g_nixnex && !g_minstagib)
938         {
939             if (cvar("g_use_ammunition"))
940             {
941                 warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
942                 warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
943                 warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
944                 warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
945             }
946             warmup_start_health = cvar("g_warmup_start_health");
947             warmup_start_armorvalue = cvar("g_warmup_start_armor");
948             if (cvar("g_warmup_allguns"))
949             {
950                 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
951                 {
952                     e = get_weaponinfo(i);
953                     if (!(e.weapon))
954                         continue;
955                     if (e.spawnflags & WEPSPAWNFLAG_NORMAL)
956                     {
957                         warmup_start_weapons |= e.weapons;
958                         weapon_action(e.weapon, WR_PRECACHE);
959                     }
960                 }
961             }
962         }
963     }
964
965     if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
966     {
967         g_grappling_hook = 0; // these two can't coexist, as they use the same button
968         start_items |= IT_FUEL_REGEN;
969         start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
970     }
971
972     if (g_jetpack)
973         start_items |= IT_JETPACK;
974
975     if (g_weapon_stay == 2)
976     {
977         if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
978         if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
979         if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
980         if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
981         if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
982         if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
983         if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
984         if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
985         if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
986         if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
987     }
988
989     start_ammo_shells = max(0, start_ammo_shells);
990     start_ammo_nails = max(0, start_ammo_nails);
991     start_ammo_cells = max(0, start_ammo_cells);
992     start_ammo_rockets = max(0, start_ammo_rockets);
993     start_ammo_fuel = max(0, start_ammo_fuel);
994
995     warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
996     warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
997     warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
998     warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
999     warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1000 }
1001
1002 float g_bugrigs;
1003 float g_bugrigs_planar_movement;
1004 float g_bugrigs_planar_movement_car_jumping;
1005 float g_bugrigs_reverse_spinning;
1006 float g_bugrigs_reverse_speeding;
1007 float g_bugrigs_reverse_stopping;
1008 float g_bugrigs_air_steering;
1009 float g_bugrigs_angle_smoothing;
1010 float g_bugrigs_friction_floor;
1011 float g_bugrigs_friction_brake;
1012 float g_bugrigs_friction_air;
1013 float g_bugrigs_accel;
1014 float g_bugrigs_speed_ref;
1015 float g_bugrigs_speed_pow;
1016 float g_bugrigs_steer;
1017
1018 float g_touchexplode;
1019 float g_touchexplode_radius;
1020 float g_touchexplode_damage;
1021 float g_touchexplode_edgedamage;
1022 float g_touchexplode_force;
1023
1024 void readlevelcvars(void)
1025 {
1026     g_bugrigs = cvar("g_bugrigs");
1027     g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1028     g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1029     g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1030     g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1031     g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1032     g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1033     g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1034     g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1035     g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1036     g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1037     g_bugrigs_accel = cvar("g_bugrigs_accel");
1038     g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1039     g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1040     g_bugrigs_steer = cvar("g_bugrigs_steer");
1041
1042     g_touchexplode = cvar("g_touchexplode");
1043     g_touchexplode_radius = cvar("g_touchexplode_radius");
1044     g_touchexplode_damage = cvar("g_touchexplode_damage");
1045     g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1046     g_touchexplode_force = cvar("g_touchexplode_force");
1047
1048 #ifdef ALLOW_FORCEMODELS
1049     sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1050 #endif
1051     sv_loddistance1 = cvar("sv_loddistance1");
1052     sv_loddistance2 = cvar("sv_loddistance2");
1053         if(sv_loddistance2 <= sv_loddistance1)
1054                 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1055     sv_clones = cvar("sv_clones");
1056     sv_cheats = cvar("sv_cheats");
1057     sv_gentle = cvar("sv_gentle");
1058     sv_foginterval = cvar("sv_foginterval");
1059     g_cloaked = cvar("g_cloaked");
1060     g_jump_grunt = cvar("g_jump_grunt");
1061     g_footsteps = cvar("g_footsteps");
1062     g_grappling_hook = cvar("g_grappling_hook");
1063     g_jetpack = cvar("g_jetpack");
1064     g_laserguided_missile = cvar("g_laserguided_missile");
1065     g_midair = cvar("g_midair");
1066     g_minstagib = cvar("g_minstagib");
1067     g_nixnex = cvar("g_nixnex");
1068     g_nixnex_with_laser = cvar("g_nixnex_with_laser");
1069     g_norecoil = cvar("g_norecoil");
1070     g_vampire = cvar("g_vampire");
1071     g_bloodloss = cvar("g_bloodloss");
1072     sv_maxidle = cvar("sv_maxidle");
1073     sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1074     sv_pogostick = cvar("sv_pogostick");
1075     sv_doublejump = cvar("sv_doublejump");
1076     g_ctf_reverse = cvar("g_ctf_reverse");
1077
1078     inWarmupStage = cvar("g_warmup");
1079     g_warmup_limit = cvar("g_warmup_limit");
1080     g_warmup_allguns = cvar("g_warmup_allguns");
1081     g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1082
1083     if ((g_race && g_race_qualifying == 2) || g_arena || g_assault || cvar("g_campaign"))
1084         inWarmupStage = 0; // these modes cannot work together, sorry
1085
1086     g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1087     g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1088     g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1089     g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1090     g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1091     g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1092     g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1093     g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1094     g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1095     g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1096     g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1097     g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1098
1099     if (g_minstagib) g_nixnex = g_weaponarena = 0;
1100     if (g_nixnex) g_weaponarena = 0;
1101     g_weaponarena = 0;
1102
1103     g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1104     g_weaponratefactor = cvar("g_weaponratefactor");
1105     g_weapondamagefactor = cvar("g_weapondamagefactor");
1106     g_weaponforcefactor = cvar("g_weaponforcefactor");
1107
1108     g_pickup_shells                    = cvar("g_pickup_shells");
1109     g_pickup_shells_max                = cvar("g_pickup_shells_max");
1110     g_pickup_nails                     = cvar("g_pickup_nails");
1111     g_pickup_nails_max                 = cvar("g_pickup_nails_max");
1112     g_pickup_rockets                   = cvar("g_pickup_rockets");
1113     g_pickup_rockets_max               = cvar("g_pickup_rockets_max");
1114     g_pickup_cells                     = cvar("g_pickup_cells");
1115     g_pickup_cells_max                 = cvar("g_pickup_cells_max");
1116     g_pickup_fuel                     = cvar("g_pickup_fuel");
1117     g_pickup_fuel_jetpack             = cvar("g_pickup_fuel_jetpack");
1118     g_pickup_fuel_max                 = cvar("g_pickup_fuel_max");
1119     g_pickup_armorsmall                = cvar("g_pickup_armorsmall");
1120     g_pickup_armorsmall_max            = cvar("g_pickup_armorsmall_max");
1121     g_pickup_armormedium               = cvar("g_pickup_armormedium");
1122     g_pickup_armormedium_max           = cvar("g_pickup_armormedium_max");
1123     g_pickup_armorbig                  = cvar("g_pickup_armorbig");
1124     g_pickup_armorbig_max              = cvar("g_pickup_armorbig_max");
1125     g_pickup_armorlarge                = cvar("g_pickup_armorlarge");
1126     g_pickup_armorlarge_max            = cvar("g_pickup_armorlarge_max");
1127     g_pickup_healthsmall               = cvar("g_pickup_healthsmall");
1128     g_pickup_healthsmall_max           = cvar("g_pickup_healthsmall_max");
1129     g_pickup_healthmedium              = cvar("g_pickup_healthmedium");
1130     g_pickup_healthmedium_max          = cvar("g_pickup_healthmedium_max");
1131     g_pickup_healthlarge               = cvar("g_pickup_healthlarge");
1132     g_pickup_healthlarge_max           = cvar("g_pickup_healthlarge_max");
1133     g_pickup_healthmega                = cvar("g_pickup_healthmega");
1134     g_pickup_healthmega_max            = cvar("g_pickup_healthmega_max");
1135
1136     g_pinata = cvar("g_pinata");
1137
1138     g_weapon_stay = cvar("g_weapon_stay");
1139     if (!g_weapon_stay && (cvar("deathmatch") == 2))
1140         g_weapon_stay = 1;
1141
1142     if not(inWarmupStage)
1143         game_starttime                 = cvar("g_start_delay");
1144
1145     readplayerstartcvars();
1146 }
1147
1148 /*
1149 // TODO sound pack system
1150 string soundpack;
1151
1152 string precache_sound_builtin (string s) = #19;
1153 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1154 string precache_sound(string s)
1155 {
1156         return precache_sound_builtin(strcat(soundpack, s));
1157 }
1158 void play2(entity e, string filename)
1159 {
1160         stuffcmd(e, strcat("play2 ", soundpack, filename, "\n"));
1161 }
1162 void sound(entity e, float chan, string samp, float vol, float atten)
1163 {
1164         sound_builtin(e, chan, strcat(soundpack, samp), vol, atten);
1165 }
1166 */
1167
1168 // Sound functions
1169 string precache_sound (string s) = #19;
1170 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1171 float precache_sound_index (string s) = #19;
1172
1173 #define SND_VOLUME      1
1174 #define SND_ATTENUATION 2
1175 #define SND_LARGEENTITY 8
1176 #define SND_LARGESOUND  16
1177
1178 float sound_allowed(float dest, entity e)
1179 {
1180     // sounds from world may always pass
1181     for (;;)
1182     {
1183         if (e.classname == "body")
1184             e = e.enemy;
1185         if (e.owner && e.owner != e)
1186             e = e.owner;
1187         else
1188             break;
1189     }
1190     // sounds to self may always pass
1191     if (dest == MSG_ONE)
1192         if (e == msg_entity)
1193             return TRUE;
1194     // sounds by players can be removed
1195     if (cvar("bot_sound_monopoly"))
1196         if (clienttype(e) == CLIENTTYPE_REAL)
1197             return FALSE;
1198     // anything else may pass
1199     return TRUE;
1200 }
1201
1202 void sound(entity e, float chan, string samp, float vol, float atten)
1203 {
1204     if (!sound_allowed(MSG_BROADCAST, e))
1205         return;
1206     sound_builtin(e, chan, samp, vol, atten);
1207 }
1208 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1209 {
1210     float entno, idx;
1211
1212     if (!sound_allowed(dest, e))
1213         return;
1214
1215     entno = num_for_edict(e);
1216     idx = precache_sound_index(samp);
1217
1218     float sflags;
1219     sflags = 0;
1220
1221     atten = floor(atten * 64);
1222     vol = floor(vol * 255);
1223
1224     if (vol != 255)
1225         sflags |= SND_VOLUME;
1226     if (atten != 64)
1227         sflags |= SND_ATTENUATION;
1228     if (entno >= 8192)
1229         sflags |= SND_LARGEENTITY;
1230     if (idx >= 256)
1231         sflags |= SND_LARGESOUND;
1232
1233     WriteByte(dest, SVC_SOUND);
1234     WriteByte(dest, sflags);
1235     if (sflags & SND_VOLUME)
1236         WriteByte(dest, vol);
1237     if (sflags & SND_ATTENUATION)
1238         WriteByte(dest, atten);
1239     if (sflags & SND_LARGEENTITY)
1240     {
1241         WriteShort(dest, entno);
1242         WriteByte(dest, chan);
1243     }
1244     else
1245     {
1246         WriteShort(dest, entno * 8 + chan);
1247     }
1248     if (sflags & SND_LARGESOUND)
1249         WriteShort(dest, idx);
1250     else
1251         WriteByte(dest, idx);
1252
1253     WriteCoord(dest, o_x);
1254     WriteCoord(dest, o_y);
1255     WriteCoord(dest, o_z);
1256 }
1257 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1258 {
1259     vector o;
1260
1261     if (!sound_allowed(dest, e))
1262         return;
1263
1264     o = e.origin + 0.5 * (e.mins + e.maxs);
1265     soundtoat(dest, e, o, chan, samp, vol, atten);
1266 }
1267 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1268 {
1269     soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1270 }
1271 void stopsoundto(float dest, entity e, float chan)
1272 {
1273     float entno;
1274
1275     if (!sound_allowed(dest, e))
1276         return;
1277
1278     entno = num_for_edict(e);
1279
1280     if (entno >= 8192)
1281     {
1282         float idx, sflags;
1283         idx = precache_sound_index("misc/null.wav");
1284         sflags = SND_LARGEENTITY;
1285         if (idx >= 256)
1286             sflags |= SND_LARGESOUND;
1287         WriteByte(dest, SVC_SOUND);
1288         WriteByte(dest, sflags);
1289         WriteShort(dest, entno);
1290         WriteByte(dest, chan);
1291         if (sflags & SND_LARGESOUND)
1292             WriteShort(dest, idx);
1293         else
1294             WriteByte(dest, idx);
1295         WriteCoord(dest, e.origin_x);
1296         WriteCoord(dest, e.origin_y);
1297         WriteCoord(dest, e.origin_z);
1298     }
1299     else
1300     {
1301         WriteByte(dest, SVC_STOPSOUND);
1302         WriteShort(dest, entno * 8 + chan);
1303     }
1304 }
1305 void stopsound(entity e, float chan)
1306 {
1307     if (!sound_allowed(MSG_BROADCAST, e))
1308         return;
1309
1310     stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1311     stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1312 }
1313
1314 void play2(entity e, string filename)
1315 {
1316     //stuffcmd(e, strcat("play2 ", filename, "\n"));
1317     msg_entity = e;
1318     soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1319 }
1320
1321 .float announcetime;
1322 float announce(entity player, string msg)
1323 {
1324     if (time > player.announcetime)
1325         if (clienttype(player) == CLIENTTYPE_REAL)
1326         {
1327             player.announcetime = time + 0.8;
1328             play2(player, msg);
1329             return TRUE;
1330         }
1331     return FALSE;
1332 }
1333 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1334 float spamsound(entity e, float chan, string samp, float vol, float atten)
1335 {
1336     if (!sound_allowed(MSG_BROADCAST, e))
1337         return FALSE;
1338
1339     if (time > e.announcetime)
1340     {
1341         e.announcetime = time;
1342         sound(e, chan, samp, vol, atten);
1343         return TRUE;
1344     }
1345     return FALSE;
1346 }
1347
1348 void play2team(float t, string filename)
1349 {
1350     local entity head;
1351
1352     if (cvar("bot_sound_monopoly"))
1353         return;
1354
1355     FOR_EACH_REALPLAYER(head)
1356     {
1357         if (head.team == t)
1358             play2(head, filename);
1359     }
1360 }
1361
1362 void play2all(string samp)
1363 {
1364     if (cvar("bot_sound_monopoly"))
1365         return;
1366
1367     sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1368 }
1369
1370 void PrecachePlayerSounds(string f);
1371 void precache_all_models(string pattern)
1372 {
1373     float globhandle, i, n;
1374     string f;
1375
1376     globhandle = search_begin(pattern, TRUE, FALSE);
1377     if (globhandle < 0)
1378         return;
1379     n = search_getsize(globhandle);
1380     for (i = 0; i < n; ++i)
1381     {
1382                 //print(search_getfilename(globhandle, i), "\n");
1383                 f = search_getfilename(globhandle, i);
1384                 if(sv_loddistance1)
1385                         precache_model(f);
1386                 if(substring(f, -9,5) == "_lod1")
1387                         continue;
1388                 if(substring(f, -9,5) == "_lod2")
1389                         continue;
1390                 if(!sv_loddistance1)
1391                         precache_model(f);
1392                 PrecachePlayerSounds(strcat(f, ".sounds"));
1393     }
1394     search_end(globhandle);
1395 }
1396
1397 void precache()
1398 {
1399     // gamemode related things
1400     precache_model ("models/misc/chatbubble.spr");
1401     precache_model ("models/misc/teambubble.spr");
1402     if (g_runematch)
1403     {
1404         precache_model ("models/runematch/curse.mdl");
1405         precache_model ("models/runematch/rune.mdl");
1406     }
1407
1408 #ifdef TTURRETS_ENABLED
1409     if (cvar("g_turrets"))
1410         turrets_precash();
1411 #endif
1412
1413     // Precache all player models if desired
1414     if (cvar("sv_precacheplayermodels"))
1415     {
1416         PrecachePlayerSounds("sound/player/default.sounds");
1417         precache_all_models("models/player/*.zym");
1418         precache_all_models("models/player/*.dpm");
1419         precache_all_models("models/player/*.md3");
1420         precache_all_models("models/player/*.psk");
1421         //precache_model("models/player/carni.zym");
1422         //precache_model("models/player/crash.zym");
1423         //precache_model("models/player/grunt.zym");
1424         //precache_model("models/player/headhunter.zym");
1425         //precache_model("models/player/insurrectionist.zym");
1426         //precache_model("models/player/jeandarc.zym");
1427         //precache_model("models/player/lurk.zym");
1428         //precache_model("models/player/lycanthrope.zym");
1429         //precache_model("models/player/marine.zym");
1430         //precache_model("models/player/nexus.zym");
1431         //precache_model("models/player/pyria.zym");
1432         //precache_model("models/player/shock.zym");
1433         //precache_model("models/player/skadi.zym");
1434         //precache_model("models/player/specop.zym");
1435         //precache_model("models/player/visitant.zym");
1436     }
1437
1438     if (cvar("sv_defaultcharacter"))
1439     {
1440         string s;
1441         s = cvar_string("sv_defaultplayermodel_red");
1442         if (s != "")
1443         {
1444             precache_model(s);
1445             PrecachePlayerSounds(strcat(s, ".sounds"));
1446         }
1447         s = cvar_string("sv_defaultplayermodel_blue");
1448         if (s != "")
1449         {
1450             precache_model(s);
1451             PrecachePlayerSounds(strcat(s, ".sounds"));
1452         }
1453         s = cvar_string("sv_defaultplayermodel_yellow");
1454         if (s != "")
1455         {
1456             precache_model(s);
1457             PrecachePlayerSounds(strcat(s, ".sounds"));
1458         }
1459         s = cvar_string("sv_defaultplayermodel_pink");
1460         if (s != "")
1461         {
1462             precache_model(s);
1463             PrecachePlayerSounds(strcat(s, ".sounds"));
1464         }
1465         s = cvar_string("sv_defaultplayermodel");
1466         if (s != "")
1467         {
1468             precache_model(s);
1469             PrecachePlayerSounds(strcat(s, ".sounds"));
1470         }
1471     }
1472
1473     if (g_footsteps)
1474     {
1475         PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1476         PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1477     }
1478
1479     // gore and miscellaneous sounds
1480     //precache_sound ("misc/h2ohit.wav");
1481     precache_model ("models/hook.md3");
1482     precache_sound ("misc/armorimpact.wav");
1483     precache_sound ("misc/bodyimpact1.wav");
1484     precache_sound ("misc/bodyimpact2.wav");
1485     precache_sound ("misc/gib.wav");
1486     precache_sound ("misc/gib_splat01.wav");
1487     precache_sound ("misc/gib_splat02.wav");
1488     precache_sound ("misc/gib_splat03.wav");
1489     precache_sound ("misc/gib_splat04.wav");
1490     precache_sound ("misc/hit.wav");
1491     PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1492     PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1493     precache_sound ("misc/null.wav");
1494     precache_sound ("misc/spawn.wav");
1495     precache_sound ("misc/talk.wav");
1496     precache_sound ("misc/teleport.wav");
1497     precache_sound ("misc/poweroff.wav");
1498     precache_sound ("player/lava.wav");
1499     precache_sound ("player/slime.wav");
1500
1501     if (g_jetpack)
1502         precache_sound ("misc/jetpack_fly.wav");
1503
1504     // announcer sounds - male
1505     precache_sound ("announcer/male/electrobitch.wav");
1506     precache_sound ("announcer/male/airshot.wav");
1507     precache_sound ("announcer/male/03kills.wav");
1508     precache_sound ("announcer/male/05kills.wav");
1509     precache_sound ("announcer/male/10kills.wav");
1510     precache_sound ("announcer/male/15kills.wav");
1511     precache_sound ("announcer/male/20kills.wav");
1512     precache_sound ("announcer/male/25kills.wav");
1513     precache_sound ("announcer/male/30kills.wav");
1514     precache_sound ("announcer/male/botlike.wav");
1515     precache_sound ("announcer/male/yoda.wav");
1516     precache_sound ("announcer/male/amazing.wav");
1517     precache_sound ("announcer/male/awesome.wav");
1518     precache_sound ("announcer/male/headshot.wav");
1519     precache_sound ("announcer/male/impressive.wav");
1520
1521     // announcer sounds - robotic
1522     precache_sound ("announcer/robotic/prepareforbattle.wav");
1523     precache_sound ("announcer/robotic/begin.wav");
1524     precache_sound ("announcer/robotic/timeoutcalled.wav");
1525     precache_sound ("announcer/robotic/1fragleft.wav");
1526     precache_sound ("announcer/robotic/2fragsleft.wav");
1527     precache_sound ("announcer/robotic/3fragsleft.wav");
1528     if (g_minstagib)
1529     {
1530         precache_sound ("announcer/robotic/lastsecond.wav");
1531         precache_sound ("announcer/robotic/narrowly.wav");
1532     }
1533
1534     precache_model ("models/sprites/0.spr32");
1535     precache_model ("models/sprites/1.spr32");
1536     precache_model ("models/sprites/2.spr32");
1537     precache_model ("models/sprites/3.spr32");
1538     precache_model ("models/sprites/4.spr32");
1539     precache_model ("models/sprites/5.spr32");
1540     precache_model ("models/sprites/6.spr32");
1541     precache_model ("models/sprites/7.spr32");
1542     precache_model ("models/sprites/8.spr32");
1543     precache_model ("models/sprites/9.spr32");
1544     precache_model ("models/sprites/10.spr32");
1545     precache_sound ("announcer/robotic/1.wav");
1546     precache_sound ("announcer/robotic/2.wav");
1547     precache_sound ("announcer/robotic/3.wav");
1548     precache_sound ("announcer/robotic/4.wav");
1549     precache_sound ("announcer/robotic/5.wav");
1550     precache_sound ("announcer/robotic/6.wav");
1551     precache_sound ("announcer/robotic/7.wav");
1552     precache_sound ("announcer/robotic/8.wav");
1553     precache_sound ("announcer/robotic/9.wav");
1554     precache_sound ("announcer/robotic/10.wav");
1555
1556     // common weapon precaches
1557     precache_sound ("weapons/weapon_switch.wav");
1558     precache_sound ("weapons/weaponpickup.wav");
1559     precache_sound ("weapons/unavailable.wav");
1560     if (g_grappling_hook)
1561     {
1562         precache_sound ("weapons/hook_fire.wav"); // hook
1563         precache_sound ("weapons/hook_impact.wav"); // hook
1564     }
1565
1566     if (cvar("sv_precacheweapons") || g_nixnex)
1567     {
1568         //precache weapon models/sounds
1569         local float wep;
1570         wep = WEP_FIRST;
1571         while (wep <= WEP_LAST)
1572         {
1573             weapon_action(wep, WR_PRECACHE);
1574             wep = wep + 1;
1575         }
1576     }
1577
1578     precache_model("models/elaser.mdl");
1579     precache_model("models/laser.mdl");
1580     precache_model("models/ebomb.mdl");
1581
1582 #if 0
1583     // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1584
1585     if (!self.noise && self.music) // quake 3 uses the music field
1586         self.noise = self.music;
1587
1588     // plays music for the level if there is any
1589     if (self.noise)
1590     {
1591         precache_sound (self.noise);
1592         ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1593     }
1594 #endif
1595 }
1596
1597 // sorry, but using \ in macros breaks line numbers
1598 #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
1599 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1600 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1601
1602 vector ExactTriggerHit_mins;
1603 vector ExactTriggerHit_maxs;
1604 float ExactTriggerHit_Recurse()
1605 {
1606     float s;
1607     entity se;
1608     float f;
1609
1610     tracebox('0 0 0', ExactTriggerHit_mins, ExactTriggerHit_maxs, '0 0 0', MOVE_NORMAL, other);
1611     if not(trace_ent)
1612         return 0;
1613     if (trace_ent == self)
1614         return 1;
1615
1616     se = trace_ent;
1617     s = se.solid;
1618     se.solid = SOLID_NOT;
1619     f = ExactTriggerHit_Recurse();
1620     se.solid = s;
1621
1622     return f;
1623 }
1624
1625 float ExactTriggerHit()
1626 {
1627     float f, s;
1628
1629     if not(self.modelindex)
1630         return 1;
1631
1632     s = self.solid;
1633     self.solid = SOLID_BSP;
1634     ExactTriggerHit_mins = other.absmin;
1635     ExactTriggerHit_maxs = other.absmax;
1636     f = ExactTriggerHit_Recurse();
1637     self.solid = s;
1638
1639     return f;
1640 }
1641
1642 // WARNING: this kills the trace globals
1643 #define EXACTTRIGGER_TOUCH if not(ExactTriggerHit()) return
1644 #define EXACTTRIGGER_INIT  InitSolidBSPTrigger(); self.solid = SOLID_TRIGGER
1645
1646 #define INITPRIO_FIRST              0
1647 #define INITPRIO_GAMETYPE           0
1648 #define INITPRIO_GAMETYPE_FALLBACK  1
1649 #define INITPRIO_CVARS              5
1650 #define INITPRIO_FINDTARGET        10
1651 #define INITPRIO_DROPTOFLOOR       20
1652 #define INITPRIO_SETLOCATION       90
1653 #define INITPRIO_LINKDOORS         91
1654 #define INITPRIO_LAST              99
1655
1656 .void(void) initialize_entity;
1657 .float initialize_entity_order;
1658 .entity initialize_entity_next;
1659 entity initialize_entity_first;
1660
1661 void make_safe_for_remove(entity e)
1662 {
1663     if (e.initialize_entity)
1664     {
1665         entity ent, prev;
1666         for (ent = initialize_entity_first; ent; )
1667         {
1668             if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1669             {
1670                 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1671                 // skip it in linked list
1672                 if (prev)
1673                 {
1674                     prev.initialize_entity_next = ent.initialize_entity_next;
1675                     ent = prev.initialize_entity_next;
1676                 }
1677                 else
1678                 {
1679                     initialize_entity_first = ent.initialize_entity_next;
1680                     ent = initialize_entity_first;
1681                 }
1682             }
1683             else
1684             {
1685                 prev = ent;
1686                 ent = ent.initialize_entity_next;
1687             }
1688         }
1689     }
1690 }
1691
1692 void objerror(string s)
1693 {
1694     make_safe_for_remove(self);
1695     objerror_builtin(s);
1696 }
1697
1698 void remove_unsafely(entity e)
1699 {
1700     remove_builtin(e);
1701 }
1702
1703 void remove_safely(entity e)
1704 {
1705     make_safe_for_remove(e);
1706     remove_builtin(e);
1707 }
1708
1709 void InitializeEntity(entity e, void(void) func, float order)
1710 {
1711     entity prev, cur;
1712
1713     if (!e || e.initialize_entity)
1714     {
1715         // make a proxy initializer entity
1716         entity e_old;
1717         e_old = e;
1718         e = spawn();
1719         e.classname = "initialize_entity";
1720         e.enemy = e_old;
1721     }
1722
1723     e.initialize_entity = func;
1724     e.initialize_entity_order = order;
1725
1726     cur = initialize_entity_first;
1727     for (;;)
1728     {
1729         if (!cur || cur.initialize_entity_order > order)
1730         {
1731             // insert between prev and cur
1732             if (prev)
1733                 prev.initialize_entity_next = e;
1734             else
1735                 initialize_entity_first = e;
1736             e.initialize_entity_next = cur;
1737             return;
1738         }
1739         prev = cur;
1740         cur = cur.initialize_entity_next;
1741     }
1742 }
1743 void InitializeEntitiesRun()
1744 {
1745     entity startoflist;
1746     startoflist = initialize_entity_first;
1747     initialize_entity_first = world;
1748     for (self = startoflist; self; )
1749     {
1750         entity e;
1751         var void(void) func;
1752         e = self.initialize_entity_next;
1753         func = self.initialize_entity;
1754         self.initialize_entity_order = 0;
1755         self.initialize_entity = func_null;
1756         self.initialize_entity_next = world;
1757         if (self.classname == "initialize_entity")
1758         {
1759             entity e_old;
1760             e_old = self.enemy;
1761             remove_builtin(self);
1762             self = e_old;
1763         }
1764         //dprint("Delayed initialization: ", self.classname, "\n");
1765         func();
1766         self = e;
1767     }
1768 }
1769
1770 .float uncustomizeentityforclient_set;
1771 .void(void) uncustomizeentityforclient;
1772 void(void) SUB_Nullpointer = #0;
1773 void UncustomizeEntitiesRun()
1774 {
1775     entity oldself;
1776     oldself = self;
1777     for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1778         self.uncustomizeentityforclient();
1779     self = oldself;
1780 }
1781 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1782 {
1783     e.customizeentityforclient = customizer;
1784     e.uncustomizeentityforclient = uncustomizer;
1785     e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1786 }
1787
1788 .float nottargeted;
1789 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1790
1791 void() SUB_Remove;
1792 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1793 {
1794     vector mi, ma;
1795
1796     if (e.classname == "")
1797         e.classname = "net_linked";
1798
1799     if (e.model == "" || self.modelindex == 0)
1800     {
1801         mi = e.mins;
1802         ma = e.maxs;
1803         setmodel(e, "null");
1804         setsize(e, mi, ma);
1805     }
1806
1807     e.SendEntity = sendfunc;
1808     e.SendFlags = 0xFFFFFF;
1809
1810     if (!docull)
1811         e.effects |= EF_NODEPTHTEST;
1812
1813     if (dt)
1814     {
1815         e.nextthink = time + dt;
1816         e.think = SUB_Remove;
1817     }
1818 }
1819
1820 void adaptor_think2touch()
1821 {
1822     entity o;
1823     o = other;
1824     other = world;
1825     self.touch();
1826     other = o;
1827 }
1828
1829 void adaptor_think2use()
1830 {
1831     entity o, a;
1832     o = other;
1833     a = activator;
1834     activator = world;
1835     other = world;
1836     self.use();
1837     other = o;
1838     activator = a;
1839 }
1840
1841 // deferred dropping
1842 void DropToFloor_Handler()
1843 {
1844     droptofloor_builtin();
1845     self.dropped_origin = self.origin;
1846 }
1847
1848 void droptofloor()
1849 {
1850     InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1851 }
1852
1853
1854
1855 float trace_hits_box_a0, trace_hits_box_a1;
1856
1857 float trace_hits_box_1d(float end, float thmi, float thma)
1858 {
1859     if (end == 0)
1860     {
1861         // just check if x is in range
1862         if (0 < thmi)
1863             return FALSE;
1864         if (0 > thma)
1865             return FALSE;
1866     }
1867     else
1868     {
1869         // do the trace with respect to x
1870         // 0 -> end has to stay in thmi -> thma
1871         trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1872         trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1873         if (trace_hits_box_a0 > trace_hits_box_a1)
1874             return FALSE;
1875     }
1876     return TRUE;
1877 }
1878
1879 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1880 {
1881     end -= start;
1882     thmi -= start;
1883     thma -= start;
1884     // now it is a trace from 0 to end
1885
1886     trace_hits_box_a0 = 0;
1887     trace_hits_box_a1 = 1;
1888
1889     if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1890         return FALSE;
1891     if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1892         return FALSE;
1893     if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1894         return FALSE;
1895
1896     return TRUE;
1897 }
1898
1899 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1900 {
1901     return trace_hits_box(start, end, thmi - ma, thma - mi);
1902 }
1903
1904 float SUB_NoImpactCheck()
1905 {
1906         // zero hitcontents = this is not the real impact, but either the
1907         // mirror-impact of something hitting the projectile instead of the
1908         // projectile hitting the something, or a touchareagrid one. Neither of
1909         // these stop the projectile from moving, so...
1910         if(trace_dphitcontents == 0)
1911         {
1912                 dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1913                 checkclient();
1914         }
1915     if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1916         return 1;
1917     if (other == world && self.size != '0 0 0')
1918     {
1919         vector tic;
1920         tic = self.velocity * sys_ticrate;
1921         tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1922         traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1923         if (trace_fraction >= 1)
1924         {
1925             dprint("Odd... did not hit...?\n");
1926         }
1927         else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1928         {
1929             dprint("Detected and prevented the sky-grapple bug.\n");
1930             return 1;
1931         }
1932     }
1933
1934     return 0;
1935 }
1936
1937 #define SUB_OwnerCheck() (other && (other == self.owner))
1938
1939 #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)
1940
1941 float MAX_IPBAN_URIS = 16;
1942
1943 float URI_GET_DISCARD   = 0;
1944 float URI_GET_IPBAN     = 1;
1945 float URI_GET_IPBAN_END = 16;
1946
1947 void URI_Get_Callback(float id, float status, string data)
1948 {
1949     dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
1950     dprint(data);
1951     dprint("\nEnd of data.\n");
1952
1953     if (id == URI_GET_DISCARD)
1954     {
1955         // discard
1956     }
1957     else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1958     {
1959         // online ban list
1960         OnlineBanList_URI_Get_Callback(id, status, data);
1961     }
1962     else
1963     {
1964         print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1965     }
1966 }
1967
1968 void print_to(entity e, string s)
1969 {
1970     if (e)
1971         sprint(e, strcat(s, "\n"));
1972     else
1973         print(s, "\n");
1974 }
1975
1976 string getrecords()
1977 {
1978     float rec;
1979     string h;
1980     float r;
1981     float i;
1982     string s;
1983
1984     rec = 0;
1985
1986     s = "";
1987
1988     if (g_ctf)
1989     {
1990         for (i = 0; i < MapInfo_count; ++i)
1991         {
1992             if (MapInfo_Get_ByID(i))
1993             {
1994                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
1995                 if (r == 0)
1996                     continue;
1997                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
1998                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
1999                 ++rec;
2000             }
2001         }
2002     }
2003
2004     if (g_race)
2005     {
2006         for (i = 0; i < MapInfo_count; ++i)
2007         {
2008             if (MapInfo_Get_ByID(i))
2009             {
2010                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/time")));
2011                 if (r == 0)
2012                     continue;
2013                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/racerecord/netname"));
2014                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2015                 ++rec;
2016             }
2017         }
2018     }
2019
2020     if (g_cts)
2021     {
2022         for (i = 0; i < MapInfo_count; ++i)
2023         {
2024             if (MapInfo_Get_ByID(i))
2025             {
2026                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/time")));
2027                 if (r == 0)
2028                     continue;
2029                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/ctsrecord/netname"));
2030                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, mmsss(r)), " ", h, "\n");
2031                 ++rec;
2032             }
2033         }
2034     }
2035
2036     MapInfo_ClearTemps();
2037
2038     if (s == "")
2039         return "No records are available on this server.\n";
2040     else
2041         return strcat("Records on this server:\n", s);
2042 }
2043
2044 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2045 {
2046     float m, i;
2047     vector start, org, delta, end, enddown, mstart;
2048
2049     m = e.dphitcontentsmask;
2050     e.dphitcontentsmask = goodcontents | badcontents;
2051
2052     org = world.mins;
2053     delta = world.maxs - world.mins;
2054
2055     for (i = 0; i < attempts; ++i)
2056     {
2057         start_x = org_x + random() * delta_x;
2058         start_y = org_y + random() * delta_y;
2059         start_z = org_z + random() * delta_z;
2060
2061         // rule 1: start inside world bounds, and outside
2062         // solid, and don't start from somewhere where you can
2063         // fall down to evil
2064         tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2065         if (trace_fraction >= 1)
2066             continue;
2067         if (trace_startsolid)
2068             continue;
2069         if (trace_dphitcontents & badcontents)
2070             continue;
2071         if (trace_dphitq3surfaceflags & badsurfaceflags)
2072             continue;
2073
2074         // rule 2: if we are too high, lower the point
2075         if (trace_fraction * delta_z > maxaboveground)
2076             start = trace_endpos + '0 0 1' * maxaboveground;
2077         enddown = trace_endpos;
2078
2079         // rule 3: make sure we aren't outside the map. This only works
2080         // for somewhat well formed maps. A good rule of thumb is that
2081         // the map should have a convex outside hull.
2082         // these can be traceLINES as we already verified the starting box
2083         mstart = start + 0.5 * (e.mins + e.maxs);
2084         traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2085         if (trace_fraction >= 1)
2086             continue;
2087         traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2088         if (trace_fraction >= 1)
2089             continue;
2090         traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2091         if (trace_fraction >= 1)
2092             continue;
2093         traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2094         if (trace_fraction >= 1)
2095             continue;
2096         traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2097         if (trace_fraction >= 1)
2098             continue;
2099
2100         // find a random vector to "look at"
2101         end_x = org_x + random() * delta_x;
2102         end_y = org_y + random() * delta_y;
2103         end_z = org_z + random() * delta_z;
2104         end = start + normalize(end - start) * vlen(delta);
2105
2106         // rule 4: start TO end must not be too short
2107         tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2108         if (trace_startsolid)
2109             continue;
2110         if (trace_fraction < minviewdistance / vlen(delta))
2111             continue;
2112
2113         // rule 5: don't want to look at sky
2114         if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2115             continue;
2116
2117         // rule 6: we must not end up in trigger_hurt
2118         if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2119         {
2120             dprint("trigger_hurt! ouch! and nothing else could find it!\n");
2121             continue;
2122         }
2123
2124         break;
2125     }
2126
2127     e.dphitcontentsmask = m;
2128
2129     if (i < attempts)
2130     {
2131         setorigin(e, start);
2132         e.angles = vectoangles(end - start);
2133         dprint("Needed ", ftos(i + 1), " attempts\n");
2134         return TRUE;
2135     }
2136     else
2137         return FALSE;
2138 }
2139
2140 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2141 {
2142     WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2143     WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2144     WriteShort(MSG_BROADCAST, effectno);
2145     WriteCoord(MSG_BROADCAST, start_x);
2146     WriteCoord(MSG_BROADCAST, start_y);
2147     WriteCoord(MSG_BROADCAST, start_z);
2148     WriteCoord(MSG_BROADCAST, end_x);
2149     WriteCoord(MSG_BROADCAST, end_y);
2150     WriteCoord(MSG_BROADCAST, end_z);
2151     WriteCoord(MSG_BROADCAST, end_dz);
2152     WriteShort(MSG_BROADCAST, spd / 16);
2153 }
2154
2155 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2156 {
2157     float end_dz;
2158     vector vecxy, velxy;
2159
2160     vecxy = end - start;
2161     vecxy_z = 0;
2162     velxy = vel;
2163     velxy_z = 0;
2164
2165     if (vlen(velxy) < 0.000001 * fabs(vel_z))
2166     {
2167         trailparticles(world, effectno, start, end);
2168         return;
2169     }
2170
2171     end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2172     zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2173 }
2174
2175 string GetGametype(); // g_world.qc
2176 void write_recordmarker(entity pl, float tstart, float dt)
2177 {
2178     GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2179
2180     // also write a marker into demo files for demotc-race-record-extractor to find
2181     stuffcmd(pl,
2182              strcat(
2183                  strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", mmsss(dt * 10)),
2184                  " ", ftos(tstart), " ", ftos(dt), "\n"));
2185 }
2186
2187 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter)
2188 {
2189         switch(self.owner.cvar_cl_gunalign)
2190         {
2191                 case 1: // right
2192                         break;
2193
2194                 case 2: // left
2195                         vecs_y = -vecs_y;
2196                         break;
2197
2198                 default:
2199                 case 3:
2200                         if(allowcenter) // 2: allow center handedness
2201                         {
2202                                 // center
2203                                 vecs_y = 0;
2204                                 vecs_z -= 4;
2205                         }
2206                         else
2207                         {
2208                                 // right
2209                         }
2210                         break;
2211         }
2212         return vecs;
2213 }
2214
2215 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2216 {
2217         string s;
2218         vector v;
2219
2220         if (cvar("g_shootfromclient"))
2221         {
2222                 return shotorg_adjustfromclient(vecs, y_is_right, (cvar("g_shootfromclient") >= 2));
2223         }
2224         else if (cvar("g_shootfromeye"))
2225         {
2226                 if (visual)
2227                 {
2228                         vecs = return shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2229                 }
2230                 else
2231                 {
2232                         vecs_y = 0;
2233                         vecs_z = 0;
2234                 }
2235         }
2236         else if (cvar("g_shootfromcenter"))
2237         {
2238                 if (visual)
2239                 {
2240                         vecs = return shotorg_adjustfromclient(vecs, y_is_right, TRUE);
2241                 }
2242                 else
2243                 {
2244                         vecs_y = 0;
2245                         vecs_z -= 4;
2246                 }
2247         }
2248         else if ((s = cvar_string("g_shootfromfixedorigin")) != "")
2249         {
2250                 v = stov(s);
2251                 if (y_is_right)
2252                         v_y = -v_y;
2253                 if (v_x != 0)
2254                         vecs_x = v_x;
2255                 vecs_y = v_y;
2256                 vecs_z = v_z;
2257         }
2258         return vecs;
2259 }
2260
2261
2262
2263 void attach_sameorigin(entity e, entity to, string tag)
2264 {
2265     vector org, t_forward, t_left, t_up, e_forward, e_up;
2266     vector org0, ang0;
2267     float tagscale;
2268
2269     ang0 = e.angles;
2270     org0 = e.origin;
2271
2272     org = e.origin - gettaginfo(to, gettagindex(to, tag));
2273     tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2274     t_forward = v_forward * tagscale;
2275     t_left = v_right * -tagscale;
2276     t_up = v_up * tagscale;
2277
2278     e.origin_x = org * t_forward;
2279     e.origin_y = org * t_left;
2280     e.origin_z = org * t_up;
2281
2282     // current forward and up directions
2283     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2284         e.angles_x = -e.angles_x;
2285     fixedmakevectors(e.angles);
2286
2287     // untransform forward, up!
2288     e_forward_x = v_forward * t_forward;
2289     e_forward_y = v_forward * t_left;
2290     e_forward_z = v_forward * t_up;
2291     e_up_x = v_up * t_forward;
2292     e_up_y = v_up * t_left;
2293     e_up_z = v_up * t_up;
2294
2295     e.angles = fixedvectoangles2(e_forward, e_up);
2296     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2297         e.angles_x = -e.angles_x;
2298
2299     setattachment(e, to, tag);
2300     setorigin(e, e.origin);
2301 }
2302
2303 void detach_sameorigin(entity e)
2304 {
2305     vector org;
2306     org = gettaginfo(e, 0);
2307     e.angles = fixedvectoangles2(v_forward, v_up);
2308     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2309         e.angles_x = -e.angles_x;
2310     e.origin = org;
2311     setattachment(e, world, "");
2312     setorigin(e, e.origin);
2313 }
2314
2315 void follow_sameorigin(entity e, entity to)
2316 {
2317     e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2318     e.aiment = to; // make the hole follow bmodel
2319     e.punchangle = to.angles; // the original angles of bmodel
2320     e.view_ofs = e.origin - to.origin; // relative origin
2321     e.v_angle = e.angles - to.angles; // relative angles
2322 }
2323
2324 void unfollow_sameorigin(entity e)
2325 {
2326     e.movetype = MOVETYPE_NONE;
2327 }
2328
2329 entity gettaginfo_relative_ent;
2330 vector gettaginfo_relative(entity e, float tag)
2331 {
2332     if (!gettaginfo_relative_ent)
2333     {
2334         gettaginfo_relative_ent = spawn();
2335         gettaginfo_relative_ent.effects = EF_NODRAW;
2336     }
2337     gettaginfo_relative_ent.model = e.model;
2338     gettaginfo_relative_ent.modelindex = e.modelindex;
2339     gettaginfo_relative_ent.frame = e.frame;
2340     return gettaginfo(gettaginfo_relative_ent, tag);
2341 }
2342
2343 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2344 {
2345     float p;
2346     p = pow(2, chan);
2347     if (pl.soundentity.cnt & p)
2348         return;
2349     soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2350     pl.soundentity.cnt |= p;
2351 }
2352
2353 void SoundEntity_StopSound(entity pl, float chan)
2354 {
2355     float p;
2356     p = pow(2, chan);
2357     if (pl.soundentity.cnt & p)
2358     {
2359         stopsoundto(MSG_ALL, pl.soundentity, chan);
2360         pl.soundentity.cnt &~= p;
2361     }
2362 }
2363
2364 void SoundEntity_Attach(entity pl)
2365 {
2366     pl.soundentity = spawn();
2367     pl.soundentity.classname = "soundentity";
2368     pl.soundentity.owner = pl;
2369     setattachment(pl.soundentity, pl, "");
2370     setmodel(pl.soundentity, "null");
2371 }
2372
2373 void SoundEntity_Detach(entity pl)
2374 {
2375     float i;
2376     for (i = 0; i <= 7; ++i)
2377         SoundEntity_StopSound(pl, i);
2378 }
2379
2380
2381 float ParseCommandPlayerSlotTarget_firsttoken;
2382 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2383 {
2384     string s;
2385     entity e, head;
2386     float n;
2387
2388     s = string_null;
2389
2390     ParseCommandPlayerSlotTarget_firsttoken = -1;
2391
2392     if (tokens > idx)
2393     {
2394         if (substring(argv(idx), 0, 1) == "#")
2395         {
2396             s = substring(argv(idx), 1, -1);
2397             ++idx;
2398             if (s == "")
2399                 if (tokens > idx)
2400                 {
2401                     s = argv(idx);
2402                     ++idx;
2403                 }
2404             if (s == ftos(stof(s)))
2405             {
2406                 e = edict_num(stof(s));
2407                 if (e.flags & FL_CLIENT)
2408                 {
2409                     ParseCommandPlayerSlotTarget_firsttoken = idx;
2410                     return e;
2411                 }
2412             }
2413         }
2414         else
2415         {
2416             // it must be a nick name
2417             s = argv(idx);
2418             ++idx;
2419
2420             n = 0;
2421             FOR_EACH_CLIENT(head)
2422             if (head.netname == s)
2423             {
2424                 e = head;
2425                 ++n;
2426             }
2427             if (n == 1)
2428             {
2429                 ParseCommandPlayerSlotTarget_firsttoken = idx;
2430                 return e;
2431             }
2432
2433             s = strdecolorize(s);
2434             n = 0;
2435             FOR_EACH_CLIENT(head)
2436             if (strdecolorize(head.netname) == s)
2437             {
2438                 e = head;
2439                 ++n;
2440             }
2441             if (n == 1)
2442             {
2443                 ParseCommandPlayerSlotTarget_firsttoken = idx;
2444                 return e;
2445             }
2446         }
2447     }
2448
2449     return world;
2450 }