1 // checkextension wrapper for log
2 float sqrt(float f); // declared later
3 float exp(float f); // declared later
4 float pow(float f, float e); // declared later
5 float checkextension(string s); // declared later
6 float log_synth(float f)
10 return sqrt(-1); // nan? -inf?
12 return sqrt(-1); // ACTUALLY this should rather be -inf, but we cannot create a +inf in QC
27 // two steps are good enough
28 l = ((6-f) * f - 5) / 4.32808512266689022212;
35 if(checkextension("DP_QC_LOG"))
36 return log_builtin(f);
41 string wordwrap_buffer;
43 void wordwrap_buffer_put(string s)
45 wordwrap_buffer = strcat(wordwrap_buffer, s);
48 string wordwrap(string s, float l)
52 wordwrap_cb(s, l, wordwrap_buffer_put);
60 void wordwrap_buffer_sprint(string s)
62 wordwrap_buffer = strcat(wordwrap_buffer, s);
65 sprint(self, wordwrap_buffer);
70 void wordwrap_sprint(string s, float l)
73 wordwrap_cb(s, l, wordwrap_buffer_sprint);
74 if(wordwrap_buffer != "")
75 sprint(self, strcat(wordwrap_buffer, "\n"));
82 string unescape(string in)
87 // but it doesn't seem to be necessary in my tests at least
92 for(i = 0; i < len; ++i)
94 s = substring(in, i, 1);
97 s = substring(in, i+1, 1);
99 str = strcat(str, "\n");
101 str = strcat(str, "\\");
103 str = strcat(str, substring(in, i, 2));
106 str = strcat(str, s);
113 void wordwrap_cb(string s, float l, void(string) callback)
116 local float lleft, i, j, wlen;
120 for (i = 0;i < strlen(s);++i)
122 if (substring(s, i, 2) == "\\n")
128 else if (substring(s, i, 1) == "\n")
133 else if (substring(s, i, 1) == " ")
143 for (j = i+1;j < strlen(s);++j)
144 // ^^ this skips over the first character of a word, which
145 // is ALWAYS part of the word
146 // this is safe since if i+1 == strlen(s), i will become
147 // strlen(s)-1 at the end of this block and the function
148 // will terminate. A space can't be the first character we
149 // read here, and neither can a \n be the start, since these
150 // two cases have been handled above.
152 c = substring(s, j, 1);
159 // we need to keep this tempstring alive even if substring is
160 // called repeatedly, so call strcat even though we're not
170 callback(substring(s, i, wlen));
171 lleft = lleft - wlen;
178 float dist_point_line(vector p, vector l0, vector ldir)
180 ldir = normalize(ldir);
182 // remove the component in line direction
183 p = p - (p * ldir) * ldir;
185 // vlen of the remaining vector
189 void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
218 float median(float a, float b, float c)
221 return bound(a, b, c);
222 return bound(c, b, a);
225 // converts a number to a string with the indicated number of decimals
226 // works for up to 10 decimals!
227 string ftos_decimals(float number, float decimals)
231 float len, isNegative;
234 number = floor(number * pow(10, decimals) + 0.5);
240 return strcat("0.", substring("0000000000", 0, decimals));
248 // it now is always positive!
251 result = ftos(number);
252 len = strlen(result);
253 // does it have a decimal point (should not happen)? If there is one, it is always at len-7)
254 // if ftos had messed it up, which should never happen: "34278.000000"
256 if(substring(result, len - 7, 1) == ".")
258 dprint("ftos(integer) has comma? Can't be. Affected result: ", result, "\n");
259 result = substring(result, 0, len - 7);
264 return result; // don't insert a point for zero decimals
265 // is it too short? If yes, insert leading zeroes
268 result = strcat(substring("0000000000", 0, decimals - len + 1), result);
271 // and now... INSERT THE POINT!
272 tmp = substring(result, len - decimals, decimals);
273 result = strcat(substring(result, 0, len - decimals), ".", tmp);
275 return strcat("-", result); // restore the sign
280 vector colormapPaletteColor(float c, float isPants)
284 case 0: return '0.800000 0.800000 0.800000';
285 case 1: return '0.600000 0.400000 0.000000';
286 case 2: return '0.000000 1.000000 0.501961';
287 case 3: return '0.000000 1.000000 0.000000';
288 case 4: return '1.000000 0.000000 0.000000';
289 case 5: return '0.000000 0.658824 1.000000';
290 case 6: return '0.000000 1.000000 1.000000';
291 case 7: return '0.501961 1.000000 0.000000';
292 case 8: return '0.501961 0.000000 1.000000';
293 case 9: return '1.000000 0.000000 1.000000';
294 case 10: return '1.000000 0.000000 0.501961';
295 case 11: return '0.600000 0.600000 0.600000';
296 case 12: return '1.000000 1.000000 0.000000';
297 case 13: return '0.000000 0.313725 1.000000';
298 case 14: return '1.000000 0.501961 0.000000';
302 '1 0 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 0.0000000000))
303 + '0 1 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 2.0943951024))
304 + '0 0 1' * (0.502 + 0.498 * sin(time / 2.7182818285 + 4.1887902048));
307 '1 0 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 5.2359877560))
308 + '0 1 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 3.1415926536))
309 + '0 0 1' * (0.502 + 0.498 * sin(time / 3.1415926536 + 1.0471975512));
310 default: return '0.000 0.000 0.000';
314 // unzone the string, and return it as tempstring. Safe to be called on string_null
315 string fstrunzone(string s)
325 // Databases (hash tables)
326 #define DB_BUCKETS 8192
327 void db_save(float db, string pFilename)
330 fh = fopen(pFilename, FILE_WRITE);
333 print(strcat("^1Can't write DB to ", pFilename));
337 fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
338 for(i = 0; i < n; ++i)
339 fputs(fh, strcat(bufstr_get(db, i), "\n"));
348 float db_load(string pFilename)
350 float db, fh, i, j, n;
355 fh = fopen(pFilename, FILE_READ);
358 if(stof(fgets(fh)) == DB_BUCKETS)
361 while((l = fgets(fh)))
364 bufstr_set(db, i, l);
370 // different count of buckets?
371 // need to reorganize the database then (SLOW)
372 while((l = fgets(fh)))
374 n = tokenizebyseparator(l, "\\");
375 for(j = 2; j < n; j += 2)
376 db_put(db, argv(j-1), uri_unescape(argv(j)));
383 void db_dump(float db, string pFilename)
385 float fh, i, j, n, m;
386 fh = fopen(pFilename, FILE_WRITE);
388 error(strcat("Can't dump DB to ", pFilename));
391 for(i = 0; i < n; ++i)
393 m = tokenizebyseparator(bufstr_get(db, i), "\\");
394 for(j = 2; j < m; j += 2)
395 fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n"));
400 void db_close(float db)
405 string db_get(float db, string pKey)
408 h = mod(crc16(FALSE, pKey), DB_BUCKETS);
409 return uri_unescape(infoget(bufstr_get(db, h), pKey));
412 void db_put(float db, string pKey, string pValue)
415 h = mod(crc16(FALSE, pKey), DB_BUCKETS);
416 bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue)));
423 db = db_load("foo.db");
424 print("LOADED. FILL...\n");
425 for(i = 0; i < DB_BUCKETS; ++i)
426 db_put(db, ftos(random()), "X");
427 print("FILLED. SAVE...\n");
428 db_save(db, "foo.db");
429 print("SAVED. CLOSE...\n");
434 // Multiline text file buffers
435 float buf_load(string pFilename)
442 fh = fopen(pFilename, FILE_READ);
446 while((l = fgets(fh)))
448 bufstr_set(buf, i, l);
455 void buf_save(float buf, string pFilename)
458 fh = fopen(pFilename, FILE_WRITE);
460 error(strcat("Can't write buf to ", pFilename));
461 n = buf_getsize(buf);
462 for(i = 0; i < n; ++i)
463 fputs(fh, strcat(bufstr_get(buf, i), "\n"));
467 string GametypeNameFromType(float g)
469 if (g == GAME_DEATHMATCH) return "dm";
470 else if (g == GAME_TEAM_DEATHMATCH) return "tdm";
471 else if (g == GAME_DOMINATION) return "dom";
472 else if (g == GAME_CTF) return "ctf";
473 else if (g == GAME_RUNEMATCH) return "rune";
474 else if (g == GAME_LMS) return "lms";
475 else if (g == GAME_ARENA) return "arena";
476 else if (g == GAME_CA) return "ca";
477 else if (g == GAME_KEYHUNT) return "kh";
478 else if (g == GAME_ONSLAUGHT) return "ons";
479 else if (g == GAME_ASSAULT) return "as";
480 else if (g == GAME_RACE) return "rc";
481 else if (g == GAME_NEXBALL) return "nexball";
482 else if (g == GAME_CTS) return "cts";
486 string mmsss(float tenths)
490 tenths = floor(tenths + 0.5);
491 minutes = floor(tenths / 600);
492 tenths -= minutes * 600;
493 s = ftos(1000 + tenths);
494 return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1));
497 string mmssss(float hundredths)
501 hundredths = floor(hundredths + 0.5);
502 minutes = floor(hundredths / 6000);
503 hundredths -= minutes * 6000;
504 s = ftos(10000 + hundredths);
505 return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 2));
508 string ScoreString(float pFlags, float pValue)
513 pValue = floor(pValue + 0.5); // round
515 if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME)))
517 else if(pFlags & SFL_RANK)
519 valstr = ftos(pValue);
521 if((l >= 2) && (substring(valstr, l - 2, 1) == "1"))
522 valstr = strcat(valstr, "th");
523 else if(substring(valstr, l - 1, 1) == "1")
524 valstr = strcat(valstr, "st");
525 else if(substring(valstr, l - 1, 1) == "2")
526 valstr = strcat(valstr, "nd");
527 else if(substring(valstr, l - 1, 1) == "3")
528 valstr = strcat(valstr, "rd");
530 valstr = strcat(valstr, "th");
532 else if(pFlags & SFL_TIME)
533 valstr = TIME_ENCODED_TOSTRING(pValue);
535 valstr = ftos(pValue);
540 vector cross(vector a, vector b)
543 '1 0 0' * (a_y * b_z - a_z * b_y)
544 + '0 1 0' * (a_z * b_x - a_x * b_z)
545 + '0 0 1' * (a_x * b_y - a_y * b_x);
548 // compressed vector format:
549 // like MD3, just even shorter
550 // 4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90
551 // 5 bit yaw (32 angles), 0=0, 8=90, 16=180, 24=270
552 // 7 bit length (logarithmic encoding), 1/8 .. about 7844
553 // length = 2^(length_encoded/8) / 8
554 // if pitch is 90, yaw does nothing and therefore indicates the sign (yaw is then either 11111 or 11110); 11111 is pointing DOWN
555 // thus, valid values are from 0000.11110.0000000 to 1111.11111.1111111
556 // the special value 0 indicates the zero vector
558 float lengthLogTable[128];
560 float invertLengthLog(float x)
562 float l, r, m, lerr, rerr;
564 if(x >= lengthLogTable[127])
566 if(x <= lengthLogTable[0])
574 m = floor((l + r) / 2);
575 if(lengthLogTable[m] < x)
581 // now: r is >=, l is <
582 lerr = (x - lengthLogTable[l]);
583 rerr = (lengthLogTable[r] - x);
589 vector decompressShortVector(float data)
592 float pitch, yaw, len;
595 pitch = (data & 0xF000) / 0x1000;
596 yaw = (data & 0x0F80) / 0x80;
597 len = (data & 0x007F);
599 //print("\ndecompress: pitch ", ftos(pitch)); print("yaw ", ftos(yaw)); print("len ", ftos(len), "\n");
612 yaw = .19634954084936207740 * yaw;
613 pitch = .19634954084936207740 * pitch - 1.57079632679489661922;
614 out_x = cos(yaw) * cos(pitch);
615 out_y = sin(yaw) * cos(pitch);
619 //print("decompressed: ", vtos(out), "\n");
621 return out * lengthLogTable[len];
624 float compressShortVector(vector vec)
627 float pitch, yaw, len;
630 //print("compress: ", vtos(vec), "\n");
631 ang = vectoangles(vec);
635 if(ang_x < -90 && ang_x > +90)
636 error("BOGUS vectoangles");
637 //print("angles: ", vtos(ang), "\n");
639 pitch = floor(0.5 + (ang_x + 90) * 16 / 180) & 15; // -90..90 to 0..14
648 yaw = floor(0.5 + ang_y * 32 / 360) & 31; // 0..360 to 0..32
649 len = invertLengthLog(vlen(vec));
651 //print("compressed: pitch ", ftos(pitch)); print("yaw ", ftos(yaw)); print("len ", ftos(len), "\n");
653 return (pitch * 0x1000) + (yaw * 0x80) + len;
656 void compressShortVector_init()
661 for(i = 0; i < 128; ++i)
663 lengthLogTable[i] = l;
667 if(cvar("developer"))
669 print("Verifying vector compression table...\n");
670 for(i = 0x0F00; i < 0xFFFF; ++i)
671 if(i != compressShortVector(decompressShortVector(i)))
673 print("BROKEN vector compression: ", ftos(i));
674 print(" -> ", vtos(decompressShortVector(i)));
675 print(" -> ", ftos(compressShortVector(decompressShortVector(i))));
684 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
686 traceline(v0, v0 + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
687 traceline(v0, v0 + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
688 traceline(v0, v0 + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
689 traceline(v0 + dvx, v0 + dvx + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
690 traceline(v0 + dvx, v0 + dvx + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
691 traceline(v0 + dvy, v0 + dvy + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
692 traceline(v0 + dvy, v0 + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
693 traceline(v0 + dvz, v0 + dvz + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
694 traceline(v0 + dvz, v0 + dvz + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
695 traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
696 traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
697 traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
702 string fixPriorityList(string order, float from, float to, float subtract, float complete)
707 n = tokenize_console(order);
709 for(i = 0; i < n; ++i)
714 if(w >= from && w <= to)
715 neworder = strcat(neworder, ftos(w), " ");
719 if(w >= from && w <= to)
720 neworder = strcat(neworder, ftos(w), " ");
727 n = tokenize_console(neworder);
728 for(w = to; w >= from; --w)
730 for(i = 0; i < n; ++i)
731 if(stof(argv(i)) == w)
733 if(i == n) // not found
734 neworder = strcat(neworder, ftos(w), " ");
738 return substring(neworder, 0, strlen(neworder) - 1);
741 string mapPriorityList(string order, string(string) mapfunc)
746 n = tokenize_console(order);
748 for(i = 0; i < n; ++i)
749 neworder = strcat(neworder, mapfunc(argv(i)), " ");
751 return substring(neworder, 0, strlen(neworder) - 1);
754 string swapInPriorityList(string order, float i, float j)
759 n = tokenize_console(order);
761 if(i >= 0 && i < n && j >= 0 && j < n && i != j)
764 for(w = 0; w < n; ++w)
767 s = strcat(s, argv(j), " ");
769 s = strcat(s, argv(i), " ");
771 s = strcat(s, argv(w), " ");
773 return substring(s, 0, strlen(s) - 1);
779 float cvar_value_issafe(string s)
781 if(strstrofs(s, "\"", 0) >= 0)
783 if(strstrofs(s, "\\", 0) >= 0)
785 if(strstrofs(s, ";", 0) >= 0)
787 if(strstrofs(s, "$", 0) >= 0)
789 if(strstrofs(s, "\r", 0) >= 0)
791 if(strstrofs(s, "\n", 0) >= 0)
797 void get_mi_min_max(float mode)
802 strunzone(mi_shortname);
803 mi_shortname = mapname;
804 if(!strcasecmp(substring(mi_shortname, 0, 5), "maps/"))
805 mi_shortname = substring(mi_shortname, 5, strlen(mi_shortname) - 5);
806 if(!strcasecmp(substring(mi_shortname, strlen(mi_shortname) - 4, 4), ".bsp"))
807 mi_shortname = substring(mi_shortname, 0, strlen(mi_shortname) - 4);
808 mi_shortname = strzone(mi_shortname);
820 MapInfo_Get_ByName(mi_shortname, 0, 0);
821 if(MapInfo_Map_mins_x < MapInfo_Map_maxs_x)
823 mi_min = MapInfo_Map_mins;
824 mi_max = MapInfo_Map_maxs;
832 tracebox('1 0 0' * mi_x,
833 '0 1 0' * mi_y + '0 0 1' * mi_z,
834 '0 1 0' * ma_y + '0 0 1' * ma_z,
838 if(!trace_startsolid)
839 mi_min_x = trace_endpos_x;
841 tracebox('0 1 0' * mi_y,
842 '1 0 0' * mi_x + '0 0 1' * mi_z,
843 '1 0 0' * ma_x + '0 0 1' * ma_z,
847 if(!trace_startsolid)
848 mi_min_y = trace_endpos_y;
850 tracebox('0 0 1' * mi_z,
851 '1 0 0' * mi_x + '0 1 0' * mi_y,
852 '1 0 0' * ma_x + '0 1 0' * ma_y,
856 if(!trace_startsolid)
857 mi_min_z = trace_endpos_z;
859 tracebox('1 0 0' * ma_x,
860 '0 1 0' * mi_y + '0 0 1' * mi_z,
861 '0 1 0' * ma_y + '0 0 1' * ma_z,
865 if(!trace_startsolid)
866 mi_max_x = trace_endpos_x;
868 tracebox('0 1 0' * ma_y,
869 '1 0 0' * mi_x + '0 0 1' * mi_z,
870 '1 0 0' * ma_x + '0 0 1' * ma_z,
874 if(!trace_startsolid)
875 mi_max_y = trace_endpos_y;
877 tracebox('0 0 1' * ma_z,
878 '1 0 0' * mi_x + '0 1 0' * mi_y,
879 '1 0 0' * ma_x + '0 1 0' * ma_y,
883 if(!trace_startsolid)
884 mi_max_z = trace_endpos_z;
889 void get_mi_min_max_texcoords(float mode)
893 get_mi_min_max(mode);
898 // extend mi_picmax to get a square aspect ratio
899 // center the map in that area
900 extend = mi_picmax - mi_picmin;
901 if(extend_y > extend_x)
903 mi_picmin_x -= (extend_y - extend_x) * 0.5;
904 mi_picmax_x += (extend_y - extend_x) * 0.5;
908 mi_picmin_y -= (extend_x - extend_y) * 0.5;
909 mi_picmax_y += (extend_x - extend_y) * 0.5;
912 // add another some percent
913 extend = (mi_picmax - mi_picmin) * (1 / 64.0);
917 // calculate the texcoords
918 mi_pictexcoord0 = mi_pictexcoord1 = mi_pictexcoord2 = mi_pictexcoord3 = '0 0 0';
919 // first the two corners of the origin
920 mi_pictexcoord0_x = (mi_min_x - mi_picmin_x) / (mi_picmax_x - mi_picmin_x);
921 mi_pictexcoord0_y = (mi_min_y - mi_picmin_y) / (mi_picmax_y - mi_picmin_y);
922 mi_pictexcoord2_x = (mi_max_x - mi_picmin_x) / (mi_picmax_x - mi_picmin_x);
923 mi_pictexcoord2_y = (mi_max_y - mi_picmin_y) / (mi_picmax_y - mi_picmin_y);
924 // then the other corners
925 mi_pictexcoord1_x = mi_pictexcoord0_x;
926 mi_pictexcoord1_y = mi_pictexcoord2_y;
927 mi_pictexcoord3_x = mi_pictexcoord2_x;
928 mi_pictexcoord3_y = mi_pictexcoord0_y;
933 void cvar_settemp(string pKey, string pValue)
935 error("cvar_settemp called from CSQC - use cvar_clientsettemp instead!");
937 void cvar_settemp_restore()
939 error("cvar_settemp_restore called from CSQC - use cvar_clientsettemp instead!");
942 void cvar_settemp(string pKey, string pValue)
946 if(cvar_string(pKey) == pValue)
948 i = cvar("settemp_idx");
949 cvar_set("settemp_idx", ftos(i+1));
950 settemp_var = strcat("_settemp_x", ftos(i));
952 registercvar(settemp_var, "", 0);
954 registercvar(settemp_var, "");
956 cvar_set("settemp_list", strcat("1 ", pKey, " ", settemp_var, " ", cvar_string("settemp_list")));
957 cvar_set(settemp_var, cvar_string(pKey));
958 cvar_set(pKey, pValue);
961 void cvar_settemp_restore()
963 // undo what cvar_settemp did
965 n = tokenize_console(cvar_string("settemp_list"));
966 for(i = 0; i < n - 3; i += 3)
967 cvar_set(argv(i + 1), cvar_string(argv(i + 2)));
968 cvar_set("settemp_list", "0");
972 float almost_equals(float a, float b)
975 eps = (max(a, -a) + max(b, -b)) * 0.001;
976 if(a - b < eps && b - a < eps)
981 float almost_in_bounds(float a, float b, float c)
984 eps = (max(a, -a) + max(c, -c)) * 0.001;
985 return b == median(a - eps, b, c + eps);
988 float power2of(float e)
992 float log2of(float x)
994 // NOTE: generated code
1067 float rgb_mi_ma_to_hue(vector rgb, float mi, float ma)
1071 else if(ma == rgb_x)
1074 return (rgb_y - rgb_z) / (ma - mi);
1076 return (rgb_y - rgb_z) / (ma - mi) + 6;
1078 else if(ma == rgb_y)
1079 return (rgb_z - rgb_x) / (ma - mi) + 2;
1080 else // if(ma == rgb_z)
1081 return (rgb_x - rgb_y) / (ma - mi) + 4;
1084 vector hue_mi_ma_to_rgb(float hue, float mi, float ma)
1088 hue -= 6 * floor(hue / 6);
1090 //else if(ma == rgb_x)
1091 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
1095 rgb_y = hue * (ma - mi) + mi;
1098 //else if(ma == rgb_y)
1099 // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120;
1102 rgb_x = (2 - hue) * (ma - mi) + mi;
1110 rgb_z = (hue - 2) * (ma - mi) + mi;
1112 //else // if(ma == rgb_z)
1113 // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240;
1117 rgb_y = (4 - hue) * (ma - mi) + mi;
1122 rgb_x = (hue - 4) * (ma - mi) + mi;
1126 //else if(ma == rgb_x)
1127 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
1128 else // if(hue <= 6)
1132 rgb_z = (6 - hue) * (ma - mi) + mi;
1138 vector rgb_to_hsv(vector rgb)
1143 mi = min3(rgb_x, rgb_y, rgb_z);
1144 ma = max3(rgb_x, rgb_y, rgb_z);
1146 hsv_x = rgb_mi_ma_to_hue(rgb, mi, ma);
1157 vector hsv_to_rgb(vector hsv)
1159 return hue_mi_ma_to_rgb(hsv_x, hsv_z * (1 - hsv_y), hsv_z);
1162 vector rgb_to_hsl(vector rgb)
1167 mi = min3(rgb_x, rgb_y, rgb_z);
1168 ma = max3(rgb_x, rgb_y, rgb_z);
1170 hsl_x = rgb_mi_ma_to_hue(rgb, mi, ma);
1172 hsl_z = 0.5 * (mi + ma);
1175 else if(hsl_z <= 0.5)
1176 hsl_y = (ma - mi) / (2*hsl_z);
1177 else // if(hsl_z > 0.5)
1178 hsl_y = (ma - mi) / (2 - 2*hsl_z);
1183 vector hsl_to_rgb(vector hsl)
1185 float mi, ma, maminusmi;
1188 maminusmi = hsl_y * 2 * hsl_z;
1190 maminusmi = hsl_y * (2 - 2 * hsl_z);
1192 // hsl_z = 0.5 * mi + 0.5 * ma
1193 // maminusmi = - mi + ma
1194 mi = hsl_z - 0.5 * maminusmi;
1195 ma = hsl_z + 0.5 * maminusmi;
1197 return hue_mi_ma_to_rgb(hsl_x, mi, ma);
1200 string rgb_to_hexcolor(vector rgb)
1205 DEC_TO_HEXDIGIT(floor(rgb_x * 15 + 0.5)),
1206 DEC_TO_HEXDIGIT(floor(rgb_y * 15 + 0.5)),
1207 DEC_TO_HEXDIGIT(floor(rgb_z * 15 + 0.5))
1211 // requires that m2>m1 in all coordinates, and that m4>m3
1212 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;};
1214 // requires the same, but is a stronger condition
1215 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;};
1220 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
1222 float ICanHasKallerz;
1224 // detect color codes support in the width function
1225 ICanHasKallerz = (w("^7", theSize) == 0);
1228 // The following function is SLOW.
1229 // For your safety and for the protection of those around you...
1230 // DO NOT CALL THIS AT HOME.
1231 // No really, don't.
1232 if(w(theText, theSize) <= maxWidth)
1233 return strlen(theText); // yeah!
1235 // binary search for right place to cut string
1237 float left, right, middle; // this always works
1239 right = strlen(theText); // this always fails
1242 middle = floor((left + right) / 2);
1243 if(w(substring(theText, 0, middle), theSize) <= maxWidth)
1248 while(left < right - 1);
1252 // NOTE: when color codes are involved, this binary search is,
1253 // mathematically, BROKEN. However, it is obviously guaranteed to
1254 // terminate, as the range still halves each time - but nevertheless, it is
1255 // guaranteed that it finds ONE valid cutoff place (where "left" is in
1256 // range, and "right" is outside).
1258 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
1259 // and decrease left on the basis of the chars detected of the truncated tag
1260 // Even if the ^xrgb tag is not complete/correct, left is decreased
1261 // (sometimes too much but with a correct result)
1262 // it fixes also ^[0-9]
1263 while(left >= 1 && substring(theText, left-1, 1) == "^")
1266 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
1268 else if (left >= 3 && substring(theText, left-3, 2) == "^x")
1270 ch = str2chr(theText, left-1);
1271 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
1274 else if (left >= 4 && substring(theText, left-4, 2) == "^x")
1276 ch = str2chr(theText, left-2);
1277 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
1279 ch = str2chr(theText, left-1);
1280 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
1289 float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w)
1291 float ICanHasKallerz;
1293 // detect color codes support in the width function
1294 ICanHasKallerz = (w("^7") == 0);
1297 // The following function is SLOW.
1298 // For your safety and for the protection of those around you...
1299 // DO NOT CALL THIS AT HOME.
1300 // No really, don't.
1301 if(w(theText) <= maxWidth)
1302 return strlen(theText); // yeah!
1304 // binary search for right place to cut string
1306 float left, right, middle; // this always works
1308 right = strlen(theText); // this always fails
1311 middle = floor((left + right) / 2);
1312 if(w(substring(theText, 0, middle)) <= maxWidth)
1317 while(left < right - 1);
1321 // NOTE: when color codes are involved, this binary search is,
1322 // mathematically, BROKEN. However, it is obviously guaranteed to
1323 // terminate, as the range still halves each time - but nevertheless, it is
1324 // guaranteed that it finds ONE valid cutoff place (where "left" is in
1325 // range, and "right" is outside).
1327 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
1328 // and decrease left on the basis of the chars detected of the truncated tag
1329 // Even if the ^xrgb tag is not complete/correct, left is decreased
1330 // (sometimes too much but with a correct result)
1331 // it fixes also ^[0-9]
1332 while(left >= 1 && substring(theText, left-1, 1) == "^")
1335 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
1337 else if (left >= 3 && substring(theText, left-3, 2) == "^x")
1339 ch = str2chr(theText, left-1);
1340 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
1343 else if (left >= 4 && substring(theText, left-4, 2) == "^x")
1345 ch = str2chr(theText, left-2);
1346 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
1348 ch = str2chr(theText, left-1);
1349 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
1358 string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1364 s = getWrappedLine_remaining;
1366 cantake = textLengthUpToWidth(s, w, theFontSize, tw);
1367 if(cantake > 0 && cantake < strlen(s))
1370 while(take > 0 && substring(s, take, 1) != " ")
1374 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
1375 if(getWrappedLine_remaining == "")
1376 getWrappedLine_remaining = string_null;
1377 return substring(s, 0, cantake);
1381 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
1382 if(getWrappedLine_remaining == "")
1383 getWrappedLine_remaining = string_null;
1384 return substring(s, 0, take);
1389 getWrappedLine_remaining = string_null;
1394 string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw)
1400 s = getWrappedLine_remaining;
1402 cantake = textLengthUpToLength(s, w, tw);
1403 if(cantake > 0 && cantake < strlen(s))
1406 while(take > 0 && substring(s, take, 1) != " ")
1410 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
1411 if(getWrappedLine_remaining == "")
1412 getWrappedLine_remaining = string_null;
1413 return substring(s, 0, cantake);
1417 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
1418 if(getWrappedLine_remaining == "")
1419 getWrappedLine_remaining = string_null;
1420 return substring(s, 0, take);
1425 getWrappedLine_remaining = string_null;
1430 string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1432 if(tw(theText, theFontSize) <= maxWidth)
1435 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("...", theFontSize), theFontSize, tw)), "...");
1438 string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw)
1440 if(tw(theText) <= maxWidth)
1443 return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
1446 float isGametypeInFilter(float gt, float tp, string pattern)
1448 string subpattern, subpattern2, subpattern3;
1449 subpattern = strcat(",", GametypeNameFromType(gt), ",");
1451 subpattern2 = ",teams,";
1453 subpattern2 = ",noteams,";
1454 if(gt == GAME_RACE || gt == GAME_CTS)
1455 subpattern3 = ",race,";
1457 subpattern3 = string_null;
1459 if(substring(pattern, 0, 1) == "-")
1461 pattern = substring(pattern, 1, strlen(pattern) - 1);
1462 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
1464 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0)
1466 if(subpattern3 && strstrofs(strcat(",", pattern, ","), subpattern3, 0) >= 0)
1471 if(substring(pattern, 0, 1) == "+")
1472 pattern = substring(pattern, 1, strlen(pattern) - 1);
1473 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
1474 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0)
1475 if((!subpattern3) || strstrofs(strcat(",", pattern, ","), subpattern3, 0) < 0)
1481 void shuffle(float n, swapfunc_t swap, entity pass)
1484 for(i = 1; i < n; ++i)
1486 // swap i-th item at a random position from 0 to i
1487 // proof for even distribution:
1490 // item n+1 gets at any position with chance 1/(n+1)
1491 // all others will get their 1/n chance reduced by factor n/(n+1)
1492 // to be on place n+1, their chance will be 1/(n+1)
1493 // 1/n * n/(n+1) = 1/(n+1)
1495 j = floor(random() * (i + 1));
1501 string substring_range(string s, float b, float e)
1503 return substring(s, b, e - b);
1506 string swapwords(string str, float i, float j)
1509 string s1, s2, s3, s4, s5;
1510 float si, ei, sj, ej, s0, en;
1511 n = tokenizebyseparator(str, " "); // must match g_maplist processing in ShuffleMaplist and "shuffle"
1512 si = argv_start_index(i);
1513 sj = argv_start_index(j);
1514 ei = argv_end_index(i);
1515 ej = argv_end_index(j);
1516 s0 = argv_start_index(0);
1517 en = argv_end_index(n-1);
1518 s1 = substring_range(str, s0, si);
1519 s2 = substring_range(str, si, ei);
1520 s3 = substring_range(str, ei, sj);
1521 s4 = substring_range(str, sj, ej);
1522 s5 = substring_range(str, ej, en);
1523 return strcat(s1, s4, s3, s2, s5);
1526 string _shufflewords_str;
1527 void _shufflewords_swapfunc(float i, float j, entity pass)
1529 _shufflewords_str = swapwords(_shufflewords_str, i, j);
1531 string shufflewords(string str)
1534 _shufflewords_str = str;
1535 n = tokenizebyseparator(str, " ");
1536 shuffle(n, _shufflewords_swapfunc, world);
1537 str = _shufflewords_str;
1538 _shufflewords_str = string_null;
1542 vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0
1558 // actually, every number solves the equation!
1569 if(a > 0) // put the smaller solution first
1571 v_x = ((-b)-D) / (2*a);
1572 v_y = ((-b)+D) / (2*a);
1576 v_x = (-b+D) / (2*a);
1577 v_y = (-b-D) / (2*a);
1583 // complex solutions!
1597 float _unacceptable_compiler_bug_1_a(float b, float c) { return b == c; }
1598 float _unacceptable_compiler_bug_1_b() { return 1; }
1599 float _unacceptable_compiler_bug_1_c(float d) { return 2 * d; }
1600 float _unacceptable_compiler_bug_1_d() { return 1; }
1602 void check_unacceptable_compiler_bugs()
1604 if(cvar("_allow_unacceptable_compiler_bugs"))
1606 tokenize_console("foo bar");
1607 if(strcat(argv(0), substring("foo bar", 4, 7 - argv_start_index(1))) == "barbar")
1608 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.");
1611 float compressShotOrigin(vector v)
1615 y = rint(v_y * 4) + 128;
1616 z = rint(v_z * 4) + 128;
1617 if(x > 255 || x < 0)
1619 print("shot origin ", vtos(v), " x out of bounds\n");
1620 x = bound(0, x, 255);
1622 if(y > 255 || y < 0)
1624 print("shot origin ", vtos(v), " y out of bounds\n");
1625 y = bound(0, y, 255);
1627 if(z > 255 || z < 0)
1629 print("shot origin ", vtos(v), " z out of bounds\n");
1630 z = bound(0, z, 255);
1632 return x * 0x10000 + y * 0x100 + z;
1634 vector decompressShotOrigin(float f)
1637 v_x = ((f & 0xFF0000) / 0x10000) / 2;
1638 v_y = ((f & 0xFF00) / 0x100 - 128) / 4;
1639 v_z = ((f & 0xFF) - 128) / 4;
1643 void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass)
1645 float start, end, root, child;
1648 start = floor((n - 2) / 2);
1651 // siftdown(start, count-1);
1653 while(root * 2 + 1 <= n-1)
1655 child = root * 2 + 1;
1657 if(cmp(child, child+1, pass) < 0)
1659 if(cmp(root, child, pass) < 0)
1661 swap(root, child, pass);
1677 // siftdown(0, end);
1679 while(root * 2 + 1 <= end)
1681 child = root * 2 + 1;
1682 if(child < end && cmp(child, child+1, pass) < 0)
1684 if(cmp(root, child, pass) < 0)
1686 swap(root, child, pass);
1696 void RandomSelection_Init()
1698 RandomSelection_totalweight = 0;
1699 RandomSelection_chosen_ent = world;
1700 RandomSelection_chosen_float = 0;
1701 RandomSelection_chosen_string = string_null;
1702 RandomSelection_best_priority = -1;
1704 void RandomSelection_Add(entity e, float f, string s, float weight, float priority)
1706 if(priority > RandomSelection_best_priority)
1708 RandomSelection_best_priority = priority;
1709 RandomSelection_chosen_ent = e;
1710 RandomSelection_chosen_float = f;
1711 RandomSelection_chosen_string = s;
1712 RandomSelection_totalweight = weight;
1714 else if(priority == RandomSelection_best_priority)
1716 RandomSelection_totalweight += weight;
1717 if(random() * RandomSelection_totalweight <= weight)
1719 RandomSelection_chosen_ent = e;
1720 RandomSelection_chosen_float = f;
1721 RandomSelection_chosen_string = s;
1726 vector healtharmor_maxdamage(float h, float a, float armorblock)
1728 // NOTE: we'll always choose the SMALLER value...
1729 float healthdamage, armordamage, armorideal;
1731 healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health
1732 armordamage = a + (h - 1); // damage we can take if we could use more armor
1733 armorideal = healthdamage * armorblock;
1735 if(armordamage < healthdamage)
1748 vector healtharmor_applydamage(float a, float armorblock, float damage)
1751 v_y = bound(0, damage * armorblock, a); // save
1752 v_x = bound(0, damage - v_y, damage); // take
1757 string getcurrentmod()
1761 m = cvar_string("fs_gamedir");
1762 n = tokenize_console(m);
1774 v = ReadShort() * 256; // note: this is signed
1775 v += ReadByte(); // note: this is unsigned
1779 void WriteInt24_t(float dest, float val)
1782 WriteShort(dest, (v = floor(val / 256)));
1783 WriteByte(dest, val - v * 256); // 0..255
1788 float float2range11(float f)
1790 // continuous function mapping all reals into -1..1
1791 return f / (fabs(f) + 1);
1794 float float2range01(float f)
1796 // continuous function mapping all reals into 0..1
1797 return 0.5 + 0.5 * float2range11(f);
1800 // from the GNU Scientific Library
1801 float gsl_ran_gaussian_lastvalue;
1802 float gsl_ran_gaussian_lastvalue_set;
1803 float gsl_ran_gaussian(float sigma)
1806 if(gsl_ran_gaussian_lastvalue_set)
1808 gsl_ran_gaussian_lastvalue_set = 0;
1809 return sigma * gsl_ran_gaussian_lastvalue;
1813 a = random() * 2 * M_PI;
1814 b = sqrt(-2 * log(random()));
1815 gsl_ran_gaussian_lastvalue = cos(a) * b;
1816 gsl_ran_gaussian_lastvalue_set = 1;
1817 return sigma * sin(a) * b;
1821 string car(string s)
1824 o = strstrofs(s, " ", 0);
1827 return substring(s, 0, o);
1829 string cdr(string s)
1832 o = strstrofs(s, " ", 0);
1835 return substring(s, o + 1, strlen(s) - (o + 1));
1837 float matchacl(string acl, string str)
1844 t = car(acl); acl = cdr(acl);
1846 if(substring(t, 0, 1) == "-")
1849 t = substring(t, 1, strlen(t) - 1);
1851 else if(substring(t, 0, 1) == "+")
1852 t = substring(t, 1, strlen(t) - 1);
1853 if(substring(t, -1, 1) == "*")
1855 t = substring(t, 0, strlen(t) - 1);
1856 s = substring(s, 0, strlen(t));
1868 float startsWith(string haystack, string needle)
1870 return substring(haystack, 0, strlen(needle)) == needle;
1872 float startsWithNocase(string haystack, string needle)
1874 return strcasecmp(substring(haystack, 0, strlen(needle)), needle) == 0;
1877 string get_model_datafilename(string m, float sk, string fil)
1882 m = "models/player/*_";
1884 m = strcat(m, ftos(sk));
1887 return strcat(m, ".", fil);
1890 float get_model_parameters(string m, float sk)
1895 get_model_parameters_modelname = string_null;
1896 get_model_parameters_modelskin = -1;
1897 get_model_parameters_name = string_null;
1898 get_model_parameters_species = -1;
1899 get_model_parameters_sex = string_null;
1900 get_model_parameters_weight = -1;
1901 get_model_parameters_age = -1;
1902 get_model_parameters_desc = string_null;
1908 if(substring(m, -4, -1) != ".txt")
1910 if(substring(m, -6, 1) != "_")
1912 sk = stof(substring(m, -5, 1));
1913 m = substring(m, 0, -7);
1916 fn = get_model_datafilename(m, sk, "txt");
1917 fh = fopen(fn, FILE_READ);
1921 get_model_parameters_modelname = m;
1922 get_model_parameters_modelskin = sk;
1923 while((s = fgets(fh)))
1926 break; // next lines will be description
1930 get_model_parameters_name = s;
1934 case "human": get_model_parameters_species = SPECIES_HUMAN; break;
1935 case "alien": get_model_parameters_species = SPECIES_ALIEN; break;
1936 case "robot_shiny": get_model_parameters_species = SPECIES_ROBOT_SHINY; break;
1937 case "robot_rusty": get_model_parameters_species = SPECIES_ROBOT_RUSTY; break;
1938 case "robot_solid": get_model_parameters_species = SPECIES_ROBOT_SOLID; break;
1939 case "animal": get_model_parameters_species = SPECIES_ANIMAL; break;
1940 case "reserved": get_model_parameters_species = SPECIES_RESERVED; break;
1943 get_model_parameters_sex = s;
1945 get_model_parameters_weight = stof(s);
1947 get_model_parameters_age = stof(s);
1950 while((s = fgets(fh)))
1952 if(get_model_parameters_desc)
1953 get_model_parameters_desc = strcat(get_model_parameters_desc, "\n");
1955 get_model_parameters_desc = strcat(get_model_parameters_desc, s);
1963 // return name of given panel id
1964 string HUD_Panel_GetName(float id)
1967 case HUD_PANEL_WEAPONICONS: return "weaponicons"; break;
1968 case HUD_PANEL_INVENTORY: return "inventory"; break;
1969 case HUD_PANEL_POWERUPS: return "powerups"; break;
1970 case HUD_PANEL_HEALTHARMOR: return "healtharmor"; break;
1971 case HUD_PANEL_NOTIFY: return "notify"; break;
1972 case HUD_PANEL_TIMER: return "timer"; break;
1973 case HUD_PANEL_RADAR: return "radar"; break;
1974 case HUD_PANEL_SCORE: return "score"; break;
1975 case HUD_PANEL_RACETIMER: return "racetimer"; break;
1976 case HUD_PANEL_VOTE: return "vote"; break;
1977 case HUD_PANEL_MODICONS: return "modicons"; break;
1978 case HUD_PANEL_PRESSEDKEYS: return "pressedkeys"; break;
1979 case HUD_PANEL_CHAT: return "chat"; break;
1980 case HUD_PANEL_ENGINEINFO: return "engineinfo"; break;