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 created_saved_value = 0;
865 if not(tmp_cvar || tmp_value)
867 dprint("Error: Invalid usage of cvar_settemp(string, string); !\n");
871 if(!cvar_type(tmp_cvar))
873 print(sprintf("Error: cvar %s doesn't exist!\n", tmp_cvar));
877 for(e = world; (e = find(e, classname, "saved_cvar_value")); )
878 if(e.netname == tmp_cvar)
879 created_saved_value = -1; // skip creation
881 if(created_saved_value != -1)
883 // creating a new entity to keep track of this cvar
885 e.classname = "saved_cvar_value";
886 e.netname = strzone(tmp_cvar);
887 e.message = strzone(cvar_string(tmp_cvar));
888 created_saved_value = 1;
891 // update the cvar to the value given
892 cvar_set(tmp_cvar, tmp_value);
894 return created_saved_value;
897 float cvar_settemp_restore()
901 while((e = find(e, classname, "saved_cvar_value")))
903 if(cvar_type(e.netname))
905 cvar_set(e.netname, e.message);
910 print(sprintf("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.\n", e.netname));
916 float almost_equals(float a, float b)
919 eps = (max(a, -a) + max(b, -b)) * 0.001;
920 if(a - b < eps && b - a < eps)
925 float almost_in_bounds(float a, float b, float c)
928 eps = (max(a, -a) + max(c, -c)) * 0.001;
931 return b == median(a - eps, b, c + eps);
934 float power2of(float e)
938 float log2of(float x)
940 // NOTE: generated code
1013 float rgb_mi_ma_to_hue(vector rgb, float mi, float ma)
1017 else if(ma == rgb_x)
1020 return (rgb_y - rgb_z) / (ma - mi);
1022 return (rgb_y - rgb_z) / (ma - mi) + 6;
1024 else if(ma == rgb_y)
1025 return (rgb_z - rgb_x) / (ma - mi) + 2;
1026 else // if(ma == rgb_z)
1027 return (rgb_x - rgb_y) / (ma - mi) + 4;
1030 vector hue_mi_ma_to_rgb(float hue, float mi, float ma)
1034 hue -= 6 * floor(hue / 6);
1036 //else if(ma == rgb_x)
1037 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
1041 rgb_y = hue * (ma - mi) + mi;
1044 //else if(ma == rgb_y)
1045 // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120;
1048 rgb_x = (2 - hue) * (ma - mi) + mi;
1056 rgb_z = (hue - 2) * (ma - mi) + mi;
1058 //else // if(ma == rgb_z)
1059 // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240;
1063 rgb_y = (4 - hue) * (ma - mi) + mi;
1068 rgb_x = (hue - 4) * (ma - mi) + mi;
1072 //else if(ma == rgb_x)
1073 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
1074 else // if(hue <= 6)
1078 rgb_z = (6 - hue) * (ma - mi) + mi;
1084 vector rgb_to_hsv(vector rgb)
1089 mi = min(rgb_x, rgb_y, rgb_z);
1090 ma = max(rgb_x, rgb_y, rgb_z);
1092 hsv_x = rgb_mi_ma_to_hue(rgb, mi, ma);
1103 vector hsv_to_rgb(vector hsv)
1105 return hue_mi_ma_to_rgb(hsv_x, hsv_z * (1 - hsv_y), hsv_z);
1108 vector rgb_to_hsl(vector rgb)
1113 mi = min(rgb_x, rgb_y, rgb_z);
1114 ma = max(rgb_x, rgb_y, rgb_z);
1116 hsl_x = rgb_mi_ma_to_hue(rgb, mi, ma);
1118 hsl_z = 0.5 * (mi + ma);
1121 else if(hsl_z <= 0.5)
1122 hsl_y = (ma - mi) / (2*hsl_z);
1123 else // if(hsl_z > 0.5)
1124 hsl_y = (ma - mi) / (2 - 2*hsl_z);
1129 vector hsl_to_rgb(vector hsl)
1131 float mi, ma, maminusmi;
1134 maminusmi = hsl_y * 2 * hsl_z;
1136 maminusmi = hsl_y * (2 - 2 * hsl_z);
1138 // hsl_z = 0.5 * mi + 0.5 * ma
1139 // maminusmi = - mi + ma
1140 mi = hsl_z - 0.5 * maminusmi;
1141 ma = hsl_z + 0.5 * maminusmi;
1143 return hue_mi_ma_to_rgb(hsl_x, mi, ma);
1146 string rgb_to_hexcolor(vector rgb)
1151 DEC_TO_HEXDIGIT(floor(rgb_x * 15 + 0.5)),
1152 DEC_TO_HEXDIGIT(floor(rgb_y * 15 + 0.5)),
1153 DEC_TO_HEXDIGIT(floor(rgb_z * 15 + 0.5))
1157 // requires that m2>m1 in all coordinates, and that m4>m3
1158 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;}
1160 // requires the same, but is a stronger condition
1161 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;}
1166 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
1169 // The following function is SLOW.
1170 // For your safety and for the protection of those around you...
1171 // DO NOT CALL THIS AT HOME.
1172 // No really, don't.
1173 if(w(theText, theSize) <= maxWidth)
1174 return strlen(theText); // yeah!
1176 // binary search for right place to cut string
1178 float left, right, middle; // this always works
1180 right = strlen(theText); // this always fails
1183 middle = floor((left + right) / 2);
1184 if(w(substring(theText, 0, middle), theSize) <= maxWidth)
1189 while(left < right - 1);
1191 if(w("^7", theSize) == 0) // detect color codes support in the width function
1193 // NOTE: when color codes are involved, this binary search is,
1194 // mathematically, BROKEN. However, it is obviously guaranteed to
1195 // terminate, as the range still halves each time - but nevertheless, it is
1196 // guaranteed that it finds ONE valid cutoff place (where "left" is in
1197 // range, and "right" is outside).
1199 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
1200 // and decrease left on the basis of the chars detected of the truncated tag
1201 // Even if the ^xrgb tag is not complete/correct, left is decreased
1202 // (sometimes too much but with a correct result)
1203 // it fixes also ^[0-9]
1204 while(left >= 1 && substring(theText, left-1, 1) == "^")
1207 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
1209 else if (left >= 3 && substring(theText, left-3, 2) == "^x")
1211 ch = str2chr(theText, left-1);
1212 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
1215 else if (left >= 4 && substring(theText, left-4, 2) == "^x")
1217 ch = str2chr(theText, left-2);
1218 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
1220 ch = str2chr(theText, left-1);
1221 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
1230 float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w)
1233 // The following function is SLOW.
1234 // For your safety and for the protection of those around you...
1235 // DO NOT CALL THIS AT HOME.
1236 // No really, don't.
1237 if(w(theText) <= maxWidth)
1238 return strlen(theText); // yeah!
1240 // binary search for right place to cut string
1242 float left, right, middle; // this always works
1244 right = strlen(theText); // this always fails
1247 middle = floor((left + right) / 2);
1248 if(w(substring(theText, 0, middle)) <= maxWidth)
1253 while(left < right - 1);
1255 if(w("^7") == 0) // detect color codes support in the width function
1257 // NOTE: when color codes are involved, this binary search is,
1258 // mathematically, BROKEN. However, it is obviously guaranteed to
1259 // terminate, as the range still halves each time - but nevertheless, it is
1260 // guaranteed that it finds ONE valid cutoff place (where "left" is in
1261 // range, and "right" is outside).
1263 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
1264 // and decrease left on the basis of the chars detected of the truncated tag
1265 // Even if the ^xrgb tag is not complete/correct, left is decreased
1266 // (sometimes too much but with a correct result)
1267 // it fixes also ^[0-9]
1268 while(left >= 1 && substring(theText, left-1, 1) == "^")
1271 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
1273 else if (left >= 3 && substring(theText, left-3, 2) == "^x")
1275 ch = str2chr(theText, left-1);
1276 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
1279 else if (left >= 4 && substring(theText, left-4, 2) == "^x")
1281 ch = str2chr(theText, left-2);
1282 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
1284 ch = str2chr(theText, left-1);
1285 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
1294 string find_last_color_code(string s)
1296 float start, len, i, carets;
1297 start = strstrofs(s, "^", 0);
1298 if (start == -1) // no caret found
1301 for(i = len; i >= start; --i)
1303 if(substring(s, i, 1) != "^")
1307 while (i-carets >= start && substring(s, i-carets, 1) == "^")
1310 // check if carets aren't all escaped
1311 if (carets == 1 || mod(carets, 2) == 1) // first check is just an optimization
1314 if(strstrofs("0123456789", substring(s, i+1, 1), 0) >= 0)
1315 return substring(s, i, 2);
1318 if(substring(s, i+1, 1) == "x")
1319 if(strstrofs("0123456789abcdefABCDEF", substring(s, i+2, 1), 0) >= 0)
1320 if(strstrofs("0123456789abcdefABCDEF", substring(s, i+3, 1), 0) >= 0)
1321 if(strstrofs("0123456789abcdefABCDEF", substring(s, i+4, 1), 0) >= 0)
1322 return substring(s, i, 5);
1324 i -= carets; // this also skips one char before the carets
1330 string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1336 s = getWrappedLine_remaining;
1340 getWrappedLine_remaining = string_null;
1341 return s; // the line has no size ANYWAY, nothing would be displayed.
1344 cantake = textLengthUpToWidth(s, w, theFontSize, tw);
1345 if(cantake > 0 && cantake < strlen(s))
1348 while(take > 0 && substring(s, take, 1) != " ")
1352 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
1353 if(getWrappedLine_remaining == "")
1354 getWrappedLine_remaining = string_null;
1355 else if (tw("^7", theFontSize) == 0)
1356 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining);
1357 return substring(s, 0, cantake);
1361 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
1362 if(getWrappedLine_remaining == "")
1363 getWrappedLine_remaining = string_null;
1364 else if (tw("^7", theFontSize) == 0)
1365 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining);
1366 return substring(s, 0, take);
1371 getWrappedLine_remaining = string_null;
1376 string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw)
1382 s = getWrappedLine_remaining;
1386 getWrappedLine_remaining = string_null;
1387 return s; // the line has no size ANYWAY, nothing would be displayed.
1390 cantake = textLengthUpToLength(s, w, tw);
1391 if(cantake > 0 && cantake < strlen(s))
1394 while(take > 0 && substring(s, take, 1) != " ")
1398 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
1399 if(getWrappedLine_remaining == "")
1400 getWrappedLine_remaining = string_null;
1401 else if (tw("^7") == 0)
1402 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining);
1403 return substring(s, 0, cantake);
1407 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
1408 if(getWrappedLine_remaining == "")
1409 getWrappedLine_remaining = string_null;
1410 else if (tw("^7") == 0)
1411 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining);
1412 return substring(s, 0, take);
1417 getWrappedLine_remaining = string_null;
1422 string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1424 if(tw(theText, theFontSize) <= maxWidth)
1427 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("...", theFontSize), theFontSize, tw)), "...");
1430 string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw)
1432 if(tw(theText) <= maxWidth)
1435 return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
1438 float isGametypeInFilter(float gt, float tp, float ts, string pattern)
1440 string subpattern, subpattern2, subpattern3, subpattern4;
1441 subpattern = strcat(",", MapInfo_Type_ToString(gt), ",");
1443 subpattern2 = ",teams,";
1445 subpattern2 = ",noteams,";
1447 subpattern3 = ",teamspawns,";
1449 subpattern3 = ",noteamspawns,";
1450 if(gt == MAPINFO_TYPE_RACE || gt == MAPINFO_TYPE_CTS)
1451 subpattern4 = ",race,";
1453 subpattern4 = string_null;
1455 if(substring(pattern, 0, 1) == "-")
1457 pattern = substring(pattern, 1, strlen(pattern) - 1);
1458 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
1460 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0)
1462 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) >= 0)
1464 if(subpattern4 && strstrofs(strcat(",", pattern, ","), subpattern4, 0) >= 0)
1469 if(substring(pattern, 0, 1) == "+")
1470 pattern = substring(pattern, 1, strlen(pattern) - 1);
1471 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
1472 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0)
1473 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) < 0)
1474 if((!subpattern4) || strstrofs(strcat(",", pattern, ","), subpattern4, 0) < 0)
1480 void shuffle(float n, swapfunc_t swap, entity pass)
1483 for(i = 1; i < n; ++i)
1485 // swap i-th item at a random position from 0 to i
1486 // proof for even distribution:
1489 // item n+1 gets at any position with chance 1/(n+1)
1490 // all others will get their 1/n chance reduced by factor n/(n+1)
1491 // to be on place n+1, their chance will be 1/(n+1)
1492 // 1/n * n/(n+1) = 1/(n+1)
1494 j = floor(random() * (i + 1));
1500 string substring_range(string s, float b, float e)
1502 return substring(s, b, e - b);
1505 string swapwords(string str, float i, float j)
1508 string s1, s2, s3, s4, s5;
1509 float si, ei, sj, ej, s0, en;
1510 n = tokenizebyseparator(str, " "); // must match g_maplist processing in ShuffleMaplist and "shuffle"
1511 si = argv_start_index(i);
1512 sj = argv_start_index(j);
1513 ei = argv_end_index(i);
1514 ej = argv_end_index(j);
1515 s0 = argv_start_index(0);
1516 en = argv_end_index(n-1);
1517 s1 = substring_range(str, s0, si);
1518 s2 = substring_range(str, si, ei);
1519 s3 = substring_range(str, ei, sj);
1520 s4 = substring_range(str, sj, ej);
1521 s5 = substring_range(str, ej, en);
1522 return strcat(s1, s4, s3, s2, s5);
1525 string _shufflewords_str;
1526 void _shufflewords_swapfunc(float i, float j, entity pass)
1528 _shufflewords_str = swapwords(_shufflewords_str, i, j);
1530 string shufflewords(string str)
1533 _shufflewords_str = str;
1534 n = tokenizebyseparator(str, " ");
1535 shuffle(n, _shufflewords_swapfunc, world);
1536 str = _shufflewords_str;
1537 _shufflewords_str = string_null;
1541 vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0
1557 // actually, every number solves the equation!
1568 if(a > 0) // put the smaller solution first
1570 v_x = ((-b)-D) / (2*a);
1571 v_y = ((-b)+D) / (2*a);
1575 v_x = (-b+D) / (2*a);
1576 v_y = (-b-D) / (2*a);
1582 // complex solutions!
1595 void check_unacceptable_compiler_bugs()
1597 if(cvar("_allow_unacceptable_compiler_bugs"))
1599 tokenize_console("foo bar");
1600 if(strcat(argv(0), substring("foo bar", 4, 7 - argv_start_index(1))) == "barbar")
1601 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.");
1605 error("The empty string counts as false. We do not want that!");
1608 float compressShotOrigin(vector v)
1612 y = rint(v_y * 4) + 128;
1613 z = rint(v_z * 4) + 128;
1614 if(x > 255 || x < 0)
1616 print("shot origin ", vtos(v), " x out of bounds\n");
1617 x = bound(0, x, 255);
1619 if(y > 255 || y < 0)
1621 print("shot origin ", vtos(v), " y out of bounds\n");
1622 y = bound(0, y, 255);
1624 if(z > 255 || z < 0)
1626 print("shot origin ", vtos(v), " z out of bounds\n");
1627 z = bound(0, z, 255);
1629 return x * 0x10000 + y * 0x100 + z;
1631 vector decompressShotOrigin(float f)
1634 v_x = ((f & 0xFF0000) / 0x10000) / 2;
1635 v_y = ((f & 0xFF00) / 0x100 - 128) / 4;
1636 v_z = ((f & 0xFF) - 128) / 4;
1640 void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass)
1642 float start, end, root, child;
1645 start = floor((n - 2) / 2);
1648 // siftdown(start, count-1);
1650 while(root * 2 + 1 <= n-1)
1652 child = root * 2 + 1;
1654 if(cmp(child, child+1, pass) < 0)
1656 if(cmp(root, child, pass) < 0)
1658 swap(root, child, pass);
1674 // siftdown(0, end);
1676 while(root * 2 + 1 <= end)
1678 child = root * 2 + 1;
1679 if(child < end && cmp(child, child+1, pass) < 0)
1681 if(cmp(root, child, pass) < 0)
1683 swap(root, child, pass);
1693 void RandomSelection_Init()
1695 RandomSelection_totalweight = 0;
1696 RandomSelection_chosen_ent = world;
1697 RandomSelection_chosen_float = 0;
1698 RandomSelection_chosen_string = string_null;
1699 RandomSelection_best_priority = -1;
1701 void RandomSelection_Add(entity e, float f, string s, float weight, float priority)
1703 if(priority > RandomSelection_best_priority)
1705 RandomSelection_best_priority = priority;
1706 RandomSelection_chosen_ent = e;
1707 RandomSelection_chosen_float = f;
1708 RandomSelection_chosen_string = s;
1709 RandomSelection_totalweight = weight;
1711 else if(priority == RandomSelection_best_priority)
1713 RandomSelection_totalweight += weight;
1714 if(random() * RandomSelection_totalweight <= weight)
1716 RandomSelection_chosen_ent = e;
1717 RandomSelection_chosen_float = f;
1718 RandomSelection_chosen_string = s;
1723 vector healtharmor_maxdamage(float h, float a, float armorblock)
1725 // NOTE: we'll always choose the SMALLER value...
1726 float healthdamage, armordamage, armorideal;
1728 healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health
1729 armordamage = a + (h - 1); // damage we can take if we could use more armor
1730 armorideal = healthdamage * armorblock;
1732 if(armordamage < healthdamage)
1745 vector healtharmor_applydamage(float a, float armorblock, float damage)
1748 v_y = bound(0, damage * armorblock, a); // save
1749 v_x = bound(0, damage - v_y, damage); // take
1754 string getcurrentmod()
1758 m = cvar_string("fs_gamedir");
1759 n = tokenize_console(m);
1771 v = ReadShort() * 256; // note: this is signed
1772 v += ReadByte(); // note: this is unsigned
1776 void WriteInt24_t(float dst, float val)
1779 WriteShort(dst, (v = floor(val / 256)));
1780 WriteByte(dst, val - v * 256); // 0..255
1785 float float2range11(float f)
1787 // continuous function mapping all reals into -1..1
1788 return f / (fabs(f) + 1);
1791 float float2range01(float f)
1793 // continuous function mapping all reals into 0..1
1794 return 0.5 + 0.5 * float2range11(f);
1797 // from the GNU Scientific Library
1798 float gsl_ran_gaussian_lastvalue;
1799 float gsl_ran_gaussian_lastvalue_set;
1800 float gsl_ran_gaussian(float sigma)
1803 if(gsl_ran_gaussian_lastvalue_set)
1805 gsl_ran_gaussian_lastvalue_set = 0;
1806 return sigma * gsl_ran_gaussian_lastvalue;
1810 a = random() * 2 * M_PI;
1811 b = sqrt(-2 * log(random()));
1812 gsl_ran_gaussian_lastvalue = cos(a) * b;
1813 gsl_ran_gaussian_lastvalue_set = 1;
1814 return sigma * sin(a) * b;
1818 string car(string s)
1821 o = strstrofs(s, " ", 0);
1824 return substring(s, 0, o);
1826 string cdr(string s)
1829 o = strstrofs(s, " ", 0);
1832 return substring(s, o + 1, strlen(s) - (o + 1));
1834 float matchacl(string acl, string str)
1841 t = car(acl); acl = cdr(acl);
1844 if(substring(t, 0, 1) == "-")
1847 t = substring(t, 1, strlen(t) - 1);
1849 else if(substring(t, 0, 1) == "+")
1850 t = substring(t, 1, strlen(t) - 1);
1852 if(substring(t, -1, 1) == "*")
1854 t = substring(t, 0, strlen(t) - 1);
1855 s = substring(str, 0, strlen(t));
1867 float startsWith(string haystack, string needle)
1869 return substring(haystack, 0, strlen(needle)) == needle;
1871 float startsWithNocase(string haystack, string needle)
1873 return strcasecmp(substring(haystack, 0, strlen(needle)), needle) == 0;
1876 string get_model_datafilename(string m, float sk, string fil)
1881 m = "models/player/*_";
1883 m = strcat(m, ftos(sk));
1886 return strcat(m, ".", fil);
1889 float get_model_parameters(string m, float sk)
1894 get_model_parameters_modelname = string_null;
1895 get_model_parameters_modelskin = -1;
1896 get_model_parameters_name = string_null;
1897 get_model_parameters_species = -1;
1898 get_model_parameters_sex = string_null;
1899 get_model_parameters_weight = -1;
1900 get_model_parameters_age = -1;
1901 get_model_parameters_desc = string_null;
1907 if(substring(m, -4, -1) != ".txt")
1909 if(substring(m, -6, 1) != "_")
1911 sk = stof(substring(m, -5, 1));
1912 m = substring(m, 0, -7);
1915 fn = get_model_datafilename(m, sk, "txt");
1916 fh = fopen(fn, FILE_READ);
1920 fn = get_model_datafilename(m, sk, "txt");
1921 fh = fopen(fn, FILE_READ);
1926 get_model_parameters_modelname = m;
1927 get_model_parameters_modelskin = sk;
1928 while((s = fgets(fh)))
1931 break; // next lines will be description
1935 get_model_parameters_name = s;
1939 case "human": get_model_parameters_species = SPECIES_HUMAN; break;
1940 case "alien": get_model_parameters_species = SPECIES_ALIEN; break;
1941 case "robot_shiny": get_model_parameters_species = SPECIES_ROBOT_SHINY; break;
1942 case "robot_rusty": get_model_parameters_species = SPECIES_ROBOT_RUSTY; break;
1943 case "robot_solid": get_model_parameters_species = SPECIES_ROBOT_SOLID; break;
1944 case "animal": get_model_parameters_species = SPECIES_ANIMAL; break;
1945 case "reserved": get_model_parameters_species = SPECIES_RESERVED; break;
1948 get_model_parameters_sex = s;
1950 get_model_parameters_weight = stof(s);
1952 get_model_parameters_age = stof(s);
1955 while((s = fgets(fh)))
1957 if(get_model_parameters_desc)
1958 get_model_parameters_desc = strcat(get_model_parameters_desc, "\n");
1960 get_model_parameters_desc = strcat(get_model_parameters_desc, s);
1968 vector vec2(vector v)
1975 vector NearestPointOnBox(entity box, vector org)
1977 vector m1, m2, nearest;
1979 m1 = box.mins + box.origin;
1980 m2 = box.maxs + box.origin;
1982 nearest_x = bound(m1_x, org_x, m2_x);
1983 nearest_y = bound(m1_y, org_y, m2_y);
1984 nearest_z = bound(m1_z, org_z, m2_z);
1990 float vercmp_recursive(string v1, string v2)
1996 dot1 = strstrofs(v1, ".", 0);
1997 dot2 = strstrofs(v2, ".", 0);
2001 s1 = substring(v1, 0, dot1);
2005 s2 = substring(v2, 0, dot2);
2007 r = stof(s1) - stof(s2);
2011 r = strcasecmp(s1, s2);
2024 return vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999));
2027 float vercmp(string v1, string v2)
2029 if(strcasecmp(v1, v2) == 0) // early out check
2038 return vercmp_recursive(v1, v2);
2041 float u8_strsize(string s)
2061 // translation helpers
2062 string language_filename(string s)
2067 if(fn == "" || fn == "dump")
2069 fn = strcat(s, ".", fn);
2070 if((fh = fopen(fn, FILE_READ)) >= 0)
2077 string CTX(string s)
2079 float p = strstrofs(s, "^", 0);
2082 return substring(s, p+1, -1);
2085 // x-encoding (encoding as zero length invisible string)
2086 const string XENCODE_2 = "xX";
2087 const string XENCODE_22 = "0123456789abcdefABCDEF";
2088 string xencode(float f)
2091 d = mod(f, 22); f = floor(f / 22);
2092 c = mod(f, 22); f = floor(f / 22);
2093 b = mod(f, 22); f = floor(f / 22);
2094 a = mod(f, 2); // f = floor(f / 2);
2097 substring(XENCODE_2, a, 1),
2098 substring(XENCODE_22, b, 1),
2099 substring(XENCODE_22, c, 1),
2100 substring(XENCODE_22, d, 1)
2103 float xdecode(string s)
2106 if(substring(s, 0, 1) != "^")
2110 a = strstrofs(XENCODE_2, substring(s, 1, 1), 0);
2111 b = strstrofs(XENCODE_22, substring(s, 2, 1), 0);
2112 c = strstrofs(XENCODE_22, substring(s, 3, 1), 0);
2113 d = strstrofs(XENCODE_22, substring(s, 4, 1), 0);
2114 if(a < 0 || b < 0 || c < 0 || d < 0)
2116 return ((a * 22 + b) * 22 + c) * 22 + d;
2119 float lowestbit(float f)
2130 string strlimitedlen(string input, string truncation, float strip_colors, float limit)
2132 if(strlen((strip_colors ? strdecolorize(input) : input)) <= limit)
2135 return strcat(substring(input, 0, (strlen(input) - strlen(truncation))), truncation);
2138 // escape the string to make it safe for consoles
2139 string MakeConsoleSafe(string input)
2141 input = strreplace("\n", "", input);
2142 input = strreplace("\\", "\\\\", input);
2143 input = strreplace("$", "$$", input);
2144 input = strreplace("\"", "\\\"", input);
2149 // get true/false value of a string with multiple different inputs
2150 float InterpretBoolean(string input)
2152 switch(strtolower(input))
2164 default: return stof(input);
2170 entity ReadCSQCEntity()
2176 return findfloat(world, entnum, f);
2180 float shutdown_running;
2185 void CSQC_Shutdown()
2191 if(shutdown_running)
2193 print("Recursive shutdown detected! Only restoring cvars...\n");
2197 shutdown_running = 1;
2200 cvar_settemp_restore(); // this must be done LAST, but in any case
2203 #define APPROXPASTTIME_ACCURACY_REQUIREMENT 0.05
2204 #define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
2205 #define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
2206 // this will use the value:
2208 // accuracy near zero is APPROXPASTTIME_MAX/(256*255)
2209 // accuracy at x is 1/derivative, i.e.
2210 // APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536
2212 void WriteApproxPastTime(float dst, float t)
2214 float dt = time - t;
2216 // warning: this is approximate; do not resend when you don't have to!
2217 // be careful with sendflags here!
2218 // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75
2221 dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt));
2224 dt = rint(bound(0, dt, 255));
2230 float ReadApproxPastTime()
2232 float dt = ReadByte();
2234 // map from range...PPROXPASTTIME_MAX / 256
2235 dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt));
2237 return servertime - dt;
2242 .float skeleton_bones_index;
2243 void Skeleton_SetBones(entity e)
2245 // set skeleton_bones to the total number of bones on the model
2246 if(e.skeleton_bones_index == e.modelindex)
2247 return; // same model, nothing to update
2250 skelindex = skel_create(e.modelindex);
2251 e.skeleton_bones = skel_get_numbones(skelindex);
2252 skel_delete(skelindex);
2253 e.skeleton_bones_index = e.modelindex;
2257 string to_execute_next_frame;
2258 void execute_next_frame()
2260 if(to_execute_next_frame)
2262 localcmd("\n", to_execute_next_frame, "\n");
2263 strunzone(to_execute_next_frame);
2264 to_execute_next_frame = string_null;
2267 void queue_to_execute_next_frame(string s)
2269 if(to_execute_next_frame)
2271 s = strcat(s, "\n", to_execute_next_frame);
2272 strunzone(to_execute_next_frame);
2274 to_execute_next_frame = strzone(s);