dc22dfcd5bb9462e7a295490f3556f80dff401c5
[voretournament/voretournament.git] / data / qcsrc / common / util.qc
1 // checkextension wrapper for log\r
2 float sqrt(float f); // declared later\r
3 float exp(float f); // declared later\r
4 float pow(float f, float e); // declared later\r
5 float checkextension(string s); // declared later\r
6 float log_synth(float f)\r
7 {\r
8         float p, l;\r
9         if(f < 0)\r
10                 return sqrt(-1); // nan? -inf?\r
11         if(f == 0)\r
12                 return sqrt(-1); // ACTUALLY this should rather be -inf, but we cannot create a +inf in QC\r
13         if(f + f == f)\r
14                 return l; // +inf\r
15         if(f < 1)\r
16         {\r
17                 f = 1 / f;\r
18                 p = -1;\r
19         }\r
20         else\r
21                 p = 1;\r
22         while(f > 2)\r
23         {\r
24                 f = sqrt(f);\r
25                 p *= 2;\r
26         }\r
27         // two steps are good enough\r
28         l = ((6-f) * f - 5) / 4.32808512266689022212;\r
29         l += exp(-l) * f - 1;\r
30         l += exp(-l) * f - 1;\r
31         return l * p;\r
32 }\r
33 float log(float f)\r
34 {\r
35         if(checkextension("DP_QC_LOG"))\r
36                 return log_builtin(f);\r
37         else\r
38                 return log_synth(f);\r
39 }\r
40 \r
41 string wordwrap_buffer;\r
42 \r
43 void wordwrap_buffer_put(string s)\r
44 {\r
45         wordwrap_buffer = strcat(wordwrap_buffer, s);\r
46 }\r
47 \r
48 string wordwrap(string s, float l)\r
49 {\r
50         string r;\r
51         wordwrap_buffer = "";\r
52         wordwrap_cb(s, l, wordwrap_buffer_put);\r
53         r = wordwrap_buffer;\r
54         wordwrap_buffer = "";\r
55         return r;\r
56 }\r
57 \r
58 #ifndef MENUQC\r
59 #ifndef CSQC\r
60 void wordwrap_buffer_sprint(string s)\r
61 {\r
62         wordwrap_buffer = strcat(wordwrap_buffer, s);\r
63         if(s == "\n")\r
64         {\r
65                 sprint(self, wordwrap_buffer);\r
66                 wordwrap_buffer = "";\r
67         }\r
68 }\r
69 \r
70 void wordwrap_sprint(string s, float l)\r
71 {\r
72         wordwrap_buffer = "";\r
73         wordwrap_cb(s, l, wordwrap_buffer_sprint);\r
74         if(wordwrap_buffer != "")\r
75                 sprint(self, strcat(wordwrap_buffer, "\n"));\r
76         wordwrap_buffer = "";\r
77         return;\r
78 }\r
79 #endif\r
80 #endif\r
81 \r
82 string unescape(string in)\r
83 {\r
84         local float i, len;\r
85         local string str, s;\r
86 \r
87         // but it doesn't seem to be necessary in my tests at least\r
88         in = strzone(in);\r
89 \r
90         len = strlen(in);\r
91         str = "";\r
92         for(i = 0; i < len; ++i)\r
93         {\r
94                 s = substring(in, i, 1);\r
95                 if(s == "\\")\r
96                 {\r
97                         s = substring(in, i+1, 1);\r
98                         if(s == "n")\r
99                                 str = strcat(str, "\n");\r
100                         else if(s == "\\")\r
101                                 str = strcat(str, "\\");\r
102                         else\r
103                                 str = strcat(str, substring(in, i, 2));\r
104                         ++i;\r
105                 } else\r
106                         str = strcat(str, s);\r
107         }\r
108 \r
109         strunzone(in);\r
110         return str;\r
111 }\r
112 \r
113 void wordwrap_cb(string s, float l, void(string) callback)\r
114 {\r
115         local string c;\r
116         local float lleft, i, j, wlen;\r
117 \r
118         s = strzone(s);\r
119         lleft = l;\r
120         for (i = 0;i < strlen(s);++i)\r
121         {\r
122                 if (substring(s, i, 2) == "\\n")\r
123                 {\r
124                         callback("\n");\r
125                         lleft = l;\r
126                         ++i;\r
127                 }\r
128                 else if (substring(s, i, 1) == "\n")\r
129                 {\r
130                         callback("\n");\r
131                         lleft = l;\r
132                 }\r
133                 else if (substring(s, i, 1) == " ")\r
134                 {\r
135                         if (lleft > 0)\r
136                         {\r
137                                 callback(" ");\r
138                                 lleft = lleft - 1;\r
139                         }\r
140                 }\r
141                 else\r
142                 {\r
143                         for (j = i+1;j < strlen(s);++j)\r
144                                 //    ^^ this skips over the first character of a word, which\r
145                                 //       is ALWAYS part of the word\r
146                                 //       this is safe since if i+1 == strlen(s), i will become\r
147                                 //       strlen(s)-1 at the end of this block and the function\r
148                                 //       will terminate. A space can't be the first character we\r
149                                 //       read here, and neither can a \n be the start, since these\r
150                                 //       two cases have been handled above.\r
151                         {\r
152                                 c = substring(s, j, 1);\r
153                                 if (c == " ")\r
154                                         break;\r
155                                 if (c == "\\")\r
156                                         break;\r
157                                 if (c == "\n")\r
158                                         break;\r
159                                 // we need to keep this tempstring alive even if substring is\r
160                                 // called repeatedly, so call strcat even though we're not\r
161                                 // doing anything\r
162                                 callback("");\r
163                         }\r
164                         wlen = j - i;\r
165                         if (lleft < wlen)\r
166                         {\r
167                                 callback("\n");\r
168                                 lleft = l;\r
169                         }\r
170                         callback(substring(s, i, wlen));\r
171                         lleft = lleft - wlen;\r
172                         i = j - 1;\r
173                 }\r
174         }\r
175         strunzone(s);\r
176 }\r
177 \r
178 float dist_point_line(vector p, vector l0, vector ldir)\r
179 {\r
180         ldir = normalize(ldir);\r
181         \r
182         // remove the component in line direction\r
183         p = p - (p * ldir) * ldir;\r
184 \r
185         // vlen of the remaining vector\r
186         return vlen(p);\r
187 }\r
188 \r
189 void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)\r
190 {\r
191         entity e;\r
192         e = start;\r
193         funcPre(pass, e);\r
194         while(e.downleft)\r
195         {\r
196                 e = e.downleft;\r
197                 funcPre(pass, e);\r
198         }\r
199         funcPost(pass, e);\r
200         while(e != start)\r
201         {\r
202                 if(e.right)\r
203                 {\r
204                         e = e.right;\r
205                         funcPre(pass, e);\r
206                         while(e.downleft)\r
207                         {\r
208                                 e = e.downleft;\r
209                                 funcPre(pass, e);\r
210                         }\r
211                 }\r
212                 else\r
213                         e = e.up;\r
214                 funcPost(pass, e);\r
215         }\r
216 }\r
217 \r
218 float median(float a, float b, float c)\r
219 {\r
220         if(a < c)\r
221                 return bound(a, b, c);\r
222         return bound(c, b, a);\r
223 }\r
224 \r
225 // converts a number to a string with the indicated number of decimals\r
226 // works for up to 10 decimals!\r
227 string ftos_decimals(float number, float decimals)\r
228 {\r
229         string result;\r
230         string tmp;\r
231         float len;\r
232 \r
233         // if negative, cut off the sign first\r
234         if(number < 0)\r
235                 return strcat("-", ftos_decimals(-number, decimals));\r
236         // it now is always positive!\r
237 \r
238         // 3.516 -> 352\r
239         number = floor(number * pow(10, decimals) + 0.5);\r
240 \r
241         // 352 -> "352"\r
242         result = ftos(number);\r
243         len = strlen(result);\r
244         // does it have a decimal point (should not happen)? If there is one, it is always at len-7)\r
245                 // if ftos had messed it up, which should never happen: "34278.000000"\r
246         if(len >= 7)\r
247                 if(substring(result, len - 7, 1) == ".")\r
248                 {\r
249                         dprint("ftos(integer) has comma? Can't be. Affected result: ", result, "\n");\r
250                         result = substring(result, 0, len - 7);\r
251                         len -= 7;\r
252                 }\r
253                 // "34278"\r
254         if(decimals == 0)\r
255                 return result; // don't insert a point for zero decimals\r
256         // is it too short? If yes, insert leading zeroes\r
257         if(len <= decimals)\r
258         {\r
259                 result = strcat(substring("0000000000", 0, decimals - len + 1), result);\r
260                 len = decimals + 1;\r
261         }\r
262         // and now... INSERT THE POINT!\r
263         tmp = substring(result, len - decimals, decimals);\r
264         result = strcat(substring(result, 0, len - decimals), ".", tmp);\r
265         return result;\r
266 }\r
267 \r
268 float time;\r
269 vector colormapPaletteColor(float c, float isPants)\r
270 {\r
271         switch(c)\r
272         {\r
273                 case  0: return '0.800000 0.800000 0.800000';\r
274                 case  1: return '0.600000 0.400000 0.000000';\r
275                 case  2: return '0.000000 1.000000 0.501961';\r
276                 case  3: return '0.000000 1.000000 0.000000';\r
277                 case  4: return '1.000000 0.000000 0.000000';\r
278                 case  5: return '0.000000 0.658824 1.000000';\r
279                 case  6: return '0.000000 1.000000 1.000000';\r
280                 case  7: return '0.501961 1.000000 0.000000';\r
281                 case  8: return '0.501961 0.000000 1.000000';\r
282                 case  9: return '1.000000 0.000000 1.000000';\r
283                 case 10: return '1.000000 0.000000 0.501961';\r
284                 case 11: return '0.600000 0.600000 0.600000';\r
285                 case 12: return '1.000000 1.000000 0.000000';\r
286                 case 13: return '0.000000 0.313725 1.000000';\r
287                 case 14: return '1.000000 0.501961 0.000000';\r
288                 case 15:\r
289                         if(isPants)\r
290                                 return\r
291                                           '1 0 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 0.0000000000))\r
292                                         + '0 1 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 2.0943951024))\r
293                                         + '0 0 1' * (0.502 + 0.498 * sin(time / 2.7182818285 + 4.1887902048));\r
294                         else\r
295                                 return\r
296                                           '1 0 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 5.2359877560))\r
297                                         + '0 1 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 3.1415926536))\r
298                                         + '0 0 1' * (0.502 + 0.498 * sin(time / 3.1415926536 + 1.0471975512));\r
299                 default: return '0.000 0.000 0.000';\r
300         }\r
301 }\r
302 \r
303 // unzone the string, and return it as tempstring. Safe to be called on string_null\r
304 string fstrunzone(string s)\r
305 {\r
306         string sc;\r
307         if not(s)\r
308                 return s;\r
309         sc = strcat(s, "");\r
310         strunzone(s);\r
311         return sc;\r
312 }\r
313 \r
314 // Databases (hash tables)\r
315 #define DB_BUCKETS 8192\r
316 void db_save(float db, string pFilename)\r
317 {\r
318         float fh, i, n;\r
319         fh = fopen(pFilename, FILE_WRITE);\r
320         if(fh < 0) \r
321         {\r
322                 print(strcat("^1Can't write DB to ", pFilename));\r
323                 return;\r
324         }\r
325         n = buf_getsize(db);\r
326         fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));\r
327         for(i = 0; i < n; ++i)\r
328                 fputs(fh, strcat(bufstr_get(db, i), "\n"));\r
329         fclose(fh);\r
330 }\r
331 \r
332 float db_create()\r
333 {\r
334         return buf_create();\r
335 }\r
336 \r
337 float db_load(string pFilename)\r
338 {\r
339         float db, fh, i, j, n;\r
340         string l;\r
341         db = buf_create();\r
342         if(db < 0)\r
343                 return -1;\r
344         fh = fopen(pFilename, FILE_READ);\r
345         if(fh < 0)\r
346                 return db;\r
347         if(stof(fgets(fh)) == DB_BUCKETS)\r
348         {\r
349                 i = 0;\r
350                 while((l = fgets(fh)))\r
351                 {\r
352                         if(l != "")\r
353                                 bufstr_set(db, i, l);\r
354                         ++i;\r
355                 }\r
356         }\r
357         else\r
358         {\r
359                 // different count of buckets?\r
360                 // need to reorganize the database then (SLOW)\r
361                 while((l = fgets(fh)))\r
362                 {\r
363                         n = tokenizebyseparator(l, "\\");\r
364                         for(j = 2; j < n; j += 2)\r
365                                 db_put(db, argv(j-1), uri_unescape(argv(j)));\r
366                 }\r
367         }\r
368         fclose(fh);\r
369         return db;\r
370 }\r
371 \r
372 void db_dump(float db, string pFilename)\r
373 {\r
374         float fh, i, j, n, m;\r
375         fh = fopen(pFilename, FILE_WRITE);\r
376         if(fh < 0)\r
377                 error(strcat("Can't dump DB to ", pFilename));\r
378         n = buf_getsize(db);\r
379         fputs(fh, "0\n");\r
380         for(i = 0; i < n; ++i)\r
381         {\r
382                 m = tokenizebyseparator(bufstr_get(db, i), "\\");\r
383                 for(j = 2; j < m; j += 2)\r
384                         fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n"));\r
385         }\r
386         fclose(fh);\r
387 }\r
388 \r
389 void db_close(float db)\r
390 {\r
391         buf_del(db);\r
392 }\r
393 \r
394 string db_get(float db, string pKey)\r
395 {\r
396         float h;\r
397         h = mod(crc16(FALSE, pKey), DB_BUCKETS);\r
398         return uri_unescape(infoget(bufstr_get(db, h), pKey));\r
399 }\r
400 \r
401 void db_put(float db, string pKey, string pValue)\r
402 {\r
403         float h;\r
404         h = mod(crc16(FALSE, pKey), DB_BUCKETS);\r
405         bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue)));\r
406 }\r
407 \r
408 void db_test()\r
409 {\r
410         float db, i;\r
411         print("LOAD...\n");\r
412         db = db_load("foo.db");\r
413         print("LOADED. FILL...\n");\r
414         for(i = 0; i < DB_BUCKETS; ++i)\r
415                 db_put(db, ftos(random()), "X");\r
416         print("FILLED. SAVE...\n");\r
417         db_save(db, "foo.db");\r
418         print("SAVED. CLOSE...\n");\r
419         db_close(db);\r
420         print("CLOSED.\n");\r
421 }\r
422 \r
423 // Multiline text file buffers\r
424 float buf_load(string pFilename)\r
425 {\r
426         float buf, fh, i;\r
427         string l;\r
428         buf = buf_create();\r
429         if(buf < 0)\r
430                 return -1;\r
431         fh = fopen(pFilename, FILE_READ);\r
432         if(fh < 0)\r
433                 return buf;\r
434         i = 0;\r
435         while((l = fgets(fh)))\r
436         {\r
437                 bufstr_set(buf, i, l);\r
438                 ++i;\r
439         }\r
440         fclose(fh);\r
441         return buf;\r
442 }\r
443 \r
444 void buf_save(float buf, string pFilename)\r
445 {\r
446         float fh, i, n;\r
447         fh = fopen(pFilename, FILE_WRITE);\r
448         if(fh < 0)\r
449                 error(strcat("Can't write buf to ", pFilename));\r
450         n = buf_getsize(buf);\r
451         for(i = 0; i < n; ++i)\r
452                 fputs(fh, strcat(bufstr_get(buf, i), "\n"));\r
453         fclose(fh);\r
454 }\r
455 \r
456 string GametypeNameFromType(float g)\r
457 {\r
458         if      (g == GAME_DEATHMATCH) return "dm";\r
459         else if (g == GAME_TEAM_DEATHMATCH) return "tdm";\r
460         else if (g == GAME_DOMINATION) return "dom";\r
461         else if (g == GAME_CTF) return "ctf";\r
462         else if (g == GAME_LMS) return "lms";\r
463         else if (g == GAME_ARENA) return "arena";\r
464         else if (g == GAME_CA) return "ca";\r
465         else if (g == GAME_KEYHUNT) return "kh";\r
466         else if (g == GAME_ONSLAUGHT) return "ons";\r
467         else if (g == GAME_ASSAULT) return "as";\r
468         else if (g == GAME_RACE) return "rc";\r
469         else if (g == GAME_CTS) return "cts";\r
470         return "dm";\r
471 }\r
472 \r
473 string mmsss(float tenths)\r
474 {\r
475         float minutes;\r
476         string s;\r
477         tenths = floor(tenths + 0.5);\r
478         minutes = floor(tenths / 600);\r
479         tenths -= minutes * 600;\r
480         s = ftos(1000 + tenths);\r
481         return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1));\r
482 }\r
483 \r
484 string mmssss(float hundredths)\r
485 {\r
486         float minutes;\r
487         string s;\r
488         hundredths = floor(hundredths + 0.5);\r
489         minutes = floor(hundredths / 6000);\r
490         hundredths -= minutes * 6000;\r
491         s = ftos(10000 + hundredths);\r
492         return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 2));\r
493 }\r
494 \r
495 string ScoreString(float pFlags, float pValue)\r
496 {\r
497         string valstr;\r
498         float l;\r
499 \r
500         pValue = floor(pValue + 0.5); // round\r
501 \r
502         if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME)))\r
503                 valstr = "";\r
504         else if(pFlags & SFL_RANK)\r
505         {\r
506                 valstr = ftos(pValue);\r
507                 l = strlen(valstr);\r
508                 if((l >= 2) && (substring(valstr, l - 2, 1) == "1"))\r
509                         valstr = strcat(valstr, "th");\r
510                 else if(substring(valstr, l - 1, 1) == "1")\r
511                         valstr = strcat(valstr, "st");\r
512                 else if(substring(valstr, l - 1, 1) == "2")\r
513                         valstr = strcat(valstr, "nd");\r
514                 else if(substring(valstr, l - 1, 1) == "3")\r
515                         valstr = strcat(valstr, "rd");\r
516                 else\r
517                         valstr = strcat(valstr, "th");\r
518         }\r
519         else if(pFlags & SFL_TIME)\r
520                 valstr = TIME_ENCODED_TOSTRING(pValue);\r
521         else\r
522                 valstr = ftos(pValue);\r
523         \r
524         return valstr;\r
525 }\r
526 \r
527 vector cross(vector a, vector b)\r
528 {\r
529         return\r
530                 '1 0 0' * (a_y * b_z - a_z * b_y)\r
531         +       '0 1 0' * (a_z * b_x - a_x * b_z)\r
532         +       '0 0 1' * (a_x * b_y - a_y * b_x);\r
533 }\r
534 \r
535 // compressed vector format:\r
536 // like MD3, just even shorter\r
537 //   4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90\r
538 //   5 bit yaw (32 angles), 0=0, 8=90, 16=180, 24=270\r
539 //   7 bit length (logarithmic encoding), 1/8 .. about 7844\r
540 //     length = 2^(length_encoded/8) / 8\r
541 // if pitch is 90, yaw does nothing and therefore indicates the sign (yaw is then either 11111 or 11110); 11111 is pointing DOWN\r
542 // thus, valid values are from 0000.11110.0000000 to 1111.11111.1111111\r
543 // the special value 0 indicates the zero vector\r
544 \r
545 float lengthLogTable[128];\r
546 \r
547 float invertLengthLog(float x)\r
548 {\r
549         float l, r, m, lerr, rerr;\r
550 \r
551         if(x >= lengthLogTable[127])\r
552                 return 127;\r
553         if(x <= lengthLogTable[0])\r
554                 return 0;\r
555 \r
556         l = 0;\r
557         r = 127;\r
558 \r
559         while(r - l > 1)\r
560         {\r
561                 m = floor((l + r) / 2);\r
562                 if(lengthLogTable[m] < x)\r
563                         l = m;\r
564                 else\r
565                         r = m;\r
566         }\r
567 \r
568         // now: r is >=, l is <\r
569         lerr = (x - lengthLogTable[l]);\r
570         rerr = (lengthLogTable[r] - x);\r
571         if(lerr < rerr)\r
572                 return l;\r
573         return r;\r
574 }\r
575 \r
576 vector decompressShortVector(float data)\r
577 {\r
578         vector out;\r
579         float pitch, yaw, len;\r
580         if(data == 0)\r
581                 return '0 0 0';\r
582         pitch = (data & 0xF000) / 0x1000;\r
583         yaw =   (data & 0x0F80) / 0x80;\r
584         len =   (data & 0x007F);\r
585 \r
586         //print("\ndecompress: pitch ", ftos(pitch)); print("yaw ", ftos(yaw)); print("len ", ftos(len), "\n");\r
587 \r
588         if(pitch == 0)\r
589         {\r
590                 out_x = 0;\r
591                 out_y = 0;\r
592                 if(yaw == 31)\r
593                         out_z = -1;\r
594                 else\r
595                         out_z = +1;\r
596         }\r
597         else\r
598         {\r
599                 yaw   = .19634954084936207740 * yaw;\r
600                 pitch = .19634954084936207740 * pitch - 1.57079632679489661922;\r
601                 out_x = cos(yaw) *  cos(pitch);\r
602                 out_y = sin(yaw) *  cos(pitch);\r
603                 out_z =            -sin(pitch);\r
604         }\r
605 \r
606         //print("decompressed: ", vtos(out), "\n");\r
607 \r
608         return out * lengthLogTable[len];\r
609 }\r
610 \r
611 float compressShortVector(vector vec)\r
612 {\r
613         vector ang;\r
614         float pitch, yaw, len;\r
615         if(vlen(vec) == 0)\r
616                 return 0;\r
617         //print("compress: ", vtos(vec), "\n");\r
618         ang = vectoangles(vec);\r
619         ang_x = -ang_x;\r
620         if(ang_x < -90)\r
621                 ang_x += 360;\r
622         if(ang_x < -90 && ang_x > +90)\r
623                 error("BOGUS vectoangles");\r
624         //print("angles: ", vtos(ang), "\n");\r
625 \r
626         pitch = floor(0.5 + (ang_x + 90) * 16 / 180) & 15; // -90..90 to 0..14\r
627         if(pitch == 0)\r
628         {\r
629                 if(vec_z < 0)\r
630                         yaw = 31;\r
631                 else\r
632                         yaw = 30;\r
633         }\r
634         else\r
635                 yaw = floor(0.5 + ang_y * 32 / 360)          & 31; // 0..360 to 0..32\r
636         len = invertLengthLog(vlen(vec));\r
637 \r
638         //print("compressed: pitch ", ftos(pitch)); print("yaw ", ftos(yaw)); print("len ", ftos(len), "\n");\r
639 \r
640         return (pitch * 0x1000) + (yaw * 0x80) + len;\r
641 }\r
642 \r
643 void compressShortVector_init()\r
644 {\r
645         float l, f, i;\r
646         l = 1;\r
647         f = pow(2, 1/8);\r
648         for(i = 0; i < 128; ++i)\r
649         {\r
650                 lengthLogTable[i] = l;\r
651                 l *= f;\r
652         }\r
653 \r
654         if(cvar("developer"))\r
655         {\r
656                 print("Verifying vector compression table...\n");\r
657                 for(i = 0x0F00; i < 0xFFFF; ++i)\r
658                         if(i != compressShortVector(decompressShortVector(i)))\r
659                         {\r
660                                 print("BROKEN vector compression: ", ftos(i));\r
661                                 print(" -> ", vtos(decompressShortVector(i)));\r
662                                 print(" -> ", ftos(compressShortVector(decompressShortVector(i))));\r
663                                 print("\n");\r
664                                 error("b0rk");\r
665                         }\r
666                 print("Done.\n");\r
667         }\r
668 }\r
669 \r
670 #ifndef MENUQC\r
671 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)\r
672 {\r
673         traceline(v0, v0 + dvx, TRUE, forent); if(trace_fraction < 1) return 0;\r
674         traceline(v0, v0 + dvy, TRUE, forent); if(trace_fraction < 1) return 0;\r
675         traceline(v0, v0 + dvz, TRUE, forent); if(trace_fraction < 1) return 0;\r
676         traceline(v0 + dvx, v0 + dvx + dvy, TRUE, forent); if(trace_fraction < 1) return 0;\r
677         traceline(v0 + dvx, v0 + dvx + dvz, TRUE, forent); if(trace_fraction < 1) return 0;\r
678         traceline(v0 + dvy, v0 + dvy + dvx, TRUE, forent); if(trace_fraction < 1) return 0;\r
679         traceline(v0 + dvy, v0 + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;\r
680         traceline(v0 + dvz, v0 + dvz + dvx, TRUE, forent); if(trace_fraction < 1) return 0;\r
681         traceline(v0 + dvz, v0 + dvz + dvy, TRUE, forent); if(trace_fraction < 1) return 0;\r
682         traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;\r
683         traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;\r
684         traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;\r
685         return 1;\r
686 }\r
687 #endif\r
688 \r
689 string fixPriorityList(string order, float from, float to, float subtract, float complete)\r
690 {\r
691         string neworder;\r
692         float i, n, w;\r
693 \r
694         n = tokenize_console(order);\r
695         neworder = "";\r
696         for(i = 0; i < n; ++i)\r
697         {\r
698                 w = stof(argv(i));\r
699                 if(w == floor(w))\r
700                 {\r
701                         if(w >= from && w <= to)\r
702                                 neworder = strcat(neworder, ftos(w), " ");\r
703                         else\r
704                         {\r
705                                 w -= subtract;\r
706                                 if(w >= from && w <= to)\r
707                                         neworder = strcat(neworder, ftos(w), " ");\r
708                         }\r
709                 }\r
710         }\r
711 \r
712         if(complete)\r
713         {\r
714                 n = tokenize_console(neworder);\r
715                 for(w = to; w >= from; --w)\r
716                 {\r
717                         for(i = 0; i < n; ++i)\r
718                                 if(stof(argv(i)) == w)\r
719                                         break;\r
720                         if(i == n) // not found\r
721                                 neworder = strcat(neworder, ftos(w), " ");\r
722                 }\r
723         }\r
724         \r
725         return substring(neworder, 0, strlen(neworder) - 1);\r
726 }\r
727 \r
728 string mapPriorityList(string order, string(string) mapfunc)\r
729 {\r
730         string neworder;\r
731         float i, n;\r
732 \r
733         n = tokenize_console(order);\r
734         neworder = "";\r
735         for(i = 0; i < n; ++i)\r
736                 neworder = strcat(neworder, mapfunc(argv(i)), " ");\r
737         \r
738         return substring(neworder, 0, strlen(neworder) - 1);\r
739 }\r
740 \r
741 string swapInPriorityList(string order, float i, float j)\r
742 {\r
743         string s;\r
744         float w, n;\r
745 \r
746         n = tokenize_console(order);\r
747 \r
748         if(i >= 0 && i < n && j >= 0 && j < n && i != j)\r
749         {\r
750                 s = "";\r
751                 for(w = 0; w < n; ++w)\r
752                 {\r
753                         if(w == i)\r
754                                 s = strcat(s, argv(j), " ");\r
755                         else if(w == j)\r
756                                 s = strcat(s, argv(i), " ");\r
757                         else\r
758                                 s = strcat(s, argv(w), " ");\r
759                 }\r
760                 return substring(s, 0, strlen(s) - 1);\r
761         }\r
762         \r
763         return order;\r
764 }\r
765 \r
766 float cvar_value_issafe(string s)\r
767 {\r
768         if(strstrofs(s, "\"", 0) >= 0)\r
769                 return 0;\r
770         if(strstrofs(s, "\\", 0) >= 0)\r
771                 return 0;\r
772         if(strstrofs(s, ";", 0) >= 0)\r
773                 return 0;\r
774         if(strstrofs(s, "$", 0) >= 0)\r
775                 return 0;\r
776         if(strstrofs(s, "\r", 0) >= 0)\r
777                 return 0;\r
778         if(strstrofs(s, "\n", 0) >= 0)\r
779                 return 0;\r
780         return 1;\r
781 }\r
782 \r
783 #ifndef MENUQC\r
784 void get_mi_min_max(float mode)\r
785 {\r
786         vector mi, ma;\r
787 \r
788         if(mi_shortname)\r
789                 strunzone(mi_shortname);\r
790         mi_shortname = mapname;\r
791         if(!strcasecmp(substring(mi_shortname, 0, 5), "maps/"))\r
792                 mi_shortname = substring(mi_shortname, 5, strlen(mi_shortname) - 5);\r
793         if(!strcasecmp(substring(mi_shortname, strlen(mi_shortname) - 4, 4), ".bsp"))\r
794                 mi_shortname = substring(mi_shortname, 0, strlen(mi_shortname) - 4);\r
795         mi_shortname = strzone(mi_shortname);\r
796 \r
797 #ifdef CSQC\r
798         mi = world.mins;\r
799         ma = world.maxs;\r
800 #else\r
801         mi = world.absmin;\r
802         ma = world.absmax;\r
803 #endif\r
804 \r
805         mi_min = mi;\r
806         mi_max = ma;\r
807         MapInfo_Get_ByName(mi_shortname, 0, 0);\r
808         if(MapInfo_Map_mins_x < MapInfo_Map_maxs_x)\r
809         {\r
810                 mi_min = MapInfo_Map_mins;\r
811                 mi_max = MapInfo_Map_maxs;\r
812         }\r
813         else\r
814         {\r
815                 // not specified\r
816                 if(mode)\r
817                 {\r
818                         // be clever\r
819                         tracebox('1 0 0' * mi_x,\r
820                                          '0 1 0' * mi_y + '0 0 1' * mi_z,\r
821                                          '0 1 0' * ma_y + '0 0 1' * ma_z,\r
822                                          '1 0 0' * ma_x,\r
823                                          MOVE_WORLDONLY,\r
824                                          world);\r
825                         if(!trace_startsolid)\r
826                                 mi_min_x = trace_endpos_x;\r
827 \r
828                         tracebox('0 1 0' * mi_y,\r
829                                          '1 0 0' * mi_x + '0 0 1' * mi_z,\r
830                                          '1 0 0' * ma_x + '0 0 1' * ma_z,\r
831                                          '0 1 0' * ma_y,\r
832                                          MOVE_WORLDONLY,\r
833                                          world);\r
834                         if(!trace_startsolid)\r
835                                 mi_min_y = trace_endpos_y;\r
836 \r
837                         tracebox('0 0 1' * mi_z,\r
838                                          '1 0 0' * mi_x + '0 1 0' * mi_y,\r
839                                          '1 0 0' * ma_x + '0 1 0' * ma_y,\r
840                                          '0 0 1' * ma_z,\r
841                                          MOVE_WORLDONLY,\r
842                                          world);\r
843                         if(!trace_startsolid)\r
844                                 mi_min_z = trace_endpos_z;\r
845 \r
846                         tracebox('1 0 0' * ma_x,\r
847                                          '0 1 0' * mi_y + '0 0 1' * mi_z,\r
848                                          '0 1 0' * ma_y + '0 0 1' * ma_z,\r
849                                          '1 0 0' * mi_x,\r
850                                          MOVE_WORLDONLY,\r
851                                          world);\r
852                         if(!trace_startsolid)\r
853                                 mi_max_x = trace_endpos_x;\r
854 \r
855                         tracebox('0 1 0' * ma_y,\r
856                                          '1 0 0' * mi_x + '0 0 1' * mi_z,\r
857                                          '1 0 0' * ma_x + '0 0 1' * ma_z,\r
858                                          '0 1 0' * mi_y,\r
859                                          MOVE_WORLDONLY,\r
860                                          world);\r
861                         if(!trace_startsolid)\r
862                                 mi_max_y = trace_endpos_y;\r
863 \r
864                         tracebox('0 0 1' * ma_z,\r
865                                          '1 0 0' * mi_x + '0 1 0' * mi_y,\r
866                                          '1 0 0' * ma_x + '0 1 0' * ma_y,\r
867                                          '0 0 1' * mi_z,\r
868                                          MOVE_WORLDONLY,\r
869                                          world);\r
870                         if(!trace_startsolid)\r
871                                 mi_max_z = trace_endpos_z;\r
872                 }\r
873         }\r
874 }\r
875 \r
876 void get_mi_min_max_texcoords(float mode)\r
877 {\r
878         vector extend;\r
879 \r
880         get_mi_min_max(mode);\r
881 \r
882         mi_picmin = mi_min;\r
883         mi_picmax = mi_max;\r
884 \r
885         // extend mi_picmax to get a square aspect ratio\r
886         // center the map in that area\r
887         extend = mi_picmax - mi_picmin;\r
888         if(extend_y > extend_x)\r
889         {\r
890                 mi_picmin_x -= (extend_y - extend_x) * 0.5;\r
891                 mi_picmax_x += (extend_y - extend_x) * 0.5;\r
892         }\r
893         else\r
894         {\r
895                 mi_picmin_y -= (extend_x - extend_y) * 0.5;\r
896                 mi_picmax_y += (extend_x - extend_y) * 0.5;\r
897         }\r
898 \r
899         // add another some percent\r
900         extend = (mi_picmax - mi_picmin) * (1 / 64.0);\r
901         mi_picmin -= extend;\r
902         mi_picmax += extend;\r
903 \r
904         // calculate the texcoords\r
905         mi_pictexcoord0 = mi_pictexcoord1 = mi_pictexcoord2 = mi_pictexcoord3 = '0 0 0';\r
906         // first the two corners of the origin\r
907         mi_pictexcoord0_x = (mi_min_x - mi_picmin_x) / (mi_picmax_x - mi_picmin_x);\r
908         mi_pictexcoord0_y = (mi_min_y - mi_picmin_y) / (mi_picmax_y - mi_picmin_y);\r
909         mi_pictexcoord2_x = (mi_max_x - mi_picmin_x) / (mi_picmax_x - mi_picmin_x);\r
910         mi_pictexcoord2_y = (mi_max_y - mi_picmin_y) / (mi_picmax_y - mi_picmin_y);\r
911         // then the other corners\r
912         mi_pictexcoord1_x = mi_pictexcoord0_x;\r
913         mi_pictexcoord1_y = mi_pictexcoord2_y;\r
914         mi_pictexcoord3_x = mi_pictexcoord2_x;\r
915         mi_pictexcoord3_y = mi_pictexcoord0_y;\r
916 }\r
917 #endif\r
918 \r
919 #ifdef CSQC\r
920 void cvar_settemp(string pKey, string pValue)\r
921 {\r
922         error("cvar_settemp called from CSQC - use cvar_clientsettemp instead!");\r
923 }\r
924 void cvar_settemp_restore()\r
925 {\r
926         error("cvar_settemp_restore called from CSQC - use cvar_clientsettemp instead!");\r
927 }\r
928 #else\r
929 void cvar_settemp(string pKey, string pValue)\r
930 {\r
931         float i;\r
932         string settemp_var;\r
933         if(cvar_string(pKey) == pValue)\r
934                 return;\r
935         i = cvar("settemp_idx");\r
936         cvar_set("settemp_idx", ftos(i+1));\r
937         settemp_var = strcat("_settemp_x", ftos(i));\r
938 #ifdef MENUQC\r
939         registercvar(settemp_var, "", 0);\r
940 #else\r
941         registercvar(settemp_var, "");\r
942 #endif\r
943         cvar_set("settemp_list", strcat("1 ", pKey, " ", settemp_var, " ", cvar_string("settemp_list")));\r
944         cvar_set(settemp_var, cvar_string(pKey));\r
945         cvar_set(pKey, pValue);\r
946 }\r
947 \r
948 void cvar_settemp_restore()\r
949 {\r
950         // undo what cvar_settemp did\r
951         float n, i;\r
952         n = tokenize_console(cvar_string("settemp_list"));\r
953         for(i = 0; i < n - 3; i += 3)\r
954                 cvar_set(argv(i + 1), cvar_string(argv(i + 2)));\r
955         cvar_set("settemp_list", "0");\r
956 }\r
957 #endif\r
958 \r
959 float almost_equals(float a, float b)\r
960 {\r
961         float eps;\r
962         eps = (max(a, -a) + max(b, -b)) * 0.001;\r
963         if(a - b < eps && b - a < eps)\r
964                 return TRUE;\r
965         return FALSE;\r
966 }\r
967 \r
968 float almost_in_bounds(float a, float b, float c)\r
969 {\r
970         float eps;\r
971         eps = (max(a, -a) + max(c, -c)) * 0.001;\r
972         return b == median(a - eps, b, c + eps);\r
973 }\r
974 \r
975 float power2of(float e)\r
976 {\r
977         return pow(2, e);\r
978 }\r
979 float log2of(float x)\r
980 {\r
981         // NOTE: generated code\r
982         if(x > 2048)\r
983                 if(x > 131072)\r
984                         if(x > 1048576)\r
985                                 if(x > 4194304)\r
986                                         return 23;\r
987                                 else\r
988                                         if(x > 2097152)\r
989                                                 return 22;\r
990                                         else\r
991                                                 return 21;\r
992                         else\r
993                                 if(x > 524288)\r
994                                         return 20;\r
995                                 else\r
996                                         if(x > 262144)\r
997                                                 return 19;\r
998                                         else\r
999                                                 return 18;\r
1000                 else\r
1001                         if(x > 16384)\r
1002                                 if(x > 65536)\r
1003                                         return 17;\r
1004                                 else\r
1005                                         if(x > 32768)\r
1006                                                 return 16;\r
1007                                         else\r
1008                                                 return 15;\r
1009                         else\r
1010                                 if(x > 8192)\r
1011                                         return 14;\r
1012                                 else\r
1013                                         if(x > 4096)\r
1014                                                 return 13;\r
1015                                         else\r
1016                                                 return 12;\r
1017         else\r
1018                 if(x > 32)\r
1019                         if(x > 256)\r
1020                                 if(x > 1024)\r
1021                                         return 11;\r
1022                                 else\r
1023                                         if(x > 512)\r
1024                                                 return 10;\r
1025                                         else\r
1026                                                 return 9;\r
1027                         else\r
1028                                 if(x > 128)\r
1029                                         return 8;\r
1030                                 else\r
1031                                         if(x > 64)\r
1032                                                 return 7;\r
1033                                         else\r
1034                                                 return 6;\r
1035                 else\r
1036                         if(x > 4)\r
1037                                 if(x > 16)\r
1038                                         return 5;\r
1039                                 else\r
1040                                         if(x > 8)\r
1041                                                 return 4;\r
1042                                         else\r
1043                                                 return 3;\r
1044                         else\r
1045                                 if(x > 2)\r
1046                                         return 2;\r
1047                                 else\r
1048                                         if(x > 1)\r
1049                                                 return 1;\r
1050                                         else\r
1051                                                 return 0;\r
1052 }\r
1053 \r
1054 float rgb_mi_ma_to_hue(vector rgb, float mi, float ma)\r
1055 {\r
1056         if(mi == ma)\r
1057                 return 0;\r
1058         else if(ma == rgb_x)\r
1059         {\r
1060                 if(rgb_y >= rgb_z)\r
1061                         return (rgb_y - rgb_z) / (ma - mi);\r
1062                 else\r
1063                         return (rgb_y - rgb_z) / (ma - mi) + 6;\r
1064         }\r
1065         else if(ma == rgb_y)\r
1066                 return (rgb_z - rgb_x) / (ma - mi) + 2;\r
1067         else // if(ma == rgb_z)\r
1068                 return (rgb_x - rgb_y) / (ma - mi) + 4;\r
1069 }\r
1070 \r
1071 vector hue_mi_ma_to_rgb(float hue, float mi, float ma)\r
1072 {\r
1073         vector rgb;\r
1074 \r
1075         hue -= 6 * floor(hue / 6);\r
1076 \r
1077         //else if(ma == rgb_x)\r
1078         //      hue = 60 * (rgb_y - rgb_z) / (ma - mi);\r
1079         if(hue <= 1)\r
1080         {\r
1081                 rgb_x = ma;\r
1082                 rgb_y = hue * (ma - mi) + mi;\r
1083                 rgb_z = mi;\r
1084         }\r
1085         //else if(ma == rgb_y)\r
1086         //      hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120;\r
1087         else if(hue <= 2)\r
1088         {\r
1089                 rgb_x = (2 - hue) * (ma - mi) + mi;\r
1090                 rgb_y = ma;\r
1091                 rgb_z = mi;\r
1092         }\r
1093         else if(hue <= 3)\r
1094         {\r
1095                 rgb_x = mi;\r
1096                 rgb_y = ma;\r
1097                 rgb_z = (hue - 2) * (ma - mi) + mi;\r
1098         }\r
1099         //else // if(ma == rgb_z)\r
1100         //      hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240;\r
1101         else if(hue <= 4)\r
1102         {\r
1103                 rgb_x = mi;\r
1104                 rgb_y = (4 - hue) * (ma - mi) + mi;\r
1105                 rgb_z = ma;\r
1106         }\r
1107         else if(hue <= 5)\r
1108         {\r
1109                 rgb_x = (hue - 4) * (ma - mi) + mi;\r
1110                 rgb_y = mi;\r
1111                 rgb_z = ma;\r
1112         }\r
1113         //else if(ma == rgb_x)\r
1114         //      hue = 60 * (rgb_y - rgb_z) / (ma - mi);\r
1115         else // if(hue <= 6)\r
1116         {\r
1117                 rgb_x = ma;\r
1118                 rgb_y = mi;\r
1119                 rgb_z = (6 - hue) * (ma - mi) + mi;\r
1120         }\r
1121 \r
1122         return rgb;\r
1123 }\r
1124 \r
1125 vector rgb_to_hsv(vector rgb)\r
1126 {\r
1127         float mi, ma;\r
1128         vector hsv;\r
1129 \r
1130         mi = min3(rgb_x, rgb_y, rgb_z);\r
1131         ma = max3(rgb_x, rgb_y, rgb_z);\r
1132 \r
1133         hsv_x = rgb_mi_ma_to_hue(rgb, mi, ma);\r
1134         hsv_z = ma;\r
1135 \r
1136         if(ma == 0)\r
1137                 hsv_y = 0;\r
1138         else\r
1139                 hsv_y = 1 - mi/ma;\r
1140         \r
1141         return hsv;\r
1142 }\r
1143 \r
1144 vector hsv_to_rgb(vector hsv)\r
1145 {\r
1146         return hue_mi_ma_to_rgb(hsv_x, hsv_z * (1 - hsv_y), hsv_z);\r
1147 }\r
1148 \r
1149 vector rgb_to_hsl(vector rgb)\r
1150 {\r
1151         float mi, ma;\r
1152         vector hsl;\r
1153 \r
1154         mi = min3(rgb_x, rgb_y, rgb_z);\r
1155         ma = max3(rgb_x, rgb_y, rgb_z);\r
1156 \r
1157         hsl_x = rgb_mi_ma_to_hue(rgb, mi, ma);\r
1158         \r
1159         hsl_z = 0.5 * (mi + ma);\r
1160         if(mi == ma)\r
1161                 hsl_y = 0;\r
1162         else if(hsl_z <= 0.5)\r
1163                 hsl_y = (ma - mi) / (2*hsl_z);\r
1164         else // if(hsl_z > 0.5)\r
1165                 hsl_y = (ma - mi) / (2 - 2*hsl_z);\r
1166         \r
1167         return hsl;\r
1168 }\r
1169 \r
1170 vector hsl_to_rgb(vector hsl)\r
1171 {\r
1172         float mi, ma, maminusmi;\r
1173 \r
1174         if(hsl_z <= 0.5)\r
1175                 maminusmi = hsl_y * 2 * hsl_z;\r
1176         else\r
1177                 maminusmi = hsl_y * (2 - 2 * hsl_z);\r
1178         \r
1179         // hsl_z     = 0.5 * mi + 0.5 * ma\r
1180         // maminusmi =     - mi +       ma\r
1181         mi = hsl_z - 0.5 * maminusmi;\r
1182         ma = hsl_z + 0.5 * maminusmi;\r
1183 \r
1184         return hue_mi_ma_to_rgb(hsl_x, mi, ma);\r
1185 }\r
1186 \r
1187 string rgb_to_hexcolor(vector rgb)\r
1188 {\r
1189         return\r
1190                 strcat(\r
1191                         "^x",\r
1192                         DEC_TO_HEXDIGIT(floor(rgb_x * 15 + 0.5)),\r
1193                         DEC_TO_HEXDIGIT(floor(rgb_y * 15 + 0.5)),\r
1194                         DEC_TO_HEXDIGIT(floor(rgb_z * 15 + 0.5))\r
1195                 );\r
1196 }\r
1197 \r
1198 // requires that m2>m1 in all coordinates, and that m4>m3\r
1199 float boxesoverlap(vector m1, vector m2, vector m3, vector m4) {return m2_x >= m3_x && m1_x <= m4_x && m2_y >= m3_y && m1_y <= m4_y && m2_z >= m3_z && m1_z <= m4_z;};\r
1200 \r
1201 // requires the same, but is a stronger condition\r
1202 float boxinsidebox(vector smins, vector smaxs, vector bmins, vector bmaxs) {return smins_x >= bmins_x && smaxs_x <= bmaxs_x && smins_y >= bmins_y && smaxs_y <= bmaxs_y && smins_z >= bmins_z && smaxs_z <= bmaxs_z;};\r
1203 \r
1204 #ifndef MENUQC\r
1205 #endif\r
1206 \r
1207 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)\r
1208 {\r
1209         float ICanHasKallerz;\r
1210 \r
1211         // detect color codes support in the width function\r
1212         ICanHasKallerz = (w("^7", theSize) == 0);\r
1213 \r
1214         // STOP.\r
1215         // The following function is SLOW.\r
1216         // For your safety and for the protection of those around you...\r
1217         // DO NOT CALL THIS AT HOME.\r
1218         // No really, don't.\r
1219         if(w(theText, theSize) <= maxWidth)\r
1220                 return strlen(theText); // yeah!\r
1221 \r
1222         // binary search for right place to cut string\r
1223         float ch;\r
1224         float left, right, middle; // this always works\r
1225         left = 0;\r
1226         right = strlen(theText); // this always fails\r
1227         do\r
1228         {\r
1229                 middle = floor((left + right) / 2);\r
1230                 if(w(substring(theText, 0, middle), theSize) <= maxWidth)\r
1231                         left = middle;\r
1232                 else\r
1233                         right = middle;\r
1234         }\r
1235         while(left < right - 1);\r
1236 \r
1237         if(ICanHasKallerz)\r
1238         {\r
1239                 // NOTE: when color codes are involved, this binary search is,\r
1240                 // mathematically, BROKEN. However, it is obviously guaranteed to\r
1241                 // terminate, as the range still halves each time - but nevertheless, it is\r
1242                 // guaranteed that it finds ONE valid cutoff place (where "left" is in\r
1243                 // range, and "right" is outside).\r
1244                 \r
1245                 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)\r
1246                 // and decrease left on the basis of the chars detected of the truncated tag\r
1247                 // Even if the ^xrgb tag is not complete/correct, left is decreased\r
1248                 // (sometimes too much but with a correct result)\r
1249                 // it fixes also ^[0-9]\r
1250                 while(left >= 1 && substring(theText, left-1, 1) == "^")\r
1251                         left-=1;\r
1252 \r
1253                 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/\r
1254                         left-=2;\r
1255                 else if (left >= 3 && substring(theText, left-3, 2) == "^x")\r
1256                         {\r
1257                                 ch = str2chr(theText, left-1);\r
1258                                 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/\r
1259                                         left-=3;\r
1260                         }\r
1261                 else if (left >= 4 && substring(theText, left-4, 2) == "^x")\r
1262                         {\r
1263                                 ch = str2chr(theText, left-2);\r
1264                                 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )\r
1265                                 {\r
1266                                         ch = str2chr(theText, left-1);\r
1267                                         if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/\r
1268                                                 left-=4;\r
1269                                 }\r
1270                         }\r
1271         }\r
1272         \r
1273         return left;\r
1274 }\r
1275 \r
1276 float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w)\r
1277 {\r
1278         float ICanHasKallerz;\r
1279 \r
1280         // detect color codes support in the width function\r
1281         ICanHasKallerz = (w("^7") == 0);\r
1282 \r
1283         // STOP.\r
1284         // The following function is SLOW.\r
1285         // For your safety and for the protection of those around you...\r
1286         // DO NOT CALL THIS AT HOME.\r
1287         // No really, don't.\r
1288         if(w(theText) <= maxWidth)\r
1289                 return strlen(theText); // yeah!\r
1290 \r
1291         // binary search for right place to cut string\r
1292         float ch;\r
1293         float left, right, middle; // this always works\r
1294         left = 0;\r
1295         right = strlen(theText); // this always fails\r
1296         do\r
1297         {\r
1298                 middle = floor((left + right) / 2);\r
1299                 if(w(substring(theText, 0, middle)) <= maxWidth)\r
1300                         left = middle;\r
1301                 else\r
1302                         right = middle;\r
1303         }\r
1304         while(left < right - 1);\r
1305 \r
1306         if(ICanHasKallerz)\r
1307         {\r
1308                 // NOTE: when color codes are involved, this binary search is,\r
1309                 // mathematically, BROKEN. However, it is obviously guaranteed to\r
1310                 // terminate, as the range still halves each time - but nevertheless, it is\r
1311                 // guaranteed that it finds ONE valid cutoff place (where "left" is in\r
1312                 // range, and "right" is outside).\r
1313                 \r
1314                 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)\r
1315                 // and decrease left on the basis of the chars detected of the truncated tag\r
1316                 // Even if the ^xrgb tag is not complete/correct, left is decreased\r
1317                 // (sometimes too much but with a correct result)\r
1318                 // it fixes also ^[0-9]\r
1319                 while(left >= 1 && substring(theText, left-1, 1) == "^")\r
1320                         left-=1;\r
1321 \r
1322                 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/\r
1323                         left-=2;\r
1324                 else if (left >= 3 && substring(theText, left-3, 2) == "^x")\r
1325                         {\r
1326                                 ch = str2chr(theText, left-1);\r
1327                                 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/\r
1328                                         left-=3;\r
1329                         }\r
1330                 else if (left >= 4 && substring(theText, left-4, 2) == "^x")\r
1331                         {\r
1332                                 ch = str2chr(theText, left-2);\r
1333                                 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )\r
1334                                 {\r
1335                                         ch = str2chr(theText, left-1);\r
1336                                         if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/\r
1337                                                 left-=4;\r
1338                                 }\r
1339                         }\r
1340         }\r
1341         \r
1342         return left;\r
1343 }\r
1344 \r
1345 string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)\r
1346 {\r
1347         float cantake;\r
1348         float take;\r
1349         string s;\r
1350 \r
1351         s = getWrappedLine_remaining;\r
1352 \r
1353         cantake = textLengthUpToWidth(s, w, theFontSize, tw);\r
1354         if(cantake > 0 && cantake < strlen(s))\r
1355         {\r
1356                 take = cantake - 1;\r
1357                 while(take > 0 && substring(s, take, 1) != " ")\r
1358                         --take;\r
1359                 if(take == 0)\r
1360                 {\r
1361                         getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);\r
1362                         if(getWrappedLine_remaining == "")\r
1363                                 getWrappedLine_remaining = string_null;\r
1364                         return substring(s, 0, cantake);\r
1365                 }\r
1366                 else\r
1367                 {\r
1368                         getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);\r
1369                         if(getWrappedLine_remaining == "")\r
1370                                 getWrappedLine_remaining = string_null;\r
1371                         return substring(s, 0, take);\r
1372                 }\r
1373         }\r
1374         else\r
1375         {\r
1376                 getWrappedLine_remaining = string_null;\r
1377                 return s;\r
1378         }\r
1379 }\r
1380 \r
1381 string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw)\r
1382 {\r
1383         float cantake;\r
1384         float take;\r
1385         string s;\r
1386 \r
1387         s = getWrappedLine_remaining;\r
1388 \r
1389         cantake = textLengthUpToLength(s, w, tw);\r
1390         if(cantake > 0 && cantake < strlen(s))\r
1391         {\r
1392                 take = cantake - 1;\r
1393                 while(take > 0 && substring(s, take, 1) != " ")\r
1394                         --take;\r
1395                 if(take == 0)\r
1396                 {\r
1397                         getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);\r
1398                         if(getWrappedLine_remaining == "")\r
1399                                 getWrappedLine_remaining = string_null;\r
1400                         return substring(s, 0, cantake);\r
1401                 }\r
1402                 else\r
1403                 {\r
1404                         getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);\r
1405                         if(getWrappedLine_remaining == "")\r
1406                                 getWrappedLine_remaining = string_null;\r
1407                         return substring(s, 0, take);\r
1408                 }\r
1409         }\r
1410         else\r
1411         {\r
1412                 getWrappedLine_remaining = string_null;\r
1413                 return s;\r
1414         }\r
1415 }\r
1416 \r
1417 string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)\r
1418 {\r
1419         if(tw(theText, theFontSize) <= maxWidth)\r
1420                 return theText;\r
1421         else\r
1422                 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("...", theFontSize), theFontSize, tw)), "...");\r
1423 }\r
1424 \r
1425 string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw)\r
1426 {\r
1427         if(tw(theText) <= maxWidth)\r
1428                 return theText;\r
1429         else\r
1430                 return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");\r
1431 }\r
1432 \r
1433 float isGametypeInFilter(float gt, float tp, string pattern)\r
1434 {\r
1435         string subpattern, subpattern2, subpattern3;\r
1436         subpattern = strcat(",", GametypeNameFromType(gt), ",");\r
1437         if(tp)\r
1438                 subpattern2 = ",teams,";\r
1439         else\r
1440                 subpattern2 = ",noteams,";\r
1441         if(gt == GAME_RACE || gt == GAME_CTS)\r
1442                 subpattern3 = ",race,";\r
1443         else\r
1444                 subpattern3 = string_null;\r
1445 \r
1446         if(substring(pattern, 0, 1) == "-")\r
1447         {\r
1448                 pattern = substring(pattern, 1, strlen(pattern) - 1);\r
1449                 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)\r
1450                         return 0;\r
1451                 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0)\r
1452                         return 0;\r
1453                 if(subpattern3 && strstrofs(strcat(",", pattern, ","), subpattern3, 0) >= 0)\r
1454                         return 0;\r
1455         }\r
1456         else\r
1457         {\r
1458                 if(substring(pattern, 0, 1) == "+")\r
1459                         pattern = substring(pattern, 1, strlen(pattern) - 1);\r
1460                 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)\r
1461                 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0)\r
1462                 if((!subpattern3) || strstrofs(strcat(",", pattern, ","), subpattern3, 0) < 0)\r
1463                         return 0;\r
1464         }\r
1465         return 1;\r
1466 }\r
1467 \r
1468 void shuffle(float n, swapfunc_t swap, entity pass)\r
1469 {\r
1470         float i, j;\r
1471         for(i = 1; i < n; ++i)\r
1472         {\r
1473                 // swap i-th item at a random position from 0 to i\r
1474                 // proof for even distribution:\r
1475                 //   n = 1: obvious\r
1476                 //   n -> n+1:\r
1477                 //     item n+1 gets at any position with chance 1/(n+1)\r
1478                 //     all others will get their 1/n chance reduced by factor n/(n+1)\r
1479                 //     to be on place n+1, their chance will be 1/(n+1)\r
1480                 //     1/n * n/(n+1) = 1/(n+1)\r
1481                 //     q.e.d.\r
1482                 j = floor(random() * (i + 1));\r
1483                 if(j != i)\r
1484                         swap(j, i, pass);\r
1485         }\r
1486 }\r
1487 \r
1488 string substring_range(string s, float b, float e)\r
1489 {\r
1490         return substring(s, b, e - b);\r
1491 }\r
1492 \r
1493 string swapwords(string str, float i, float j)\r
1494 {\r
1495         float n;\r
1496         string s1, s2, s3, s4, s5;\r
1497         float si, ei, sj, ej, s0, en;\r
1498         n = tokenizebyseparator(str, " "); // must match g_maplist processing in ShuffleMaplist and "shuffle"\r
1499         si = argv_start_index(i);\r
1500         sj = argv_start_index(j);\r
1501         ei = argv_end_index(i);\r
1502         ej = argv_end_index(j);\r
1503         s0 = argv_start_index(0);\r
1504         en = argv_end_index(n-1);\r
1505         s1 = substring_range(str, s0, si);\r
1506         s2 = substring_range(str, si, ei);\r
1507         s3 = substring_range(str, ei, sj);\r
1508         s4 = substring_range(str, sj, ej);\r
1509         s5 = substring_range(str, ej, en);\r
1510         return strcat(s1, s4, s3, s2, s5);\r
1511 }\r
1512 \r
1513 string _shufflewords_str;\r
1514 void _shufflewords_swapfunc(float i, float j, entity pass)\r
1515 {\r
1516         _shufflewords_str = swapwords(_shufflewords_str, i, j);\r
1517 }\r
1518 string shufflewords(string str)\r
1519 {\r
1520         float n;\r
1521         _shufflewords_str = str;\r
1522         n = tokenizebyseparator(str, " ");\r
1523         shuffle(n, _shufflewords_swapfunc, world);\r
1524         str = _shufflewords_str;\r
1525         _shufflewords_str = string_null;\r
1526         return str;\r
1527 }\r
1528 \r
1529 vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0\r
1530 {\r
1531         vector v;\r
1532         float D;\r
1533         v = '0 0 0';\r
1534         if(a == 0)\r
1535         {\r
1536                 if(b != 0)\r
1537                 {\r
1538                         v_x = v_y = -c / b;\r
1539                         v_z = 1;\r
1540                 }\r
1541                 else\r
1542                 {\r
1543                         if(c == 0)\r
1544                         {\r
1545                                 // actually, every number solves the equation!\r
1546                                 v_z = 1;\r
1547                         }\r
1548                 }\r
1549         }\r
1550         else\r
1551         {\r
1552                 D = b*b - 4*a*c;\r
1553                 if(D >= 0)\r
1554                 {\r
1555                         D = sqrt(D);\r
1556                         if(a > 0) // put the smaller solution first\r
1557                         {\r
1558                                 v_x = ((-b)-D) / (2*a);\r
1559                                 v_y = ((-b)+D) / (2*a);\r
1560                         }\r
1561                         else\r
1562                         {\r
1563                                 v_x = (-b+D) / (2*a);\r
1564                                 v_y = (-b-D) / (2*a);\r
1565                         }\r
1566                         v_z = 1;\r
1567                 }\r
1568                 else\r
1569                 {\r
1570                         // complex solutions!\r
1571                         D = sqrt(-D);\r
1572                         v_x = -b / (2*a);\r
1573                         if(a > 0)\r
1574                                 v_y =  D / (2*a);\r
1575                         else\r
1576                                 v_y = -D / (2*a);\r
1577                         v_z = 0;\r
1578                 }\r
1579         }\r
1580         return v;\r
1581 }\r
1582 \r
1583 \r
1584 float _unacceptable_compiler_bug_1_a(float b, float c) { return b == c; }\r
1585 float _unacceptable_compiler_bug_1_b() { return 1; }\r
1586 float _unacceptable_compiler_bug_1_c(float d) { return 2 * d; }\r
1587 float _unacceptable_compiler_bug_1_d() { return 1; }\r
1588 \r
1589 void check_unacceptable_compiler_bugs()\r
1590 {\r
1591         if(cvar("_allow_unacceptable_compiler_bugs"))\r
1592                 return;\r
1593         tokenize_console("foo bar");\r
1594         if(strcat(argv(0), substring("foo bar", 4, 7 - argv_start_index(1))) == "barbar")\r
1595                 error("fteqcc bug introduced with revision 3178 detected. Please upgrade fteqcc to a later revision, downgrade fteqcc to revision 3177, or pester Spike until he fixes it. You can set _allow_unacceptable_compiler_bugs 1 to skip this check, but expect stuff to be horribly broken then.");\r
1596 }\r
1597 \r
1598 float compressShotOrigin(vector v)\r
1599 {\r
1600         float x, y, z;\r
1601         x = rint(v_x * 2);\r
1602         y = rint(v_y * 4) + 128;\r
1603         z = rint(v_z * 4) + 128;\r
1604         if(x > 255 || x < 0)\r
1605         {\r
1606                 print("shot origin ", vtos(v), " x out of bounds\n");\r
1607                 x = bound(0, x, 255);\r
1608         }\r
1609         if(y > 255 || y < 0)\r
1610         {\r
1611                 print("shot origin ", vtos(v), " y out of bounds\n");\r
1612                 y = bound(0, y, 255);\r
1613         }\r
1614         if(z > 255 || z < 0)\r
1615         {\r
1616                 print("shot origin ", vtos(v), " z out of bounds\n");\r
1617                 z = bound(0, z, 255);\r
1618         }\r
1619         return x * 0x10000 + y * 0x100 + z;\r
1620 }\r
1621 vector decompressShotOrigin(float f)\r
1622 {\r
1623         vector v;\r
1624         v_x = ((f & 0xFF0000) / 0x10000) / 2;\r
1625         v_y = ((f & 0xFF00) / 0x100 - 128) / 4;\r
1626         v_z = ((f & 0xFF) - 128) / 4;\r
1627         return v;\r
1628 }\r
1629 \r
1630 void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass)\r
1631 {\r
1632         float start, end, root, child;\r
1633 \r
1634         // heapify\r
1635         start = floor((n - 2) / 2);\r
1636         while(start >= 0)\r
1637         {\r
1638                 // siftdown(start, count-1);\r
1639                 root = start;\r
1640                 while(root * 2 + 1 <= n-1)\r
1641                 {\r
1642                         child = root * 2 + 1;\r
1643                         if(child < n-1)\r
1644                                 if(cmp(child, child+1, pass) < 0)\r
1645                                         ++child;\r
1646                         if(cmp(root, child, pass) < 0)\r
1647                         {\r
1648                                 swap(root, child, pass);\r
1649                                 root = child;\r
1650                         }\r
1651                         else\r
1652                                 break;\r
1653                 }\r
1654                 // end of siftdown\r
1655                 --start;\r
1656         }\r
1657 \r
1658         // extract\r
1659         end = n - 1;\r
1660         while(end > 0)\r
1661         {\r
1662                 swap(0, end, pass);\r
1663                 --end;\r
1664                 // siftdown(0, end);\r
1665                 root = 0;\r
1666                 while(root * 2 + 1 <= end)\r
1667                 {\r
1668                         child = root * 2 + 1;\r
1669                         if(child < end && cmp(child, child+1, pass) < 0)\r
1670                                 ++child;\r
1671                         if(cmp(root, child, pass) < 0)\r
1672                         {\r
1673                                 swap(root, child, pass);\r
1674                                 root = child;\r
1675                         }\r
1676                         else\r
1677                                 break;\r
1678                 }\r
1679                 // end of siftdown\r
1680         }\r
1681 }\r
1682 \r
1683 void RandomSelection_Init()\r
1684 {\r
1685         RandomSelection_totalweight = 0;\r
1686         RandomSelection_chosen_ent = world;\r
1687         RandomSelection_chosen_float = 0;\r
1688         RandomSelection_chosen_string = string_null;\r
1689         RandomSelection_best_priority = -1;\r
1690 }\r
1691 void RandomSelection_Add(entity e, float f, string s, float weight, float priority)\r
1692 {\r
1693         if(priority > RandomSelection_best_priority)\r
1694         {\r
1695                 RandomSelection_best_priority = priority;\r
1696                 RandomSelection_chosen_ent = e;\r
1697                 RandomSelection_chosen_float = f;\r
1698                 RandomSelection_chosen_string = s;\r
1699                 RandomSelection_totalweight = weight;\r
1700         }\r
1701         else if(priority == RandomSelection_best_priority)\r
1702         {\r
1703                 RandomSelection_totalweight += weight;\r
1704                 if(random() * RandomSelection_totalweight <= weight)\r
1705                 {\r
1706                         RandomSelection_chosen_ent = e;\r
1707                         RandomSelection_chosen_float = f;\r
1708                         RandomSelection_chosen_string = s;\r
1709                 }\r
1710         }\r
1711 }\r
1712 \r
1713 vector healtharmor_maxdamage(float h, float a, float armorblock)\r
1714 {\r
1715         // NOTE: we'll always choose the SMALLER value...\r
1716         float healthdamage, armordamage, armorideal;\r
1717         vector v;\r
1718         healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health\r
1719         armordamage = a + (h - 1); // damage we can take if we could use more armor\r
1720         armorideal = healthdamage * armorblock;\r
1721         v_y = armorideal;\r
1722         if(armordamage < healthdamage)\r
1723         {\r
1724                 v_x = armordamage;\r
1725                 v_z = 1;\r
1726         }\r
1727         else\r
1728         {\r
1729                 v_x = healthdamage;\r
1730                 v_z = 0;\r
1731         }\r
1732         return v;\r
1733 }\r
1734 \r
1735 vector healtharmor_applydamage(float a, float armorblock, float damage)\r
1736 {\r
1737         vector v;\r
1738         v_y = bound(0, damage * armorblock, a); // save\r
1739         v_x = bound(0, damage - v_y, damage); // take\r
1740         v_z = 0;\r
1741         return v;\r
1742 }\r
1743 \r
1744 string getcurrentmod()\r
1745 {\r
1746         float n;\r
1747         string m;\r
1748         m = cvar_string("fs_gamedir");\r
1749         n = tokenize_console(m);\r
1750         if(n == 0)\r
1751                 return "data";\r
1752         else\r
1753                 return argv(n - 1);\r
1754 }\r
1755 \r
1756 #ifndef MENUQC\r
1757 #ifdef CSQC\r
1758 float ReadInt24_t()\r
1759 {\r
1760         float v;\r
1761         v = ReadShort() * 256; // note: this is signed\r
1762         v += ReadByte(); // note: this is unsigned\r
1763         return v;\r
1764 }\r
1765 #else\r
1766 void WriteInt24_t(float dest, float val)\r
1767 {\r
1768         float v;\r
1769         WriteShort(dest, (v = floor(val / 256)));\r
1770         WriteByte(dest, val - v * 256); // 0..255\r
1771 }\r
1772 #endif\r
1773 #endif\r
1774 \r
1775 float float2range11(float f)\r
1776 {\r
1777         // continuous function mapping all reals into -1..1\r
1778         return f / (fabs(f) + 1);\r
1779 }\r
1780 \r
1781 float float2range01(float f)\r
1782 {\r
1783         // continuous function mapping all reals into 0..1\r
1784         return 0.5 + 0.5 * float2range11(f);\r
1785 }\r
1786 \r
1787 // from the GNU Scientific Library\r
1788 float gsl_ran_gaussian_lastvalue;\r
1789 float gsl_ran_gaussian_lastvalue_set;\r
1790 float gsl_ran_gaussian(float sigma)\r
1791 {\r
1792         float a, b;\r
1793         if(gsl_ran_gaussian_lastvalue_set)\r
1794         {\r
1795                 gsl_ran_gaussian_lastvalue_set = 0;\r
1796                 return sigma * gsl_ran_gaussian_lastvalue;\r
1797         }\r
1798         else\r
1799         {\r
1800                 a = random() * 2 * M_PI;\r
1801                 b = sqrt(-2 * log(random()));\r
1802                 gsl_ran_gaussian_lastvalue = cos(a) * b;\r
1803                 gsl_ran_gaussian_lastvalue_set = 1;\r
1804                 return sigma * sin(a) * b;\r
1805         }\r
1806 }\r
1807 \r
1808 string car(string s)\r
1809 {\r
1810         float o;\r
1811         o = strstrofs(s, " ", 0);\r
1812         if(o < 0)\r
1813                 return s;\r
1814         return substring(s, 0, o);\r
1815 }\r
1816 string cdr(string s)\r
1817 {\r
1818         float o;\r
1819         o = strstrofs(s, " ", 0);\r
1820         if(o < 0)\r
1821                 return string_null;\r
1822         return substring(s, o + 1, strlen(s) - (o + 1));\r
1823 }\r
1824 float matchacl(string acl, string str)\r
1825 {\r
1826         string t, s;\r
1827         float r, d;\r
1828         r = 0;\r
1829         while(acl)\r
1830         {\r
1831                 t = car(acl); acl = cdr(acl);\r
1832                 d = 1;\r
1833                 if(substring(t, 0, 1) == "-")\r
1834                 {\r
1835                         d = -1;\r
1836                         t = substring(t, 1, strlen(t) - 1);\r
1837                 }\r
1838                 else if(substring(t, 0, 1) == "+")\r
1839                         t = substring(t, 1, strlen(t) - 1);\r
1840                 if(substring(t, -1, 1) == "*")\r
1841                 {\r
1842                         t = substring(t, 0, strlen(t) - 1);\r
1843                         s = substring(s, 0, strlen(t));\r
1844                 }\r
1845                 else\r
1846                         s = str;\r
1847 \r
1848                 if(s == t)\r
1849                 {\r
1850                         r = d;\r
1851                 }\r
1852         }\r
1853         return r;\r
1854 }\r
1855 float startsWith(string haystack, string needle)\r
1856 {\r
1857         return substring(haystack, 0, strlen(needle)) == needle;\r
1858 }\r
1859 float startsWithNocase(string haystack, string needle)\r
1860 {\r
1861         return strcasecmp(substring(haystack, 0, strlen(needle)), needle) == 0;\r
1862 }\r
1863 \r
1864 #ifndef MENUQC\r
1865 #endif\r