]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/server/miscfunctions.qc
Merge remote branch 'origin/master' into samual/flyingspectators
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / miscfunctions.qc
1 var void remove(entity e);
2 void objerror(string s);
3 void droptofloor();
4 .vector dropped_origin;
5
6 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
7 void crosshair_trace(entity pl)
8 {
9         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));
10 }
11 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag);
12 void WarpZone_crosshair_trace(entity pl)
13 {
14         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));
15 }
16
17 void() spawnfunc_info_player_deathmatch; // needed for the other spawnpoints
18 void() spawnpoint_use;
19 string GetMapname();
20 string ColoredTeamName(float t);
21
22 string admin_name(void)
23 {
24         if(autocvar_sv_adminnick != "")
25                 return autocvar_sv_adminnick;
26         else
27                 return "SERVER ADMIN";
28 }
29
30 float DistributeEvenly_amount;
31 float DistributeEvenly_totalweight;
32 void DistributeEvenly_Init(float amount, float totalweight)
33 {
34     if (DistributeEvenly_amount)
35     {
36         dprint("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ");
37         dprint(ftos(DistributeEvenly_totalweight), " left!)\n");
38     }
39     if (totalweight == 0)
40         DistributeEvenly_amount = 0;
41     else
42         DistributeEvenly_amount = amount;
43     DistributeEvenly_totalweight = totalweight;
44 }
45 float DistributeEvenly_Get(float weight)
46 {
47     float f;
48     if (weight <= 0)
49         return 0;
50     f = floor(0.5 + DistributeEvenly_amount * weight / DistributeEvenly_totalweight);
51     DistributeEvenly_totalweight -= weight;
52     DistributeEvenly_amount -= f;
53     return f;
54 }
55
56 #define move_out_of_solid(e) WarpZoneLib_MoveOutOfSolid(e)
57
58
59 string STR_PLAYER = "player";
60 string STR_SPECTATOR = "spectator";
61 string STR_OBSERVER = "observer";
62
63 #if 0
64 #define FOR_EACH_CLIENT(v) for(v = world; (v = findflags(v, flags, FL_CLIENT)) != world; )
65 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
66 #define FOR_EACH_PLAYER(v) for(v = world; (v = find(v, classname, STR_PLAYER)) != world; )
67 #define FOR_EACH_REALPLAYER(v) FOR_EACH_PLAYER(v) if(clienttype(v) == CLIENTTYPE_REAL)
68 #else
69 #define FOR_EACH_CLIENTSLOT(v) for(v = world; (v = nextent(v)) && (num_for_edict(v) <= maxclients); )
70 #define FOR_EACH_CLIENT(v) FOR_EACH_CLIENTSLOT(v) if(v.flags & FL_CLIENT)
71 #define FOR_EACH_REALCLIENT(v) FOR_EACH_CLIENT(v) if(clienttype(v) == CLIENTTYPE_REAL)
72 #define FOR_EACH_PLAYER(v) FOR_EACH_CLIENT(v) if(v.classname == STR_PLAYER)
73 #define FOR_EACH_REALPLAYER(v) FOR_EACH_REALCLIENT(v) if(v.classname == STR_PLAYER)
74 #endif
75
76 // copies a string to a tempstring (so one can strunzone it)
77 string strcat1(string s) = #115; // FRIK_FILE
78
79 float logfile_open;
80 float logfile;
81
82 string GetAdvancedDeathReports(entity enPlayer) // Extra fragmessage information
83 {
84         local float nPlayerHealth = rint(enPlayer.health);
85         local float nPlayerArmor = rint(enPlayer.armorvalue);
86         local float nPlayerHandicap = enPlayer.cvar_cl_handicap;
87         local float nPlayerPing = rint(enPlayer.ping);
88         local string strPlayerPingColor;
89         local string strMessage;
90         if(nPlayerPing >= 150)
91                 strPlayerPingColor = "^1";
92         else
93                 strPlayerPingColor = "^2";
94
95         if((autocvar_sv_fragmessage_information_stats) && (enPlayer.health >= 1))
96                 strMessage = strcat(strMessage, "\n^7(Health ^1", ftos(nPlayerHealth), "^7 / Armor ^2", ftos(nPlayerArmor), "^7)");
97
98         if(autocvar_sv_fragmessage_information_ping) {
99                 if(clienttype(enPlayer) == CLIENTTYPE_BOT) // Bots have no ping
100                         strMessage = strcat(strMessage, " ^7(^2Bot");
101                 else
102                         strMessage = strcat(strMessage, " ^7(Ping ", strPlayerPingColor, ftos(nPlayerPing), "ms");
103                 if(autocvar_sv_fragmessage_information_handicap)
104                         if(autocvar_sv_fragmessage_information_handicap == 2)
105                                 if(nPlayerHandicap <= 1)
106                                         strMessage = strcat(strMessage, "^7 / Handicap ^2Off^7)");
107                                 else
108                                         strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
109                         else if not(nPlayerHandicap <= 1)
110                                 strMessage = strcat(strMessage, "^7 / Handicap ^2", ftos(nPlayerHandicap), "^7)");
111                 else
112                         strMessage = strcat(strMessage, "^7)");
113         } else if(autocvar_sv_fragmessage_information_handicap) {
114                 if(autocvar_sv_fragmessage_information_handicap == 2)
115                         if(nPlayerHandicap <= 1)
116                                 strMessage = strcat(strMessage, "\n^7(Handicap ^2Off^7)");
117                         else
118                                 strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
119                 else if(nPlayerHandicap > 1)
120                         strMessage = strcat(strMessage, "\n^7(Handicap ^2", ftos(nPlayerHandicap), "^7)");
121         }
122         return strMessage;
123 }
124 void bcenterprint(string s)
125 {
126     // TODO replace by MSG_ALL (would show it to spectators too, though)?
127     entity head;
128     FOR_EACH_PLAYER(head)
129     if (clienttype(head) == CLIENTTYPE_REAL)
130         centerprint(head, s);
131 }
132
133 void GameLogEcho(string s)
134 {
135     string fn;
136     float matches;
137
138     if (autocvar_sv_eventlog_files)
139     {
140         if (!logfile_open)
141         {
142             logfile_open = TRUE;
143             matches = autocvar_sv_eventlog_files_counter + 1;
144             cvar_set("sv_eventlog_files_counter", ftos(matches));
145             fn = ftos(matches);
146             if (strlen(fn) < 8)
147                 fn = strcat(substring("00000000", 0, 8 - strlen(fn)), fn);
148             fn = strcat(autocvar_sv_eventlog_files_nameprefix, fn, autocvar_sv_eventlog_files_namesuffix);
149             logfile = fopen(fn, FILE_APPEND);
150             fputs(logfile, ":logversion:3\n");
151         }
152         if (logfile >= 0)
153         {
154             if (autocvar_sv_eventlog_files_timestamps)
155                 fputs(logfile, strcat(":time:", strftime(TRUE, "%Y-%m-%d %H:%M:%S", "\n", s, "\n")));
156             else
157                 fputs(logfile, strcat(s, "\n"));
158         }
159     }
160     if (autocvar_sv_eventlog_console)
161     {
162         print(s, "\n");
163     }
164 }
165
166 void GameLogInit()
167 {
168     logfile_open = 0;
169     // will be opened later
170 }
171
172 void GameLogClose()
173 {
174     if (logfile_open && logfile >= 0)
175     {
176         fclose(logfile);
177         logfile = -1;
178     }
179 }
180
181 vector PL_VIEW_OFS;
182 vector PL_MIN;
183 vector PL_MAX;
184 vector PL_HEAD;
185 vector PL_CROUCH_VIEW_OFS;
186 vector PL_CROUCH_MIN;
187 vector PL_CROUCH_MAX;
188
189 float spawnpoint_nag;
190 void relocate_spawnpoint()
191 {
192     PL_VIEW_OFS                             = stov(autocvar_sv_player_viewoffset);
193     PL_MIN                                  = stov(autocvar_sv_player_mins);
194     PL_MAX                                  = stov(autocvar_sv_player_maxs);
195     PL_HEAD                                 = stov(autocvar_sv_player_headsize);
196     PL_CROUCH_VIEW_OFS                      = stov(autocvar_sv_player_crouch_viewoffset);
197     PL_CROUCH_MIN                           = stov(autocvar_sv_player_crouch_mins);
198     PL_CROUCH_MAX                           = stov(autocvar_sv_player_crouch_maxs);
199
200     // nudge off the floor
201     setorigin(self, self.origin + '0 0 1');
202
203     tracebox(self.origin, PL_MIN, PL_MAX, self.origin, TRUE, self);
204     if (trace_startsolid)
205     {
206         vector o;
207         o = self.origin;
208         self.mins = PL_MIN;
209         self.maxs = PL_MAX;
210         if (!move_out_of_solid(self))
211             objerror("could not get out of solid at all!");
212         print("^1NOTE: this map needs FIXING. Spawnpoint at ", vtos(o - '0 0 1'));
213         print(" needs to be moved out of solid, e.g. by '", ftos(self.origin_x - o_x));
214         print(" ", ftos(self.origin_y - o_y));
215         print(" ", ftos(self.origin_z - o_z), "'\n");
216         if (autocvar_g_spawnpoints_auto_move_out_of_solid)
217         {
218             if (!spawnpoint_nag)
219                 print("\{1}^1NOTE: this map needs FIXING (it contains spawnpoints in solid, see server log)\n");
220             spawnpoint_nag = 1;
221         }
222         else
223         {
224             setorigin(self, o);
225             self.mins = self.maxs = '0 0 0';
226             objerror("player spawn point in solid, mapper sucks!\n");
227             return;
228         }
229     }
230
231     self.use = spawnpoint_use;
232     self.team_saved = self.team;
233     if (!self.cnt)
234         self.cnt = 1;
235
236     if (have_team_spawns != 0)
237         if (self.team)
238             have_team_spawns = 1;
239     have_team_spawns_forteam[self.team] = 1;
240
241     if (autocvar_r_showbboxes)
242     {
243         // show where spawnpoints point at too
244         makevectors(self.angles);
245         entity e;
246         e = spawn();
247         e.classname = "info_player_foo";
248         setorigin(e, self.origin + v_forward * 24);
249         setsize(e, '-8 -8 -8', '8 8 8');
250         e.solid = SOLID_TRIGGER;
251     }
252 }
253
254 #define strstr strstrofs
255 /*
256 // NOTE: DO NOT USE THIS FUNCTION TOO OFTEN.
257 // IT WILL MOST PROBABLY DESTROY _ALL_ OTHER TEMP
258 // STRINGS AND TAKE QUITE LONG. haystack and needle MUST
259 // BE CONSTANT OR strzoneD!
260 float strstr(string haystack, string needle, float offset)
261 {
262         float len, endpos;
263         string found;
264         len = strlen(needle);
265         endpos = strlen(haystack) - len;
266         while(offset <= endpos)
267         {
268                 found = substring(haystack, offset, len);
269                 if(found == needle)
270                         return offset;
271                 offset = offset + 1;
272         }
273         return -1;
274 }
275 */
276
277 float NUM_NEAREST_ENTITIES = 4;
278 entity nearest_entity[NUM_NEAREST_ENTITIES];
279 float nearest_length[NUM_NEAREST_ENTITIES];
280 entity findnearest(vector point, .string field, string value, vector axismod)
281 {
282     entity localhead;
283     float i;
284     float j;
285     float len;
286     vector dist;
287
288     float num_nearest;
289     num_nearest = 0;
290
291     localhead = find(world, field, value);
292     while (localhead)
293     {
294         if ((localhead.items == IT_KEY1 || localhead.items == IT_KEY2) && localhead.target == "###item###")
295             dist = localhead.oldorigin;
296         else
297             dist = localhead.origin;
298         dist = dist - point;
299         dist = dist_x * axismod_x * '1 0 0' + dist_y * axismod_y * '0 1 0' + dist_z * axismod_z * '0 0 1';
300         len = vlen(dist);
301
302         for (i = 0; i < num_nearest; ++i)
303         {
304             if (len < nearest_length[i])
305                 break;
306         }
307
308         // now i tells us where to insert at
309         //   INSERTION SORT! YOU'VE SEEN IT! RUN!
310         if (i < NUM_NEAREST_ENTITIES)
311         {
312             for (j = NUM_NEAREST_ENTITIES - 1; j >= i; --j)
313             {
314                 nearest_length[j + 1] = nearest_length[j];
315                 nearest_entity[j + 1] = nearest_entity[j];
316             }
317             nearest_length[i] = len;
318             nearest_entity[i] = localhead;
319             if (num_nearest < NUM_NEAREST_ENTITIES)
320                 num_nearest = num_nearest + 1;
321         }
322
323         localhead = find(localhead, field, value);
324     }
325
326     // now use the first one from our list that we can see
327     for (i = 0; i < num_nearest; ++i)
328     {
329         traceline(point, nearest_entity[i].origin, TRUE, world);
330         if (trace_fraction == 1)
331         {
332             if (i != 0)
333             {
334                 dprint("Nearest point (");
335                 dprint(nearest_entity[0].netname);
336                 dprint(") is not visible, using a visible one.\n");
337             }
338             return nearest_entity[i];
339         }
340     }
341
342     if (num_nearest == 0)
343         return world;
344
345     dprint("Not seeing any location point, using nearest as fallback.\n");
346     /* DEBUGGING CODE:
347     dprint("Candidates were: ");
348     for(j = 0; j < num_nearest; ++j)
349     {
350         if(j != 0)
351                 dprint(", ");
352         dprint(nearest_entity[j].netname);
353     }
354     dprint("\n");
355     */
356
357     return nearest_entity[0];
358 }
359
360 void spawnfunc_target_location()
361 {
362     self.classname = "target_location";
363     // location name in netname
364     // eventually support: count, teamgame selectors, line of sight?
365 };
366
367 void spawnfunc_info_location()
368 {
369     self.classname = "target_location";
370     self.message = self.netname;
371 };
372
373 string NearestLocation(vector p)
374 {
375     entity loc;
376     string ret;
377     ret = "somewhere";
378     loc = findnearest(p, classname, "target_location", '1 1 1');
379     if (loc)
380     {
381         ret = loc.message;
382     }
383     else
384     {
385         loc = findnearest(p, target, "###item###", '1 1 4');
386         if (loc)
387             ret = loc.netname;
388     }
389     return ret;
390 }
391
392 string formatmessage(string msg)
393 {
394         float p, p1, p2;
395         float n;
396         vector cursor;
397         entity cursor_ent;
398         string escape;
399         string replacement;
400         p = 0;
401         n = 7;
402
403         WarpZone_crosshair_trace(self);
404         cursor = trace_endpos;
405         cursor_ent = trace_ent;
406
407         while (1) {
408                 if (n < 1)
409                         break; // too many replacements
410
411                 n = n - 1;
412                 p1 = strstr(msg, "%", p); // NOTE: this destroys msg as it's a tempstring!
413                 p2 = strstr(msg, "\\", p); // NOTE: this destroys msg as it's a tempstring!
414
415                 if (p1 < 0)
416                         p1 = p2;
417
418                 if (p2 < 0)
419                         p2 = p1;
420
421                 p = min(p1, p2);
422
423                 if (p < 0)
424                         break;
425
426                 replacement = substring(msg, p, 2);
427                 escape = substring(msg, p + 1, 1);
428
429                 if (escape == "%")
430                         replacement = "%";
431                 else if (escape == "\\")
432                         replacement = "\\";
433                 else if (escape == "n")
434                         replacement = "\n";
435                 else if (escape == "a")
436                         replacement = ftos(floor(self.armorvalue));
437                 else if (escape == "h")
438                         replacement = ftos(floor(self.health));
439                 else if (escape == "l")
440                         replacement = NearestLocation(self.origin);
441                 else if (escape == "y")
442                         replacement = NearestLocation(cursor);
443                 else if (escape == "d")
444                         replacement = NearestLocation(self.death_origin);
445                 else if (escape == "w") {
446                         float wep;
447                         wep = self.weapon;
448                         if (!wep)
449                                 wep = self.switchweapon;
450                         if (!wep)
451                                 wep = self.cnt;
452                         replacement = W_Name(wep);
453                 } else if (escape == "W") {
454                         if (self.items & IT_SHELLS) replacement = "shells";
455                         else if (self.items & IT_NAILS) replacement = "bullets";
456                         else if (self.items & IT_ROCKETS) replacement = "rockets";
457                         else if (self.items & IT_CELLS) replacement = "cells";
458                         else replacement = "batteries"; // ;)
459                 } else if (escape == "x") {
460                         replacement = cursor_ent.netname;
461                         if (!replacement || !cursor_ent)
462                                 replacement = "nothing";
463                 } else if (escape == "s")
464                         replacement = ftos(vlen(self.velocity - self.velocity_z * '0 0 1'));
465                 else if (escape == "S")
466                         replacement = ftos(vlen(self.velocity));
467
468                 msg = strcat(substring(msg, 0, p), replacement, substring(msg, p+2, strlen(msg) - (p+2)));
469                 p = p + strlen(replacement);
470         }
471         return msg;
472 }
473
474 float boolean(float value) { // if value is 0 return FALSE (0), otherwise return TRUE (1)
475         return (value == 0) ? FALSE : TRUE;
476 }
477
478 /*
479 =============
480 GetCvars
481 =============
482 Called with:
483   0:  sends the request
484   >0: receives a cvar from name=argv(f) value=argv(f+1)
485 */
486 void GetCvars_handleString(string thisname, float f, .string field, string name)
487 {
488         if (f < 0)
489         {
490                 if (self.field)
491                         strunzone(self.field);
492                 self.field = string_null;
493         }
494         else if (f > 0)
495         {
496                 if (thisname == name)
497                 {
498                         if (self.field)
499                                 strunzone(self.field);
500                         self.field = strzone(argv(f + 1));
501                 }
502         }
503         else
504                 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
505 }
506 void GetCvars_handleString_Fixup(string thisname, float f, .string field, string name, string(string) func)
507 {
508         GetCvars_handleString(thisname, f, field, name);
509         if (f >= 0) // also initialize to the fitting value for "" when sending cvars out
510                 if (thisname == name)
511                 {
512                         string s;
513                         s = func(strcat1(self.field));
514                         if (s != self.field)
515                         {
516                                 strunzone(self.field);
517                                 self.field = strzone(s);
518                         }
519                 }
520 }
521 void GetCvars_handleFloat(string thisname, float f, .float field, string name)
522 {
523         if (f < 0)
524         {
525         }
526         else if (f > 0)
527         {
528                 if (thisname == name)
529                         self.field = stof(argv(f + 1));
530         }
531         else
532                 stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
533 }
534 void GetCvars_handleFloatOnce(string thisname, float f, .float field, string name)
535 {
536         if (f < 0)
537         {
538         }
539         else if (f > 0)
540         {
541                 if (thisname == name)
542                 {
543                         if(!self.field)
544                         {
545                                 self.field = stof(argv(f + 1));
546                                 if(!self.field)
547                                         self.field = -1;
548                         }
549                 }
550         }
551         else
552         {
553                 if(!self.field)
554                         stuffcmd(self, strcat("cl_cmd sendcvar ", name, "\n"));
555         }
556 }
557 float w_getbestweapon(entity e);
558 string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(string wo)
559 {
560         string o;
561         o = W_FixWeaponOrder_ForceComplete(wo);
562         if(self.weaponorder_byimpulse)
563         {
564                 strunzone(self.weaponorder_byimpulse);
565                 self.weaponorder_byimpulse = string_null;
566         }
567         self.weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o));
568         return o;
569 }
570 void GetCvars(float f)
571 {
572         string s;
573
574         if (f > 0)
575                 s = strcat1(argv(f));
576
577         get_cvars_f = f;
578         get_cvars_s = s;
579         MUTATOR_CALLHOOK(GetCvars);
580         GetCvars_handleFloat(s, f, autoswitch, "cl_autoswitch");
581         GetCvars_handleFloat(s, f, cvar_cl_playerdetailreduction, "cl_playerdetailreduction");
582         GetCvars_handleFloat(s, f, cvar_scr_centertime, "scr_centertime");
583         GetCvars_handleString(s, f, cvar_g_xonoticversion, "g_xonoticversion");
584         GetCvars_handleFloat(s, f, cvar_cl_handicap, "cl_handicap");
585         GetCvars_handleFloat(s, f, cvar_cl_clippedspectating, "cl_clippedspectating");
586         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriority, "cl_weaponpriority", W_FixWeaponOrder_ForceComplete_AndBuildImpulseList);
587         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[0], "cl_weaponpriority0", W_FixWeaponOrder_AllowIncomplete);
588         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[1], "cl_weaponpriority1", W_FixWeaponOrder_AllowIncomplete);
589         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[2], "cl_weaponpriority2", W_FixWeaponOrder_AllowIncomplete);
590         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[3], "cl_weaponpriority3", W_FixWeaponOrder_AllowIncomplete);
591         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[4], "cl_weaponpriority4", W_FixWeaponOrder_AllowIncomplete);
592         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[5], "cl_weaponpriority5", W_FixWeaponOrder_AllowIncomplete);
593         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[6], "cl_weaponpriority6", W_FixWeaponOrder_AllowIncomplete);
594         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[7], "cl_weaponpriority7", W_FixWeaponOrder_AllowIncomplete);
595         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[8], "cl_weaponpriority8", W_FixWeaponOrder_AllowIncomplete);
596         GetCvars_handleString_Fixup(s, f, cvar_cl_weaponpriorities[9], "cl_weaponpriority9", W_FixWeaponOrder_AllowIncomplete);
597         GetCvars_handleFloat(s, f, cvar_cl_weaponimpulsemode, "cl_weaponimpulsemode");
598         GetCvars_handleFloat(s, f, cvar_cl_autotaunt, "cl_autotaunt");
599         GetCvars_handleFloat(s, f, cvar_cl_noantilag, "cl_noantilag");
600         GetCvars_handleFloat(s, f, cvar_cl_voice_directional, "cl_voice_directional");
601         GetCvars_handleFloat(s, f, cvar_cl_voice_directional_taunt_attenuation, "cl_voice_directional_taunt_attenuation");
602         GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_share, "cl_accuracy_data_share");
603         GetCvars_handleFloat(s, f, cvar_cl_accuracy_data_receive, "cl_accuracy_data_receive");
604
605         self.cvar_cl_accuracy_data_share = boolean(self.cvar_cl_accuracy_data_share);
606         self.cvar_cl_accuracy_data_receive = boolean(self.cvar_cl_accuracy_data_receive);
607
608 #ifdef ALLOW_FORCEMODELS
609         GetCvars_handleFloat(s, f, cvar_cl_forceplayermodels, "cl_forceplayermodels");
610         GetCvars_handleFloat(s, f, cvar_cl_forceplayermodelsfromxonotic, "cl_forceplayermodelsfromxonotic");
611 #endif
612         GetCvars_handleFloatOnce(s, f, cvar_cl_gunalign, "cl_gunalign");
613         GetCvars_handleFloat(s, f, cvar_cl_allow_uid2name, "cl_allow_uid2name");
614         GetCvars_handleFloat(s, f, cvar_cl_allow_uidtracking, "cl_allow_uidtracking");
615
616         // fixup of switchweapon (needed for LMS or when spectating is disabled, as PutClientInServer comes too early)
617         if (f > 0)
618         {
619                 if (s == "cl_weaponpriority")
620                         self.switchweapon = w_getbestweapon(self);
621                 if (s == "cl_allow_uidtracking")
622                         PlayerStats_AddPlayer(self);
623         }
624 }
625
626 void backtrace(string msg)
627 {
628     float dev, war;
629     dev = autocvar_developer;
630     war = autocvar_prvm_backtraceforwarnings;
631     cvar_set("developer", "1");
632     cvar_set("prvm_backtraceforwarnings", "1");
633     print("\n");
634     print("--- CUT HERE ---\nWARNING: ");
635     print(msg);
636     print("\n");
637     remove(world); // isn't there any better way to cause a backtrace?
638     print("\n--- CUT UNTIL HERE ---\n");
639     cvar_set("developer", ftos(dev));
640     cvar_set("prvm_backtraceforwarnings", ftos(war));
641 }
642
643 string Team_ColorCode(float teamid)
644 {
645     if (teamid == COLOR_TEAM1)
646         return "^1";
647     else if (teamid == COLOR_TEAM2)
648         return "^4";
649     else if (teamid == COLOR_TEAM3)
650         return "^3";
651     else if (teamid == COLOR_TEAM4)
652         return "^6";
653     else
654         return "^7";
655 }
656
657 string Team_ColorName(float t)
658 {
659     // fixme: Search for team entities and get their .netname's!
660     if (t == COLOR_TEAM1)
661         return "Red";
662     if (t == COLOR_TEAM2)
663         return "Blue";
664     if (t == COLOR_TEAM3)
665         return "Yellow";
666     if (t == COLOR_TEAM4)
667         return "Pink";
668     return "Neutral";
669 }
670
671 string Team_ColorNameLowerCase(float t)
672 {
673     // fixme: Search for team entities and get their .netname's!
674     if (t == COLOR_TEAM1)
675         return "red";
676     if (t == COLOR_TEAM2)
677         return "blue";
678     if (t == COLOR_TEAM3)
679         return "yellow";
680     if (t == COLOR_TEAM4)
681         return "pink";
682     return "neutral";
683 }
684
685 float ColourToNumber(string team_colour)
686 {
687         if (team_colour == "red")
688                 return COLOR_TEAM1;
689
690         if (team_colour == "blue")
691                 return COLOR_TEAM2;
692
693         if (team_colour == "yellow")
694                 return COLOR_TEAM3;
695
696         if (team_colour == "pink")
697                 return COLOR_TEAM4;
698
699         if (team_colour == "auto")
700                 return 0;
701
702         return -1;
703 }
704
705 float NumberToTeamNumber(float number)
706 {
707         if (number == 1)
708                 return COLOR_TEAM1;
709
710         if (number == 2)
711                 return COLOR_TEAM2;
712
713         if (number == 3)
714                 return COLOR_TEAM3;
715
716         if (number == 4)
717                 return COLOR_TEAM4;
718
719         return -1;
720 }
721
722 #define CENTERPRIO_POINT 1
723 #define CENTERPRIO_SPAM 2
724 #define CENTERPRIO_VOTE 4
725 #define CENTERPRIO_NORMAL 5
726 #define CENTERPRIO_SHIELDING 7
727 #define CENTERPRIO_MAPVOTE 9
728 #define CENTERPRIO_IDLEKICK 50
729 #define CENTERPRIO_ADMIN 99
730 .float centerprint_priority;
731 .float centerprint_expires;
732 void centerprint_atprio(entity e, float prio, string s)
733 {
734     if (intermission_running)
735         if (prio < CENTERPRIO_MAPVOTE)
736             return;
737     if (time > e.centerprint_expires)
738         e.centerprint_priority = 0;
739     if (prio >= e.centerprint_priority)
740     {
741         e.centerprint_priority = prio;
742         if (timeoutStatus == 2)
743             e.centerprint_expires = time + (e.cvar_scr_centertime * TIMEOUT_SLOWMO_VALUE);
744         else
745             e.centerprint_expires = time + e.cvar_scr_centertime;
746         centerprint_builtin(e, s);
747     }
748 }
749 void centerprint_expire(entity e, float prio)
750 {
751     if (prio == e.centerprint_priority)
752     {
753         e.centerprint_priority = 0;
754         centerprint_builtin(e, "");
755     }
756 }
757 void centerprint(entity e, string s)
758 {
759     centerprint_atprio(e, CENTERPRIO_NORMAL, s);
760 }
761
762 // decolorizes and team colors the player name when needed
763 string playername(entity p)
764 {
765     string t;
766     if (teams_matter && !intermission_running && p.classname == "player")
767     {
768         t = Team_ColorCode(p.team);
769         return strcat(t, strdecolorize(p.netname));
770     }
771     else
772         return p.netname;
773 }
774
775 vector randompos(vector m1, vector m2)
776 {
777     local vector v;
778     m2 = m2 - m1;
779     v_x = m2_x * random() + m1_x;
780     v_y = m2_y * random() + m1_y;
781     v_z = m2_z * random() + m1_z;
782     return  v;
783 };
784
785 //#NO AUTOCVARS START
786
787 float g_pickup_shells;
788 float g_pickup_shells_max;
789 float g_pickup_nails;
790 float g_pickup_nails_max;
791 float g_pickup_rockets;
792 float g_pickup_rockets_max;
793 float g_pickup_cells;
794 float g_pickup_cells_max;
795 float g_pickup_fuel;
796 float g_pickup_fuel_jetpack;
797 float g_pickup_fuel_max;
798 float g_pickup_armorsmall;
799 float g_pickup_armorsmall_max;
800 float g_pickup_armorsmall_anyway;
801 float g_pickup_armormedium;
802 float g_pickup_armormedium_max;
803 float g_pickup_armormedium_anyway;
804 float g_pickup_armorbig;
805 float g_pickup_armorbig_max;
806 float g_pickup_armorbig_anyway;
807 float g_pickup_armorlarge;
808 float g_pickup_armorlarge_max;
809 float g_pickup_armorlarge_anyway;
810 float g_pickup_healthsmall;
811 float g_pickup_healthsmall_max;
812 float g_pickup_healthsmall_anyway;
813 float g_pickup_healthmedium;
814 float g_pickup_healthmedium_max;
815 float g_pickup_healthmedium_anyway;
816 float g_pickup_healthlarge;
817 float g_pickup_healthlarge_max;
818 float g_pickup_healthlarge_anyway;
819 float g_pickup_healthmega;
820 float g_pickup_healthmega_max;
821 float g_pickup_healthmega_anyway;
822 float g_pickup_ammo_anyway;
823 float g_pickup_weapons_anyway;
824 float g_weaponarena;
825 float g_weaponarena_random;
826 float g_weaponarena_random_with_laser;
827 string g_weaponarena_list;
828 float g_weaponspeedfactor;
829 float g_weaponratefactor;
830 float g_weapondamagefactor;
831 float g_weaponforcefactor;
832 float g_weaponspreadfactor;
833
834 float start_weapons;
835 float start_items;
836 float start_ammo_shells;
837 float start_ammo_nails;
838 float start_ammo_rockets;
839 float start_ammo_cells;
840 float start_ammo_fuel;
841 float start_health;
842 float start_armorvalue;
843 float warmup_start_weapons;
844 float warmup_start_ammo_shells;
845 float warmup_start_ammo_nails;
846 float warmup_start_ammo_rockets;
847 float warmup_start_ammo_cells;
848 float warmup_start_ammo_fuel;
849 float warmup_start_health;
850 float warmup_start_armorvalue;
851 float g_weapon_stay;
852 float g_ghost_items;
853
854 entity get_weaponinfo(float w);
855
856 float want_weapon(string cvarprefix, entity weaponinfo, float allguns)
857 {
858         var float i = weaponinfo.weapon;
859
860         if (!i)
861                 return 0;
862
863         var float t = cvar(strcat(cvarprefix, weaponinfo.netname));
864
865         if (t < 0) // "default" weapon selection
866         {
867                 if (g_lms || g_ca || allguns)
868                         t = (weaponinfo.spawnflags & WEP_FLAG_NORMAL);
869                 else if(t < -1)
870                         t = 0;
871                 else if (g_cts)
872                         t = (i == WEP_SHOTGUN);
873                 else if (g_nexball)
874                         t = 0; // weapon is set a few lines later
875                 else
876                         t = (i == WEP_LASER || i == WEP_SHOTGUN);
877                 if(g_grappling_hook) // if possible, redirect off-hand hook to on-hand hook
878                         t |= (i == WEP_HOOK);
879         }
880
881         // we cannot disable porto in Nexball, we must force it
882         if(g_nexball && i == WEP_PORTO)
883                 t = 1;
884
885         return t;
886 }
887
888 void readplayerstartcvars()
889 {
890         entity e;
891         float i, j, t;
892         string s;
893
894         // initialize starting values for players
895         start_weapons = 0;
896         start_items = 0;
897         start_ammo_shells = 0;
898         start_ammo_nails = 0;
899         start_ammo_rockets = 0;
900         start_ammo_cells = 0;
901         start_health = cvar("g_balance_health_start");
902         start_armorvalue = cvar("g_balance_armor_start");
903
904         g_weaponarena = 0;
905         s = cvar_string("g_weaponarena");
906         if (s == "0" || s == "")
907         {
908                 if(g_lms || g_ca)
909                         s = "most";
910         }
911
912         if (s == "0" || s == "")
913         {
914                 // no arena
915         }
916         else if (s == "off")
917         {
918                 // forcibly turn off weaponarena
919         }
920         else if (s == "all")
921         {
922                 g_weaponarena_list = "All Weapons";
923                 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
924                 {
925                         e = get_weaponinfo(j);
926                         g_weaponarena |= e.weapons;
927                         weapon_action(e.weapon, WR_PRECACHE);
928                 }
929         }
930         else if (s == "most")
931         {
932                 g_weaponarena_list = "Most Weapons";
933                 for (j = WEP_FIRST; j <= WEP_LAST; ++j)
934                 {
935                         e = get_weaponinfo(j);
936                         if (e.spawnflags & WEP_FLAG_NORMAL)
937                         {
938                                 g_weaponarena |= e.weapons;
939                                 weapon_action(e.weapon, WR_PRECACHE);
940                         }
941                 }
942         }
943         else if (s == "none")
944         {
945                 g_weaponarena_list = "No Weapons";
946                 g_weaponarena = WEPBIT_ALL + 1; // this supports no single weapon bit!
947         }
948         else
949         {
950                 t = tokenize_console(s);
951                 g_weaponarena_list = "";
952                 for (i = 0; i < t; ++i)
953                 {
954                         s = argv(i);
955                         for (j = WEP_FIRST; j <= WEP_LAST; ++j)
956                         {
957                                 e = get_weaponinfo(j);
958                                 if (e.netname == s)
959                                 {
960                                         g_weaponarena |= e.weapons;
961                                         weapon_action(e.weapon, WR_PRECACHE);
962                                         g_weaponarena_list = strcat(g_weaponarena_list, e.message, " & ");
963                                         break;
964                                 }
965                         }
966                         if (j > WEP_LAST)
967                         {
968                                 print("The weapon mutator list contains an unknown weapon ", s, ". Skipped.\n");
969                         }
970                 }
971                 g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3));
972         }
973
974         if(g_weaponarena)
975                 g_weaponarena_random = cvar("g_weaponarena_random");
976         else
977                 g_weaponarena_random = 0;
978         g_weaponarena_random_with_laser = cvar("g_weaponarena_random_with_laser");
979
980         if (g_weaponarena)
981         {
982                 start_weapons = g_weaponarena;
983                 if(!(g_lms || g_ca))
984                         start_items |= IT_UNLIMITED_AMMO;
985         }
986         else if (g_minstagib)
987         {
988                 start_health = 100;
989                 start_armorvalue = 0;
990                 start_weapons = WEPBIT_MINSTANEX;
991                 weapon_action(WEP_MINSTANEX, WR_PRECACHE);
992                 g_minstagib_invis_alpha = cvar("g_minstagib_invis_alpha");
993
994                 if (g_minstagib_invis_alpha <= 0)
995                         g_minstagib_invis_alpha = -1;
996         }
997         else
998         {
999                 for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1000                 {
1001                         e = get_weaponinfo(i);
1002                         if(want_weapon("g_start_weapon_", e, FALSE))
1003                                 start_weapons |= e.weapons;
1004                 }
1005         }
1006
1007         if(!cvar("g_use_ammunition"))
1008                 start_items |= IT_UNLIMITED_AMMO;
1009
1010         if(g_minstagib)
1011         {
1012                 start_ammo_cells = cvar("g_minstagib_ammo_start");
1013                 start_ammo_fuel = cvar("g_start_ammo_fuel");
1014         }
1015         else if(start_items & IT_UNLIMITED_WEAPON_AMMO)
1016         {
1017                 start_ammo_rockets = 999;
1018                 start_ammo_shells = 999;
1019                 start_ammo_cells = 999;
1020                 start_ammo_nails = 999;
1021                 start_ammo_fuel = 999;
1022         }
1023         else
1024         {
1025                 if(g_lms || g_ca)
1026                 {
1027                         start_ammo_shells = cvar("g_lms_start_ammo_shells");
1028                         start_ammo_nails = cvar("g_lms_start_ammo_nails");
1029                         start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
1030                         start_ammo_cells = cvar("g_lms_start_ammo_cells");
1031                         start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
1032                 }
1033                 else
1034                 {
1035                         start_ammo_shells = cvar("g_start_ammo_shells");
1036                         start_ammo_nails = cvar("g_start_ammo_nails");
1037                         start_ammo_rockets = cvar("g_start_ammo_rockets");
1038                         start_ammo_cells = cvar("g_start_ammo_cells");
1039                         start_ammo_fuel = cvar("g_start_ammo_fuel");
1040                 }
1041         }
1042
1043         if (g_lms || g_ca)
1044         {
1045                 start_health = cvar("g_lms_start_health");
1046                 start_armorvalue = cvar("g_lms_start_armor");
1047         }
1048
1049         if (inWarmupStage)
1050         {
1051                 warmup_start_ammo_shells = start_ammo_shells;
1052                 warmup_start_ammo_nails = start_ammo_nails;
1053                 warmup_start_ammo_rockets = start_ammo_rockets;
1054                 warmup_start_ammo_cells = start_ammo_cells;
1055                 warmup_start_ammo_fuel = start_ammo_fuel;
1056                 warmup_start_health = start_health;
1057                 warmup_start_armorvalue = start_armorvalue;
1058                 warmup_start_weapons = start_weapons;
1059
1060                 if (!g_weaponarena && !g_minstagib && !g_ca)
1061                 {
1062                         warmup_start_ammo_shells = cvar("g_warmup_start_ammo_shells");
1063                         warmup_start_ammo_cells = cvar("g_warmup_start_ammo_cells");
1064                         warmup_start_ammo_nails = cvar("g_warmup_start_ammo_nails");
1065                         warmup_start_ammo_rockets = cvar("g_warmup_start_ammo_rockets");
1066                         warmup_start_ammo_fuel = cvar("g_warmup_start_ammo_fuel");
1067                         warmup_start_health = cvar("g_warmup_start_health");
1068                         warmup_start_armorvalue = cvar("g_warmup_start_armor");
1069                         warmup_start_weapons = 0;
1070                         for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1071                         {
1072                                 e = get_weaponinfo(i);
1073                                 if(want_weapon("g_start_weapon_", e, cvar("g_warmup_allguns")))
1074                                         warmup_start_weapons |= e.weapons;
1075                         }
1076                 }
1077         }
1078
1079         if (g_jetpack || (g_grappling_hook && (start_weapons & WEPBIT_HOOK)))
1080         {
1081                 g_grappling_hook = 0; // these two can't coexist, as they use the same button
1082                 start_items |= IT_FUEL_REGEN;
1083                 start_ammo_fuel = max(start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1084                 warmup_start_ammo_fuel = max(warmup_start_ammo_fuel, cvar("g_balance_fuel_rotstable"));
1085         }
1086
1087         if (g_jetpack)
1088                 start_items |= IT_JETPACK;
1089
1090         if (g_weapon_stay == 2)
1091         {
1092                 if (!start_ammo_shells) start_ammo_shells = g_pickup_shells;
1093                 if (!start_ammo_nails) start_ammo_nails = g_pickup_nails;
1094                 if (!start_ammo_cells) start_ammo_cells = g_pickup_cells;
1095                 if (!start_ammo_rockets) start_ammo_rockets = g_pickup_rockets;
1096                 if (!start_ammo_fuel) start_ammo_fuel = g_pickup_fuel;
1097                 if (!warmup_start_ammo_shells) warmup_start_ammo_shells = g_pickup_shells;
1098                 if (!warmup_start_ammo_nails) warmup_start_ammo_nails = g_pickup_nails;
1099                 if (!warmup_start_ammo_cells) warmup_start_ammo_cells = g_pickup_cells;
1100                 if (!warmup_start_ammo_rockets) warmup_start_ammo_rockets = g_pickup_rockets;
1101                 if (!warmup_start_ammo_fuel) warmup_start_ammo_fuel = g_pickup_fuel;
1102         }
1103
1104         MUTATOR_CALLHOOK(SetStartItems);
1105
1106         for (i = WEP_FIRST; i <= WEP_LAST; ++i)
1107         {
1108                 e = get_weaponinfo(i);
1109                 if(e.weapons & (start_weapons | warmup_start_weapons))
1110                         weapon_action(e.weapon, WR_PRECACHE);
1111         }
1112
1113         start_ammo_shells = max(0, start_ammo_shells);
1114         start_ammo_nails = max(0, start_ammo_nails);
1115         start_ammo_cells = max(0, start_ammo_cells);
1116         start_ammo_rockets = max(0, start_ammo_rockets);
1117         start_ammo_fuel = max(0, start_ammo_fuel);
1118
1119         warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
1120         warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
1121         warmup_start_ammo_cells = max(0, warmup_start_ammo_cells);
1122         warmup_start_ammo_rockets = max(0, warmup_start_ammo_rockets);
1123         warmup_start_ammo_fuel = max(0, warmup_start_ammo_fuel);
1124 }
1125
1126 float g_bugrigs;
1127 float g_bugrigs_planar_movement;
1128 float g_bugrigs_planar_movement_car_jumping;
1129 float g_bugrigs_reverse_spinning;
1130 float g_bugrigs_reverse_speeding;
1131 float g_bugrigs_reverse_stopping;
1132 float g_bugrigs_air_steering;
1133 float g_bugrigs_angle_smoothing;
1134 float g_bugrigs_friction_floor;
1135 float g_bugrigs_friction_brake;
1136 float g_bugrigs_friction_air;
1137 float g_bugrigs_accel;
1138 float g_bugrigs_speed_ref;
1139 float g_bugrigs_speed_pow;
1140 float g_bugrigs_steer;
1141
1142 float g_touchexplode;
1143 float g_touchexplode_radius;
1144 float g_touchexplode_damage;
1145 float g_touchexplode_edgedamage;
1146 float g_touchexplode_force;
1147
1148 float sv_autotaunt;
1149 float sv_taunt;
1150
1151 float sv_pitch_min;
1152 float sv_pitch_max;
1153 float sv_pitch_fixyaw;
1154
1155 string GetGametype(); // g_world.qc
1156 void readlevelcvars(void)
1157 {
1158         // first load all the mutators
1159         if(cvar("g_nix"))
1160                 MUTATOR_ADD(mutator_nix);
1161         if(cvar("g_dodging"))
1162                 MUTATOR_ADD(mutator_dodging);
1163         if(cvar("g_rocket_flying"))
1164                 MUTATOR_ADD(mutator_rocketflying);
1165         if(cvar("g_vampire"))
1166                 MUTATOR_ADD(mutator_vampire);
1167
1168         if(cvar("sv_allow_fullbright"))
1169                 serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
1170
1171     g_bugrigs = cvar("g_bugrigs");
1172     g_bugrigs_planar_movement = cvar("g_bugrigs_planar_movement");
1173     g_bugrigs_planar_movement_car_jumping = cvar("g_bugrigs_planar_movement_car_jumping");
1174     g_bugrigs_reverse_spinning = cvar("g_bugrigs_reverse_spinning");
1175     g_bugrigs_reverse_speeding = cvar("g_bugrigs_reverse_speeding");
1176     g_bugrigs_reverse_stopping = cvar("g_bugrigs_reverse_stopping");
1177     g_bugrigs_air_steering = cvar("g_bugrigs_air_steering");
1178     g_bugrigs_angle_smoothing = cvar("g_bugrigs_angle_smoothing");
1179     g_bugrigs_friction_floor = cvar("g_bugrigs_friction_floor");
1180     g_bugrigs_friction_brake = cvar("g_bugrigs_friction_brake");
1181     g_bugrigs_friction_air = cvar("g_bugrigs_friction_air");
1182     g_bugrigs_accel = cvar("g_bugrigs_accel");
1183     g_bugrigs_speed_ref = cvar("g_bugrigs_speed_ref");
1184     g_bugrigs_speed_pow = cvar("g_bugrigs_speed_pow");
1185     g_bugrigs_steer = cvar("g_bugrigs_steer");
1186
1187     g_touchexplode = cvar("g_touchexplode");
1188     g_touchexplode_radius = cvar("g_touchexplode_radius");
1189     g_touchexplode_damage = cvar("g_touchexplode_damage");
1190     g_touchexplode_edgedamage = cvar("g_touchexplode_edgedamage");
1191     g_touchexplode_force = cvar("g_touchexplode_force");
1192
1193 #ifdef ALLOW_FORCEMODELS
1194         sv_clforceplayermodels = cvar("sv_clforceplayermodels");
1195 #endif
1196         sv_loddistance1 = cvar("sv_loddistance1");
1197         sv_loddistance2 = cvar("sv_loddistance2");
1198
1199         if(sv_loddistance2 <= sv_loddistance1)
1200                 sv_loddistance2 = 1073741824; // enough to turn off LOD 2 reliably
1201
1202         sv_clones = cvar("sv_clones");
1203         sv_gentle = cvar("sv_gentle");
1204         sv_foginterval = cvar("sv_foginterval");
1205         g_cloaked = cvar("g_cloaked");
1206     if(g_cts)
1207         g_cloaked = 1; // always enable cloak in CTS
1208         g_jump_grunt = cvar("g_jump_grunt");
1209         g_footsteps = cvar("g_footsteps");
1210         g_grappling_hook = cvar("g_grappling_hook");
1211         g_jetpack = cvar("g_jetpack");
1212         g_midair = cvar("g_midair");
1213         g_minstagib = cvar("g_minstagib");
1214         g_norecoil = cvar("g_norecoil");
1215         g_bloodloss = cvar("g_bloodloss");
1216         sv_maxidle = cvar("sv_maxidle");
1217         sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle");
1218         sv_pogostick = cvar("sv_pogostick");
1219         g_ctf_reverse = cvar("g_ctf_reverse");
1220         sv_autotaunt = cvar("sv_autotaunt");
1221         sv_taunt = cvar("sv_taunt");
1222
1223         inWarmupStage = cvar("g_warmup");
1224         g_warmup_limit = cvar("g_warmup_limit");
1225         g_warmup_allguns = cvar("g_warmup_allguns");
1226         g_warmup_allow_timeout = cvar("g_warmup_allow_timeout");
1227
1228         if ((g_race && g_race_qualifying == 2) || g_runematch || g_arena || g_assault || cvar("g_campaign"))
1229                 inWarmupStage = 0; // these modes cannot work together, sorry
1230
1231         g_pickup_respawntime_weapon = cvar("g_pickup_respawntime_weapon");
1232         g_pickup_respawntime_ammo = cvar("g_pickup_respawntime_ammo");
1233         g_pickup_respawntime_short = cvar("g_pickup_respawntime_short");
1234         g_pickup_respawntime_medium = cvar("g_pickup_respawntime_medium");
1235         g_pickup_respawntime_long = cvar("g_pickup_respawntime_long");
1236         g_pickup_respawntime_powerup = cvar("g_pickup_respawntime_powerup");
1237         g_pickup_respawntimejitter_weapon = cvar("g_pickup_respawntimejitter_weapon");
1238         g_pickup_respawntimejitter_ammo = cvar("g_pickup_respawntimejitter_ammo");
1239         g_pickup_respawntimejitter_short = cvar("g_pickup_respawntimejitter_short");
1240         g_pickup_respawntimejitter_medium = cvar("g_pickup_respawntimejitter_medium");
1241         g_pickup_respawntimejitter_long = cvar("g_pickup_respawntimejitter_long");
1242         g_pickup_respawntimejitter_powerup = cvar("g_pickup_respawntimejitter_powerup");
1243
1244         g_weaponspeedfactor = cvar("g_weaponspeedfactor");
1245         g_weaponratefactor = cvar("g_weaponratefactor");
1246         g_weapondamagefactor = cvar("g_weapondamagefactor");
1247         g_weaponforcefactor = cvar("g_weaponforcefactor");
1248         g_weaponspreadfactor = cvar("g_weaponspreadfactor");
1249
1250         g_pickup_shells = cvar("g_pickup_shells");
1251         g_pickup_shells_max = cvar("g_pickup_shells_max");
1252         g_pickup_nails = cvar("g_pickup_nails");
1253         g_pickup_nails_max = cvar("g_pickup_nails_max");
1254         g_pickup_rockets = cvar("g_pickup_rockets");
1255         g_pickup_rockets_max = cvar("g_pickup_rockets_max");
1256         g_pickup_cells = cvar("g_pickup_cells");
1257         g_pickup_cells_max = cvar("g_pickup_cells_max");
1258         g_pickup_fuel = cvar("g_pickup_fuel");
1259         g_pickup_fuel_jetpack = cvar("g_pickup_fuel_jetpack");
1260         g_pickup_fuel_max = cvar("g_pickup_fuel_max");
1261         g_pickup_armorsmall = cvar("g_pickup_armorsmall");
1262         g_pickup_armorsmall_max = cvar("g_pickup_armorsmall_max");
1263         g_pickup_armorsmall_anyway = cvar("g_pickup_armorsmall_anyway");
1264         g_pickup_armormedium = cvar("g_pickup_armormedium");
1265         g_pickup_armormedium_max = cvar("g_pickup_armormedium_max");
1266         g_pickup_armormedium_anyway = cvar("g_pickup_armormedium_anyway");
1267         g_pickup_armorbig = cvar("g_pickup_armorbig");
1268         g_pickup_armorbig_max = cvar("g_pickup_armorbig_max");
1269         g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway");
1270         g_pickup_armorlarge = cvar("g_pickup_armorlarge");
1271         g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max");
1272         g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway");
1273         g_pickup_healthsmall = cvar("g_pickup_healthsmall");
1274         g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max");
1275         g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway");
1276         g_pickup_healthmedium = cvar("g_pickup_healthmedium");
1277         g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max");
1278         g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway");
1279         g_pickup_healthlarge = cvar("g_pickup_healthlarge");
1280         g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max");
1281         g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway");
1282         g_pickup_healthmega = cvar("g_pickup_healthmega");
1283         g_pickup_healthmega_max = cvar("g_pickup_healthmega_max");
1284         g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway");
1285
1286         g_pickup_ammo_anyway = cvar("g_pickup_ammo_anyway");
1287         g_pickup_weapons_anyway = cvar("g_pickup_weapons_anyway");
1288
1289         g_pinata = cvar("g_pinata");
1290
1291     g_weapon_stay = cvar(strcat("g_", GetGametype(), "_weapon_stay"));
1292     if(!g_weapon_stay)
1293         g_weapon_stay = cvar("g_weapon_stay");
1294
1295         if (!g_weapon_stay && (cvar("deathmatch") == 2))
1296                 g_weapon_stay = 1;
1297
1298         g_ghost_items = cvar("g_ghost_items");
1299
1300         if(g_ghost_items >= 1)
1301                 g_ghost_items = 0.25; // default alpha value
1302
1303         if not(inWarmupStage && !g_ca)
1304                 game_starttime = cvar("g_start_delay");
1305
1306         sv_pitch_min = cvar("sv_pitch_min");
1307         sv_pitch_max = cvar("sv_pitch_max");
1308         sv_pitch_fixyaw = cvar("sv_pitch_fixyaw");
1309
1310         readplayerstartcvars();
1311 }
1312
1313 //#NO AUTOCVARS END
1314
1315 // Sound functions
1316 string precache_sound (string s) = #19;
1317 void(entity e, float chan, string samp, float vol, float atten) sound_builtin = #8;
1318 float precache_sound_index (string s) = #19;
1319
1320 #define SND_VOLUME      1
1321 #define SND_ATTENUATION 2
1322 #define SND_LARGEENTITY 8
1323 #define SND_LARGESOUND  16
1324
1325 float sound_allowed(float dest, entity e)
1326 {
1327     // sounds from world may always pass
1328     for (;;)
1329     {
1330         if (e.classname == "body")
1331             e = e.enemy;
1332         if (e.owner && e.owner != e)
1333             e = e.owner;
1334         else
1335             break;
1336     }
1337     // sounds to self may always pass
1338     if (dest == MSG_ONE)
1339         if (e == msg_entity)
1340             return TRUE;
1341     // sounds by players can be removed
1342     if (autocvar_bot_sound_monopoly)
1343         if (clienttype(e) == CLIENTTYPE_REAL)
1344             return FALSE;
1345     // anything else may pass
1346     return TRUE;
1347 }
1348
1349 void sound(entity e, float chan, string samp, float vol, float atten)
1350 {
1351     if (!sound_allowed(MSG_BROADCAST, e))
1352         return;
1353     sound_builtin(e, chan, samp, vol, atten);
1354 }
1355 void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
1356 {
1357     float entno, idx;
1358
1359     if (!sound_allowed(dest, e))
1360         return;
1361
1362     entno = num_for_edict(e);
1363     idx = precache_sound_index(samp);
1364
1365     float sflags;
1366     sflags = 0;
1367
1368     atten = floor(atten * 64);
1369     vol = floor(vol * 255);
1370
1371     if (vol != 255)
1372         sflags |= SND_VOLUME;
1373     if (atten != 64)
1374         sflags |= SND_ATTENUATION;
1375     if (entno >= 8192)
1376         sflags |= SND_LARGEENTITY;
1377     if (idx >= 256)
1378         sflags |= SND_LARGESOUND;
1379
1380     WriteByte(dest, SVC_SOUND);
1381     WriteByte(dest, sflags);
1382     if (sflags & SND_VOLUME)
1383         WriteByte(dest, vol);
1384     if (sflags & SND_ATTENUATION)
1385         WriteByte(dest, atten);
1386     if (sflags & SND_LARGEENTITY)
1387     {
1388         WriteShort(dest, entno);
1389         WriteByte(dest, chan);
1390     }
1391     else
1392     {
1393         WriteShort(dest, entno * 8 + chan);
1394     }
1395     if (sflags & SND_LARGESOUND)
1396         WriteShort(dest, idx);
1397     else
1398         WriteByte(dest, idx);
1399
1400     WriteCoord(dest, o_x);
1401     WriteCoord(dest, o_y);
1402     WriteCoord(dest, o_z);
1403 }
1404 void soundto(float dest, entity e, float chan, string samp, float vol, float atten)
1405 {
1406     vector o;
1407
1408     if (!sound_allowed(dest, e))
1409         return;
1410
1411     o = e.origin + 0.5 * (e.mins + e.maxs);
1412     soundtoat(dest, e, o, chan, samp, vol, atten);
1413 }
1414 void soundat(entity e, vector o, float chan, string samp, float vol, float atten)
1415 {
1416     soundtoat(MSG_BROADCAST, e, o, chan, samp, vol, atten);
1417 }
1418 void stopsoundto(float dest, entity e, float chan)
1419 {
1420     float entno;
1421
1422     if (!sound_allowed(dest, e))
1423         return;
1424
1425     entno = num_for_edict(e);
1426
1427     if (entno >= 8192)
1428     {
1429         float idx, sflags;
1430         idx = precache_sound_index("misc/null.wav");
1431         sflags = SND_LARGEENTITY;
1432         if (idx >= 256)
1433             sflags |= SND_LARGESOUND;
1434         WriteByte(dest, SVC_SOUND);
1435         WriteByte(dest, sflags);
1436         WriteShort(dest, entno);
1437         WriteByte(dest, chan);
1438         if (sflags & SND_LARGESOUND)
1439             WriteShort(dest, idx);
1440         else
1441             WriteByte(dest, idx);
1442         WriteCoord(dest, e.origin_x);
1443         WriteCoord(dest, e.origin_y);
1444         WriteCoord(dest, e.origin_z);
1445     }
1446     else
1447     {
1448         WriteByte(dest, SVC_STOPSOUND);
1449         WriteShort(dest, entno * 8 + chan);
1450     }
1451 }
1452 void stopsound(entity e, float chan)
1453 {
1454     if (!sound_allowed(MSG_BROADCAST, e))
1455         return;
1456
1457     stopsoundto(MSG_BROADCAST, e, chan); // unreliable, gets there fast
1458     stopsoundto(MSG_ALL, e, chan); // in case of packet loss
1459 }
1460
1461 void play2(entity e, string filename)
1462 {
1463     //stuffcmd(e, strcat("play2 ", filename, "\n"));
1464     msg_entity = e;
1465     soundtoat(MSG_ONE, world, '0 0 0', CHAN_AUTO, filename, VOL_BASE, ATTN_NONE);
1466 }
1467
1468 // use this one if you might be causing spam (e.g. from touch functions that might get called more than once per frame)
1469 .float spamtime;
1470 float spamsound(entity e, float chan, string samp, float vol, float atten)
1471 {
1472     if (!sound_allowed(MSG_BROADCAST, e))
1473         return FALSE;
1474
1475     if (time > e.spamtime)
1476     {
1477         e.spamtime = time;
1478         sound(e, chan, samp, vol, atten);
1479         return TRUE;
1480     }
1481     return FALSE;
1482 }
1483
1484 void play2team(float t, string filename)
1485 {
1486     local entity head;
1487
1488     if (autocvar_bot_sound_monopoly)
1489         return;
1490
1491     FOR_EACH_REALPLAYER(head)
1492     {
1493         if (head.team == t)
1494             play2(head, filename);
1495     }
1496 }
1497
1498 void play2all(string samp)
1499 {
1500     if (autocvar_bot_sound_monopoly)
1501         return;
1502
1503     sound(world, CHAN_AUTO, samp, VOL_BASE, ATTN_NONE);
1504 }
1505
1506 void PrecachePlayerSounds(string f);
1507 void precache_playermodel(string m)
1508 {
1509         float globhandle, i, n;
1510         string f;
1511
1512         if(substring(m, -9,5) == "_lod1")
1513                 return;
1514         if(substring(m, -9,5) == "_lod2")
1515                 return;
1516         precache_model(m);
1517         if(sv_loddistance1)
1518         {
1519                 f = strcat(substring(m, 0, -5), "_lod1", substring(m, -4, -1));
1520                 if(fexists(f))
1521                         precache_model(f);
1522                 f = strcat(substring(m, 0, -5), "_lod2", substring(m, -4, -1));
1523                 if(fexists(f))
1524                         precache_model(f);
1525         }
1526
1527         globhandle = search_begin(strcat(m, "_*.sounds"), TRUE, FALSE);
1528         if (globhandle < 0)
1529                 return;
1530         n = search_getsize(globhandle);
1531         for (i = 0; i < n; ++i)
1532         {
1533                 //print(search_getfilename(globhandle, i), "\n");
1534                 f = search_getfilename(globhandle, i);
1535                 PrecachePlayerSounds(f);
1536         }
1537         search_end(globhandle);
1538 }
1539 void precache_all_playermodels(string pattern)
1540 {
1541         float globhandle, i, n;
1542         string f;
1543
1544         globhandle = search_begin(pattern, TRUE, FALSE);
1545         if (globhandle < 0)
1546                 return;
1547         n = search_getsize(globhandle);
1548         for (i = 0; i < n; ++i)
1549         {
1550                 //print(search_getfilename(globhandle, i), "\n");
1551                 f = search_getfilename(globhandle, i);
1552                 precache_playermodel(f);
1553         }
1554         search_end(globhandle);
1555 }
1556
1557 void precache()
1558 {
1559     // gamemode related things
1560     precache_model ("models/misc/chatbubble.spr");
1561     if (g_runematch)
1562     {
1563         precache_model ("models/runematch/curse.mdl");
1564         precache_model ("models/runematch/rune.mdl");
1565     }
1566
1567 #ifdef TTURRETS_ENABLED
1568     if (autocvar_g_turrets)
1569         turrets_precash();
1570 #endif
1571
1572     // Precache all player models if desired
1573     if (autocvar_sv_precacheplayermodels)
1574     {
1575         PrecachePlayerSounds("sound/player/default.sounds");
1576         precache_all_playermodels("models/player/*.zym");
1577         precache_all_playermodels("models/player/*.dpm");
1578         precache_all_playermodels("models/player/*.md3");
1579         precache_all_playermodels("models/player/*.psk");
1580         precache_all_playermodels("models/player/*.iqm");
1581     }
1582
1583     if (autocvar_sv_defaultcharacter)
1584     {
1585         string s;
1586         s = autocvar_sv_defaultplayermodel_red;
1587         if (s != "")
1588             precache_playermodel(s);
1589         s = autocvar_sv_defaultplayermodel_blue;
1590         if (s != "")
1591             precache_playermodel(s);
1592         s = autocvar_sv_defaultplayermodel_yellow;
1593         if (s != "")
1594             precache_playermodel(s);
1595         s = autocvar_sv_defaultplayermodel_pink;
1596         if (s != "")
1597             precache_playermodel(s);
1598         s = autocvar_sv_defaultplayermodel;
1599         if (s != "")
1600             precache_playermodel(s);
1601     }
1602
1603     if (g_footsteps)
1604     {
1605         PrecacheGlobalSound((globalsound_step = "misc/footstep0 6"));
1606         PrecacheGlobalSound((globalsound_metalstep = "misc/metalfootstep0 6"));
1607     }
1608
1609     // gore and miscellaneous sounds
1610     //precache_sound ("misc/h2ohit.wav");
1611     precache_model ("models/hook.md3");
1612     precache_sound ("misc/armorimpact.wav");
1613     precache_sound ("misc/bodyimpact1.wav");
1614     precache_sound ("misc/bodyimpact2.wav");
1615     precache_sound ("misc/gib.wav");
1616     precache_sound ("misc/gib_splat01.wav");
1617     precache_sound ("misc/gib_splat02.wav");
1618     precache_sound ("misc/gib_splat03.wav");
1619     precache_sound ("misc/gib_splat04.wav");
1620     PrecacheGlobalSound((globalsound_fall = "misc/hitground 4"));
1621     PrecacheGlobalSound((globalsound_metalfall = "misc/metalhitground 4"));
1622     precache_sound ("misc/null.wav");
1623     precache_sound ("misc/spawn.wav");
1624     precache_sound ("misc/talk.wav");
1625     precache_sound ("misc/teleport.wav");
1626     precache_sound ("misc/poweroff.wav");
1627     precache_sound ("player/lava.wav");
1628     precache_sound ("player/slime.wav");
1629
1630     if (g_jetpack)
1631         precache_sound ("misc/jetpack_fly.wav");
1632
1633     precache_model ("models/sprites/0.spr32");
1634     precache_model ("models/sprites/1.spr32");
1635     precache_model ("models/sprites/2.spr32");
1636     precache_model ("models/sprites/3.spr32");
1637     precache_model ("models/sprites/4.spr32");
1638     precache_model ("models/sprites/5.spr32");
1639     precache_model ("models/sprites/6.spr32");
1640     precache_model ("models/sprites/7.spr32");
1641     precache_model ("models/sprites/8.spr32");
1642     precache_model ("models/sprites/9.spr32");
1643     precache_model ("models/sprites/10.spr32");
1644
1645     // common weapon precaches
1646         precache_sound ("weapons/reload.wav"); // until weapons have individual reload sounds, precache the reload sound here
1647     precache_sound ("weapons/weapon_switch.wav");
1648     precache_sound ("weapons/weaponpickup.wav");
1649     precache_sound ("weapons/unavailable.wav");
1650     precache_sound ("weapons/dryfire.wav");
1651     if (g_grappling_hook)
1652     {
1653         precache_sound ("weapons/hook_fire.wav"); // hook
1654         precache_sound ("weapons/hook_impact.wav"); // hook
1655     }
1656
1657     if(autocvar_sv_precacheweapons)
1658     {
1659         //precache weapon models/sounds
1660         local float wep;
1661         wep = WEP_FIRST;
1662         while (wep <= WEP_LAST)
1663         {
1664             weapon_action(wep, WR_PRECACHE);
1665             wep = wep + 1;
1666         }
1667     }
1668
1669     precache_model("models/elaser.mdl");
1670     precache_model("models/laser.mdl");
1671     precache_model("models/ebomb.mdl");
1672
1673 #if 0
1674     // Disabled this code because it simply does not work (e.g. ignores bgmvolume, overlaps with "cd loop" controlled tracks).
1675
1676     if (!self.noise && self.music) // quake 3 uses the music field
1677         self.noise = self.music;
1678
1679     // plays music for the level if there is any
1680     if (self.noise)
1681     {
1682         precache_sound (self.noise);
1683         ambientsound ('0 0 0', self.noise, VOL_BASE, ATTN_NONE);
1684     }
1685 #endif
1686 }
1687
1688 // sorry, but using \ in macros breaks line numbers
1689 #define WRITESPECTATABLE_MSG_ONE_VARNAME(varname,statement) entity varname; varname = msg_entity; FOR_EACH_REALCLIENT(msg_entity) if(msg_entity == varname || (msg_entity.classname == STR_SPECTATOR && msg_entity.enemy == varname)) statement msg_entity = varname
1690 #define WRITESPECTATABLE_MSG_ONE(statement) WRITESPECTATABLE_MSG_ONE_VARNAME(oldmsg_entity, statement)
1691 #define WRITESPECTATABLE(msg,statement) if(msg == MSG_ONE) { WRITESPECTATABLE_MSG_ONE(statement); } else statement float WRITESPECTATABLE_workaround = 0
1692
1693 // WARNING: this kills the trace globals
1694 #define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch()) return
1695 #define EXACTTRIGGER_INIT  WarpZoneLib_ExactTrigger_Init()
1696
1697 #define INITPRIO_FIRST              0
1698 #define INITPRIO_GAMETYPE           0
1699 #define INITPRIO_GAMETYPE_FALLBACK  1
1700 #define INITPRIO_FINDTARGET        10
1701 #define INITPRIO_DROPTOFLOOR       20
1702 #define INITPRIO_SETLOCATION       90
1703 #define INITPRIO_LINKDOORS         91
1704 #define INITPRIO_LAST              99
1705
1706 .void(void) initialize_entity;
1707 .float initialize_entity_order;
1708 .entity initialize_entity_next;
1709 entity initialize_entity_first;
1710
1711 void make_safe_for_remove(entity e)
1712 {
1713     if (e.initialize_entity)
1714     {
1715         entity ent, prev;
1716         for (ent = initialize_entity_first; ent; )
1717         {
1718             if ((ent == e) || ((ent.classname == "initialize_entity") && (ent.enemy == e)))
1719             {
1720                 //print("make_safe_for_remove: getting rid of initializer ", etos(ent), "\n");
1721                 // skip it in linked list
1722                 if (prev)
1723                 {
1724                     prev.initialize_entity_next = ent.initialize_entity_next;
1725                     ent = prev.initialize_entity_next;
1726                 }
1727                 else
1728                 {
1729                     initialize_entity_first = ent.initialize_entity_next;
1730                     ent = initialize_entity_first;
1731                 }
1732             }
1733             else
1734             {
1735                 prev = ent;
1736                 ent = ent.initialize_entity_next;
1737             }
1738         }
1739     }
1740 }
1741
1742 void objerror(string s)
1743 {
1744     make_safe_for_remove(self);
1745     objerror_builtin(s);
1746 }
1747
1748 .float remove_except_protected_forbidden;
1749 void remove_except_protected(entity e)
1750 {
1751         if(e.remove_except_protected_forbidden)
1752                 error("not allowed to remove this at this point");
1753         remove_builtin(e);
1754 }
1755
1756 void remove_unsafely(entity e)
1757 {
1758     remove_builtin(e);
1759 }
1760
1761 void remove_safely(entity e)
1762 {
1763     make_safe_for_remove(e);
1764     remove_builtin(e);
1765 }
1766
1767 void InitializeEntity(entity e, void(void) func, float order)
1768 {
1769     entity prev, cur;
1770
1771     if (!e || e.initialize_entity)
1772     {
1773         // make a proxy initializer entity
1774         entity e_old;
1775         e_old = e;
1776         e = spawn();
1777         e.classname = "initialize_entity";
1778         e.enemy = e_old;
1779     }
1780
1781     e.initialize_entity = func;
1782     e.initialize_entity_order = order;
1783
1784     cur = initialize_entity_first;
1785     for (;;)
1786     {
1787         if (!cur || cur.initialize_entity_order > order)
1788         {
1789             // insert between prev and cur
1790             if (prev)
1791                 prev.initialize_entity_next = e;
1792             else
1793                 initialize_entity_first = e;
1794             e.initialize_entity_next = cur;
1795             return;
1796         }
1797         prev = cur;
1798         cur = cur.initialize_entity_next;
1799     }
1800 }
1801 void InitializeEntitiesRun()
1802 {
1803     entity startoflist;
1804     startoflist = initialize_entity_first;
1805     initialize_entity_first = world;
1806     remove = remove_except_protected;
1807     for (self = startoflist; self; self = self.initialize_entity_next)
1808     {
1809         self.remove_except_protected_forbidden = 1;
1810     }
1811     for (self = startoflist; self; )
1812     {
1813         entity e;
1814         var void(void) func;
1815         e = self.initialize_entity_next;
1816         func = self.initialize_entity;
1817         self.initialize_entity_order = 0;
1818         self.initialize_entity = func_null;
1819         self.initialize_entity_next = world;
1820         self.remove_except_protected_forbidden = 0;
1821         if (self.classname == "initialize_entity")
1822         {
1823             entity e_old;
1824             e_old = self.enemy;
1825             remove_builtin(self);
1826             self = e_old;
1827         }
1828         //dprint("Delayed initialization: ", self.classname, "\n");
1829         if(func != func_null)
1830             func();
1831         else
1832         {
1833             eprint(self);
1834             backtrace(strcat("Null function in: ", self.classname, "\n"));
1835         }
1836         self = e;
1837     }
1838     remove = remove_unsafely;
1839 }
1840
1841 .float uncustomizeentityforclient_set;
1842 .void(void) uncustomizeentityforclient;
1843 void(void) SUB_Nullpointer = #0;
1844 void UncustomizeEntitiesRun()
1845 {
1846     entity oldself;
1847     oldself = self;
1848     for (self = world; (self = findfloat(self, uncustomizeentityforclient_set, 1)); )
1849         self.uncustomizeentityforclient();
1850     self = oldself;
1851 }
1852 void SetCustomizer(entity e, float(void) customizer, void(void) uncustomizer)
1853 {
1854     e.customizeentityforclient = customizer;
1855     e.uncustomizeentityforclient = uncustomizer;
1856     e.uncustomizeentityforclient_set = (uncustomizer != SUB_Nullpointer);
1857 }
1858
1859 .float nottargeted;
1860 #define IFTARGETED if(!self.nottargeted && self.targetname != "")
1861
1862 void() SUB_Remove;
1863 void Net_LinkEntity(entity e, float docull, float dt, float(entity, float) sendfunc)
1864 {
1865     vector mi, ma;
1866
1867     if (e.classname == "")
1868         e.classname = "net_linked";
1869
1870     if (e.model == "" || self.modelindex == 0)
1871     {
1872         mi = e.mins;
1873         ma = e.maxs;
1874         setmodel(e, "null");
1875         setsize(e, mi, ma);
1876     }
1877
1878     e.SendEntity = sendfunc;
1879     e.SendFlags = 0xFFFFFF;
1880
1881     if (!docull)
1882         e.effects |= EF_NODEPTHTEST;
1883
1884     if (dt)
1885     {
1886         e.nextthink = time + dt;
1887         e.think = SUB_Remove;
1888     }
1889 }
1890
1891 void adaptor_think2touch()
1892 {
1893     entity o;
1894     o = other;
1895     other = world;
1896     self.touch();
1897     other = o;
1898 }
1899
1900 void adaptor_think2use()
1901 {
1902     entity o, a;
1903     o = other;
1904     a = activator;
1905     activator = world;
1906     other = world;
1907     self.use();
1908     other = o;
1909     activator = a;
1910 }
1911
1912 void adaptor_think2use_hittype_splash() // for timed projectile detonation
1913 {
1914         if not(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
1915                 self.projectiledeathtype |= HITTYPE_SPLASH;
1916         adaptor_think2use();
1917 }
1918
1919 // deferred dropping
1920 void DropToFloor_Handler()
1921 {
1922     droptofloor_builtin();
1923     self.dropped_origin = self.origin;
1924 }
1925
1926 void droptofloor()
1927 {
1928     InitializeEntity(self, DropToFloor_Handler, INITPRIO_DROPTOFLOOR);
1929 }
1930
1931
1932
1933 float trace_hits_box_a0, trace_hits_box_a1;
1934
1935 float trace_hits_box_1d(float end, float thmi, float thma)
1936 {
1937     if (end == 0)
1938     {
1939         // just check if x is in range
1940         if (0 < thmi)
1941             return FALSE;
1942         if (0 > thma)
1943             return FALSE;
1944     }
1945     else
1946     {
1947         // do the trace with respect to x
1948         // 0 -> end has to stay in thmi -> thma
1949         trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
1950         trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
1951         if (trace_hits_box_a0 > trace_hits_box_a1)
1952             return FALSE;
1953     }
1954     return TRUE;
1955 }
1956
1957 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
1958 {
1959     end -= start;
1960     thmi -= start;
1961     thma -= start;
1962     // now it is a trace from 0 to end
1963
1964     trace_hits_box_a0 = 0;
1965     trace_hits_box_a1 = 1;
1966
1967     if (!trace_hits_box_1d(end_x, thmi_x, thma_x))
1968         return FALSE;
1969     if (!trace_hits_box_1d(end_y, thmi_y, thma_y))
1970         return FALSE;
1971     if (!trace_hits_box_1d(end_z, thmi_z, thma_z))
1972         return FALSE;
1973
1974     return TRUE;
1975 }
1976
1977 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
1978 {
1979     return trace_hits_box(start, end, thmi - ma, thma - mi);
1980 }
1981
1982 float SUB_NoImpactCheck()
1983 {
1984         // zero hitcontents = this is not the real impact, but either the
1985         // mirror-impact of something hitting the projectile instead of the
1986         // projectile hitting the something, or a touchareagrid one. Neither of
1987         // these stop the projectile from moving, so...
1988         if(trace_dphitcontents == 0)
1989         {
1990                 //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n");
1991                 dprint(sprintf(_("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)));
1992                 checkclient();
1993         }
1994     if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
1995         return 1;
1996     if (other == world && self.size != '0 0 0')
1997     {
1998         vector tic;
1999         tic = self.velocity * sys_frametime;
2000         tic = tic + normalize(tic) * vlen(self.maxs - self.mins);
2001         traceline(self.origin - tic, self.origin + tic, MOVE_NORMAL, self);
2002         if (trace_fraction >= 1)
2003         {
2004             dprint("Odd... did not hit...?\n");
2005         }
2006         else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
2007         {
2008             dprint("Detected and prevented the sky-grapple bug.\n");
2009             return 1;
2010         }
2011     }
2012
2013     return 0;
2014 }
2015
2016 #define SUB_OwnerCheck() (other && (other == self.owner))
2017
2018 float WarpZone_Projectile_Touch_ImpactFilter_Callback()
2019 {
2020         if(SUB_OwnerCheck())
2021                 return TRUE;
2022         if(SUB_NoImpactCheck())
2023         {
2024                 remove(self);
2025                 return TRUE;
2026         }
2027         if(trace_ent && trace_ent.solid > SOLID_TRIGGER)
2028                 UpdateCSQCProjectileNextFrame(self);
2029         return FALSE;
2030 }
2031 #define PROJECTILE_TOUCH if(WarpZone_Projectile_Touch()) return
2032
2033 float MAX_IPBAN_URIS           = 16;
2034                               
2035 float URI_GET_DISCARD          = 0;
2036 float URI_GET_IPBAN            = 1;
2037 float URI_GET_IPBAN_END        = 16;
2038 float URI_GET_PLAYERSTATS_SENT = 17;
2039
2040 void URI_Get_Callback(float id, float status, string data)
2041 {
2042     dprint("Received HTTP request data for id ", ftos(id), "; status is ", ftos(status), "\nData is:\n");
2043     dprint(data);
2044     dprint("\nEnd of data.\n");
2045
2046     if (id == URI_GET_DISCARD)
2047     {
2048         // discard
2049     }
2050     else if (id >= URI_GET_IPBAN && id <= URI_GET_IPBAN_END)
2051     {
2052         // online ban list
2053         OnlineBanList_URI_Get_Callback(id, status, data);
2054     }
2055     else if (id == URI_GET_PLAYERSTATS_SENT)
2056     {
2057         PlayerStats_Sent_URI_Get_Callback(id, status, data);
2058     }
2059     else
2060     {
2061         print("Received HTTP request data for an invalid id ", ftos(id), ".\n");
2062     }
2063 }
2064
2065 void print_to(entity e, string s)
2066 {
2067     if (e)
2068         sprint(e, strcat(s, "\n"));
2069     else
2070         print(s, "\n");
2071 }
2072
2073 string uid2name(string myuid) {
2074         string s;
2075         s = db_get(ServerProgsDB, strcat("uid2name", myuid));
2076         
2077         if(s == "")
2078                 s = "^1Unregistered Player";
2079         return s;
2080 }
2081
2082 float race_readTime(string map, float pos)
2083 {
2084         string rr;
2085         if(g_cts)
2086                 rr = CTS_RECORD;
2087         else
2088                 rr = RACE_RECORD;
2089
2090         return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
2091 }
2092
2093 string race_readUID(string map, float pos)
2094 {
2095         string rr;
2096         if(g_cts)
2097                 rr = CTS_RECORD;
2098         else
2099                 rr = RACE_RECORD;
2100
2101         return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
2102 }
2103
2104 float race_readPos(string map, float t) {
2105         float i;
2106         for (i = 1; i <= RANKINGS_CNT; ++i)
2107                 if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
2108                         return i;
2109
2110         return 0; // pos is zero if unranked
2111 }
2112
2113 void race_writeTime(string map, float t, string myuid)
2114 {
2115         string rr;
2116         if(g_cts)
2117                 rr = CTS_RECORD;
2118         else
2119                 rr = RACE_RECORD;
2120
2121         float newpos;
2122         newpos = race_readPos(map, t);
2123
2124         float i, prevpos;
2125         for(i = 1; i <= RANKINGS_CNT; ++i)
2126         {
2127                 if(race_readUID(map, i) == myuid)
2128                         prevpos = i;
2129         }
2130         if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
2131                 for (i = prevpos; i > newpos; --i) {
2132                         db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2133                         db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2134                 }
2135         } else { // player has no ranked record yet
2136                 for (i = RANKINGS_CNT; i > newpos; --i) {
2137                         db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
2138                         db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
2139                 }
2140         }
2141
2142         // store new time itself
2143         db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
2144         db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
2145 }
2146
2147 string race_readName(string map, float pos)
2148 {
2149         string rr;
2150         if(g_cts)
2151                 rr = CTS_RECORD;
2152         else
2153                 rr = RACE_RECORD;
2154
2155         return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
2156 }
2157
2158 string race_placeName(float pos) {
2159         if(floor((mod(pos, 100))/10) * 10 != 10) // examples: 12th, 111th, 213th will not execute this block
2160         {
2161                 if(mod(pos, 10) == 1)
2162                         return strcat(ftos(pos), "st");
2163                 else if(mod(pos, 10) == 2)
2164                         return strcat(ftos(pos), "nd");
2165                 else if(mod(pos, 10) == 3)
2166                         return strcat(ftos(pos), "rd");
2167                 else
2168                         return strcat(ftos(pos), "th");
2169         }
2170         else
2171                 return strcat(ftos(pos), "th");
2172 }
2173 string getrecords(float page) // 50 records per page
2174 {
2175     float rec;
2176     string h;
2177     float r;
2178     float i;
2179     string s;
2180
2181     rec = 0;
2182
2183     s = "";
2184
2185     if (g_ctf)
2186     {
2187         for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2188         {
2189             if (MapInfo_Get_ByID(i))
2190             {
2191                 r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time")));
2192                 if (r == 0)
2193                     continue;
2194                 // TODO: uid2name
2195                 h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname"));
2196                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n");
2197                 ++rec;
2198             }
2199         }
2200     }
2201
2202     if (g_race)
2203     {
2204         for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2205         {
2206             if (MapInfo_Get_ByID(i))
2207             {
2208                 r = race_readTime(MapInfo_Map_bspname, 1);
2209                 if (r == 0)
2210                     continue;
2211                 h = race_readName(MapInfo_Map_bspname, 1);
2212                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2213                 ++rec;
2214             }
2215         }
2216     }
2217
2218     if (g_cts)
2219     {
2220         for (i = page * 200; i < MapInfo_count && i < page * 200 + 200; ++i)
2221         {
2222             if (MapInfo_Get_ByID(i))
2223             {
2224                 r = race_readTime(MapInfo_Map_bspname, 1);
2225                 if (r == 0)
2226                     continue;
2227                 h = race_readName(MapInfo_Map_bspname, 1);
2228                 s = strcat(s, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n");
2229                 ++rec;
2230             }
2231         }
2232     }
2233
2234     MapInfo_ClearTemps();
2235
2236     if (s == "" && page == 0)
2237         return "No records are available on this server.\n";
2238     else
2239         return s;
2240 }
2241
2242 string getrankings()
2243 {
2244     string n;
2245     float t;
2246     float i;
2247     string s;
2248     string p;
2249     string map;
2250
2251     s = "";
2252
2253     map = GetMapname();
2254
2255     for (i = 1; i <= RANKINGS_CNT; ++i)
2256     {
2257         t = race_readTime(map, i);
2258         if (t == 0)
2259             continue;
2260         n = race_readName(map, i);
2261         p = race_placeName(i);
2262         s = strcat(s, strpad(8, p), " ", strpad(-8, TIME_ENCODED_TOSTRING(t)), " ", n, "\n");
2263     }
2264
2265     MapInfo_ClearTemps();
2266
2267     if (s == "")
2268         return strcat("No records are available for the map: ", map, "\n");
2269     else
2270         return strcat("Records for ", map, ":\n", s);
2271 }
2272
2273 #define LADDER_FIRSTPOINT 100
2274 #define LADDER_CNT 10
2275         // position X still gives LADDER_FIRSTPOINT/X points
2276 #define LADDER_SIZE 30
2277         // ladder shows the top X players
2278 string top_uids[LADDER_SIZE];
2279 float top_scores[LADDER_SIZE];
2280 string getladder()
2281 {
2282     float i, j, k, uidcnt;
2283     string s, temp_s;
2284
2285     s = "";
2286     temp_s = "";
2287
2288     string rr;
2289     if(g_cts)
2290         rr = CTS_RECORD;
2291     else
2292         rr = RACE_RECORD;
2293
2294     string myuid;
2295
2296     for (k = 0; k < MapInfo_count; ++k)
2297     {
2298         if (MapInfo_Get_ByID(k))
2299         {
2300                 for (i = 0; i <= LADDER_CNT; ++i) { // i = 0 because it is the speed award
2301                         if(i == 0) // speed award
2302                         {
2303                                 if(stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/speed"))) == 0)
2304                                         continue;
2305
2306                                 myuid = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, rr, "speed/crypto_idfp"));
2307                         }
2308                         else // normal record, if it exists (else break)
2309                         {
2310                                 if(race_readTime(MapInfo_Map_bspname, i) == 0)
2311                                         continue;
2312
2313                                 myuid = race_readUID(MapInfo_Map_bspname, i);
2314                         }
2315
2316                         // string s contains:
2317                         // arg 0 = # of speed recs
2318                         // arg 1 = # of 1st place recs
2319                         // arg 2 = # of 2nd place recs
2320                         // ... etc
2321                         // LADDER_CNT+1 = total points
2322
2323                         temp_s = db_get(TemporaryDB, strcat("ladder", myuid));
2324                         if (temp_s == "")
2325                         {
2326                             db_put(TemporaryDB, strcat("uid", ftos(uidcnt)), myuid);
2327                             ++uidcnt;
2328                             for (j = 0; j <= LADDER_CNT + 1; ++j)
2329                             {
2330                                 if(j != LADDER_CNT + 1)
2331                                     temp_s = strcat(temp_s, "0 ");
2332                                 else
2333                                     temp_s = strcat(temp_s, "0");
2334                             }
2335                         }
2336
2337                         tokenize_console(temp_s);
2338                         s = "";
2339
2340                         if(i == 0) // speed award
2341                             for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2342                             {
2343                                 if(j == 0) // speed award
2344                                     s = strcat(s, ftos(stof(argv(j)) +1)); // add 1 to speed rec count and write
2345                                 else
2346                                     s = strcat(s, " ", argv(j)); // just copy over everything else
2347                             }
2348                         else // record
2349                             for (j = 0; j <= LADDER_CNT; ++j) // loop over each arg in the string
2350                             {
2351                                 if(j == 0)
2352                                     s = strcat(s, argv(j)); // speed award, dont prefix with " "
2353                                 else if(j == i) // wanted rec!
2354                                     s = strcat(s, " ", ftos(stof(argv(j)) +1)); // update argv(j)
2355                                 else
2356                                     s = strcat(s, " ", argv(j)); // just copy over everything else
2357                             }
2358
2359                         // total points are (by default) calculated like this:
2360                         // speedrec = floor(100 / 10) = 10 points
2361                         // 1st place = floor(100 / 1) = 100 points
2362                         // 2nd place = floor(100 / 2) = 50 points
2363                         // 3rd place = floor(100 / 3) = 33 points
2364                         // 4th place = floor(100 / 4) = 25 points
2365                         // 5th place = floor(100 / 5) = 20 points
2366                         // ... etc
2367
2368                         if(i == 0)
2369                             s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + LADDER_FIRSTPOINT / 10)); // speed award, add LADDER_FIRSTPOINT / 10 points
2370                         else
2371                             s = strcat(s, " ", ftos(stof(argv(LADDER_CNT+1)) + floor(LADDER_FIRSTPOINT / i))); // record, add LADDER_FIRSTPOINT / i points
2372
2373                         db_put(TemporaryDB, strcat("ladder", myuid), s);
2374                 }
2375         }
2376     }
2377
2378     float thiscnt;
2379     string thisuid;
2380     for (i = 0; i <= uidcnt; ++i) // for each known uid
2381     {
2382         thisuid = db_get(TemporaryDB, strcat("uid", ftos(i)));
2383         temp_s = db_get(TemporaryDB, strcat("ladder", thisuid));
2384         tokenize_console(temp_s);
2385         thiscnt = stof(argv(LADDER_CNT+1));
2386
2387         if(thiscnt > top_scores[LADDER_SIZE-1])
2388         for (j = 0; j < LADDER_SIZE; ++j) // for each place in ladder
2389         {
2390             if(thiscnt > top_scores[j])
2391             {
2392                 for (k = LADDER_SIZE-1; k >= j; --k)
2393                 {
2394                     top_uids[k] = top_uids[k-1];
2395                     top_scores[k] = top_scores[k-1];
2396                 }
2397                 top_uids[j] = thisuid;
2398                 top_scores[j] = thiscnt;
2399                 break;
2400             }
2401         }
2402     }
2403
2404     s = "^3-----------------------\n\n";
2405
2406     s = strcat(s, "Pos ^3|");
2407     s = strcat(s, " ^7Total  ^3|");
2408     for (i = 1; i <= LADDER_CNT; ++i)
2409     {
2410         s = strcat(s, " ^7", race_placeName(i), " ^3|");
2411     }
2412     s = strcat(s, " ^7Speed awards ^3| ^7Name");
2413
2414     s = strcat(s, "\n^3----+--------");
2415     for (i = 1; i <= min(9, LADDER_CNT); ++i)
2416     {
2417         s = strcat(s, "+-----");
2418     }
2419 #if LADDER_CNT > 9
2420     for (i = 1; i <= LADDER_CNT - 9; ++i)
2421     {
2422         s = strcat(s, "+------");
2423     }
2424 #endif
2425
2426     s = strcat(s, "+--------------+--------------------\n");
2427
2428     for (i = 0; i < LADDER_SIZE; ++i)
2429     {
2430         temp_s = db_get(TemporaryDB, strcat("ladder", top_uids[i]));
2431         tokenize_console(temp_s);
2432         if (argv(LADDER_CNT+1) == "") // total is 0, skip
2433             continue;
2434         s = strcat(s, strpad(4, race_placeName(i+1)), "^3| ^7"); // pos
2435         s = strcat(s, strpad(7, argv(LADDER_CNT+1)), "^3| ^7"); // total
2436         for (j = 1; j <= min(9, LADDER_CNT); ++j)
2437         {
2438             s = strcat(s, strpad(4, argv(j)), "^3| ^7"); // 1st, 2nd, 3rd etc cnt
2439         }
2440 #if LADDER_CNT > 9
2441         for (j = 10; j <= LADDER_CNT; ++j)
2442         {
2443             s = strcat(s, strpad(4, argv(j)), " ^3| ^7"); // 1st, 2nd, 3rd etc cnt
2444         }
2445 #endif
2446
2447         s = strcat(s, strpad(13, argv(0)), "^3| ^7"); // speed award cnt
2448         s = strcat(s, uid2name(top_uids[i]), "\n"); // name
2449     }
2450
2451     MapInfo_ClearTemps();
2452
2453     if (s == "")
2454         return "No ladder on this server!\n";
2455     else
2456         return strcat("Top ", ftos(LADDER_SIZE), " ladder rankings:\n", s);
2457 }
2458
2459
2460 float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
2461 {
2462     float m, i;
2463     vector start, org, delta, end, enddown, mstart;
2464
2465     m = e.dphitcontentsmask;
2466     e.dphitcontentsmask = goodcontents | badcontents;
2467
2468     org = world.mins;
2469     delta = world.maxs - world.mins;
2470
2471     for (i = 0; i < attempts; ++i)
2472     {
2473         start_x = org_x + random() * delta_x;
2474         start_y = org_y + random() * delta_y;
2475         start_z = org_z + random() * delta_z;
2476
2477         // rule 1: start inside world bounds, and outside
2478         // solid, and don't start from somewhere where you can
2479         // fall down to evil
2480         tracebox(start, e.mins, e.maxs, start - '0 0 1' * delta_z, MOVE_NORMAL, e);
2481         if (trace_fraction >= 1)
2482             continue;
2483         if (trace_startsolid)
2484             continue;
2485         if (trace_dphitcontents & badcontents)
2486             continue;
2487         if (trace_dphitq3surfaceflags & badsurfaceflags)
2488             continue;
2489
2490         // rule 2: if we are too high, lower the point
2491         if (trace_fraction * delta_z > maxaboveground)
2492             start = trace_endpos + '0 0 1' * maxaboveground;
2493         enddown = trace_endpos;
2494
2495         // rule 3: make sure we aren't outside the map. This only works
2496         // for somewhat well formed maps. A good rule of thumb is that
2497         // the map should have a convex outside hull.
2498         // these can be traceLINES as we already verified the starting box
2499         mstart = start + 0.5 * (e.mins + e.maxs);
2500         traceline(mstart, mstart + '1 0 0' * delta_x, MOVE_NORMAL, e);
2501         if (trace_fraction >= 1)
2502             continue;
2503         traceline(mstart, mstart - '1 0 0' * delta_x, MOVE_NORMAL, e);
2504         if (trace_fraction >= 1)
2505             continue;
2506         traceline(mstart, mstart + '0 1 0' * delta_y, MOVE_NORMAL, e);
2507         if (trace_fraction >= 1)
2508             continue;
2509         traceline(mstart, mstart - '0 1 0' * delta_y, MOVE_NORMAL, e);
2510         if (trace_fraction >= 1)
2511             continue;
2512         traceline(mstart, mstart + '0 0 1' * delta_z, MOVE_NORMAL, e);
2513         if (trace_fraction >= 1)
2514             continue;
2515
2516         // find a random vector to "look at"
2517         end_x = org_x + random() * delta_x;
2518         end_y = org_y + random() * delta_y;
2519         end_z = org_z + random() * delta_z;
2520         end = start + normalize(end - start) * vlen(delta);
2521
2522         // rule 4: start TO end must not be too short
2523         tracebox(start, e.mins, e.maxs, end, MOVE_NORMAL, e);
2524         if (trace_startsolid)
2525             continue;
2526         if (trace_fraction < minviewdistance / vlen(delta))
2527             continue;
2528
2529         // rule 5: don't want to look at sky
2530         if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)
2531             continue;
2532
2533         // rule 6: we must not end up in trigger_hurt
2534         if (tracebox_hits_trigger_hurt(start, e.mins, e.maxs, enddown))
2535             continue;
2536
2537         break;
2538     }
2539
2540     e.dphitcontentsmask = m;
2541
2542     if (i < attempts)
2543     {
2544         setorigin(e, start);
2545         e.angles = vectoangles(end - start);
2546         dprint("Needed ", ftos(i + 1), " attempts\n");
2547         return TRUE;
2548     }
2549     else
2550         return FALSE;
2551 }
2552
2553 float zcurveparticles_effectno;
2554 vector zcurveparticles_start;
2555 float zcurveparticles_spd;
2556
2557 void endzcurveparticles()
2558 {
2559         if(zcurveparticles_effectno)
2560         {
2561                 // terminator
2562                 WriteShort(MSG_BROADCAST, zcurveparticles_spd | 0x8000);
2563         }
2564         zcurveparticles_effectno = 0;
2565 }
2566
2567 void zcurveparticles(float effectno, vector start, vector end, float end_dz, float spd)
2568 {
2569         spd = bound(0, floor(spd / 16), 32767);
2570         if(effectno != zcurveparticles_effectno || start != zcurveparticles_start)
2571         {
2572                 endzcurveparticles();
2573                 WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
2574                 WriteByte(MSG_BROADCAST, TE_CSQC_ZCURVEPARTICLES);
2575                 WriteShort(MSG_BROADCAST, effectno);
2576                 WriteCoord(MSG_BROADCAST, start_x);
2577                 WriteCoord(MSG_BROADCAST, start_y);
2578                 WriteCoord(MSG_BROADCAST, start_z);
2579                 zcurveparticles_effectno = effectno;
2580                 zcurveparticles_start = start;
2581         }
2582         else
2583                 WriteShort(MSG_BROADCAST, zcurveparticles_spd);
2584         WriteCoord(MSG_BROADCAST, end_x);
2585         WriteCoord(MSG_BROADCAST, end_y);
2586         WriteCoord(MSG_BROADCAST, end_z);
2587         WriteCoord(MSG_BROADCAST, end_dz);
2588         zcurveparticles_spd = spd;
2589 }
2590
2591 void zcurveparticles_from_tracetoss(float effectno, vector start, vector end, vector vel)
2592 {
2593         float end_dz;
2594         vector vecxy, velxy;
2595
2596         vecxy = end - start;
2597         vecxy_z = 0;
2598         velxy = vel;
2599         velxy_z = 0;
2600
2601         if (vlen(velxy) < 0.000001 * fabs(vel_z))
2602         {
2603                 endzcurveparticles();
2604                 trailparticles(world, effectno, start, end);
2605                 return;
2606         }
2607
2608         end_dz = vlen(vecxy) / vlen(velxy) * vel_z - (end_z - start_z);
2609         zcurveparticles(effectno, start, end, end_dz, vlen(vel));
2610 }
2611
2612 void write_recordmarker(entity pl, float tstart, float dt)
2613 {
2614     GameLogEcho(strcat(":recordset:", ftos(pl.playerid), ":", ftos(dt)));
2615
2616     // also write a marker into demo files for demotc-race-record-extractor to find
2617     stuffcmd(pl,
2618              strcat(
2619                  strcat("//", strconv(2, 0, 0, GetGametype()), " RECORD SET ", TIME_ENCODED_TOSTRING(TIME_ENCODE(dt))),
2620                  " ", ftos(tstart), " ", ftos(dt), "\n"));
2621 }
2622
2623 vector shotorg_adjustfromclient(vector vecs, float y_is_right, float allowcenter, float algn)
2624 {
2625         switch(algn)
2626         {
2627                 case 1: // right
2628                         break;
2629
2630                 case 2: // left
2631                         vecs_y = -vecs_y;
2632                         break;
2633
2634                 default:
2635                 case 3:
2636                         if(allowcenter) // 2: allow center handedness
2637                         {
2638                                 // center
2639                                 vecs_y = 0;
2640                                 vecs_z -= 2;
2641                         }
2642                         else
2643                         {
2644                                 // right
2645                         }
2646                         break;
2647
2648                 case 4:
2649                         if(allowcenter) // 2: allow center handedness
2650                         {
2651                                 // center
2652                                 vecs_y = 0;
2653                                 vecs_z -= 2;
2654                         }
2655                         else
2656                         {
2657                                 // left
2658                                 vecs_y = -vecs_y;
2659                         }
2660                         break;
2661         }
2662         return vecs;
2663 }
2664
2665 vector shotorg_adjust_values(vector vecs, float y_is_right, float visual, float algn)
2666 {
2667         string s;
2668         vector v;
2669
2670         if (autocvar_g_shootfromeye)
2671         {
2672                 if (visual)
2673                 {
2674                         vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE, algn);
2675                 }
2676                 else
2677                 {
2678                         vecs_y = 0;
2679                         vecs_z = 0;
2680                 }
2681         }
2682         else if (autocvar_g_shootfromcenter)
2683         {
2684                 if (visual)
2685                 {
2686                         vecs = shotorg_adjustfromclient(vecs, y_is_right, TRUE, algn);
2687                 }
2688                 else
2689                 {
2690                         vecs_y = 0;
2691                         vecs_z -= 2;
2692                 }
2693         }
2694         else if ((s = autocvar_g_shootfromfixedorigin) != "")
2695         {
2696                 v = stov(s);
2697                 if (y_is_right)
2698                         v_y = -v_y;
2699                 if (v_x != 0)
2700                         vecs_x = v_x;
2701                 vecs_y = v_y;
2702                 vecs_z = v_z;
2703         }
2704         else if (autocvar_g_shootfromclient)
2705         {
2706                 vecs = shotorg_adjustfromclient(vecs, y_is_right, (autocvar_g_shootfromclient >= 2), algn);
2707         }
2708         return vecs;
2709 }
2710
2711 vector shotorg_adjust(vector vecs, float y_is_right, float visual)
2712 {
2713         return shotorg_adjust_values(vecs, y_is_right, visual, self.owner.cvar_cl_gunalign);
2714 }
2715
2716
2717 void attach_sameorigin(entity e, entity to, string tag)
2718 {
2719     vector org, t_forward, t_left, t_up, e_forward, e_up;
2720     vector org0, ang0;
2721     float tagscale;
2722
2723     ang0 = e.angles;
2724     org0 = e.origin;
2725
2726     org = e.origin - gettaginfo(to, gettagindex(to, tag));
2727     tagscale = pow(vlen(v_forward), -2); // undo a scale on the tag
2728     t_forward = v_forward * tagscale;
2729     t_left = v_right * -tagscale;
2730     t_up = v_up * tagscale;
2731
2732     e.origin_x = org * t_forward;
2733     e.origin_y = org * t_left;
2734     e.origin_z = org * t_up;
2735
2736     // current forward and up directions
2737     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2738                 e.angles = AnglesTransform_FromVAngles(e.angles);
2739         else
2740                 e.angles = AnglesTransform_FromAngles(e.angles);
2741     fixedmakevectors(e.angles);
2742
2743     // untransform forward, up!
2744     e_forward_x = v_forward * t_forward;
2745     e_forward_y = v_forward * t_left;
2746     e_forward_z = v_forward * t_up;
2747     e_up_x = v_up * t_forward;
2748     e_up_y = v_up * t_left;
2749     e_up_z = v_up * t_up;
2750
2751     e.angles = fixedvectoangles2(e_forward, e_up);
2752     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2753                 e.angles = AnglesTransform_ToVAngles(e.angles);
2754         else
2755                 e.angles = AnglesTransform_ToAngles(e.angles);
2756
2757     setattachment(e, to, tag);
2758     setorigin(e, e.origin);
2759 }
2760
2761 void detach_sameorigin(entity e)
2762 {
2763     vector org;
2764     org = gettaginfo(e, 0);
2765     e.angles = fixedvectoangles2(v_forward, v_up);
2766     if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2767                 e.angles = AnglesTransform_ToVAngles(e.angles);
2768         else
2769                 e.angles = AnglesTransform_ToAngles(e.angles);
2770     setorigin(e, org);
2771     setattachment(e, world, "");
2772     setorigin(e, e.origin);
2773 }
2774
2775 void follow_sameorigin(entity e, entity to)
2776 {
2777     e.movetype = MOVETYPE_FOLLOW; // make the hole follow
2778     e.aiment = to; // make the hole follow bmodel
2779     e.punchangle = to.angles; // the original angles of bmodel
2780     e.view_ofs = e.origin - to.origin; // relative origin
2781     e.v_angle = e.angles - to.angles; // relative angles
2782 }
2783
2784 void unfollow_sameorigin(entity e)
2785 {
2786     e.movetype = MOVETYPE_NONE;
2787 }
2788
2789 entity gettaginfo_relative_ent;
2790 vector gettaginfo_relative(entity e, float tag)
2791 {
2792     if (!gettaginfo_relative_ent)
2793     {
2794         gettaginfo_relative_ent = spawn();
2795         gettaginfo_relative_ent.effects = EF_NODRAW;
2796     }
2797     gettaginfo_relative_ent.model = e.model;
2798     gettaginfo_relative_ent.modelindex = e.modelindex;
2799     gettaginfo_relative_ent.frame = e.frame;
2800     return gettaginfo(gettaginfo_relative_ent, tag);
2801 }
2802
2803 void SoundEntity_StartSound(entity pl, float chan, string samp, float vol, float attn)
2804 {
2805     float p;
2806     p = pow(2, chan);
2807     if (pl.soundentity.cnt & p)
2808         return;
2809     soundtoat(MSG_ALL, pl.soundentity, gettaginfo(pl.soundentity, 0), chan, samp, vol, attn);
2810     pl.soundentity.cnt |= p;
2811 }
2812
2813 void SoundEntity_StopSound(entity pl, float chan)
2814 {
2815     float p;
2816     p = pow(2, chan);
2817     if (pl.soundentity.cnt & p)
2818     {
2819         stopsoundto(MSG_ALL, pl.soundentity, chan);
2820         pl.soundentity.cnt &~= p;
2821     }
2822 }
2823
2824 void SoundEntity_Attach(entity pl)
2825 {
2826     pl.soundentity = spawn();
2827     pl.soundentity.classname = "soundentity";
2828     pl.soundentity.owner = pl;
2829     setattachment(pl.soundentity, pl, "");
2830     setmodel(pl.soundentity, "null");
2831 }
2832
2833 void SoundEntity_Detach(entity pl)
2834 {
2835     float i;
2836     for (i = 0; i <= 7; ++i)
2837         SoundEntity_StopSound(pl, i);
2838 }
2839
2840
2841 float ParseCommandPlayerSlotTarget_firsttoken;
2842 entity GetCommandPlayerSlotTargetFromTokenizedCommand(float tokens, float idx) // idx = start index
2843 {
2844         string s;
2845         entity e, head;
2846         float n;
2847
2848         s = string_null;
2849
2850         ParseCommandPlayerSlotTarget_firsttoken = -1;
2851
2852         if (tokens > idx)
2853         {
2854                 if (substring(argv(idx), 0, 1) == "#")
2855                 {
2856                         s = substring(argv(idx), 1, -1);
2857                         ++idx;
2858                         if (s == "") if (tokens > idx)
2859                         {
2860                                 s = argv(idx);
2861                                 ++idx;
2862                         }
2863                         ParseCommandPlayerSlotTarget_firsttoken = idx;
2864                         n = stof(s);
2865                         if (s == ftos(n) && n > 0 && n <= maxclients)
2866                         {
2867                                 e = edict_num(n);
2868                                 if (e.flags & FL_CLIENT)
2869                                         return e;
2870                         }
2871                 }
2872                 else
2873                 {
2874                         // it must be a nick name
2875                         s = argv(idx);
2876                         ++idx;
2877                         ParseCommandPlayerSlotTarget_firsttoken = idx;
2878
2879                         n = 0;
2880                         FOR_EACH_CLIENT(head)
2881                                 if (head.netname == s)
2882                                 {
2883                                         e = head;
2884                                         ++n;
2885                                 }
2886                         if (n == 1)
2887                                 return e;
2888
2889                         s = strdecolorize(s);
2890                         n = 0;
2891                         FOR_EACH_CLIENT(head)
2892                                 if (strdecolorize(head.netname) == s)
2893                                 {
2894                                         e = head;
2895                                         ++n;
2896                                 }
2897                         if (n == 1)
2898                                 return e;
2899                 }
2900         }
2901
2902         return world;
2903 }
2904
2905 .float scale2;
2906
2907 float modeleffect_SendEntity(entity to, float sf)
2908 {
2909         float f;
2910         WriteByte(MSG_ENTITY, ENT_CLIENT_MODELEFFECT);
2911
2912         f = 0;
2913         if(self.velocity != '0 0 0')
2914                 f |= 1;
2915         if(self.angles != '0 0 0')
2916                 f |= 2;
2917         if(self.avelocity != '0 0 0')
2918                 f |= 4;
2919
2920         WriteByte(MSG_ENTITY, f);
2921         WriteShort(MSG_ENTITY, self.modelindex);
2922         WriteByte(MSG_ENTITY, self.skin);
2923         WriteByte(MSG_ENTITY, self.frame);
2924         WriteCoord(MSG_ENTITY, self.origin_x);
2925         WriteCoord(MSG_ENTITY, self.origin_y);
2926         WriteCoord(MSG_ENTITY, self.origin_z);
2927         if(f & 1)
2928         {
2929                 WriteCoord(MSG_ENTITY, self.velocity_x);
2930                 WriteCoord(MSG_ENTITY, self.velocity_y);
2931                 WriteCoord(MSG_ENTITY, self.velocity_z);
2932         }
2933         if(f & 2)
2934         {
2935                 WriteCoord(MSG_ENTITY, self.angles_x);
2936                 WriteCoord(MSG_ENTITY, self.angles_y);
2937                 WriteCoord(MSG_ENTITY, self.angles_z);
2938         }
2939         if(f & 4)
2940         {
2941                 WriteCoord(MSG_ENTITY, self.avelocity_x);
2942                 WriteCoord(MSG_ENTITY, self.avelocity_y);
2943                 WriteCoord(MSG_ENTITY, self.avelocity_z);
2944         }
2945         WriteShort(MSG_ENTITY, self.scale * 256.0);
2946         WriteShort(MSG_ENTITY, self.scale2 * 256.0);
2947         WriteByte(MSG_ENTITY, self.teleport_time * 100.0);
2948         WriteByte(MSG_ENTITY, self.fade_time * 100.0);
2949         WriteByte(MSG_ENTITY, self.alpha * 255.0);
2950
2951         return TRUE;
2952 }
2953
2954 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)
2955 {
2956         entity e;
2957         float sz;
2958         e = spawn();
2959         e.classname = "modeleffect";
2960         setmodel(e, m);
2961         e.frame = f;
2962         setorigin(e, o);
2963         e.velocity = v;
2964         e.angles = ang;
2965         e.avelocity = angv;
2966         e.alpha = a;
2967         e.teleport_time = t1;
2968         e.fade_time = t2;
2969         e.skin = s;
2970         if(s0 >= 0)
2971                 e.scale = s0 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2972         else
2973                 e.scale = -s0;
2974         if(s2 >= 0)
2975                 e.scale2 = s2 / max6(-e.mins_x, -e.mins_y, -e.mins_z, e.maxs_x, e.maxs_y, e.maxs_z);
2976         else
2977                 e.scale2 = -s2;
2978         sz = max(e.scale, e.scale2);
2979         setsize(e, e.mins * sz, e.maxs * sz);
2980         Net_LinkEntity(e, FALSE, 0.1, modeleffect_SendEntity);
2981 }
2982
2983 void shockwave_spawn(string m, vector org, float sz, float t1, float t2)
2984 {
2985         return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2);
2986 }
2987
2988 float randombit(float bits)
2989 {
2990         if not(bits & (bits-1)) // this ONLY holds for powers of two!
2991                 return bits;
2992
2993         float n, f, b, r;
2994
2995         r = random();
2996         b = 0;
2997         n = 0;
2998
2999         for(f = 1; f <= bits; f *= 2)
3000         {
3001                 if(bits & f)
3002                 {
3003                         ++n;
3004                         r *= n;
3005                         if(r <= 1)
3006                                 b = f;
3007                         else
3008                                 r = (r - 1) / (n - 1);
3009                 }
3010         }
3011
3012         return b;
3013 }
3014
3015 float randombits(float bits, float k, float error_return)
3016 {
3017         float r;
3018         r = 0;
3019         while(k > 0 && bits != r)
3020         {
3021                 r += randombit(bits - r);
3022                 --k;
3023         }
3024         if(error_return)
3025                 if(k > 0)
3026                         return -1; // all
3027         return r;
3028 }
3029
3030 void randombit_test(float bits, float iter)
3031 {
3032         while(iter > 0)
3033         {
3034                 print(ftos(randombit(bits)), "\n");
3035                 --iter;
3036         }
3037 }
3038
3039 float ExponentialFalloff(float mindist, float maxdist, float halflifedist, float d)
3040 {
3041         if(halflifedist > 0)
3042                 return pow(0.5, (bound(mindist, d, maxdist) - mindist) / halflifedist);
3043         else if(halflifedist < 0)
3044                 return pow(0.5, (bound(mindist, d, maxdist) - maxdist) / halflifedist);
3045         else
3046                 return 1;
3047 }
3048
3049
3050
3051
3052 #ifdef RELEASE
3053 #define cvar_string_normal cvar_string_builtin
3054 #define cvar_normal cvar_builtin
3055 #else
3056 string cvar_string_normal(string n)
3057 {
3058         if not(cvar_type(n) & 1)
3059                 backtrace(strcat("Attempt to access undefined cvar: ", n));
3060         return cvar_string_builtin(n);
3061 }
3062
3063 float cvar_normal(string n)
3064 {
3065         return stof(cvar_string_normal(n));
3066 }
3067 #endif
3068 #define cvar_set_normal cvar_set_builtin
3069
3070 void defer_think()
3071 {
3072     entity oself;
3073
3074     oself           = self;
3075     self            = self.owner;
3076     oself.think     = SUB_Remove;
3077     oself.nextthink = time;
3078
3079     oself.use();
3080 }
3081
3082 /*
3083     Execute func() after time + fdelay.
3084     self when func is executed = self when defer is called
3085 */
3086 void defer(float fdelay, void() func)
3087 {
3088     entity e;
3089
3090     e           = spawn();
3091     e.owner     = self;
3092     e.use       = func;
3093     e.think     = defer_think;
3094     e.nextthink = time + fdelay;
3095 }