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