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