1 string wordwrap_buffer;
3 void wordwrap_buffer_put(string s)
5 wordwrap_buffer = strcat(wordwrap_buffer, s);
8 string wordwrap(string s, float l)
12 wordwrap_cb(s, l, wordwrap_buffer_put);
20 void wordwrap_buffer_sprint(string s)
22 wordwrap_buffer = strcat(wordwrap_buffer, s);
25 sprint(self, wordwrap_buffer);
30 void wordwrap_sprint(string s, float l)
33 wordwrap_cb(s, l, wordwrap_buffer_sprint);
34 if(wordwrap_buffer != "")
35 sprint(self, strcat(wordwrap_buffer, "\n"));
42 string unescape(string in)
47 // but it doesn't seem to be necessary in my tests at least
52 for(i = 0; i < len; ++i)
54 s = substring(in, i, 1);
57 s = substring(in, i+1, 1);
59 str = strcat(str, "\n");
61 str = strcat(str, "\\");
63 str = strcat(str, substring(in, i, 2));
73 void wordwrap_cb(string s, float l, void(string) callback)
76 local float lleft, i, j, wlen;
80 for (i = 0;i < strlen(s);++i)
82 if (substring(s, i, 2) == "\\n")
88 else if (substring(s, i, 1) == "\n")
93 else if (substring(s, i, 1) == " ")
103 for (j = i+1;j < strlen(s);++j)
104 // ^^ this skips over the first character of a word, which
105 // is ALWAYS part of the word
106 // this is safe since if i+1 == strlen(s), i will become
107 // strlen(s)-1 at the end of this block and the function
108 // will terminate. A space can't be the first character we
109 // read here, and neither can a \n be the start, since these
110 // two cases have been handled above.
112 c = substring(s, j, 1);
119 // we need to keep this tempstring alive even if substring is
120 // called repeatedly, so call strcat even though we're not
130 callback(substring(s, i, wlen));
131 lleft = lleft - wlen;
138 float dist_point_line(vector p, vector l0, vector ldir)
140 ldir = normalize(ldir);
142 // remove the component in line direction
143 p = p - (p * ldir) * ldir;
145 // vlen of the remaining vector
149 void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
178 float median(float a, float b, float c)
181 return bound(a, b, c);
182 return bound(c, b, a);
185 // converts a number to a string with the indicated number of decimals
186 // works for up to 10 decimals!
187 string ftos_decimals(float number, float decimals)
193 // if negative, cut off the sign first
195 return strcat("-", ftos_decimals(-number, decimals));
196 // it now is always positive!
199 number = floor(number * pow(10, decimals) + 0.5);
202 result = ftos(number);
203 len = strlen(result);
204 // does it have a decimal point (should not happen)? If there is one, it is always at len-7)
205 // if ftos had messed it up, which should never happen: "34278.000000"
207 if(substring(result, len - 7, 1) == ".")
209 dprint("ftos(integer) has comma? Can't be. Affected result: ", result, "\n");
210 result = substring(result, 0, len - 7);
215 return result; // don't insert a point for zero decimals
216 // is it too short? If yes, insert leading zeroes
219 result = strcat(substring("0000000000", 0, decimals - len + 1), result);
222 // and now... INSERT THE POINT!
223 tmp = substring(result, len - decimals, decimals);
224 result = strcat(substring(result, 0, len - decimals), ".", tmp);
229 vector colormapPaletteColor(float c, float isPants)
233 case 0: return '0.800000 0.800000 0.800000';
234 case 1: return '0.600000 0.400000 0.000000';
235 case 2: return '0.000000 1.000000 0.501961';
236 case 3: return '0.000000 1.000000 0.000000';
237 case 4: return '1.000000 0.000000 0.000000';
238 case 5: return '0.000000 0.501961 1.000000';
239 case 6: return '0.000000 1.000000 1.000000';
240 case 7: return '0.501961 1.000000 0.000000';
241 case 8: return '0.501961 0.000000 1.000000';
242 case 9: return '1.000000 0.000000 1.000000';
243 case 10: return '1.000000 0.000000 0.501961';
244 case 11: return '0.600000 0.600000 0.600000';
245 case 12: return '1.000000 1.000000 0.000000';
246 case 13: return '0.000000 0.000000 1.000000';
247 case 14: return '1.000000 0.501961 0.000000';
251 '1 0 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 0.0000000000))
252 + '0 1 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 2.0943951024))
253 + '0 0 1' * (0.502 + 0.498 * sin(time / 2.7182818285 + 4.1887902048));
256 '1 0 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 5.2359877560))
257 + '0 1 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 3.1415926536))
258 + '0 0 1' * (0.502 + 0.498 * sin(time / 3.1415926536 + 1.0471975512));
259 default: return '0.000 0.000 0.000';
263 // unzone the string, and return it as tempstring. Safe to be called on string_null
264 string fstrunzone(string s)
274 // Databases (hash tables)
275 #define DB_BUCKETS 8192
276 void db_save(float db, string pFilename)
279 fh = fopen(pFilename, FILE_WRITE);
282 print(strcat("^1Can't write DB to ", pFilename));
286 fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
287 for(i = 0; i < n; ++i)
288 fputs(fh, strcat(bufstr_get(db, i), "\n"));
297 float db_load(string pFilename)
299 float db, fh, i, j, n;
304 fh = fopen(pFilename, FILE_READ);
307 if(stof(fgets(fh)) == DB_BUCKETS)
310 while((l = fgets(fh)))
313 bufstr_set(db, i, l);
319 // different count of buckets?
320 // need to reorganize the database then (SLOW)
321 while((l = fgets(fh)))
323 n = tokenizebyseparator(l, "\\");
324 for(j = 2; j < n; j += 2)
325 db_put(db, argv(j-1), uri_unescape(argv(j)));
332 void db_dump(float db, string pFilename)
334 float fh, i, j, n, m;
335 fh = fopen(pFilename, FILE_WRITE);
337 error(strcat("Can't dump DB to ", pFilename));
340 for(i = 0; i < n; ++i)
342 m = tokenizebyseparator(bufstr_get(db, i), "\\");
343 for(j = 2; j < m; j += 2)
344 fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n"));
349 void db_close(float db)
354 string db_get(float db, string pKey)
357 h = mod(crc16(FALSE, pKey), DB_BUCKETS);
358 return uri_unescape(infoget(bufstr_get(db, h), pKey));
361 void db_put(float db, string pKey, string pValue)
364 h = mod(crc16(FALSE, pKey), DB_BUCKETS);
365 bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue)));
372 db = db_load("foo.db");
373 print("LOADED. FILL...\n");
374 for(i = 0; i < DB_BUCKETS; ++i)
375 db_put(db, ftos(random()), "X");
376 print("FILLED. SAVE...\n");
377 db_save(db, "foo.db");
378 print("SAVED. CLOSE...\n");
383 // Multiline text file buffers
384 float buf_load(string pFilename)
391 fh = fopen(pFilename, FILE_READ);
395 while((l = fgets(fh)))
397 bufstr_set(buf, i, l);
404 void buf_save(float buf, string pFilename)
407 fh = fopen(pFilename, FILE_WRITE);
409 error(strcat("Can't write buf to ", pFilename));
410 n = buf_getsize(buf);
411 for(i = 0; i < n; ++i)
412 fputs(fh, strcat(bufstr_get(buf, i), "\n"));
416 string GametypeNameFromType(float g)
418 if (g == GAME_DEATHMATCH) return "dm";
419 else if (g == GAME_TEAM_DEATHMATCH) return "tdm";
420 else if (g == GAME_DOMINATION) return "dom";
421 else if (g == GAME_CTF) return "ctf";
422 else if (g == GAME_RUNEMATCH) return "rune";
423 else if (g == GAME_LMS) return "lms";
424 else if (g == GAME_ARENA) return "arena";
425 else if (g == GAME_KEYHUNT) return "kh";
426 else if (g == GAME_ONSLAUGHT) return "ons";
427 else if (g == GAME_ASSAULT) return "as";
428 else if (g == GAME_RACE) return "race";
429 else if (g == GAME_NEXBALL) return "nexball";
433 string mmsss(float tenths)
437 tenths = floor(tenths + 0.5);
438 minutes = floor(tenths / 600);
439 tenths -= minutes * 600;
440 s = ftos(1000 + tenths);
441 return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1));
444 string ScoreString(float pFlags, float pValue)
449 pValue = floor(pValue + 0.5); // round
451 if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME)))
453 else if(pFlags & SFL_RANK)
455 valstr = ftos(pValue);
457 if((l >= 2) && (substring(valstr, l - 2, 1) == "1"))
458 valstr = strcat(valstr, "th");
459 else if(substring(valstr, l - 1, 1) == "1")
460 valstr = strcat(valstr, "st");
461 else if(substring(valstr, l - 1, 1) == "2")
462 valstr = strcat(valstr, "nd");
463 else if(substring(valstr, l - 1, 1) == "3")
464 valstr = strcat(valstr, "rd");
466 valstr = strcat(valstr, "th");
468 else if(pFlags & SFL_TIME)
469 valstr = mmsss(pValue);
471 valstr = ftos(pValue);
476 vector cross(vector a, vector b)
479 '1 0 0' * (a_y * b_z - a_z * b_y)
480 + '0 1 0' * (a_z * b_x - a_x * b_z)
481 + '0 0 1' * (a_x * b_y - a_y * b_x);
484 // compressed vector format:
485 // like MD3, just even shorter
486 // 4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90
487 // 5 bit yaw (32 angles), 0=0, 8=90, 16=180, 24=270
488 // 7 bit length (logarithmic encoding), 1/8 .. about 7844
489 // length = 2^(length_encoded/8) / 8
490 // if pitch is 90, yaw does nothing and therefore indicates the sign (yaw is then either 11111 or 11110); 11111 is pointing DOWN
491 // thus, valid values are from 0000.11110.0000000 to 1111.11111.1111111
492 // the special value 0 indicates the zero vector
494 float lengthLogTable[128];
496 float invertLengthLog(float x)
498 float l, r, m, lerr, rerr;
500 if(x >= lengthLogTable[127])
502 if(x <= lengthLogTable[0])
510 m = floor((l + r) / 2);
511 if(lengthLogTable[m] < x)
517 // now: r is >=, l is <
518 lerr = (x - lengthLogTable[l]);
519 rerr = (lengthLogTable[r] - x);
525 vector decompressShortVector(float data)
528 float pitch, yaw, len;
531 pitch = (data & 0xF000) / 0x1000;
532 yaw = (data & 0x0F80) / 0x80;
533 len = (data & 0x007F);
535 //print("\ndecompress: pitch ", ftos(pitch)); print("yaw ", ftos(yaw)); print("len ", ftos(len), "\n");
548 yaw = .19634954084936207740 * yaw;
549 pitch = .19634954084936207740 * pitch - 1.57079632679489661922;
550 out_x = cos(yaw) * cos(pitch);
551 out_y = sin(yaw) * cos(pitch);
555 //print("decompressed: ", vtos(out), "\n");
557 return out * lengthLogTable[len];
560 float compressShortVector(vector vec)
563 float pitch, yaw, len;
566 //print("compress: ", vtos(vec), "\n");
567 ang = vectoangles(vec);
571 if(ang_x < -90 && ang_x > +90)
572 error("BOGUS vectoangles");
573 //print("angles: ", vtos(ang), "\n");
575 pitch = floor(0.5 + (ang_x + 90) * 16 / 180) & 15; // -90..90 to 0..14
584 yaw = floor(0.5 + ang_y * 32 / 360) & 31; // 0..360 to 0..32
585 len = invertLengthLog(vlen(vec));
587 //print("compressed: pitch ", ftos(pitch)); print("yaw ", ftos(yaw)); print("len ", ftos(len), "\n");
589 return (pitch * 0x1000) + (yaw * 0x80) + len;
592 void compressShortVector_init()
597 for(i = 0; i < 128; ++i)
599 lengthLogTable[i] = l;
603 if(cvar("developer"))
605 print("Verifying vector compression table...\n");
606 for(i = 0x0F00; i < 0xFFFF; ++i)
607 if(i != compressShortVector(decompressShortVector(i)))
609 print("BROKEN vector compression: ", ftos(i));
610 print(" -> ", vtos(decompressShortVector(i)));
611 print(" -> ", ftos(compressShortVector(decompressShortVector(i))));
620 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
622 traceline(v0, v0 + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
623 traceline(v0, v0 + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
624 traceline(v0, v0 + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
625 traceline(v0 + dvx, v0 + dvx + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
626 traceline(v0 + dvx, v0 + dvx + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
627 traceline(v0 + dvy, v0 + dvy + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
628 traceline(v0 + dvy, v0 + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
629 traceline(v0 + dvz, v0 + dvz + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
630 traceline(v0 + dvz, v0 + dvz + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
631 traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
632 traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
633 traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
637 void fixedmakevectors(vector a)
639 // a makevectors that actually inverts vectoangles
645 string fixPriorityList(string order, float from, float to, float subtract, float complete)
650 n = tokenize_sane(order);
651 for(i = 0; i < n; ++i)
656 if(w >= from && w <= to)
657 neworder = strcat(neworder, ftos(w), " ");
661 if(w >= from && w <= to)
662 neworder = strcat(neworder, ftos(w), " ");
669 n = tokenize_sane(neworder);
670 for(w = to; w >= from; --w)
672 for(i = 0; i < n; ++i)
673 if(stof(argv(i)) == w)
675 if(i == n) // not found
676 neworder = strcat(neworder, ftos(w), " ");
680 return substring(neworder, 0, strlen(neworder) - 1);
683 string swapInPriorityList(string order, float i, float j)
688 n = tokenize_sane(order);
690 if(i >= 0 && i < n && j >= 0 && j < n && i != j)
693 for(w = 0; w < n; ++w)
696 s = strcat(s, argv(j), " ");
698 s = strcat(s, argv(i), " ");
700 s = strcat(s, argv(w), " ");
702 return substring(s, 0, strlen(s) - 1);
708 float cvar_value_issafe(string s)
710 if(strstrofs(s, "\"", 0) >= 0)
712 if(strstrofs(s, "\\", 0) >= 0)
714 if(strstrofs(s, ";", 0) >= 0)
716 if(strstrofs(s, "$", 0) >= 0)
718 if(strstrofs(s, "\r", 0) >= 0)
720 if(strstrofs(s, "\n", 0) >= 0)
726 void get_mi_min_max(float mode)
731 strunzone(mi_shortname);
732 mi_shortname = mapname;
733 if(!strcasecmp(substring(mi_shortname, 0, 5), "maps/"))
734 mi_shortname = substring(mi_shortname, 5, strlen(mi_shortname) - 5);
735 if(!strcasecmp(substring(mi_shortname, strlen(mi_shortname) - 4, 4), ".bsp"))
736 mi_shortname = substring(mi_shortname, 0, strlen(mi_shortname) - 4);
737 mi_shortname = strzone(mi_shortname);
749 MapInfo_Get_ByName(mi_shortname, 0, 0);
750 if(MapInfo_Map_mins_x < MapInfo_Map_maxs_x)
752 mi_min = MapInfo_Map_mins;
753 mi_max = MapInfo_Map_maxs;
761 tracebox('1 0 0' * mi_x,
762 '0 1 0' * mi_y + '0 0 1' * mi_z,
763 '0 1 0' * ma_y + '0 0 1' * ma_z,
767 if(!trace_startsolid)
768 mi_min_x = trace_endpos_x;
770 tracebox('0 1 0' * mi_y,
771 '1 0 0' * mi_x + '0 0 1' * mi_z,
772 '1 0 0' * ma_x + '0 0 1' * ma_z,
776 if(!trace_startsolid)
777 mi_min_y = trace_endpos_y;
779 tracebox('0 0 1' * mi_z,
780 '1 0 0' * mi_x + '0 1 0' * mi_y,
781 '1 0 0' * ma_x + '0 1 0' * ma_y,
785 if(!trace_startsolid)
786 mi_min_z = trace_endpos_z;
788 tracebox('1 0 0' * ma_x,
789 '0 1 0' * mi_y + '0 0 1' * mi_z,
790 '0 1 0' * ma_y + '0 0 1' * ma_z,
794 if(!trace_startsolid)
795 mi_max_x = trace_endpos_x;
797 tracebox('0 1 0' * ma_y,
798 '1 0 0' * mi_x + '0 0 1' * mi_z,
799 '1 0 0' * ma_x + '0 0 1' * ma_z,
803 if(!trace_startsolid)
804 mi_max_y = trace_endpos_y;
806 tracebox('0 0 1' * ma_z,
807 '1 0 0' * mi_x + '0 1 0' * mi_y,
808 '1 0 0' * ma_x + '0 1 0' * ma_y,
812 if(!trace_startsolid)
813 mi_max_z = trace_endpos_z;
818 void get_mi_min_max_texcoords(float mode)
822 get_mi_min_max(mode);
827 // extend mi_picmax to get a square aspect ratio
828 // center the map in that area
829 extend = mi_picmax - mi_picmin;
830 if(extend_y > extend_x)
832 mi_picmin_x -= (extend_y - extend_x) * 0.5;
833 mi_picmax_x += (extend_y - extend_x) * 0.5;
837 mi_picmin_y -= (extend_x - extend_y) * 0.5;
838 mi_picmax_y += (extend_x - extend_y) * 0.5;
841 // add another some percent
842 extend = (mi_picmax - mi_picmin) * (1 / 64.0);
846 // calculate the texcoords
847 mi_pictexcoord0 = mi_pictexcoord1 = mi_pictexcoord2 = mi_pictexcoord3 = '0 0 0';
848 // first the two corners of the origin
849 mi_pictexcoord0_x = (mi_min_x - mi_picmin_x) / (mi_picmax_x - mi_picmin_x);
850 mi_pictexcoord0_y = (mi_min_y - mi_picmin_y) / (mi_picmax_y - mi_picmin_y);
851 mi_pictexcoord2_x = (mi_max_x - mi_picmin_x) / (mi_picmax_x - mi_picmin_x);
852 mi_pictexcoord2_y = (mi_max_y - mi_picmin_y) / (mi_picmax_y - mi_picmin_y);
853 // then the other corners
854 mi_pictexcoord1_x = mi_pictexcoord0_x;
855 mi_pictexcoord1_y = mi_pictexcoord2_y;
856 mi_pictexcoord3_x = mi_pictexcoord2_x;
857 mi_pictexcoord3_y = mi_pictexcoord0_y;
862 void cvar_settemp(string pKey, string pValue)
864 error("cvar_settemp called from CSQC - use cvar_clientsettemp instead!");
866 void cvar_settemp_restore()
868 error("cvar_settemp_restore called from CSQC - use cvar_clientsettemp instead!");
871 void cvar_settemp(string pKey, string pValue)
873 cvar_set("settemp_list", strcat("1 ", pKey, " ", cvar_string("settemp_var"), " ", cvar_string("settemp_list")));
875 registercvar(cvar_string("settemp_var"), "", 0);
877 registercvar(cvar_string("settemp_var"), "");
879 cvar_set(cvar_string("settemp_var"), cvar_string(pKey));
880 cvar_set("settemp_var", strcat(cvar_string("settemp_var"), "x"));
881 cvar_set(pKey, pValue);
884 void cvar_settemp_restore()
886 // undo what cvar_settemp did
888 n = tokenize_sane(cvar_string("settemp_list"));
889 for(i = 0; i < n - 3; i += 3)
890 cvar_set(argv(i + 1), cvar_string(argv(i + 2)));
891 cvar_set("settemp_list", "0");
895 float almost_equals(float a, float b)
898 eps = (max(a, -a) + max(b, -b)) * 0.001;
899 if(a - b < eps && b - a < eps)
904 float almost_in_bounds(float a, float b, float c)
907 eps = (max(a, -a) + max(c, -c)) * 0.001;
908 return b == median(a - eps, b, c + eps);
914 float (string s) _tokenize_builtin = #58;
915 string (float argnum) _argv_builtin = #59;
916 float (string s, string sep) _tokenizebyseparator_builtin = #479;
918 float (string s) _tokenize_builtin = #441;
919 string (float argnum) _argv_builtin = #442;
920 float (string s, string sep) _tokenizebyseparator_builtin = #479;
922 float(string s) _tokenize_console = #514;
923 float(float i) _argv_start_index_builtin = #515;
924 float(float i) _argv_end_index_builtin = #516;
926 float MAX_TOKENS = 256;
927 string _argv_sane_buffer[MAX_TOKENS];
928 float _argv_sane_startpos[MAX_TOKENS];
929 float _argv_sane_endpos[MAX_TOKENS];
932 string _argv_sane(float i)
934 // Perl-ish -1 for the last argument
937 return strcat("", _argv_sane_buffer[i]); // force tempstring
940 float _argv_start_index_sane(float i)
942 // Perl-ish -1 for the last argument
945 return _argv_sane_startpos[i];
948 float _argv_end_index_sane(float i)
950 // Perl-ish -1 for the last argument
953 return _argv_sane_endpos[i];
956 //string TOKENIZE_SANE_WHITESPACE_CHARS = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff";
957 string TOKENIZE_SANE_WHITESPACE_CHARS = "\x20\x0d\x0a\x09";
958 string TOKENIZE_SANE_COMMENT_BREAKERS = "\x0d\x0a";
959 // change this if DP changes its type to "char"!
961 float _tokenize_sane(string s)
963 // This MUST match COM_ParseToken_Console!
964 string com_token, tmp;
969 for(i = 0; i < _argc_sane; ++i)
971 if(_argv_sane_buffer[i])
972 strunzone(_argv_sane_buffer[i]);
973 _argv_sane_buffer[i] = string_null;
974 _argv_sane_startpos[i] = 0;
984 for(; data < end && strstrofs(TOKENIZE_SANE_WHITESPACE_CHARS, substring(s, data, 1), 0) >= 0; ++data)
989 if(substring(s, data, 2) == "//")
993 // Any call to the tokenizer ALREADY assumes it's a single line, so we can safely abort if we see a comment.
996 while(data < end && strstrofs(TOKENIZE_SANE_COMMENT_BREAKERS, substring(s, data, 1), 0) >= 0)
998 continue; // go to skipwhite again
1001 // I'd like to simply put a "break" here, but then fteqcc says this function has unreachable code
1004 else if(substring(s, data, 1) == "\"")
1008 _argv_sane_startpos[_argc_sane] = data;
1009 for(++data; data < end && substring(s, data, 1) != "\""; ++data)
1011 // allow escaped " and \ case
1012 tmp = substring(s, data, 2);
1013 if(tmp == "\\\"" || tmp == "\\\\")
1015 com_token = strcat(com_token, substring(s, data, 1));
1017 if(substring(s, data, 1) == "\"")
1019 _argv_sane_endpos[_argc_sane] = data;
1020 _argv_sane_buffer[_argc_sane] = strzone(com_token);
1027 _argv_sane_startpos[_argc_sane] = data;
1028 for(; data < end && strstrofs(TOKENIZE_SANE_WHITESPACE_CHARS, substring(s, data, 1), 0) < 0; ++data)
1029 com_token = strcat(com_token, substring(s, data, 1));
1030 _argv_sane_endpos[_argc_sane] = data;
1031 _argv_sane_buffer[_argc_sane] = strzone(com_token);
1042 // matching the console 1:1
1044 float tokenize_sane_force_native(string s)
1046 argv = _argv_builtin;
1047 argv_start_index = _argv_start_index_builtin;
1048 argv_end_index = _argv_end_index_builtin;
1049 return _tokenize_console(s);
1052 float tokenize_sane_force_emulation(string s)
1055 argv_start_index = _argv_start_index_sane;
1056 argv_end_index = _argv_end_index_sane;
1057 return _tokenize_sane(s);
1060 float tokenize_sane(string s)
1062 if(checkextension("DP_QC_TOKENIZE_CONSOLE"))
1063 return tokenize_sane_force_native(s);
1064 return tokenize_sane_force_emulation(s);
1067 float tokenize_insane(string s)
1069 argv = _argv_builtin;
1070 argv_start_index = _argv_start_index_builtin;
1071 argv_end_index = _argv_end_index_builtin;
1072 return _tokenize_builtin(s);
1075 float tokenizebyseparator(string s, string sep)
1077 argv = _argv_builtin;
1078 argv_start_index = _argv_start_index_builtin;
1079 argv_end_index = _argv_end_index_builtin;
1080 return _tokenizebyseparator_builtin(s, sep);
1083 float power2of(float e)
1087 float log2of(float x)
1089 // NOTE: generated code
1162 float rgb_mi_ma_to_hue(vector rgb, float mi, float ma)
1166 else if(ma == rgb_x)
1169 return (rgb_y - rgb_z) / (ma - mi);
1171 return (rgb_y - rgb_z) / (ma - mi) + 6;
1173 else if(ma == rgb_y)
1174 return (rgb_z - rgb_x) / (ma - mi) + 2;
1175 else // if(ma == rgb_z)
1176 return (rgb_x - rgb_y) / (ma - mi) + 4;
1179 vector hue_mi_ma_to_rgb(float hue, float mi, float ma)
1183 hue -= 6 * floor(hue / 6);
1185 //else if(ma == rgb_x)
1186 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
1190 rgb_y = hue * (ma - mi) + mi;
1193 //else if(ma == rgb_y)
1194 // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120;
1197 rgb_x = (2 - hue) * (ma - mi) + mi;
1205 rgb_z = (hue - 2) * (ma - mi) + mi;
1207 //else // if(ma == rgb_z)
1208 // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240;
1212 rgb_y = (4 - hue) * (ma - mi) + mi;
1217 rgb_x = (hue - 4) * (ma - mi) + mi;
1221 //else if(ma == rgb_x)
1222 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
1223 else // if(hue <= 6)
1227 rgb_z = (6 - hue) * (ma - mi) + mi;
1233 vector rgb_to_hsv(vector rgb)
1238 mi = min3(rgb_x, rgb_y, rgb_z);
1239 ma = max3(rgb_x, rgb_y, rgb_z);
1241 hsv_x = rgb_mi_ma_to_hue(rgb, mi, ma);
1252 vector hsv_to_rgb(vector hsv)
1254 return hue_mi_ma_to_rgb(hsv_x, hsv_z * (1 - hsv_y), hsv_z);
1257 vector rgb_to_hsl(vector rgb)
1262 mi = min3(rgb_x, rgb_y, rgb_z);
1263 ma = max3(rgb_x, rgb_y, rgb_z);
1265 hsl_x = rgb_mi_ma_to_hue(rgb, mi, ma);
1267 hsl_z = 0.5 * (mi + ma);
1270 else if(hsl_z <= 0.5)
1271 hsl_y = (ma - mi) / (2*hsl_z);
1272 else // if(hsl_z > 0.5)
1273 hsl_y = (ma - mi) / (2 - 2*hsl_z);
1278 vector hsl_to_rgb(vector hsl)
1280 float mi, ma, maminusmi;
1283 maminusmi = hsl_y * 2 * hsl_z;
1285 maminusmi = hsl_y * (2 - 2 * hsl_z);
1287 // hsl_z = 0.5 * mi + 0.5 * ma
1288 // maminusmi = - mi + ma
1289 mi = hsl_z - 0.5 * maminusmi;
1290 ma = hsl_z + 0.5 * maminusmi;
1292 return hue_mi_ma_to_rgb(hsl_x, mi, ma);
1295 string rgb_to_hexcolor(vector rgb)
1300 DEC_TO_HEXDIGIT(floor(rgb_x * 15 + 0.5)),
1301 DEC_TO_HEXDIGIT(floor(rgb_y * 15 + 0.5)),
1302 DEC_TO_HEXDIGIT(floor(rgb_z * 15 + 0.5))
1306 // requires that m2>m1 in all coordinates, and that m4>m3
1307 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;};
1309 // requires the same, but is a stronger condition
1310 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;};
1313 // angles transforms
1314 // angles in fixedmakevectors/fixedvectoangles space
1315 vector AnglesTransform_Apply(vector transform, vector v)
1317 fixedmakevectors(transform);
1318 return v_forward * v_x
1323 vector AnglesTransform_Multiply(vector t1, vector t2)
1325 vector m_forward, m_up;
1326 fixedmakevectors(t2); m_forward = v_forward; m_up = v_up;
1327 m_forward = AnglesTransform_Apply(t1, m_forward); m_up = AnglesTransform_Apply(t1, m_up);
1328 return fixedvectoangles2(m_forward, m_up);
1331 vector AnglesTransform_Invert(vector transform)
1333 vector i_forward, i_up;
1334 fixedmakevectors(transform);
1335 // we want angles that turn v_forward into '1 0 0', v_right into '0 1 0' and v_up into '0 0 1'
1336 // but these are orthogonal unit vectors!
1337 // so to invert, we can simply fixedvectoangles the TRANSPOSED matrix
1338 // TODO is this always -transform?
1339 i_forward_x = v_forward_x;
1340 i_forward_y = -v_right_x;
1341 i_forward_z = v_up_x;
1342 i_up_x = v_forward_z;
1343 i_up_y = -v_right_z;
1345 return fixedvectoangles2(i_forward, i_up);
1348 vector AnglesTransform_TurnDirection(vector transform)
1350 // turn 180 degrees around v_up
1351 // changes in-direction to out-direction
1352 fixedmakevectors(transform);
1353 return fixedvectoangles2(-1 * v_forward, 1 * v_up);
1356 vector AnglesTransform_Divide(vector to_transform, vector from_transform)
1358 return AnglesTransform_Multiply(to_transform, AnglesTransform_Invert(from_transform));
1362 float textLengthUpToWidth(string theText, float maxWidth, textLengthUpToWidth_widthFunction_t w)
1364 float ICanHasKallerz;
1366 // detect color codes support in the width function
1367 ICanHasKallerz = (w("^7") == 0);
1370 // The following function is SLOW.
1371 // For your safety and for the protection of those around you...
1372 // DO NOT CALL THIS AT HOME.
1373 // No really, don't.
1374 if(w(theText) <= maxWidth)
1375 return strlen(theText); // yeah!
1377 // binary search for right place to cut string
1379 float left, right, middle; // this always works
1381 right = strlen(theText); // this always fails
1384 middle = floor((left + right) / 2);
1385 if(w(substring(theText, 0, middle)) <= maxWidth)
1390 while(left < right - 1);
1394 // NOTE: when color codes are involved, this binary search is,
1395 // mathematically, BROKEN. However, it is obviously guaranteed to
1396 // terminate, as the range still halves each time - but nevertheless, it is
1397 // guaranteed that it finds ONE valid cutoff place (where "left" is in
1398 // range, and "right" is outside).
1400 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
1401 // and decrease left on the basis of the chars detected of the truncated tag
1402 // Even if the ^xrgb tag is not complete/correct, left is decreased
1403 // (sometimes too much but with a correct result)
1404 // it fixes also ^[0-9]
1405 while(left >= 1 && substring(theText, left-1, 1) == "^")
1408 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
1410 else if (left >= 3 && substring(theText, left-3, 2) == "^x")
1412 ch = str2chr(theText, left-1);
1413 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
1416 else if (left >= 4 && substring(theText, left-4, 2) == "^x")
1418 ch = str2chr(theText, left-2);
1419 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
1421 ch = str2chr(theText, left-1);
1422 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
1431 string getWrappedLine(float w, textLengthUpToWidth_widthFunction_t tw)
1437 s = getWrappedLine_remaining;
1439 cantake = textLengthUpToWidth(s, w, tw);
1440 if(cantake > 0 && cantake < strlen(s))
1443 while(take > 0 && substring(s, take, 1) != " ")
1447 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
1448 if(getWrappedLine_remaining == "")
1449 getWrappedLine_remaining = string_null;
1450 return substring(s, 0, cantake);
1454 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
1455 if(getWrappedLine_remaining == "")
1456 getWrappedLine_remaining = string_null;
1457 return substring(s, 0, take);
1462 getWrappedLine_remaining = string_null;
1467 string textShortenToWidth(string theText, float maxWidth, textLengthUpToWidth_widthFunction_t tw)
1469 if(tw(theText) <= maxWidth)
1472 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("..."), tw)), "...");
1475 float isGametypeInFilter(float gt, float tp, string pattern)
1477 string subpattern, subpattern2;
1478 subpattern = strcat(",", GametypeNameFromType(gt), ",");
1480 subpattern2 = ",teams,";
1482 subpattern2 = ",noteams,";
1484 if(substring(pattern, 0, 1) == "-")
1486 pattern = substring(pattern, 1, strlen(pattern) - 1);
1487 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
1489 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0)
1494 if(substring(pattern, 0, 1) == "+")
1495 pattern = substring(pattern, 1, strlen(pattern) - 1);
1496 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
1497 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0)
1503 void shuffle(float n, shuffle_swapfunc_t swap)
1506 for(i = 1; i < n; ++i)
1508 // swap i-th item at a random position from 0 to i
1509 // proof for even distribution:
1512 // item n+1 gets at any position with chance 1/(n+1)
1513 // all others will get their 1/n chance reduced by factor n/(n+1)
1514 // to be on place n+1, their chance will be 1/(n+1)
1515 // 1/n * n/(n+1) = 1/(n+1)
1517 j = floor(random() * (i + 1));
1523 string substring_range(string s, float b, float e)
1525 return substring(s, b, e - b);
1528 string swapwords(string str, float i, float j)
1531 string s1, s2, s3, s4, s5;
1532 float si, ei, sj, ej, s0, en;
1533 n = tokenizebyseparator(str, " "); // must match g_maplist processing in ShuffleMaplist and "shuffle"
1534 si = argv_start_index(i);
1535 sj = argv_start_index(j);
1536 ei = argv_end_index(i);
1537 ej = argv_end_index(j);
1538 s0 = argv_start_index(0);
1539 en = argv_end_index(n-1);
1540 s1 = substring_range(str, s0, si);
1541 s2 = substring_range(str, si, ei);
1542 s3 = substring_range(str, ei, sj);
1543 s4 = substring_range(str, sj, ej);
1544 s5 = substring_range(str, ej, en);
1545 return strcat(s1, s4, s3, s2, s5);
1548 string _shufflewords_str;
1549 void _shufflewords_swapfunc(float i, float j)
1551 _shufflewords_str = swapwords(_shufflewords_str, i, j);
1553 string shufflewords(string str)
1556 _shufflewords_str = str;
1557 n = tokenizebyseparator(str, " ");
1558 shuffle(n, _shufflewords_swapfunc);
1559 str = _shufflewords_str;
1560 _shufflewords_str = string_null;
1564 vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0
1580 // actually, every number solves the equation!
1591 if(a > 0) // put the smaller solution first
1593 v_x = ((-b)-D) / (2*a);
1594 v_y = ((-b)+D) / (2*a);
1598 v_x = (-b+D) / (2*a);
1599 v_y = (-b-D) / (2*a);
1605 // complex solutions!