]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/miscfunctions.qc
Rename the underscore headers
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / miscfunctions.qc
1 #include "miscfunctions.qh"
2 #include "_all.qh"
3
4 #include "antilag.qh"
5 #include "command/common.qh"
6 #include "constants.qh"
7 #include "g_hook.qh"
8 #include "ipban.qh"
9 #include "mutators/mutators_include.qh"
10 #include "tturrets/include/turrets_early.qh"
11 #include "t_items.qh"
12 #include "weapons/accuracy.qh"
13 #include "weapons/csqcprojectile.qh"
14 #include "weapons/selection.qh"
15 #include "../common/command/generic.qh"
16 #include "../common/constants.qh"
17 #include "../common/deathtypes.qh"
18 #include "../common/mapinfo.qh"
19 #include "../common/notifications.qh"
20 #include "../common/playerstats.qh"
21 #include "../common/teams.qh"
22 #include "../common/urllib.qh"
23 #include "../common/util.qh"
24 #include "../common/weapons/weapons.qh"
25 #include "../csqcmodellib/sv_model.qh"
26 #include "../warpzonelib/anglestransform.qh"
27 #include "../warpzonelib/server.qh"
28
29 void crosshair_trace(entity pl)
30 {
31         traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
32 }
33 void crosshair_trace_plusvisibletriggers(entity pl)
34 {
35         entity first;
36         entity e;
37         first = findchainfloat(solid, SOLID_TRIGGER);
38
39         for (e = first; e; e = e.chain)
40                 if (e.model != "")
41                         e.solid = SOLID_BSP;
42
43         crosshair_trace(pl);
44
45         for (e = first; e; e = e.chain)
46                 e.solid = SOLID_TRIGGER;
47 }
48 void WarpZone_crosshair_trace(entity pl)
49 {
50         WarpZone_traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl));
51 }
52
53
54 string admin_name(void)
55 {
56         if(autocvar_sv_adminnick != "")
57                 return autocvar_sv_adminnick;
58         else
59                 return "SERVER ADMIN";
60 }
61
62 void DistributeEvenly_Init(float amount, float totalweight)
63 {
64     if (DistributeEvenly_amount)
65     {
66         dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
67         dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
68     }
69     if (totalweight == 0)
70         DistributeEvenly_amount = 0;
71     else
72         DistributeEvenly_amount = amount;
73     DistributeEvenly_totalweight = totalweight;
74 }
75 float DistributeEvenly_Get(float weight)
76 {
77     float f;
78     if (weight <= 0)
79         return 0;
80     f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
81     DistributeEvenly_totalweight -= weight;
82     DistributeEvenly_amount -= f;
83     return f;
84 }
85 float DistributeEvenly_GetRandomized(float weight)
86 {
87     float f;
88     if (weight <= 0)
89         return 0;
90     f = floor(random() + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
91     DistributeEvenly_totalweight -= weight;
92     DistributeEvenly_amount -= f;
93     return f;
94 }
95
96
97 void GameLogEcho(string s)
98 {
99     string fn;
100     int matches;
101
102     if (autocvar_sv_eventlog_files)
103     {
104         if (!logfile_open)
105         {
106             logfile_open = true;
107             matches = autocvar_sv_eventlog_files_counter + 1;
108             cvar_set("sv_eventlog_files_counter", itos(matches));
109             fn = ftos(matches);
110             if (strlen(fn) < 8)
111                 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
112             fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
113             logfile = fopen(fn, FILE_APPEND);
114             fputs(logfile, ":logversion:3\n");
115         }
116         if (logfile >= 0)
117         {
118             if (autocvar_sv_eventlog_files_timestamps)
119                 fputs(logfile, strcat(":time:", strftime(true, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
120             else
121                 fputs(logfile, strcat(s, "\n"));
122         }
123     }
124     if (autocvar_sv_eventlog_console)
125     {
126         print(s, "\n");
127     }
128 }
129
130 void GameLogInit()
131 {
132     logfile_open = 0;
133     // will be opened later
134 }
135
136 void GameLogClose()
137 {
138     if (logfile_open && logfile >= 0)
139     {
140         fclose(logfile);
141         logfile = -1;
142     }
143 }
144
145 entity findnearest(vector point, .string field, string value, vector axismod)
146 {
147     entity localhead;
148     float i;
149     float j;
150     float len;
151     vector dist;
152
153     float num_nearest;
154     num_nearest = 0;
155
156     localhead = find(world, field, value);
157     while (localhead)
158     {
159         if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
160             dist = localhead.oldorigin;
161         else
162             dist = localhead.origin;
163         dist = dist - point;
164         dist = dist.x * axismod.x * '1 0 0' + dist.y * axismod.y * '0 1 0' + dist.z * axismod.z * '0 0 1';
165         len = vlen(dist);
166
167         for (i = 0; i < num_nearest; ++i)
168         {
169             if (len < nearest_length[i])
170                 break;
171         }
172
173         // now i tells us where to insert at
174         //   INSERTION SORT! YOU'VE SEEN IT! RUN!
175         if (i < NUM_NEAREST_ENTITIES)
176         {
177             for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
178             {
179                 nearest_length[j + 1] = nearest_length[j];
180                 nearest_entity[j + 1] = nearest_entity[j];
181             }
182             nearest_length[i] = len;
183             nearest_entity[i] = localhead;
184             if (num_nearest < NUM_NEAREST_ENTITIES)
185                 num_nearest = num_nearest + 1;
186         }
187
188         localhead = find(localhead, field, value);
189     }
190
191     // now use the first one from our list that we can see
192     for (i = 0; i < num_nearest; ++i)
193     {
194         traceline(point, nearest_entity[i].origin, true, world);
195         if (trace_fraction == 1)
196         {
197             if (i != 0)
198             {
199                 dprint("Nearest point (");
200                 dprint(nearest_entity[0].netname);
201                 dprint(") is not visible, using a visible one.\n");
202             }
203             return nearest_entity[i];
204         }
205     }
206
207     if (num_nearest == 0)
208         return world;
209
210     dprint("Not seeing any location point, using nearest as fallback.\n");
211     /* DEBUGGING CODE:
212     dprint("Candidates were: ");
213     for(j = 0; j < num_nearest; ++j)
214     {
215         if(j != 0)
216                 dprint(", ");
217         dprint(nearest_entity[j].netname);
218     }
219     dprint("\n");
220     */
221
222     return nearest_entity[0];
223 }
224
225 void spawnfunc_target_location()
226 {
227     self.classname = "target_location";
228     // location name in netname
229     // eventually support: count, teamgame selectors, line of sight?
230 }
231
232 void spawnfunc_info_location()
233 {
234     self.classname = "target_location";
235     self.message = self.netname;
236 }
237
238 string NearestLocation(vector p)
239 {
240     entity loc;
241     string ret;
242     ret = "somewhere";
243     loc = findnearest(p, classname, "target_location", '1 1 1');
244     if (loc)
245     {
246         ret = loc.message;
247     }
248     else
249     {
250         loc = findnearest(p, target, "###item###", '1 1 4');
251         if (loc)
252             ret = loc.netname;
253     }
254     return ret;
255 }
256
257 string formatmessage(string msg)
258 {
259         float p, p1, p2;
260         float n;
261         vector cursor;
262         entity cursor_ent;
263         string escape;
264         string replacement;
265         p = 0;
266         n = 7;
267
268         WarpZone_crosshair_trace(self);
269         cursor = trace_endpos;
270         cursor_ent = trace_ent;
271
272         while (1) {
273                 if (n < 1)
274                         break; // too many replacements
275
276                 n = n - 1;
277                 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
278                 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
279
280                 if (p1 < 0)
281                         p1 = p2;
282
283                 if (p2 < 0)
284                         p2 = p1;
285
286                 p = min(p1, p2);
287
288                 if (p < 0)
289                         break;
290
291                 replacement = substring(msg, p, 2);
292                 escape = substring(msg, p + 1, 1);
293
294                 if (escape == "%")
295                         replacement = "%";
296                 else if (escape == "\\")
297                         replacement = "\\";
298                 else if (escape == "n")
299                         replacement = "\n";
300                 else if (escape == "a")
301                         replacement = ftos(floor(self.armorvalue));
302                 else if (escape == "h")
303                         replacement = ftos(floor(self.health));
304                 else if (escape == "l")
305                         replacement = NearestLocation(self.origin);
306                 else if (escape == "y")
307                         replacement = NearestLocation(cursor);
308                 else if (escape == "d")
309                         replacement = NearestLocation(self.death_origin);
310                 else if (escape == "w") {
311                         float wep;
312                         wep = self.weapon;
313                         if (!wep)
314                                 wep = self.switchweapon;
315                         if (!wep)
316                                 wep = self.cnt;
317                         replacement = WEP_NAME(wep);
318                 } else if (escape == "W") {
319                         if (self.items & IT_SHELLS) replacement = "shells";
320                         else if (self.items & IT_NAILS) replacement = "bullets";
321                         else if (self.items & IT_ROCKETS) replacement = "rockets";
322                         else if (self.items & IT_CELLS) replacement = "cells";
323                         else if (self.items & IT_PLASMA) replacement = "plasma";
324                         else replacement = "batteries"; // ;)
325                 } else if (escape == "x") {
326                         replacement = cursor_ent.netname;
327                         if (replacement == "" || !cursor_ent)
328                                 replacement = "nothing";
329                 } else if (escape == "s")
330                         replacement = ftos(vlen(self.velocity - self.velocity.z * '0 0 1'));
331                 else if (escape == "S")
332                         replacement = ftos(vlen(self.velocity));
333
334                 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
335                 p = p + strlen(replacement);
336         }
337         return msg;
338 }
339
340 float boolean(float value) { // if value is 0 return false (0), otherwise return true (1)
341         return (value == 0) ? false : true;
342 }
343
344 /*
345 =============
346 GetCvars
347 =============
348 Called with:
349   0:  sends the request
350   >0: receives a cvar from name=argv(f) value=argv(f+1)
351 */
352 void GetCvars_handleString(string thisname, float f, .string field, string name)
353 {
354         if (f < 0)
355         {
356                 if (self.(field))
357                         strunzone(self.(field));
358                 self.(field) = string_null;
359         }
360         else if (f > 0)
361         {
362                 if (thisname == name)
363                 {
364                         if (self.(field))
365                                 strunzone(self.(field));
366                         self.(field) = strzone(argv(f + 1));
367                 }
368         }
369         else
370                 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
371 }
372 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
373 {
374         GetCvars_handleString(thisname, f, field, name);
375         if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
376                 if (thisname == name)
377                 {
378                         string s = func(strcat1(self.(field)));
379                         if (s != self.(field))
380                         {
381                                 strunzone(self.(field));
382                                 self.(field) = strzone(s);
383                         }
384                 }
385 }
386 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
387 {
388         if (f < 0)
389         {
390         }
391         else if (f > 0)
392         {
393                 if (thisname == name)
394                         self.(field) = stof(argv(f + 1));
395         }
396         else
397                 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
398 }
399 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
400 {
401         if (f < 0)
402         {
403         }
404         else if (f > 0)
405         {
406                 if (thisname == name)
407                 {
408                         if (!self.(field))
409                         {
410                                 self.(field) = stof(argv(f + 1));
411                                 if (!self.(field))
412                                         self.(field) = -1;
413                         }
414                 }
415         }
416         else
417         {
418                 if (!self.(field))
419                         stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
420         }
421 }
422 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
423 {
424         string o;
425         o = W_FixWeaponOrder_ForceComplete(wo);
426         if(self.weaponorder_byimpulse)
427         {
428                 strunzone(self.weaponorder_byimpulse);
429                 self.weaponorder_byimpulse = string_null;
430         }
431         self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
432         return o;
433 }
434 void GetCvars(float f)
435 {
436         string s = string_null;
437
438         if (f > 0)
439                 s = strcat1(argv(f));
440
441         get_cvars_f = f;
442         get_cvars_s = s;
443
444         MUTATOR_CALLHOOK(GetCvars);
445
446         Notification_GetCvars();
447
448         GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
449         GetCvars_handleFloat(s, f, cvar_cl_autoscreenshot, "cl_autoscreenshot");
450         GetCvars_handleFloat(s, f, cvar_cl_jetpack_jump, "cl_jetpack_jump");
451         GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
452         GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
453         GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
454         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
455         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
456         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
457         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
458         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
459         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
460         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
461         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
462         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
463         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
464         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
465         GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
466         GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
467         GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
468         GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
469         GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
470         GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
471         GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
472
473         self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
474         self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
475
476         GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
477         GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
478         GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
479         GetCvars_handleFloat(s, f, cvar_cl_movement_track_canjump, "cl_movement_track_canjump");
480         GetCvars_handleFloat(s, f, cvar_cl_newusekeysupported, "cl_newusekeysupported");
481
482         // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
483         if (f > 0)
484         {
485                 if (s == "cl_weaponpriority")
486                         self.switchweapon = w_getbestweapon(self);
487                 if (s == "cl_allow_uidtracking")
488                         PlayerStats_GameReport_AddPlayer(self);
489         }
490 }
491
492 // decolorizes and team colors the player name when needed
493 string playername(entity p)
494 {
495     string t;
496     if (teamplay && !intermission_running && IS_PLAYER(p))
497     {
498         t = Team_ColorCode(p.team);
499         return strcat(t, strdecolorize(p.netname));
500     }
501     else
502         return p.netname;
503 }
504
505 vector randompos(vector m1, vector m2)
506 {
507     vector v;
508     m2 = m2 - m1;
509     v.x = m2_x * random() + m1_x;
510     v.y = m2_y * random() + m1_y;
511     v.z = m2_z * random() + m1_z;
512     return  v;
513 }
514
515 float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still needs done?
516 {
517         int i = weaponinfo.weapon;
518         int d = 0;
519
520         if (!i)
521                 return 0;
522
523         if (g_lms || g_ca || allguns)
524         {
525                 if(weaponinfo.spawnflags & WEP_FLAG_NORMAL)
526                         d = true;
527                 else
528                         d = false;
529         }
530         else if (g_cts)
531                 d = (i == WEP_SHOTGUN);
532         else if (g_nexball)
533                 d = 0; // weapon is set a few lines later
534         else
535                 d = !(!weaponinfo.weaponstart);
536
537         if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
538                 d |= (i == WEP_HOOK);
539         if(!g_cts && (weaponinfo.spawnflags & WEP_FLAG_MUTATORBLOCKED)) // never default mutator blocked guns
540                 d = 0;
541
542         float t = weaponinfo.weaponstartoverride;
543
544         //print(strcat("want_weapon: ", weaponinfo.netname, " - d: ", ftos(d), ", t: ", ftos(t), ". \n"));
545
546         // bit order in t:
547         // 1: want or not
548         // 2: is default?
549         // 4: is set by default?
550         if(t < 0)
551                 t = 4 | (3 * d);
552         else
553                 t |= (2 * d);
554
555         return t;
556 }
557
558 void readplayerstartcvars()
559 {
560         entity e;
561         float i, j, t;
562         string s;
563
564         // initialize starting values for players
565         start_weapons = '0 0 0';
566         start_weapons_default = '0 0 0';
567         start_weapons_defaultmask = '0 0 0';
568         start_items = 0;
569         start_ammo_shells = 0;
570         start_ammo_nails = 0;
571         start_ammo_rockets = 0;
572         start_ammo_cells = 0;
573         start_ammo_plasma = 0;
574         start_health = cvar("g_balance_health_start");
575         start_armorvalue = cvar("g_balance_armor_start");
576
577         g_weaponarena = 0;
578         g_weaponarena_weapons = '0 0 0';
579
580         s = cvar_string("g_weaponarena");
581         if (s == "0" || s == "")
582         {
583                 if(g_ca)
584                         s = "most";
585         }
586
587         if (s == "0" || s == "")
588         {
589                 // no arena
590         }
591         else if (s == "off")
592         {
593                 // forcibly turn off weaponarena
594         }
595         else if (s == "all" || s == "1")
596         {
597                 g_weaponarena = 1;
598                 g_weaponarena_list = "All Weapons";
599                 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
600                 {
601                         e = get_weaponinfo(j);
602                         if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
603                                 g_weaponarena_weapons |= WepSet_FromWeapon(j);
604                 }
605         }
606         else if (s == "most")
607         {
608                 g_weaponarena = 1;
609                 g_weaponarena_list = "Most Weapons";
610                 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
611                 {
612                         e = get_weaponinfo(j);
613                         if (!(e.spawnflags & WEP_FLAG_MUTATORBLOCKED))
614                                 if (e.spawnflags & WEP_FLAG_NORMAL)
615                                         g_weaponarena_weapons |= WepSet_FromWeapon(j);
616                 }
617         }
618         else if (s == "none")
619         {
620                 g_weaponarena = 1;
621                 g_weaponarena_list = "No Weapons";
622         }
623         else
624         {
625                 g_weaponarena = 1;
626                 t = tokenize_console(s);
627                 g_weaponarena_list = "";
628                 for (i = 0; i < t; ++i)
629                 {
630                         s = argv(i);
631                         for (j = WEP_FIRST; j <= WEP_LAST; ++j)
632                         {
633                                 e = get_weaponinfo(j);
634                                 if (e.netname == s)
635                                 {
636                                         g_weaponarena_weapons |= WepSet_FromWeapon(j);
637                                         g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
638                                         break;
639                                 }
640                         }
641                         if (j > WEP_LAST)
642                         {
643                                 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
644                         }
645                 }
646                 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
647         }
648
649         if(g_weaponarena)
650                 g_weaponarena_random = cvar("g_weaponarena_random");
651         else
652                 g_weaponarena_random = 0;
653         g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster");
654
655         if (g_weaponarena)
656         {
657                 g_weapon_stay = 0; // incompatible
658                 start_weapons = g_weaponarena_weapons;
659                 start_items |= IT_UNLIMITED_AMMO;
660         }
661         else
662         {
663                 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
664                 {
665                         e = get_weaponinfo(i);
666                         int w = want_weapon(e, false);
667                         if(w & 1)
668                                 start_weapons |= WepSet_FromWeapon(i);
669                         if(w & 2)
670                                 start_weapons_default |= WepSet_FromWeapon(i);
671                         if(w & 4)
672                                 start_weapons_defaultmask |= WepSet_FromWeapon(i);
673                 }
674         }
675
676         if(!cvar("g_use_ammunition"))
677                 start_items |= IT_UNLIMITED_AMMO;
678
679         if(start_items & IT_UNLIMITED_WEAPON_AMMO)
680         {
681                 start_ammo_shells = 999;
682                 start_ammo_nails = 999;
683                 start_ammo_rockets = 999;
684                 start_ammo_cells = 999;
685                 start_ammo_plasma = 999;
686                 start_ammo_fuel = 999;
687         }
688         else
689         {
690                 start_ammo_shells = cvar("g_start_ammo_shells");
691                 start_ammo_nails = cvar("g_start_ammo_nails");
692                 start_ammo_rockets = cvar("g_start_ammo_rockets");
693                 start_ammo_cells = cvar("g_start_ammo_cells");
694                 start_ammo_plasma = cvar("g_start_ammo_plasma");
695                 start_ammo_fuel = cvar("g_start_ammo_fuel");
696         }
697
698         if (warmup_stage)
699         {
700                 warmup_start_ammo_shells = start_ammo_shells;
701                 warmup_start_ammo_nails = start_ammo_nails;
702                 warmup_start_ammo_rockets = start_ammo_rockets;
703                 warmup_start_ammo_cells = start_ammo_cells;
704                 warmup_start_ammo_plasma = start_ammo_plasma;
705                 warmup_start_ammo_fuel = start_ammo_fuel;
706                 warmup_start_health = start_health;
707                 warmup_start_armorvalue = start_armorvalue;
708                 warmup_start_weapons = start_weapons;
709                 warmup_start_weapons_default = start_weapons_default;
710                 warmup_start_weapons_defaultmask = start_weapons_defaultmask;
711
712                 if (!g_weaponarena && !g_ca)
713                 {
714                         warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
715                         warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
716                         warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
717                         warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
718                         warmup_start_ammo_plasma = cvar("g_warmup_start_ammo_plasma");
719                         warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
720                         warmup_start_health = cvar("g_warmup_start_health");
721                         warmup_start_armorvalue = cvar("g_warmup_start_armor");
722                         warmup_start_weapons = '0 0 0';
723                         warmup_start_weapons_default = '0 0 0';
724                         warmup_start_weapons_defaultmask = '0 0 0';
725                         for (i = WEP_FIRST; i <= WEP_LAST; ++i)
726                         {
727                                 e = get_weaponinfo(i);
728                                 int w = want_weapon(e, g_warmup_allguns);
729                                 if(w & 1)
730                                         warmup_start_weapons |= WepSet_FromWeapon(i);
731                                 if(w & 2)
732                                         warmup_start_weapons_default |= WepSet_FromWeapon(i);
733                                 if(w & 4)
734                                         warmup_start_weapons_defaultmask |= WepSet_FromWeapon(i);
735                         }
736                 }
737         }
738
739         if (g_jetpack)
740                 start_items |= IT_JETPACK;
741
742         MUTATOR_CALLHOOK(SetStartItems);
743
744         if ((start_items & IT_JETPACK) || (g_grappling_hook && (start_weapons & WEPSET_HOOK)))
745         {
746                 start_items |= IT_FUEL_REGEN;
747                 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
748                 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
749         }
750
751         WepSet precache_weapons = start_weapons;
752         if (g_warmup_allguns != 1)
753                 precache_weapons |= warmup_start_weapons;
754         for (i = WEP_FIRST; i <= WEP_LAST; ++i)
755         {
756                 e = get_weaponinfo(i);
757                 if(precache_weapons & WepSet_FromWeapon(i))
758                         WEP_ACTION(i, WR_INIT);
759         }
760
761         start_ammo_shells = max(0, start_ammo_shells);
762         start_ammo_nails = max(0, start_ammo_nails);
763         start_ammo_rockets = max(0, start_ammo_rockets);
764         start_ammo_cells = max(0, start_ammo_cells);
765         start_ammo_plasma = max(0, start_ammo_plasma);
766         start_ammo_fuel = max(0, start_ammo_fuel);
767
768         warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
769         warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
770         warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
771         warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
772         warmup_start_ammo_plasma = max(0, warmup_start_ammo_plasma);
773         warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
774 }
775
776 float sound_allowed(float _dest, entity e)
777 {
778     // sounds from world may always pass
779     for (;;)
780     {
781         if (e.classname == "body")
782             e = e.enemy;
783         else if (e.realowner && e.realowner != e)
784             e = e.realowner;
785         else if (e.owner && e.owner != e)
786             e = e.owner;
787         else
788             break;
789     }
790     // sounds to self may always pass
791     if (_dest == MSG_ONE)
792         if (e == msg_entity)
793             return true;
794     // sounds by players can be removed
795     if (autocvar_bot_sound_monopoly)
796         if (IS_REAL_CLIENT(e))
797             return false;
798     // anything else may pass
799     return true;
800 }
801
802 #undef sound
803 void sound(entity e, float chan, string samp, float vol, float _atten)
804 {
805     if (!sound_allowed(MSG_BROADCAST, e))
806         return;
807     sound7(e, chan, samp, vol, _atten, 0, 0);
808 }
809
810 void soundtoat(float _dest, entity e, vector o, float chan, string samp, float vol, float _atten)
811 {
812     float entno, idx;
813
814     if (!sound_allowed(_dest, e))
815         return;
816
817     entno = num_for_edict(e);
818     idx = precache_sound_index(samp);
819
820     int sflags;
821     sflags = 0;
822
823     _atten = floor(_atten * 64);
824     vol = floor(vol * 255);
825
826     if (vol != 255)
827         sflags |= SND_VOLUME;
828     if (_atten != 64)
829         sflags |= SND_ATTENUATION;
830     if (entno >= 8192 || chan < 0 || chan > 7)
831         sflags |= SND_LARGEENTITY;
832     if (idx >= 256)
833         sflags |= SND_LARGESOUND;
834
835     WriteByte(_dest, SVC_SOUND);
836     WriteByte(_dest, sflags);
837     if (sflags & SND_VOLUME)
838         WriteByte(_dest, vol);
839     if (sflags & SND_ATTENUATION)
840         WriteByte(_dest, _atten);
841     if (sflags & SND_LARGEENTITY)
842     {
843         WriteShort(_dest, entno);
844         WriteByte(_dest, chan);
845     }
846     else
847     {
848         WriteShort(_dest, entno * 8 + chan);
849     }
850     if (sflags & SND_LARGESOUND)
851         WriteShort(_dest, idx);
852     else
853         WriteByte(_dest, idx);
854
855     WriteCoord(_dest, o.x);
856     WriteCoord(_dest, o.y);
857     WriteCoord(_dest, o.z);
858 }
859 void soundto(float _dest, entity e, float chan, string samp, float vol, float _atten)
860 {
861     vector o;
862
863     if (!sound_allowed(_dest, e))
864         return;
865
866     o = e.origin + 0.5 * (e.mins + e.maxs);
867     soundtoat(_dest, e, o, chan, samp, vol, _atten);
868 }
869 void soundat(entity e, vector o, float chan, string samp, float vol, float _atten)
870 {
871     soundtoat(((chan & 8) ? MSG_ALL : MSG_BROADCAST), e, o, chan, samp, vol, _atten);
872 }
873 void stopsoundto(float _dest, entity e, float chan)
874 {
875     float entno;
876
877     if (!sound_allowed(_dest, e))
878         return;
879
880     entno = num_for_edict(e);
881
882     if (entno >= 8192 || chan < 0 || chan > 7)
883     {
884         float idx, sflags;
885         idx = precache_sound_index("misc/null.wav");
886         sflags = SND_LARGEENTITY;
887         if (idx >= 256)
888             sflags |= SND_LARGESOUND;
889         WriteByte(_dest, SVC_SOUND);
890         WriteByte(_dest, sflags);
891         WriteShort(_dest, entno);
892         WriteByte(_dest, chan);
893         if (sflags & SND_LARGESOUND)
894             WriteShort(_dest, idx);
895         else
896             WriteByte(_dest, idx);
897         WriteCoord(_dest, e.origin.x);
898         WriteCoord(_dest, e.origin.y);
899         WriteCoord(_dest, e.origin.z);
900     }
901     else
902     {
903         WriteByte(_dest, SVC_STOPSOUND);
904         WriteShort(_dest, entno * 8 + chan);
905     }
906 }
907 void stopsound(entity e, float chan)
908 {
909     if (!sound_allowed(MSG_BROADCAST, e))
910         return;
911
912     stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
913     stopsoundto(MSG_ALL, e, chan); // in case of packet loss
914 }
915
916 void play2(entity e, string filename)
917 {
918     //stuffcmd(e, strcat("play2 ", filename, "\n"));
919     msg_entity = e;
920     soundtoat(MSG_ONE, world, '0 0 0', CH_INFO, filename, VOL_BASE, ATTEN_NONE);
921 }
922
923 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
924 .float spamtime;
925 float spamsound(entity e, float chan, string samp, float vol, float _atten)
926 {
927     if (!sound_allowed(MSG_BROADCAST, e))
928         return false;
929
930     if (time > e.spamtime)
931     {
932         e.spamtime = time;
933         sound(e, chan, samp, vol, _atten);
934         return true;
935     }
936     return false;
937 }
938
939 void play2team(float t, string filename)
940 {
941     entity head;
942
943     if (autocvar_bot_sound_monopoly)
944         return;
945
946     FOR_EACH_REALPLAYER(head)
947     {
948         if (head.team == t)
949             play2(head, filename);
950     }
951 }
952
953 void play2all(string samp)
954 {
955     if (autocvar_bot_sound_monopoly)
956         return;
957
958     sound(world, CH_INFO, samp, VOL_BASE, ATTEN_NONE);
959 }
960
961 void PrecachePlayerSounds(string f);
962 void precache_playermodel(string m)
963 {
964         float globhandle, i, n;
965         string f;
966
967         if(substring(m, -9,5) == "_lod1")
968                 return;
969         if(substring(m, -9,5) == "_lod2")
970                 return;
971         precache_model(m);
972         f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
973         if(fexists(f))
974                 precache_model(f);
975         f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
976         if(fexists(f))
977                 precache_model(f);
978
979         globhandle = search_begin(strcat(m, "_*.sounds"), true, false);
980         if (globhandle < 0)
981                 return;
982         n = search_getsize(globhandle);
983         for (i = 0; i < n; ++i)
984         {
985                 //print(search_getfilename(globhandle, i), "\n");
986                 f = search_getfilename(globhandle, i);
987                 PrecachePlayerSounds(f);
988         }
989         search_end(globhandle);
990 }
991 void precache_all_playermodels(string pattern)
992 {
993         float globhandle, i, n;
994         string f;
995
996         globhandle = search_begin(pattern, true, false);
997         if (globhandle < 0)
998                 return;
999         n = search_getsize(globhandle);
1000         for (i = 0; i < n; ++i)
1001         {
1002                 //print(search_getfilename(globhandle, i), "\n");
1003                 f = search_getfilename(globhandle, i);
1004                 precache_playermodel(f);
1005         }
1006         search_end(globhandle);
1007 }
1008
1009 void precache()
1010 {
1011     // gamemode related things
1012     precache_model ("models/misc/chatbubble.spr");
1013         precache_model("models/ice/ice.md3");
1014
1015 #ifdef TTURRETS_ENABLED
1016     if (autocvar_g_turrets)
1017         turrets_precash();
1018 #endif
1019
1020     // Precache all player models if desired
1021     if (autocvar_sv_precacheplayermodels)
1022     {
1023         PrecachePlayerSounds("sound/player/default.sounds");
1024         precache_all_playermodels("models/player/*.zym");
1025         precache_all_playermodels("models/player/*.dpm");
1026         precache_all_playermodels("models/player/*.md3");
1027         precache_all_playermodels("models/player/*.psk");
1028         precache_all_playermodels("models/player/*.iqm");
1029     }
1030
1031     if (autocvar_sv_defaultcharacter)
1032     {
1033         string s;
1034         s = autocvar_sv_defaultplayermodel_red;
1035         if (s != "")
1036             precache_playermodel(s);
1037         s = autocvar_sv_defaultplayermodel_blue;
1038         if (s != "")
1039             precache_playermodel(s);
1040         s = autocvar_sv_defaultplayermodel_yellow;
1041         if (s != "")
1042             precache_playermodel(s);
1043         s = autocvar_sv_defaultplayermodel_pink;
1044         if (s != "")
1045             precache_playermodel(s);
1046         s = autocvar_sv_defaultplayermodel;
1047         if (s != "")
1048             precache_playermodel(s);
1049     }
1050
1051     if (g_footsteps)
1052     {
1053         PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1054         PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1055     }
1056
1057     // gore and miscellaneous sounds
1058     //precache_sound ("misc/h2ohit.wav");
1059     precache_model ("models/hook.md3");
1060     precache_sound ("misc/armorimpact.wav");
1061     precache_sound ("misc/bodyimpact1.wav");
1062     precache_sound ("misc/bodyimpact2.wav");
1063     precache_sound ("misc/gib.wav");
1064     precache_sound ("misc/gib_splat01.wav");
1065     precache_sound ("misc/gib_splat02.wav");
1066     precache_sound ("misc/gib_splat03.wav");
1067     precache_sound ("misc/gib_splat04.wav");
1068     PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1069     PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1070     precache_sound ("misc/null.wav");
1071     precache_sound ("misc/spawn.wav");
1072     precache_sound ("misc/talk.wav");
1073     precache_sound ("misc/teleport.wav");
1074     precache_sound ("misc/poweroff.wav");
1075     precache_sound ("player/lava.wav");
1076     precache_sound ("player/slime.wav");
1077
1078     precache_model ("models/sprites/0.spr32");
1079     precache_model ("models/sprites/1.spr32");
1080     precache_model ("models/sprites/2.spr32");
1081     precache_model ("models/sprites/3.spr32");
1082     precache_model ("models/sprites/4.spr32");
1083     precache_model ("models/sprites/5.spr32");
1084     precache_model ("models/sprites/6.spr32");
1085     precache_model ("models/sprites/7.spr32");
1086     precache_model ("models/sprites/8.spr32");
1087     precache_model ("models/sprites/9.spr32");
1088     precache_model ("models/sprites/10.spr32");
1089
1090     // common weapon precaches
1091         precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1092     precache_sound ("weapons/weapon_switch.wav");
1093     precache_sound ("weapons/weaponpickup.wav");
1094     precache_sound ("weapons/unavailable.wav");
1095     precache_sound ("weapons/dryfire.wav");
1096     if (g_grappling_hook)
1097     {
1098         precache_sound ("weapons/hook_fire.wav"); // hook
1099         precache_sound ("weapons/hook_impact.wav"); // hook
1100     }
1101
1102     precache_model("models/elaser.mdl");
1103     precache_model("models/laser.mdl");
1104     precache_model("models/ebomb.mdl");
1105
1106 #if 0
1107     // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1108
1109     if (!self.noise && self.music) // quake 3 uses the music field
1110         self.noise = self.music;
1111
1112     // plays music for the level if there is any
1113     if (self.noise)
1114     {
1115         precache_sound (self.noise);
1116         ambientsound ('0 0 0', self.noise, VOL_BASE, ATTEN_NONE);
1117     }
1118 #endif
1119
1120 #include "precache-for-csqc.inc"
1121 }
1122
1123
1124 void make_safe_for_remove(entity e)
1125 {
1126     if (e.initialize_entity)
1127     {
1128         entity ent, prev = world;
1129         for (ent = initialize_entity_first; ent; )
1130         {
1131             if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1132             {
1133                 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1134                 // skip it in linked list
1135                 if (prev)
1136                 {
1137                     prev.initialize_entity_next = ent.initialize_entity_next;
1138                     ent = prev.initialize_entity_next;
1139                 }
1140                 else
1141                 {
1142                     initialize_entity_first = ent.initialize_entity_next;
1143                     ent = initialize_entity_first;
1144                 }
1145             }
1146             else
1147             {
1148                 prev = ent;
1149                 ent = ent.initialize_entity_next;
1150             }
1151         }
1152     }
1153 }
1154
1155 void objerror(string s)
1156 {
1157     make_safe_for_remove(self);
1158     builtin_objerror(s);
1159 }
1160
1161 .float remove_except_protected_forbidden;
1162 void remove_except_protected(entity e)
1163 {
1164         if(e.remove_except_protected_forbidden)
1165                 error("not allowed to remove this at this point");
1166         builtin_remove(e);
1167 }
1168
1169 void remove_unsafely(entity e)
1170 {
1171     if(e.classname == "spike")
1172         error("Removing spikes is forbidden (crylink bug), please report");
1173     builtin_remove(e);
1174 }
1175
1176 void remove_safely(entity e)
1177 {
1178     make_safe_for_remove(e);
1179     builtin_remove(e);
1180 }
1181
1182 void InitializeEntity(entity e, void(void) func, float order)
1183 {
1184     entity prev, cur;
1185
1186     if (!e || e.initialize_entity)
1187     {
1188         // make a proxy initializer entity
1189         entity e_old;
1190         e_old = e;
1191         e = spawn();
1192         e.classname = "initialize_entity";
1193         e.enemy = e_old;
1194     }
1195
1196     e.initialize_entity = func;
1197     e.initialize_entity_order = order;
1198
1199     cur = initialize_entity_first;
1200     prev = world;
1201     for (;;)
1202     {
1203         if (!cur || cur.initialize_entity_order > order)
1204         {
1205             // insert between prev and cur
1206             if (prev)
1207                 prev.initialize_entity_next = e;
1208             else
1209                 initialize_entity_first = e;
1210             e.initialize_entity_next = cur;
1211             return;
1212         }
1213         prev = cur;
1214         cur = cur.initialize_entity_next;
1215     }
1216 }
1217 void InitializeEntitiesRun()
1218 {
1219     entity startoflist;
1220     startoflist = initialize_entity_first;
1221     initialize_entity_first = world;
1222     remove = remove_except_protected;
1223     for (self = startoflist; self; self = self.initialize_entity_next)
1224     {
1225         self.remove_except_protected_forbidden = 1;
1226     }
1227     for (self = startoflist; self; )
1228     {
1229         entity e;
1230         var void(void) func;
1231         e = self.initialize_entity_next;
1232         func = self.initialize_entity;
1233         self.initialize_entity_order = 0;
1234         self.initialize_entity = func_null;
1235         self.initialize_entity_next = world;
1236         self.remove_except_protected_forbidden = 0;
1237         if (self.classname == "initialize_entity")
1238         {
1239             entity e_old;
1240             e_old = self.enemy;
1241             builtin_remove(self);
1242             self = e_old;
1243         }
1244         //dprint("Delayed initialization: ", self.classname, "\n");
1245         if(func)
1246             func();
1247         else
1248         {
1249             eprint(self);
1250             backtrace(strcat("Null function in: ", self.classname, "\n"));
1251         }
1252         self = e;
1253     }
1254     remove = remove_unsafely;
1255 }
1256
1257 void UncustomizeEntitiesRun()
1258 {
1259     entity oldself;
1260     oldself = self;
1261     for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1262         self.uncustomizeentityforclient();
1263     self = oldself;
1264 }
1265 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1266 {
1267     e.customizeentityforclient = customizer;
1268     e.uncustomizeentityforclient = uncustomizer;
1269     e.uncustomizeentityforclient_set = !!uncustomizer;
1270 }
1271
1272
1273 void Net_LinkEntity(entity e, float docull, float dt, bool(entity, int) sendfunc)
1274 {
1275     vector mi, ma;
1276
1277     if (e.classname == "")
1278         e.classname = "net_linked";
1279
1280     if (e.model == "" || self.modelindex == 0)
1281     {
1282         mi = e.mins;
1283         ma = e.maxs;
1284         setmodel(e, "null");
1285         setsize(e, mi, ma);
1286     }
1287
1288     e.SendEntity = sendfunc;
1289     e.SendFlags = 0xFFFFFF;
1290
1291     if (!docull)
1292         e.effects |= EF_NODEPTHTEST;
1293
1294     if (dt)
1295     {
1296         e.nextthink = time + dt;
1297         e.think = SUB_Remove;
1298     }
1299 }
1300
1301
1302 .float(entity) isEliminated;
1303 float EliminatedPlayers_SendEntity(entity to, float sendflags)
1304 {
1305         float i, f, b;
1306         entity e;
1307         WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
1308         WriteByte(MSG_ENTITY, sendflags);
1309
1310         if(sendflags & 1)
1311         {
1312                 for(i = 1; i <= maxclients; i += 8)
1313                 {
1314                         for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
1315                         {
1316                                 if(eliminatedPlayers.isEliminated(e))
1317                                         f |= b;
1318                         }
1319                         WriteByte(MSG_ENTITY, f);
1320                 }
1321         }
1322
1323         return true;
1324 }
1325
1326 void EliminatedPlayers_Init(float(entity) isEliminated_func)
1327 {
1328         if(eliminatedPlayers)
1329         {
1330                 backtrace("Can't spawn eliminatedPlayers again!");
1331                 return;
1332         }
1333         Net_LinkEntity(eliminatedPlayers = spawn(), false, 0, EliminatedPlayers_SendEntity);
1334         eliminatedPlayers.isEliminated = isEliminated_func;
1335 }
1336
1337
1338 void adaptor_think2touch()
1339 {
1340     entity o;
1341     o = other;
1342     other = world;
1343     self.touch();
1344     other = o;
1345 }
1346
1347 void adaptor_think2use()
1348 {
1349     entity o, a;
1350     o = other;
1351     a = activator;
1352     activator = world;
1353     other = world;
1354     self.use();
1355     other = o;
1356     activator = a;
1357 }
1358
1359 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1360 {
1361         if(!(self.flags & FL_ONGROUND)) // if onground, we ARE touching something, but HITTYPE_SPLASH is to be networked if the damage causing projectile is not touching ANYTHING
1362                 self.projectiledeathtype |= HITTYPE_SPLASH;
1363         adaptor_think2use();
1364 }
1365
1366 // deferred dropping
1367 void DropToFloor_Handler()
1368 {
1369     builtin_droptofloor();
1370     self.dropped_origin = self.origin;
1371 }
1372
1373 void droptofloor()
1374 {
1375     InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1376 }
1377
1378
1379
1380 float trace_hits_box_a0, trace_hits_box_a1;
1381
1382 float trace_hits_box_1d(float end, float thmi, float thma)
1383 {
1384     if (end == 0)
1385     {
1386         // just check if x is in range
1387         if (0 < thmi)
1388             return false;
1389         if (0 > thma)
1390             return false;
1391     }
1392     else
1393     {
1394         // do the trace with respect to x
1395         // 0 -> end has to stay in thmi -> thma
1396         trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1397         trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1398         if (trace_hits_box_a0 > trace_hits_box_a1)
1399             return false;
1400     }
1401     return true;
1402 }
1403
1404 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1405 {
1406     end -= start;
1407     thmi -= start;
1408     thma -= start;
1409     // now it is a trace from 0 to end
1410
1411     trace_hits_box_a0 = 0;
1412     trace_hits_box_a1 = 1;
1413
1414     if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
1415         return false;
1416     if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
1417         return false;
1418     if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
1419         return false;
1420
1421     return true;
1422 }
1423
1424 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1425 {
1426     return trace_hits_box(start, end, thmi - ma, thma - mi);
1427 }
1428
1429 float SUB_NoImpactCheck()
1430 {
1431         // zero hitcontents = this is not the real impact, but either the
1432         // mirror-impact of something hitting the projectile instead of the
1433         // projectile hitting the something, or a touchareagrid one. Neither of
1434         // these stop the projectile from moving, so...
1435         if(trace_dphitcontents == 0)
1436         {
1437                 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1438                 dprintf("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Profectile will self-destruct. (edict: %d, classname: %s, origin: %s)\n", num_for_edict(self), self.classname, vtos(self.origin));
1439                 checkclient();
1440         }
1441     if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1442         return 1;
1443     if (other == world && self.size != '0 0 0')
1444     {
1445         vector tic;
1446         tic = self.velocity * sys_frametime;
1447         tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
1448         traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
1449         if (trace_fraction >= 1)
1450         {
1451             dprint("Odd... did not hit...?\n");
1452         }
1453         else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1454         {
1455             dprint("Detected and prevented the sky-grapple bug.\n");
1456             return 1;
1457         }
1458     }
1459
1460     return 0;
1461 }
1462
1463 #define SUB_OwnerCheck() (other && (other == self.owner))
1464
1465 void W_Crylink_Dequeue(entity e);
1466 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
1467 {
1468         if(SUB_OwnerCheck())
1469                 return true;
1470         if(SUB_NoImpactCheck())
1471         {
1472                 if(self.classname == "nade")
1473                         return false; // no checks here
1474                 else if(self.classname == "grapplinghook")
1475                         RemoveGrapplingHook(self.realowner);
1476                 else if(self.classname == "spike")
1477                 {
1478                         W_Crylink_Dequeue(self);
1479                         remove(self);
1480                 }
1481                 else
1482                         remove(self);
1483                 return true;
1484         }
1485         if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
1486                 UpdateCSQCProjectile(self);
1487         return false;
1488 }
1489
1490
1491 void URI_Get_Callback(float id, float status, string data)
1492 {
1493         if(url_URI_Get_Callback(id, status, data))
1494         {
1495                 // handled
1496         }
1497         else if (id == URI_GET_DISCARD)
1498         {
1499                 // discard
1500         }
1501         else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END)
1502         {
1503                 // sv_cmd curl
1504                 Curl_URI_Get_Callback(id, status, data);
1505         }
1506         else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
1507         {
1508                 // online ban list
1509                 OnlineBanList_URI_Get_Callback(id, status, data);
1510         }
1511         else
1512         {
1513                 print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
1514         }
1515 }
1516
1517 string uid2name(string myuid) {
1518         string s;
1519         s = db_get(ServerProgsDB, strcat("/uid2name/", myuid));
1520
1521         // FIXME remove this later after 0.6 release
1522         // convert old style broken records to correct style
1523         if(s == "")
1524         {
1525                 s = db_get(ServerProgsDB, strcat("uid2name", myuid));
1526                 if(s != "")
1527                 {
1528                         db_put(ServerProgsDB, strcat("/uid2name/", myuid), s);
1529                         db_put(ServerProgsDB, strcat("uid2name", myuid), "");
1530                 }
1531         }
1532
1533         if(s == "")
1534                 s = "^1Unregistered Player";
1535         return s;
1536 }
1537
1538 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
1539 {
1540     float m, i;
1541     vector start, org, delta, end, enddown, mstart;
1542     entity sp;
1543
1544     m = e.dphitcontentsmask;
1545     e.dphitcontentsmask = goodcontents | badcontents;
1546
1547     org = world.mins;
1548     delta = world.maxs - world.mins;
1549
1550     start = end = org;
1551
1552     for (i = 0; i < attempts; ++i)
1553     {
1554         start.x = org.x + random() * delta.x;
1555         start.y = org.y + random() * delta.y;
1556         start.z = org.z + random() * delta.z;
1557
1558         // rule 1: start inside world bounds, and outside
1559         // solid, and don't start from somewhere where you can
1560         // fall down to evil
1561         tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta.z, MOVE_NORMAL, e);
1562         if (trace_fraction >= 1)
1563             continue;
1564         if (trace_startsolid)
1565             continue;
1566         if (trace_dphitcontents & badcontents)
1567             continue;
1568         if (trace_dphitq3surfaceflags & badsurfaceflags)
1569             continue;
1570
1571         // rule 2: if we are too high, lower the point
1572         if (trace_fraction * delta.z > maxaboveground)
1573             start = trace_endpos + '0 0 1' * maxaboveground;
1574         enddown = trace_endpos;
1575
1576         // rule 3: make sure we aren't outside the map. This only works
1577         // for somewhat well formed maps. A good rule of thumb is that
1578         // the map should have a convex outside hull.
1579         // these can be traceLINES as we already verified the starting box
1580         mstart = start + 0.5 * (e.mins + e.maxs);
1581         traceline(mstart, mstart + '1 0 0' * delta.x, MOVE_NORMAL, e);
1582         if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1583             continue;
1584         traceline(mstart, mstart - '1 0 0' * delta.x, MOVE_NORMAL, e);
1585         if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1586             continue;
1587         traceline(mstart, mstart + '0 1 0' * delta.y, MOVE_NORMAL, e);
1588         if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1589             continue;
1590         traceline(mstart, mstart - '0 1 0' * delta.y, MOVE_NORMAL, e);
1591         if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1592             continue;
1593         traceline(mstart, mstart + '0 0 1' * delta.z, MOVE_NORMAL, e);
1594         if (trace_fraction >= 1 || trace_dphittexturename == "common/caulk")
1595             continue;
1596
1597         // rule 4: we must "see" some spawnpoint or item
1598         for(sp = world; (sp = find(sp, classname, "info_player_deathmatch")); )
1599                 if(checkpvs(mstart, sp))
1600                         if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1)
1601                                 break;
1602         if(!sp)
1603         {
1604                 for(sp = world; (sp = findflags(sp, flags, FL_ITEM)); )
1605                         if(checkpvs(mstart, sp))
1606                                 if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1)
1607                                         break;
1608                 if(!sp)
1609                         continue;
1610         }
1611
1612         // find a random vector to "look at"
1613         end.x = org.x + random() * delta.x;
1614         end.y = org.y + random() * delta.y;
1615         end.z = org.z + random() * delta.z;
1616         end = start + normalize(end - start) * vlen(delta);
1617
1618         // rule 4: start TO end must not be too short
1619         tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
1620         if (trace_startsolid)
1621             continue;
1622         if (trace_fraction < minviewdistance / vlen(delta))
1623             continue;
1624
1625         // rule 5: don't want to look at sky
1626         if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
1627             continue;
1628
1629         // rule 6: we must not end up in trigger_hurt
1630         if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
1631             continue;
1632
1633         break;
1634     }
1635
1636     e.dphitcontentsmask = m;
1637
1638     if (i < attempts)
1639     {
1640         setorigin(e, start);
1641         e.angles = vectoangles(end - start);
1642         dprint("Needed ", ftos(i + 1), " attempts\n");
1643         return true;
1644     }
1645     else
1646         return false;
1647 }
1648
1649 void write_recordmarker(entity pl, float tstart, float dt)
1650 {
1651     GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
1652
1653     // also write a marker into demo files for demotc-race-record-extractor to find
1654     stuffcmd(pl,
1655              strcat(
1656                  strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
1657                  " ", ftos(tstart), " ", ftos(dt), "\n"));
1658 }
1659
1660 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
1661 {
1662         switch(algn)
1663         {
1664                 default:
1665                 case 3: // right
1666                         break;
1667
1668                 case 4: // left
1669                         vecs.y = -vecs.y;
1670                         break;
1671
1672                 case 1:
1673                         if(allowcenter) // 2: allow center handedness
1674                         {
1675                                 // center
1676                                 vecs.y = 0;
1677                                 vecs.z -= 2;
1678                         }
1679                         else
1680                         {
1681                                 // right
1682                         }
1683                         break;
1684
1685                 case 2:
1686                         if(allowcenter) // 2: allow center handedness
1687                         {
1688                                 // center
1689                                 vecs.y = 0;
1690                                 vecs.z -= 2;
1691                         }
1692                         else
1693                         {
1694                                 // left
1695                                 vecs.y = -vecs.y;
1696                         }
1697                         break;
1698         }
1699         return vecs;
1700 }
1701
1702 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
1703 {
1704         string s;
1705         vector v;
1706
1707         if (autocvar_g_shootfromeye)
1708         {
1709                 if (visual)
1710                 {
1711                         if (autocvar_g_shootfromclient) { vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn); }
1712                         else { vecs.y = 0; vecs.z -= 2; }
1713                 }
1714                 else
1715                 {
1716                         vecs.y = 0;
1717                         vecs.z = 0;
1718                 }
1719         }
1720         else if (autocvar_g_shootfromcenter)
1721         {
1722                 vecs.y = 0;
1723                 vecs.z -= 2;
1724         }
1725         else if ((s = autocvar_g_shootfromfixedorigin) != "")
1726         {
1727                 v = stov(s);
1728                 if (y_is_right)
1729                         v.y = -v.y;
1730                 if (v.x != 0)
1731                         vecs.x = v.x;
1732                 vecs.y = v.y;
1733                 vecs.z = v.z;
1734         }
1735         else if (autocvar_g_shootfromclient)
1736         {
1737                 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
1738         }
1739         return vecs;
1740 }
1741
1742 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
1743 {
1744         return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
1745 }
1746
1747
1748 void attach_sameorigin(entity e, entity to, string tag)
1749 {
1750     vector org, t_forward, t_left, t_up, e_forward, e_up;
1751     float tagscale;
1752
1753     org = e.origin - gettaginfo(to, gettagindex(to, tag));
1754     tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
1755     t_forward = v_forward * tagscale;
1756     t_left = v_right * -tagscale;
1757     t_up = v_up * tagscale;
1758
1759     e.origin_x = org * t_forward;
1760     e.origin_y = org * t_left;
1761     e.origin_z = org * t_up;
1762
1763     // current forward and up directions
1764     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1765                 e.angles = AnglesTransform_FromVAngles(e.angles);
1766         else
1767                 e.angles = AnglesTransform_FromAngles(e.angles);
1768     fixedmakevectors(e.angles);
1769
1770     // untransform forward, up!
1771     e_forward.x = v_forward * t_forward;
1772     e_forward.y = v_forward * t_left;
1773     e_forward.z = v_forward * t_up;
1774     e_up.x = v_up * t_forward;
1775     e_up.y = v_up * t_left;
1776     e_up.z = v_up * t_up;
1777
1778     e.angles = fixedvectoangles2(e_forward, e_up);
1779     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1780                 e.angles = AnglesTransform_ToVAngles(e.angles);
1781         else
1782                 e.angles = AnglesTransform_ToAngles(e.angles);
1783
1784     setattachment(e, to, tag);
1785     setorigin(e, e.origin);
1786 }
1787
1788 void detach_sameorigin(entity e)
1789 {
1790     vector org;
1791     org = gettaginfo(e, 0);
1792     e.angles = fixedvectoangles2(v_forward, v_up);
1793     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1794                 e.angles = AnglesTransform_ToVAngles(e.angles);
1795         else
1796                 e.angles = AnglesTransform_ToAngles(e.angles);
1797     setorigin(e, org);
1798     setattachment(e, world, "");
1799     setorigin(e, e.origin);
1800 }
1801
1802 void follow_sameorigin(entity e, entity to)
1803 {
1804     e.movetype = MOVETYPE_FOLLOW; // make the hole follow
1805     e.aiment = to; // make the hole follow bmodel
1806     e.punchangle = to.angles; // the original angles of bmodel
1807     e.view_ofs = e.origin - to.origin; // relative origin
1808     e.v_angle = e.angles - to.angles; // relative angles
1809 }
1810
1811 void unfollow_sameorigin(entity e)
1812 {
1813     e.movetype = MOVETYPE_NONE;
1814 }
1815
1816 entity gettaginfo_relative_ent;
1817 vector gettaginfo_relative(entity e, float tag)
1818 {
1819     if (!gettaginfo_relative_ent)
1820     {
1821         gettaginfo_relative_ent = spawn();
1822         gettaginfo_relative_ent.effects = EF_NODRAW;
1823     }
1824     gettaginfo_relative_ent.model = e.model;
1825     gettaginfo_relative_ent.modelindex = e.modelindex;
1826     gettaginfo_relative_ent.frame = e.frame;
1827     return gettaginfo(gettaginfo_relative_ent, tag);
1828 }
1829
1830 .float scale2;
1831
1832 float modeleffect_SendEntity(entity to, int sf)
1833 {
1834         float f;
1835         WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
1836
1837         f = 0;
1838         if(self.velocity != '0 0 0')
1839                 f |= 1;
1840         if(self.angles != '0 0 0')
1841                 f |= 2;
1842         if(self.avelocity != '0 0 0')
1843                 f |= 4;
1844
1845         WriteByte(MSG_ENTITY, f);
1846         WriteShort(MSG_ENTITY, self.modelindex);
1847         WriteByte(MSG_ENTITY, self.skin);
1848         WriteByte(MSG_ENTITY, self.frame);
1849         WriteCoord(MSG_ENTITY, self.origin.x);
1850         WriteCoord(MSG_ENTITY, self.origin.y);
1851         WriteCoord(MSG_ENTITY, self.origin.z);
1852         if(f & 1)
1853         {
1854                 WriteCoord(MSG_ENTITY, self.velocity.x);
1855                 WriteCoord(MSG_ENTITY, self.velocity.y);
1856                 WriteCoord(MSG_ENTITY, self.velocity.z);
1857         }
1858         if(f & 2)
1859         {
1860                 WriteCoord(MSG_ENTITY, self.angles.x);
1861                 WriteCoord(MSG_ENTITY, self.angles.y);
1862                 WriteCoord(MSG_ENTITY, self.angles.z);
1863         }
1864         if(f & 4)
1865         {
1866                 WriteCoord(MSG_ENTITY, self.avelocity.x);
1867                 WriteCoord(MSG_ENTITY, self.avelocity.y);
1868                 WriteCoord(MSG_ENTITY, self.avelocity.z);
1869         }
1870         WriteShort(MSG_ENTITY, self.scale * 256.0);
1871         WriteShort(MSG_ENTITY, self.scale2 * 256.0);
1872         WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
1873         WriteByte(MSG_ENTITY, self.fade_time * 100.0);
1874         WriteByte(MSG_ENTITY, self.alpha * 255.0);
1875
1876         return true;
1877 }
1878
1879 void modeleffect_spawn(string m, float s, float f, vector o, vector v, vector ang, vector angv, float s0, float s2, float a, float t1, float t2)
1880 {
1881         entity e;
1882         float sz;
1883         e = spawn();
1884         e.classname = "modeleffect";
1885         setmodel(e, m);
1886         e.frame = f;
1887         setorigin(e, o);
1888         e.velocity = v;
1889         e.angles = ang;
1890         e.avelocity = angv;
1891         e.alpha = a;
1892         e.teleport_time = t1;
1893         e.fade_time = t2;
1894         e.skin = s;
1895         if(s0 >= 0)
1896                 e.scale = s0 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1897         else
1898                 e.scale = -s0;
1899         if(s2 >= 0)
1900                 e.scale2 = s2 / max6(-e.mins.x, -e.mins.y, -e.mins.z, e.maxs.x, e.maxs.y, e.maxs.z);
1901         else
1902                 e.scale2 = -s2;
1903         sz = max(e.scale, e.scale2);
1904         setsize(e, e.mins * sz, e.maxs * sz);
1905         Net_LinkEntity(e, false, 0.1, modeleffect_SendEntity);
1906 }
1907
1908 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
1909 {
1910         return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
1911 }
1912
1913 float randombit(float bits)
1914 {
1915         if(!(bits & (bits-1))) // this ONLY holds for powers of two!
1916                 return bits;
1917
1918         float n, f, b, r;
1919
1920         r = random();
1921         b = 0;
1922         n = 0;
1923
1924         for(f = 1; f <= bits; f *= 2)
1925         {
1926                 if(bits & f)
1927                 {
1928                         ++n;
1929                         r *= n;
1930                         if(r <= 1)
1931                                 b = f;
1932                         else
1933                                 r = (r - 1) / (n - 1);
1934                 }
1935         }
1936
1937         return b;
1938 }
1939
1940 float randombits(float bits, float k, float error_return)
1941 {
1942         float r;
1943         r = 0;
1944         while(k > 0 && bits != r)
1945         {
1946                 r += randombit(bits - r);
1947                 --k;
1948         }
1949         if(error_return)
1950                 if(k > 0)
1951                         return -1; // all
1952         return r;
1953 }
1954
1955 void randombit_test(float bits, float iter)
1956 {
1957         while(iter > 0)
1958         {
1959                 print(ftos(randombit(bits)), "\n");
1960                 --iter;
1961         }
1962 }
1963
1964 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
1965 {
1966         if(halflifedist > 0)
1967                 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
1968         else if(halflifedist < 0)
1969                 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
1970         else
1971                 return 1;
1972 }
1973
1974
1975 void defer_think()
1976 {
1977     entity oself;
1978
1979     oself           = self;
1980     self            = self.owner;
1981     oself.think     = SUB_Remove;
1982     oself.nextthink = time;
1983
1984     oself.use();
1985 }
1986
1987 /*
1988     Execute func() after time + fdelay.
1989     self when func is executed = self when defer is called
1990 */
1991 void defer(float fdelay, void() func)
1992 {
1993     entity e;
1994
1995     e           = spawn();
1996     e.owner     = self;
1997     e.use       = func;
1998     e.think     = defer_think;
1999     e.nextthink = time + fdelay;
2000 }
2001
2002 .string aiment_classname;
2003 .float aiment_deadflag;
2004 void SetMovetypeFollow(entity ent, entity e)
2005 {
2006         // FIXME this may not be warpzone aware
2007         ent.movetype = MOVETYPE_FOLLOW; // make the hole follow
2008         ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported.
2009         ent.aiment = e; // make the hole follow bmodel
2010         ent.punchangle = e.angles; // the original angles of bmodel
2011         ent.view_ofs = ent.origin - e.origin; // relative origin
2012         ent.v_angle = ent.angles - e.angles; // relative angles
2013         ent.aiment_classname = strzone(e.classname);
2014         ent.aiment_deadflag = e.deadflag;
2015 }
2016 void UnsetMovetypeFollow(entity ent)
2017 {
2018         ent.movetype = MOVETYPE_FLY;
2019         PROJECTILE_MAKETRIGGER(ent);
2020         ent.aiment = world;
2021 }
2022 float LostMovetypeFollow(entity ent)
2023 {
2024 /*
2025         if(ent.movetype != MOVETYPE_FOLLOW)
2026                 if(ent.aiment)
2027                         error("???");
2028 */
2029         if(ent.aiment)
2030         {
2031                 if(ent.aiment.classname != ent.aiment_classname)
2032                         return 1;
2033                 if(ent.aiment.deadflag != ent.aiment_deadflag)
2034                         return 1;
2035         }
2036         return 0;
2037 }
2038
2039 float isPushable(entity e)
2040 {
2041         if(e.iscreature)
2042                 return true;
2043         if(e.pushable)
2044                 return true;
2045         switch(e.classname)
2046         {
2047                 case "body":
2048                 case "droppedweapon":
2049                 case "keepawayball":
2050                 case "nexball_basketball":
2051                 case "nexball_football":
2052                         return true;
2053                 case "bullet": // antilagged bullets can't hit this either
2054                         return false;
2055         }
2056         if (e.projectiledeathtype)
2057                 return true;
2058         return false;
2059 }