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"));
43 string draw_UseSkinFor(string pic)
45 if(substring(pic, 0, 1) == "/")
46 return substring(pic, 1, strlen(pic)-1);
48 return strcat(draw_currentSkin, "/", pic);
52 string unescape(string in)
57 // but it doesn't seem to be necessary in my tests at least
62 for(i = 0; i < len; ++i)
64 s = substring(in, i, 1);
67 s = substring(in, i+1, 1);
69 str = strcat(str, "\n");
71 str = strcat(str, "\\");
73 str = strcat(str, substring(in, i, 2));
83 void wordwrap_cb(string s, float l, void(string) callback)
86 float lleft, i, j, wlen;
90 for (i = 0;i < strlen(s);++i)
92 if (substring(s, i, 2) == "\\n")
98 else if (substring(s, i, 1) == "\n")
103 else if (substring(s, i, 1) == " ")
113 for (j = i+1;j < strlen(s);++j)
114 // ^^ this skips over the first character of a word, which
115 // is ALWAYS part of the word
116 // this is safe since if i+1 == strlen(s), i will become
117 // strlen(s)-1 at the end of this block and the function
118 // will terminate. A space can't be the first character we
119 // read here, and neither can a \n be the start, since these
120 // two cases have been handled above.
122 c = substring(s, j, 1);
129 // we need to keep this tempstring alive even if substring is
130 // called repeatedly, so call strcat even though we're not
140 callback(substring(s, i, wlen));
141 lleft = lleft - wlen;
148 float dist_point_line(vector p, vector l0, vector ldir)
150 ldir = normalize(ldir);
152 // remove the component in line direction
153 p = p - (p * ldir) * ldir;
155 // vlen of the remaining vector
159 void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
188 float median(float a, float b, float c)
191 return bound(a, b, c);
192 return bound(c, b, a);
195 // converts a number to a string with the indicated number of decimals
196 // works for up to 10 decimals!
197 string ftos_decimals(float number, float decimals)
199 // inhibit stupid negative zero
202 // we have sprintf...
203 return sprintf("%.*f", decimals, number);
207 vector colormapPaletteColor(float c, float isPants)
211 case 0: return '1.000000 1.000000 1.000000';
212 case 1: return '1.000000 0.333333 0.000000';
213 case 2: return '0.000000 1.000000 0.501961';
214 case 3: return '0.000000 1.000000 0.000000';
215 case 4: return '1.000000 0.000000 0.000000';
216 case 5: return '0.000000 0.666667 1.000000';
217 case 6: return '0.000000 1.000000 1.000000';
218 case 7: return '0.501961 1.000000 0.000000';
219 case 8: return '0.501961 0.000000 1.000000';
220 case 9: return '1.000000 0.000000 1.000000';
221 case 10: return '1.000000 0.000000 0.501961';
222 case 11: return '0.000000 0.000000 1.000000';
223 case 12: return '1.000000 1.000000 0.000000';
224 case 13: return '0.000000 0.333333 1.000000';
225 case 14: return '1.000000 0.666667 0.000000';
229 '1 0 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 0.0000000000))
230 + '0 1 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 2.0943951024))
231 + '0 0 1' * (0.502 + 0.498 * sin(time / 2.7182818285 + 4.1887902048));
234 '1 0 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 5.2359877560))
235 + '0 1 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 3.1415926536))
236 + '0 0 1' * (0.502 + 0.498 * sin(time / 3.1415926536 + 1.0471975512));
237 default: return '0.000 0.000 0.000';
241 // unzone the string, and return it as tempstring. Safe to be called on string_null
242 string fstrunzone(string s)
252 float fexists(string f)
255 fh = fopen(f, FILE_READ);
262 // Databases (hash tables)
263 #define DB_BUCKETS 8192
264 void db_save(float db, string pFilename)
267 fh = fopen(pFilename, FILE_WRITE);
270 print(strcat("^1Can't write DB to ", pFilename));
274 fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
275 for(i = 0; i < n; ++i)
276 fputs(fh, strcat(bufstr_get(db, i), "\n"));
285 float db_load(string pFilename)
287 float db, fh, i, j, n;
292 fh = fopen(pFilename, FILE_READ);
296 if(stof(l) == DB_BUCKETS)
299 while((l = fgets(fh)))
302 bufstr_set(db, i, l);
308 // different count of buckets, or a dump?
309 // need to reorganize the database then (SLOW)
311 // note: we also parse the first line (l) in case the DB file is
312 // missing the bucket count
315 n = tokenizebyseparator(l, "\\");
316 for(j = 2; j < n; j += 2)
317 db_put(db, argv(j-1), uri_unescape(argv(j)));
319 while((l = fgets(fh)));
325 void db_dump(float db, string pFilename)
327 float fh, i, j, n, m;
328 fh = fopen(pFilename, FILE_WRITE);
330 error(strcat("Can't dump DB to ", pFilename));
333 for(i = 0; i < n; ++i)
335 m = tokenizebyseparator(bufstr_get(db, i), "\\");
336 for(j = 2; j < m; j += 2)
337 fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n"));
342 void db_close(float db)
347 string db_get(float db, string pKey)
350 h = mod(crc16(FALSE, pKey), DB_BUCKETS);
351 return uri_unescape(infoget(bufstr_get(db, h), pKey));
354 void db_put(float db, string pKey, string pValue)
357 h = mod(crc16(FALSE, pKey), DB_BUCKETS);
358 bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue)));
365 db = db_load("foo.db");
366 print("LOADED. FILL...\n");
367 for(i = 0; i < DB_BUCKETS; ++i)
368 db_put(db, ftos(random()), "X");
369 print("FILLED. SAVE...\n");
370 db_save(db, "foo.db");
371 print("SAVED. CLOSE...\n");
376 // Multiline text file buffers
377 float buf_load(string pFilename)
384 fh = fopen(pFilename, FILE_READ);
391 while((l = fgets(fh)))
393 bufstr_set(buf, i, l);
400 void buf_save(float buf, string pFilename)
403 fh = fopen(pFilename, FILE_WRITE);
405 error(strcat("Can't write buf to ", pFilename));
406 n = buf_getsize(buf);
407 for(i = 0; i < n; ++i)
408 fputs(fh, strcat(bufstr_get(buf, i), "\n"));
412 string mmsss(float tenths)
416 tenths = floor(tenths + 0.5);
417 minutes = floor(tenths / 600);
418 tenths -= minutes * 600;
419 s = ftos(1000 + tenths);
420 return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1));
423 string mmssss(float hundredths)
427 hundredths = floor(hundredths + 0.5);
428 minutes = floor(hundredths / 6000);
429 hundredths -= minutes * 6000;
430 s = ftos(10000 + hundredths);
431 return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 2));
434 string ScoreString(float pFlags, float pValue)
439 pValue = floor(pValue + 0.5); // round
441 if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME)))
443 else if(pFlags & SFL_RANK)
445 valstr = ftos(pValue);
447 if((l >= 2) && (substring(valstr, l - 2, 1) == "1"))
448 valstr = strcat(valstr, "th");
449 else if(substring(valstr, l - 1, 1) == "1")
450 valstr = strcat(valstr, "st");
451 else if(substring(valstr, l - 1, 1) == "2")
452 valstr = strcat(valstr, "nd");
453 else if(substring(valstr, l - 1, 1) == "3")
454 valstr = strcat(valstr, "rd");
456 valstr = strcat(valstr, "th");
458 else if(pFlags & SFL_TIME)
459 valstr = TIME_ENCODED_TOSTRING(pValue);
461 valstr = ftos(pValue);
466 vector cross(vector a, vector b)
469 '1 0 0' * (a_y * b_z - a_z * b_y)
470 + '0 1 0' * (a_z * b_x - a_x * b_z)
471 + '0 0 1' * (a_x * b_y - a_y * b_x);
474 // compressed vector format:
475 // like MD3, just even shorter
476 // 4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90
477 // 5 bit yaw (32 angles), 0=0, 8=90, 16=180, 24=270
478 // 7 bit length (logarithmic encoding), 1/8 .. about 7844
479 // length = 2^(length_encoded/8) / 8
480 // if pitch is 90, yaw does nothing and therefore indicates the sign (yaw is then either 11111 or 11110); 11111 is pointing DOWN
481 // thus, valid values are from 0000.11110.0000000 to 1111.11111.1111111
482 // the special value 0 indicates the zero vector
484 float lengthLogTable[128];
486 float invertLengthLog(float x)
488 float l, r, m, lerr, rerr;
490 if(x >= lengthLogTable[127])
492 if(x <= lengthLogTable[0])
500 m = floor((l + r) / 2);
501 if(lengthLogTable[m] < x)
507 // now: r is >=, l is <
508 lerr = (x - lengthLogTable[l]);
509 rerr = (lengthLogTable[r] - x);
515 vector decompressShortVector(float data)
521 p = (data & 0xF000) / 0x1000;
522 y = (data & 0x0F80) / 0x80;
523 len = (data & 0x007F);
525 //print("\ndecompress: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n");
538 y = .19634954084936207740 * y;
539 p = .19634954084936207740 * p - 1.57079632679489661922;
540 out_x = cos(y) * cos(p);
541 out_y = sin(y) * cos(p);
545 //print("decompressed: ", vtos(out), "\n");
547 return out * lengthLogTable[len];
550 float compressShortVector(vector vec)
556 //print("compress: ", vtos(vec), "\n");
557 ang = vectoangles(vec);
561 if(ang_x < -90 && ang_x > +90)
562 error("BOGUS vectoangles");
563 //print("angles: ", vtos(ang), "\n");
565 p = floor(0.5 + (ang_x + 90) * 16 / 180) & 15; // -90..90 to 0..14
574 y = floor(0.5 + ang_y * 32 / 360) & 31; // 0..360 to 0..32
575 len = invertLengthLog(vlen(vec));
577 //print("compressed: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n");
579 return (p * 0x1000) + (y * 0x80) + len;
582 void compressShortVector_init()
587 for(i = 0; i < 128; ++i)
589 lengthLogTable[i] = l;
593 if(cvar("developer"))
595 print("Verifying vector compression table...\n");
596 for(i = 0x0F00; i < 0xFFFF; ++i)
597 if(i != compressShortVector(decompressShortVector(i)))
599 print("BROKEN vector compression: ", ftos(i));
600 print(" -> ", vtos(decompressShortVector(i)));
601 print(" -> ", ftos(compressShortVector(decompressShortVector(i))));
610 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
612 traceline(v0, v0 + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
613 traceline(v0, v0 + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
614 traceline(v0, v0 + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
615 traceline(v0 + dvx, v0 + dvx + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
616 traceline(v0 + dvx, v0 + dvx + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
617 traceline(v0 + dvy, v0 + dvy + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
618 traceline(v0 + dvy, v0 + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
619 traceline(v0 + dvz, v0 + dvz + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
620 traceline(v0 + dvz, v0 + dvz + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
621 traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
622 traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
623 traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
628 string fixPriorityList(string order, float from, float to, float subtract, float complete)
633 n = tokenize_console(order);
635 for(i = 0; i < n; ++i)
640 if(w >= from && w <= to)
641 neworder = strcat(neworder, ftos(w), " ");
645 if(w >= from && w <= to)
646 neworder = strcat(neworder, ftos(w), " ");
653 n = tokenize_console(neworder);
654 for(w = to; w >= from; --w)
656 for(i = 0; i < n; ++i)
657 if(stof(argv(i)) == w)
659 if(i == n) // not found
660 neworder = strcat(neworder, ftos(w), " ");
664 return substring(neworder, 0, strlen(neworder) - 1);
667 string mapPriorityList(string order, string(string) mapfunc)
672 n = tokenize_console(order);
674 for(i = 0; i < n; ++i)
675 neworder = strcat(neworder, mapfunc(argv(i)), " ");
677 return substring(neworder, 0, strlen(neworder) - 1);
680 string swapInPriorityList(string order, float i, float j)
685 n = tokenize_console(order);
687 if(i >= 0 && i < n && j >= 0 && j < n && i != j)
690 for(w = 0; w < n; ++w)
693 s = strcat(s, argv(j), " ");
695 s = strcat(s, argv(i), " ");
697 s = strcat(s, argv(w), " ");
699 return substring(s, 0, strlen(s) - 1);
705 float cvar_value_issafe(string s)
707 if(strstrofs(s, "\"", 0) >= 0)
709 if(strstrofs(s, "\\", 0) >= 0)
711 if(strstrofs(s, ";", 0) >= 0)
713 if(strstrofs(s, "$", 0) >= 0)
715 if(strstrofs(s, "\r", 0) >= 0)
717 if(strstrofs(s, "\n", 0) >= 0)
723 void get_mi_min_max(float mode)
728 strunzone(mi_shortname);
729 mi_shortname = mapname;
730 if(!strcasecmp(substring(mi_shortname, 0, 5), "maps/"))
731 mi_shortname = substring(mi_shortname, 5, strlen(mi_shortname) - 5);
732 if(!strcasecmp(substring(mi_shortname, strlen(mi_shortname) - 4, 4), ".bsp"))
733 mi_shortname = substring(mi_shortname, 0, strlen(mi_shortname) - 4);
734 mi_shortname = strzone(mi_shortname);
746 MapInfo_Get_ByName(mi_shortname, 0, 0);
747 if(MapInfo_Map_mins_x < MapInfo_Map_maxs_x)
749 mi_min = MapInfo_Map_mins;
750 mi_max = MapInfo_Map_maxs;
758 tracebox('1 0 0' * mi_x,
759 '0 1 0' * mi_y + '0 0 1' * mi_z,
760 '0 1 0' * ma_y + '0 0 1' * ma_z,
764 if(!trace_startsolid)
765 mi_min_x = trace_endpos_x;
767 tracebox('0 1 0' * mi_y,
768 '1 0 0' * mi_x + '0 0 1' * mi_z,
769 '1 0 0' * ma_x + '0 0 1' * ma_z,
773 if(!trace_startsolid)
774 mi_min_y = trace_endpos_y;
776 tracebox('0 0 1' * mi_z,
777 '1 0 0' * mi_x + '0 1 0' * mi_y,
778 '1 0 0' * ma_x + '0 1 0' * ma_y,
782 if(!trace_startsolid)
783 mi_min_z = trace_endpos_z;
785 tracebox('1 0 0' * ma_x,
786 '0 1 0' * mi_y + '0 0 1' * mi_z,
787 '0 1 0' * ma_y + '0 0 1' * ma_z,
791 if(!trace_startsolid)
792 mi_max_x = trace_endpos_x;
794 tracebox('0 1 0' * ma_y,
795 '1 0 0' * mi_x + '0 0 1' * mi_z,
796 '1 0 0' * ma_x + '0 0 1' * ma_z,
800 if(!trace_startsolid)
801 mi_max_y = trace_endpos_y;
803 tracebox('0 0 1' * ma_z,
804 '1 0 0' * mi_x + '0 1 0' * mi_y,
805 '1 0 0' * ma_x + '0 1 0' * ma_y,
809 if(!trace_startsolid)
810 mi_max_z = trace_endpos_z;
815 void get_mi_min_max_texcoords(float mode)
819 get_mi_min_max(mode);
824 // extend mi_picmax to get a square aspect ratio
825 // center the map in that area
826 extend = mi_picmax - mi_picmin;
827 if(extend_y > extend_x)
829 mi_picmin_x -= (extend_y - extend_x) * 0.5;
830 mi_picmax_x += (extend_y - extend_x) * 0.5;
834 mi_picmin_y -= (extend_x - extend_y) * 0.5;
835 mi_picmax_y += (extend_x - extend_y) * 0.5;
838 // add another some percent
839 extend = (mi_picmax - mi_picmin) * (1 / 64.0);
843 // calculate the texcoords
844 mi_pictexcoord0 = mi_pictexcoord1 = mi_pictexcoord2 = mi_pictexcoord3 = '0 0 0';
845 // first the two corners of the origin
846 mi_pictexcoord0_x = (mi_min_x - mi_picmin_x) / (mi_picmax_x - mi_picmin_x);
847 mi_pictexcoord0_y = (mi_min_y - mi_picmin_y) / (mi_picmax_y - mi_picmin_y);
848 mi_pictexcoord2_x = (mi_max_x - mi_picmin_x) / (mi_picmax_x - mi_picmin_x);
849 mi_pictexcoord2_y = (mi_max_y - mi_picmin_y) / (mi_picmax_y - mi_picmin_y);
850 // then the other corners
851 mi_pictexcoord1_x = mi_pictexcoord0_x;
852 mi_pictexcoord1_y = mi_pictexcoord2_y;
853 mi_pictexcoord3_x = mi_pictexcoord2_x;
854 mi_pictexcoord3_y = mi_pictexcoord0_y;
858 float cvar_settemp(string tmp_cvar, string tmp_value)
860 float created_saved_value;
863 if not(tmp_cvar || tmp_value)
865 dprint("Error: Invalid usage of cvar_settemp(string, string); !\n");
869 if(!cvar_type(tmp_cvar))
871 print(sprintf("Error: cvar %s doesn't exist!\n", tmp_cvar));
875 for(e = world; (e = find(e, classname, "saved_cvar_value")); )
876 if(e.netname == tmp_cvar)
877 goto saved; // skip creation
879 // creating a new entity to keep track of this cvar
881 e.classname = "saved_cvar_value";
882 e.netname = strzone(tmp_cvar);
883 e.message = strzone(cvar_string(tmp_cvar));
884 created_saved_value = TRUE;
886 // an entity for this cvar already exists
889 // update the cvar to the value given
890 cvar_set(tmp_cvar, tmp_value);
892 return created_saved_value;
895 float cvar_settemp_restore()
899 while((e = find(e, classname, "saved_cvar_value")))
901 if(cvar_type(e.netname))
903 cvar_set(e.netname, e.message);
908 print(sprintf("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.\n", e.netname));
914 float almost_equals(float a, float b)
917 eps = (max(a, -a) + max(b, -b)) * 0.001;
918 if(a - b < eps && b - a < eps)
923 float almost_in_bounds(float a, float b, float c)
926 eps = (max(a, -a) + max(c, -c)) * 0.001;
929 return b == median(a - eps, b, c + eps);
932 float power2of(float e)
936 float log2of(float x)
938 // NOTE: generated code
1011 float rgb_mi_ma_to_hue(vector rgb, float mi, float ma)
1015 else if(ma == rgb_x)
1018 return (rgb_y - rgb_z) / (ma - mi);
1020 return (rgb_y - rgb_z) / (ma - mi) + 6;
1022 else if(ma == rgb_y)
1023 return (rgb_z - rgb_x) / (ma - mi) + 2;
1024 else // if(ma == rgb_z)
1025 return (rgb_x - rgb_y) / (ma - mi) + 4;
1028 vector hue_mi_ma_to_rgb(float hue, float mi, float ma)
1032 hue -= 6 * floor(hue / 6);
1034 //else if(ma == rgb_x)
1035 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
1039 rgb_y = hue * (ma - mi) + mi;
1042 //else if(ma == rgb_y)
1043 // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120;
1046 rgb_x = (2 - hue) * (ma - mi) + mi;
1054 rgb_z = (hue - 2) * (ma - mi) + mi;
1056 //else // if(ma == rgb_z)
1057 // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240;
1061 rgb_y = (4 - hue) * (ma - mi) + mi;
1066 rgb_x = (hue - 4) * (ma - mi) + mi;
1070 //else if(ma == rgb_x)
1071 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
1072 else // if(hue <= 6)
1076 rgb_z = (6 - hue) * (ma - mi) + mi;
1082 vector rgb_to_hsv(vector rgb)
1087 mi = min(rgb_x, rgb_y, rgb_z);
1088 ma = max(rgb_x, rgb_y, rgb_z);
1090 hsv_x = rgb_mi_ma_to_hue(rgb, mi, ma);
1101 vector hsv_to_rgb(vector hsv)
1103 return hue_mi_ma_to_rgb(hsv_x, hsv_z * (1 - hsv_y), hsv_z);
1106 vector rgb_to_hsl(vector rgb)
1111 mi = min(rgb_x, rgb_y, rgb_z);
1112 ma = max(rgb_x, rgb_y, rgb_z);
1114 hsl_x = rgb_mi_ma_to_hue(rgb, mi, ma);
1116 hsl_z = 0.5 * (mi + ma);
1119 else if(hsl_z <= 0.5)
1120 hsl_y = (ma - mi) / (2*hsl_z);
1121 else // if(hsl_z > 0.5)
1122 hsl_y = (ma - mi) / (2 - 2*hsl_z);
1127 vector hsl_to_rgb(vector hsl)
1129 float mi, ma, maminusmi;
1132 maminusmi = hsl_y * 2 * hsl_z;
1134 maminusmi = hsl_y * (2 - 2 * hsl_z);
1136 // hsl_z = 0.5 * mi + 0.5 * ma
1137 // maminusmi = - mi + ma
1138 mi = hsl_z - 0.5 * maminusmi;
1139 ma = hsl_z + 0.5 * maminusmi;
1141 return hue_mi_ma_to_rgb(hsl_x, mi, ma);
1144 string rgb_to_hexcolor(vector rgb)
1149 DEC_TO_HEXDIGIT(floor(rgb_x * 15 + 0.5)),
1150 DEC_TO_HEXDIGIT(floor(rgb_y * 15 + 0.5)),
1151 DEC_TO_HEXDIGIT(floor(rgb_z * 15 + 0.5))
1155 // requires that m2>m1 in all coordinates, and that m4>m3
1156 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;}
1158 // requires the same, but is a stronger condition
1159 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;}
1164 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
1167 // The following function is SLOW.
1168 // For your safety and for the protection of those around you...
1169 // DO NOT CALL THIS AT HOME.
1170 // No really, don't.
1171 if(w(theText, theSize) <= maxWidth)
1172 return strlen(theText); // yeah!
1174 // binary search for right place to cut string
1176 float left, right, middle; // this always works
1178 right = strlen(theText); // this always fails
1181 middle = floor((left + right) / 2);
1182 if(w(substring(theText, 0, middle), theSize) <= maxWidth)
1187 while(left < right - 1);
1189 if(w("^7", theSize) == 0) // detect color codes support in the width function
1191 // NOTE: when color codes are involved, this binary search is,
1192 // mathematically, BROKEN. However, it is obviously guaranteed to
1193 // terminate, as the range still halves each time - but nevertheless, it is
1194 // guaranteed that it finds ONE valid cutoff place (where "left" is in
1195 // range, and "right" is outside).
1197 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
1198 // and decrease left on the basis of the chars detected of the truncated tag
1199 // Even if the ^xrgb tag is not complete/correct, left is decreased
1200 // (sometimes too much but with a correct result)
1201 // it fixes also ^[0-9]
1202 while(left >= 1 && substring(theText, left-1, 1) == "^")
1205 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
1207 else if (left >= 3 && substring(theText, left-3, 2) == "^x")
1209 ch = str2chr(theText, left-1);
1210 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
1213 else if (left >= 4 && substring(theText, left-4, 2) == "^x")
1215 ch = str2chr(theText, left-2);
1216 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
1218 ch = str2chr(theText, left-1);
1219 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
1228 float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w)
1231 // The following function is SLOW.
1232 // For your safety and for the protection of those around you...
1233 // DO NOT CALL THIS AT HOME.
1234 // No really, don't.
1235 if(w(theText) <= maxWidth)
1236 return strlen(theText); // yeah!
1238 // binary search for right place to cut string
1240 float left, right, middle; // this always works
1242 right = strlen(theText); // this always fails
1245 middle = floor((left + right) / 2);
1246 if(w(substring(theText, 0, middle)) <= maxWidth)
1251 while(left < right - 1);
1253 if(w("^7") == 0) // detect color codes support in the width function
1255 // NOTE: when color codes are involved, this binary search is,
1256 // mathematically, BROKEN. However, it is obviously guaranteed to
1257 // terminate, as the range still halves each time - but nevertheless, it is
1258 // guaranteed that it finds ONE valid cutoff place (where "left" is in
1259 // range, and "right" is outside).
1261 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
1262 // and decrease left on the basis of the chars detected of the truncated tag
1263 // Even if the ^xrgb tag is not complete/correct, left is decreased
1264 // (sometimes too much but with a correct result)
1265 // it fixes also ^[0-9]
1266 while(left >= 1 && substring(theText, left-1, 1) == "^")
1269 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
1271 else if (left >= 3 && substring(theText, left-3, 2) == "^x")
1273 ch = str2chr(theText, left-1);
1274 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
1277 else if (left >= 4 && substring(theText, left-4, 2) == "^x")
1279 ch = str2chr(theText, left-2);
1280 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
1282 ch = str2chr(theText, left-1);
1283 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
1292 string find_last_color_code(string s)
1294 float start, len, i, carets;
1295 start = strstrofs(s, "^", 0);
1296 if (start == -1) // no caret found
1299 for(i = len; i >= start; --i)
1301 if(substring(s, i, 1) != "^")
1305 while (i-carets >= start && substring(s, i-carets, 1) == "^")
1308 // check if carets aren't all escaped
1309 if (carets == 1 || mod(carets, 2) == 1) // first check is just an optimization
1312 if(strstrofs("0123456789", substring(s, i+1, 1), 0) >= 0)
1313 return substring(s, i, 2);
1316 if(substring(s, i+1, 1) == "x")
1317 if(strstrofs("0123456789abcdefABCDEF", substring(s, i+2, 1), 0) >= 0)
1318 if(strstrofs("0123456789abcdefABCDEF", substring(s, i+3, 1), 0) >= 0)
1319 if(strstrofs("0123456789abcdefABCDEF", substring(s, i+4, 1), 0) >= 0)
1320 return substring(s, i, 5);
1322 i -= carets; // this also skips one char before the carets
1328 string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1334 s = getWrappedLine_remaining;
1338 getWrappedLine_remaining = string_null;
1339 return s; // the line has no size ANYWAY, nothing would be displayed.
1342 cantake = textLengthUpToWidth(s, w, theFontSize, tw);
1343 if(cantake > 0 && cantake < strlen(s))
1346 while(take > 0 && substring(s, take, 1) != " ")
1350 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
1351 if(getWrappedLine_remaining == "")
1352 getWrappedLine_remaining = string_null;
1353 else if (tw("^7", theFontSize) == 0)
1354 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining);
1355 return substring(s, 0, cantake);
1359 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
1360 if(getWrappedLine_remaining == "")
1361 getWrappedLine_remaining = string_null;
1362 else if (tw("^7", theFontSize) == 0)
1363 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining);
1364 return substring(s, 0, take);
1369 getWrappedLine_remaining = string_null;
1374 string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw)
1380 s = getWrappedLine_remaining;
1384 getWrappedLine_remaining = string_null;
1385 return s; // the line has no size ANYWAY, nothing would be displayed.
1388 cantake = textLengthUpToLength(s, w, tw);
1389 if(cantake > 0 && cantake < strlen(s))
1392 while(take > 0 && substring(s, take, 1) != " ")
1396 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
1397 if(getWrappedLine_remaining == "")
1398 getWrappedLine_remaining = string_null;
1399 else if (tw("^7") == 0)
1400 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining);
1401 return substring(s, 0, cantake);
1405 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
1406 if(getWrappedLine_remaining == "")
1407 getWrappedLine_remaining = string_null;
1408 else if (tw("^7") == 0)
1409 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining);
1410 return substring(s, 0, take);
1415 getWrappedLine_remaining = string_null;
1420 string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1422 if(tw(theText, theFontSize) <= maxWidth)
1425 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("...", theFontSize), theFontSize, tw)), "...");
1428 string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw)
1430 if(tw(theText) <= maxWidth)
1433 return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
1436 float isGametypeInFilter(float gt, float tp, float ts, string pattern)
1438 string subpattern, subpattern2, subpattern3, subpattern4;
1439 subpattern = strcat(",", MapInfo_Type_ToString(gt), ",");
1441 subpattern2 = ",teams,";
1443 subpattern2 = ",noteams,";
1445 subpattern3 = ",teamspawns,";
1447 subpattern3 = ",noteamspawns,";
1448 if(gt == MAPINFO_TYPE_RACE || gt == MAPINFO_TYPE_CTS)
1449 subpattern4 = ",race,";
1451 subpattern4 = string_null;
1453 if(substring(pattern, 0, 1) == "-")
1455 pattern = substring(pattern, 1, strlen(pattern) - 1);
1456 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
1458 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0)
1460 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) >= 0)
1462 if(subpattern4 && strstrofs(strcat(",", pattern, ","), subpattern4, 0) >= 0)
1467 if(substring(pattern, 0, 1) == "+")
1468 pattern = substring(pattern, 1, strlen(pattern) - 1);
1469 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
1470 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0)
1471 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) < 0)
1472 if((!subpattern4) || strstrofs(strcat(",", pattern, ","), subpattern4, 0) < 0)
1478 void shuffle(float n, swapfunc_t swap, entity pass)
1481 for(i = 1; i < n; ++i)
1483 // swap i-th item at a random position from 0 to i
1484 // proof for even distribution:
1487 // item n+1 gets at any position with chance 1/(n+1)
1488 // all others will get their 1/n chance reduced by factor n/(n+1)
1489 // to be on place n+1, their chance will be 1/(n+1)
1490 // 1/n * n/(n+1) = 1/(n+1)
1492 j = floor(random() * (i + 1));
1498 string substring_range(string s, float b, float e)
1500 return substring(s, b, e - b);
1503 string swapwords(string str, float i, float j)
1506 string s1, s2, s3, s4, s5;
1507 float si, ei, sj, ej, s0, en;
1508 n = tokenizebyseparator(str, " "); // must match g_maplist processing in ShuffleMaplist and "shuffle"
1509 si = argv_start_index(i);
1510 sj = argv_start_index(j);
1511 ei = argv_end_index(i);
1512 ej = argv_end_index(j);
1513 s0 = argv_start_index(0);
1514 en = argv_end_index(n-1);
1515 s1 = substring_range(str, s0, si);
1516 s2 = substring_range(str, si, ei);
1517 s3 = substring_range(str, ei, sj);
1518 s4 = substring_range(str, sj, ej);
1519 s5 = substring_range(str, ej, en);
1520 return strcat(s1, s4, s3, s2, s5);
1523 string _shufflewords_str;
1524 void _shufflewords_swapfunc(float i, float j, entity pass)
1526 _shufflewords_str = swapwords(_shufflewords_str, i, j);
1528 string shufflewords(string str)
1531 _shufflewords_str = str;
1532 n = tokenizebyseparator(str, " ");
1533 shuffle(n, _shufflewords_swapfunc, world);
1534 str = _shufflewords_str;
1535 _shufflewords_str = string_null;
1539 vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0
1555 // actually, every number solves the equation!
1566 if(a > 0) // put the smaller solution first
1568 v_x = ((-b)-D) / (2*a);
1569 v_y = ((-b)+D) / (2*a);
1573 v_x = (-b+D) / (2*a);
1574 v_y = (-b-D) / (2*a);
1580 // complex solutions!
1593 void check_unacceptable_compiler_bugs()
1595 if(cvar("_allow_unacceptable_compiler_bugs"))
1597 tokenize_console("foo bar");
1598 if(strcat(argv(0), substring("foo bar", 4, 7 - argv_start_index(1))) == "barbar")
1599 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.");
1603 error("The empty string counts as false. We do not want that!");
1606 float compressShotOrigin(vector v)
1610 y = rint(v_y * 4) + 128;
1611 z = rint(v_z * 4) + 128;
1612 if(x > 255 || x < 0)
1614 print("shot origin ", vtos(v), " x out of bounds\n");
1615 x = bound(0, x, 255);
1617 if(y > 255 || y < 0)
1619 print("shot origin ", vtos(v), " y out of bounds\n");
1620 y = bound(0, y, 255);
1622 if(z > 255 || z < 0)
1624 print("shot origin ", vtos(v), " z out of bounds\n");
1625 z = bound(0, z, 255);
1627 return x * 0x10000 + y * 0x100 + z;
1629 vector decompressShotOrigin(float f)
1632 v_x = ((f & 0xFF0000) / 0x10000) / 2;
1633 v_y = ((f & 0xFF00) / 0x100 - 128) / 4;
1634 v_z = ((f & 0xFF) - 128) / 4;
1638 void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass)
1640 float start, end, root, child;
1643 start = floor((n - 2) / 2);
1646 // siftdown(start, count-1);
1648 while(root * 2 + 1 <= n-1)
1650 child = root * 2 + 1;
1652 if(cmp(child, child+1, pass) < 0)
1654 if(cmp(root, child, pass) < 0)
1656 swap(root, child, pass);
1672 // siftdown(0, end);
1674 while(root * 2 + 1 <= end)
1676 child = root * 2 + 1;
1677 if(child < end && cmp(child, child+1, pass) < 0)
1679 if(cmp(root, child, pass) < 0)
1681 swap(root, child, pass);
1691 void RandomSelection_Init()
1693 RandomSelection_totalweight = 0;
1694 RandomSelection_chosen_ent = world;
1695 RandomSelection_chosen_float = 0;
1696 RandomSelection_chosen_string = string_null;
1697 RandomSelection_best_priority = -1;
1699 void RandomSelection_Add(entity e, float f, string s, float weight, float priority)
1701 if(priority > RandomSelection_best_priority)
1703 RandomSelection_best_priority = priority;
1704 RandomSelection_chosen_ent = e;
1705 RandomSelection_chosen_float = f;
1706 RandomSelection_chosen_string = s;
1707 RandomSelection_totalweight = weight;
1709 else if(priority == RandomSelection_best_priority)
1711 RandomSelection_totalweight += weight;
1712 if(random() * RandomSelection_totalweight <= weight)
1714 RandomSelection_chosen_ent = e;
1715 RandomSelection_chosen_float = f;
1716 RandomSelection_chosen_string = s;
1721 vector healtharmor_maxdamage(float h, float a, float armorblock)
1723 // NOTE: we'll always choose the SMALLER value...
1724 float healthdamage, armordamage, armorideal;
1726 healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health
1727 armordamage = a + (h - 1); // damage we can take if we could use more armor
1728 armorideal = healthdamage * armorblock;
1730 if(armordamage < healthdamage)
1743 vector healtharmor_applydamage(float a, float armorblock, float damage)
1746 v_y = bound(0, damage * armorblock, a); // save
1747 v_x = bound(0, damage - v_y, damage); // take
1752 string getcurrentmod()
1756 m = cvar_string("fs_gamedir");
1757 n = tokenize_console(m);
1769 v = ReadShort() * 256; // note: this is signed
1770 v += ReadByte(); // note: this is unsigned
1774 void WriteInt24_t(float dst, float val)
1777 WriteShort(dst, (v = floor(val / 256)));
1778 WriteByte(dst, val - v * 256); // 0..255
1783 float float2range11(float f)
1785 // continuous function mapping all reals into -1..1
1786 return f / (fabs(f) + 1);
1789 float float2range01(float f)
1791 // continuous function mapping all reals into 0..1
1792 return 0.5 + 0.5 * float2range11(f);
1795 // from the GNU Scientific Library
1796 float gsl_ran_gaussian_lastvalue;
1797 float gsl_ran_gaussian_lastvalue_set;
1798 float gsl_ran_gaussian(float sigma)
1801 if(gsl_ran_gaussian_lastvalue_set)
1803 gsl_ran_gaussian_lastvalue_set = 0;
1804 return sigma * gsl_ran_gaussian_lastvalue;
1808 a = random() * 2 * M_PI;
1809 b = sqrt(-2 * log(random()));
1810 gsl_ran_gaussian_lastvalue = cos(a) * b;
1811 gsl_ran_gaussian_lastvalue_set = 1;
1812 return sigma * sin(a) * b;
1816 string car(string s)
1819 o = strstrofs(s, " ", 0);
1822 return substring(s, 0, o);
1824 string cdr(string s)
1827 o = strstrofs(s, " ", 0);
1830 return substring(s, o + 1, strlen(s) - (o + 1));
1832 float matchacl(string acl, string str)
1839 t = car(acl); acl = cdr(acl);
1841 if(substring(t, 0, 1) == "-")
1844 t = substring(t, 1, strlen(t) - 1);
1846 else if(substring(t, 0, 1) == "+")
1847 t = substring(t, 1, strlen(t) - 1);
1848 if(substring(t, -1, 1) == "*")
1850 t = substring(t, 0, strlen(t) - 1);
1851 s = substring(s, 0, strlen(t));
1863 float startsWith(string haystack, string needle)
1865 return substring(haystack, 0, strlen(needle)) == needle;
1867 float startsWithNocase(string haystack, string needle)
1869 return strcasecmp(substring(haystack, 0, strlen(needle)), needle) == 0;
1872 string get_model_datafilename(string m, float sk, string fil)
1877 m = "models/player/*_";
1879 m = strcat(m, ftos(sk));
1882 return strcat(m, ".", fil);
1885 float get_model_parameters(string m, float sk)
1890 get_model_parameters_modelname = string_null;
1891 get_model_parameters_modelskin = -1;
1892 get_model_parameters_name = string_null;
1893 get_model_parameters_species = -1;
1894 get_model_parameters_sex = string_null;
1895 get_model_parameters_weight = -1;
1896 get_model_parameters_age = -1;
1897 get_model_parameters_desc = string_null;
1903 if(substring(m, -4, -1) != ".txt")
1905 if(substring(m, -6, 1) != "_")
1907 sk = stof(substring(m, -5, 1));
1908 m = substring(m, 0, -7);
1911 fn = get_model_datafilename(m, sk, "txt");
1912 fh = fopen(fn, FILE_READ);
1916 fn = get_model_datafilename(m, sk, "txt");
1917 fh = fopen(fn, FILE_READ);
1922 get_model_parameters_modelname = m;
1923 get_model_parameters_modelskin = sk;
1924 while((s = fgets(fh)))
1927 break; // next lines will be description
1931 get_model_parameters_name = s;
1935 case "human": get_model_parameters_species = SPECIES_HUMAN; break;
1936 case "alien": get_model_parameters_species = SPECIES_ALIEN; break;
1937 case "robot_shiny": get_model_parameters_species = SPECIES_ROBOT_SHINY; break;
1938 case "robot_rusty": get_model_parameters_species = SPECIES_ROBOT_RUSTY; break;
1939 case "robot_solid": get_model_parameters_species = SPECIES_ROBOT_SOLID; break;
1940 case "animal": get_model_parameters_species = SPECIES_ANIMAL; break;
1941 case "reserved": get_model_parameters_species = SPECIES_RESERVED; break;
1944 get_model_parameters_sex = s;
1946 get_model_parameters_weight = stof(s);
1948 get_model_parameters_age = stof(s);
1951 while((s = fgets(fh)))
1953 if(get_model_parameters_desc)
1954 get_model_parameters_desc = strcat(get_model_parameters_desc, "\n");
1956 get_model_parameters_desc = strcat(get_model_parameters_desc, s);
1964 vector vec2(vector v)
1971 vector NearestPointOnBox(entity box, vector org)
1973 vector m1, m2, nearest;
1975 m1 = box.mins + box.origin;
1976 m2 = box.maxs + box.origin;
1978 nearest_x = bound(m1_x, org_x, m2_x);
1979 nearest_y = bound(m1_y, org_y, m2_y);
1980 nearest_z = bound(m1_z, org_z, m2_z);
1986 float vercmp_recursive(string v1, string v2)
1992 dot1 = strstrofs(v1, ".", 0);
1993 dot2 = strstrofs(v2, ".", 0);
1997 s1 = substring(v1, 0, dot1);
2001 s2 = substring(v2, 0, dot2);
2003 r = stof(s1) - stof(s2);
2007 r = strcasecmp(s1, s2);
2020 return vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999));
2023 float vercmp(string v1, string v2)
2025 if(strcasecmp(v1, v2) == 0) // early out check
2034 return vercmp_recursive(v1, v2);
2037 float u8_strsize(string s)
2057 // translation helpers
2058 string language_filename(string s)
2063 if(fn == "" || fn == "dump")
2065 fn = strcat(s, ".", fn);
2066 if((fh = fopen(fn, FILE_READ)) >= 0)
2073 string CTX(string s)
2075 float p = strstrofs(s, "^", 0);
2078 return substring(s, p+1, -1);
2081 // x-encoding (encoding as zero length invisible string)
2082 const string XENCODE_2 = "xX";
2083 const string XENCODE_22 = "0123456789abcdefABCDEF";
2084 string xencode(float f)
2087 d = mod(f, 22); f = floor(f / 22);
2088 c = mod(f, 22); f = floor(f / 22);
2089 b = mod(f, 22); f = floor(f / 22);
2090 a = mod(f, 2); // f = floor(f / 2);
2093 substring(XENCODE_2, a, 1),
2094 substring(XENCODE_22, b, 1),
2095 substring(XENCODE_22, c, 1),
2096 substring(XENCODE_22, d, 1)
2099 float xdecode(string s)
2102 if(substring(s, 0, 1) != "^")
2106 a = strstrofs(XENCODE_2, substring(s, 1, 1), 0);
2107 b = strstrofs(XENCODE_22, substring(s, 2, 1), 0);
2108 c = strstrofs(XENCODE_22, substring(s, 3, 1), 0);
2109 d = strstrofs(XENCODE_22, substring(s, 4, 1), 0);
2110 if(a < 0 || b < 0 || c < 0 || d < 0)
2112 return ((a * 22 + b) * 22 + c) * 22 + d;
2115 float lowestbit(float f)
2126 string strlimitedlen(string input, string truncation, float strip_colors, float limit)
2128 if(strlen((strip_colors ? strdecolorize(input) : input)) <= limit)
2131 return strcat(substring(input, 0, (strlen(input) - strlen(truncation))), truncation);
2134 // escape the string to make it safe for consoles
2135 string MakeConsoleSafe(string input)
2137 input = strreplace("\n", "", input);
2138 input = strreplace("\\", "\\\\", input);
2139 input = strreplace("$", "$$", input);
2140 input = strreplace("\"", "\\\"", input);
2145 // get true/false value of a string with multiple different inputs
2146 float InterpretBoolean(string input)
2148 switch(strtolower(input))
2160 default: return stof(input);
2166 entity ReadCSQCEntity()
2172 return findfloat(world, entnum, f);
2176 float shutdown_running;
2181 void CSQC_Shutdown()
2187 if(shutdown_running)
2189 print("Recursive shutdown detected! Only restoring cvars...\n");
2193 shutdown_running = 1;
2196 cvar_settemp_restore(); // this must be done LAST, but in any case
2199 #define APPROXPASTTIME_ACCURACY_REQUIREMENT 0.05
2200 #define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
2201 #define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
2202 // this will use the value:
2204 // accuracy near zero is APPROXPASTTIME_MAX/(256*255)
2205 // accuracy at x is 1/derivative, i.e.
2206 // APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536
2208 void WriteApproxPastTime(float dst, float t)
2210 float dt = time - t;
2212 // warning: this is approximate; do not resend when you don't have to!
2213 // be careful with sendflags here!
2214 // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75
2217 dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt));
2220 dt = rint(bound(0, dt, 255));
2226 float ReadApproxPastTime()
2228 float dt = ReadByte();
2230 // map from range...PPROXPASTTIME_MAX / 256
2231 dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt));
2233 return servertime - dt;
2238 .float skeleton_bones_index;
2239 void Skeleton_SetBones(entity e)
2241 // set skeleton_bones to the total number of bones on the model
2242 if(e.skeleton_bones_index == e.modelindex)
2243 return; // same model, nothing to update
2246 skelindex = skel_create(e.modelindex);
2247 e.skeleton_bones = skel_get_numbones(skelindex);
2248 skel_delete(skelindex);
2249 e.skeleton_bones_index = e.modelindex;
2253 string to_execute_next_frame;
2254 void execute_next_frame()
2256 if(to_execute_next_frame)
2258 localcmd("\n", to_execute_next_frame, "\n");
2259 strunzone(to_execute_next_frame);
2260 to_execute_next_frame = string_null;
2263 void queue_to_execute_next_frame(string s)
2265 if(to_execute_next_frame)
2267 s = strcat(s, "\n", to_execute_next_frame);
2268 strunzone(to_execute_next_frame);
2270 to_execute_next_frame = strzone(s);