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