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