1 // checkextension wrapper for log
\r
2 float sqrt(float f); // declared later
\r
3 float exp(float f); // declared later
\r
4 float pow(float f, float e); // declared later
\r
5 float checkextension(string s); // declared later
\r
6 float log_synth(float f)
\r
10 return sqrt(-1); // nan? -inf?
\r
12 return sqrt(-1); // ACTUALLY this should rather be -inf, but we cannot create a +inf in QC
\r
27 // two steps are good enough
\r
28 l = ((6-f) * f - 5) / 4.32808512266689022212;
\r
29 l += exp(-l) * f - 1;
\r
30 l += exp(-l) * f - 1;
\r
35 if(checkextension("DP_QC_LOG"))
\r
36 return log_builtin(f);
\r
38 return log_synth(f);
\r
41 string wordwrap_buffer;
\r
43 void wordwrap_buffer_put(string s)
\r
45 wordwrap_buffer = strcat(wordwrap_buffer, s);
\r
48 string wordwrap(string s, float l)
\r
51 wordwrap_buffer = "";
\r
52 wordwrap_cb(s, l, wordwrap_buffer_put);
\r
53 r = wordwrap_buffer;
\r
54 wordwrap_buffer = "";
\r
60 void wordwrap_buffer_sprint(string s)
\r
62 wordwrap_buffer = strcat(wordwrap_buffer, s);
\r
65 sprint(self, wordwrap_buffer);
\r
66 wordwrap_buffer = "";
\r
70 void wordwrap_sprint(string s, float l)
\r
72 wordwrap_buffer = "";
\r
73 wordwrap_cb(s, l, wordwrap_buffer_sprint);
\r
74 if(wordwrap_buffer != "")
\r
75 sprint(self, strcat(wordwrap_buffer, "\n"));
\r
76 wordwrap_buffer = "";
\r
82 string unescape(string in)
\r
85 local string str, s;
\r
87 // but it doesn't seem to be necessary in my tests at least
\r
92 for(i = 0; i < len; ++i)
\r
94 s = substring(in, i, 1);
\r
97 s = substring(in, i+1, 1);
\r
99 str = strcat(str, "\n");
\r
101 str = strcat(str, "\\");
\r
103 str = strcat(str, substring(in, i, 2));
\r
106 str = strcat(str, s);
\r
113 void wordwrap_cb(string s, float l, void(string) callback)
\r
116 local float lleft, i, j, wlen;
\r
120 for (i = 0;i < strlen(s);++i)
\r
122 if (substring(s, i, 2) == "\\n")
\r
128 else if (substring(s, i, 1) == "\n")
\r
133 else if (substring(s, i, 1) == " ")
\r
143 for (j = i+1;j < strlen(s);++j)
\r
144 // ^^ this skips over the first character of a word, which
\r
145 // is ALWAYS part of the word
\r
146 // this is safe since if i+1 == strlen(s), i will become
\r
147 // strlen(s)-1 at the end of this block and the function
\r
148 // will terminate. A space can't be the first character we
\r
149 // read here, and neither can a \n be the start, since these
\r
150 // two cases have been handled above.
\r
152 c = substring(s, j, 1);
\r
159 // we need to keep this tempstring alive even if substring is
\r
160 // called repeatedly, so call strcat even though we're not
\r
170 callback(substring(s, i, wlen));
\r
171 lleft = lleft - wlen;
\r
178 float dist_point_line(vector p, vector l0, vector ldir)
\r
180 ldir = normalize(ldir);
\r
182 // remove the component in line direction
\r
183 p = p - (p * ldir) * ldir;
\r
185 // vlen of the remaining vector
\r
189 void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
\r
218 float median(float a, float b, float c)
\r
221 return bound(a, b, c);
\r
222 return bound(c, b, a);
\r
225 // converts a number to a string with the indicated number of decimals
\r
226 // works for up to 10 decimals!
\r
227 string ftos_decimals(float number, float decimals)
\r
233 // if negative, cut off the sign first
\r
235 return strcat("-", ftos_decimals(-number, decimals));
\r
236 // it now is always positive!
\r
239 number = floor(number * pow(10, decimals) + 0.5);
\r
242 result = ftos(number);
\r
243 len = strlen(result);
\r
244 // does it have a decimal point (should not happen)? If there is one, it is always at len-7)
\r
245 // if ftos had messed it up, which should never happen: "34278.000000"
\r
247 if(substring(result, len - 7, 1) == ".")
\r
249 dprint("ftos(integer) has comma? Can't be. Affected result: ", result, "\n");
\r
250 result = substring(result, 0, len - 7);
\r
255 return result; // don't insert a point for zero decimals
\r
256 // is it too short? If yes, insert leading zeroes
\r
257 if(len <= decimals)
\r
259 result = strcat(substring("0000000000", 0, decimals - len + 1), result);
\r
260 len = decimals + 1;
\r
262 // and now... INSERT THE POINT!
\r
263 tmp = substring(result, len - decimals, decimals);
\r
264 result = strcat(substring(result, 0, len - decimals), ".", tmp);
\r
269 vector colormapPaletteColor(float c, float isPants)
\r
273 case 0: return '0.800000 0.800000 0.800000';
\r
274 case 1: return '0.600000 0.400000 0.000000';
\r
275 case 2: return '0.000000 1.000000 0.501961';
\r
276 case 3: return '0.000000 1.000000 0.000000';
\r
277 case 4: return '1.000000 0.000000 0.000000';
\r
278 case 5: return '0.000000 0.658824 1.000000';
\r
279 case 6: return '0.000000 1.000000 1.000000';
\r
280 case 7: return '0.501961 1.000000 0.000000';
\r
281 case 8: return '0.501961 0.000000 1.000000';
\r
282 case 9: return '1.000000 0.000000 1.000000';
\r
283 case 10: return '1.000000 0.000000 0.501961';
\r
284 case 11: return '0.600000 0.600000 0.600000';
\r
285 case 12: return '1.000000 1.000000 0.000000';
\r
286 case 13: return '0.000000 0.313725 1.000000';
\r
287 case 14: return '1.000000 0.501961 0.000000';
\r
291 '1 0 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 0.0000000000))
\r
292 + '0 1 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 2.0943951024))
\r
293 + '0 0 1' * (0.502 + 0.498 * sin(time / 2.7182818285 + 4.1887902048));
\r
296 '1 0 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 5.2359877560))
\r
297 + '0 1 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 3.1415926536))
\r
298 + '0 0 1' * (0.502 + 0.498 * sin(time / 3.1415926536 + 1.0471975512));
\r
299 default: return '0.000 0.000 0.000';
\r
303 // unzone the string, and return it as tempstring. Safe to be called on string_null
\r
304 string fstrunzone(string s)
\r
309 sc = strcat(s, "");
\r
314 // Databases (hash tables)
\r
315 #define DB_BUCKETS 8192
\r
316 void db_save(float db, string pFilename)
\r
319 fh = fopen(pFilename, FILE_WRITE);
\r
322 print(strcat("^1Can't write DB to ", pFilename));
\r
325 n = buf_getsize(db);
\r
326 fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
\r
327 for(i = 0; i < n; ++i)
\r
328 fputs(fh, strcat(bufstr_get(db, i), "\n"));
\r
334 return buf_create();
\r
337 float db_load(string pFilename)
\r
339 float db, fh, i, j, n;
\r
344 fh = fopen(pFilename, FILE_READ);
\r
347 if(stof(fgets(fh)) == DB_BUCKETS)
\r
350 while((l = fgets(fh)))
\r
353 bufstr_set(db, i, l);
\r
359 // different count of buckets?
\r
360 // need to reorganize the database then (SLOW)
\r
361 while((l = fgets(fh)))
\r
363 n = tokenizebyseparator(l, "\\");
\r
364 for(j = 2; j < n; j += 2)
\r
365 db_put(db, argv(j-1), uri_unescape(argv(j)));
\r
372 void db_dump(float db, string pFilename)
\r
374 float fh, i, j, n, m;
\r
375 fh = fopen(pFilename, FILE_WRITE);
\r
377 error(strcat("Can't dump DB to ", pFilename));
\r
378 n = buf_getsize(db);
\r
380 for(i = 0; i < n; ++i)
\r
382 m = tokenizebyseparator(bufstr_get(db, i), "\\");
\r
383 for(j = 2; j < m; j += 2)
\r
384 fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n"));
\r
389 void db_close(float db)
\r
394 string db_get(float db, string pKey)
\r
397 h = mod(crc16(FALSE, pKey), DB_BUCKETS);
\r
398 return uri_unescape(infoget(bufstr_get(db, h), pKey));
\r
401 void db_put(float db, string pKey, string pValue)
\r
404 h = mod(crc16(FALSE, pKey), DB_BUCKETS);
\r
405 bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue)));
\r
411 print("LOAD...\n");
\r
412 db = db_load("foo.db");
\r
413 print("LOADED. FILL...\n");
\r
414 for(i = 0; i < DB_BUCKETS; ++i)
\r
415 db_put(db, ftos(random()), "X");
\r
416 print("FILLED. SAVE...\n");
\r
417 db_save(db, "foo.db");
\r
418 print("SAVED. CLOSE...\n");
\r
420 print("CLOSED.\n");
\r
423 // Multiline text file buffers
\r
424 float buf_load(string pFilename)
\r
428 buf = buf_create();
\r
431 fh = fopen(pFilename, FILE_READ);
\r
435 while((l = fgets(fh)))
\r
437 bufstr_set(buf, i, l);
\r
444 void buf_save(float buf, string pFilename)
\r
447 fh = fopen(pFilename, FILE_WRITE);
\r
449 error(strcat("Can't write buf to ", pFilename));
\r
450 n = buf_getsize(buf);
\r
451 for(i = 0; i < n; ++i)
\r
452 fputs(fh, strcat(bufstr_get(buf, i), "\n"));
\r
456 string GametypeNameFromType(float g)
\r
458 if (g == GAME_DEATHMATCH) return "dm";
\r
459 else if (g == GAME_TEAM_DEATHMATCH) return "tdm";
\r
460 else if (g == GAME_DOMINATION) return "dom";
\r
461 else if (g == GAME_CTF) return "ctf";
\r
462 else if (g == GAME_LMS) return "lms";
\r
463 else if (g == GAME_ARENA) return "arena";
\r
464 else if (g == GAME_CA) return "ca";
\r
465 else if (g == GAME_KEYHUNT) return "kh";
\r
466 else if (g == GAME_ONSLAUGHT) return "ons";
\r
467 else if (g == GAME_ASSAULT) return "as";
\r
468 else if (g == GAME_RACE) return "rc";
\r
469 else if (g == GAME_CTS) return "cts";
\r
473 string mmsss(float tenths)
\r
477 tenths = floor(tenths + 0.5);
\r
478 minutes = floor(tenths / 600);
\r
479 tenths -= minutes * 600;
\r
480 s = ftos(1000 + tenths);
\r
481 return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1));
\r
484 string mmssss(float hundredths)
\r
488 hundredths = floor(hundredths + 0.5);
\r
489 minutes = floor(hundredths / 6000);
\r
490 hundredths -= minutes * 6000;
\r
491 s = ftos(10000 + hundredths);
\r
492 return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 2));
\r
495 string ScoreString(float pFlags, float pValue)
\r
500 pValue = floor(pValue + 0.5); // round
\r
502 if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME)))
\r
504 else if(pFlags & SFL_RANK)
\r
506 valstr = ftos(pValue);
\r
507 l = strlen(valstr);
\r
508 if((l >= 2) && (substring(valstr, l - 2, 1) == "1"))
\r
509 valstr = strcat(valstr, "th");
\r
510 else if(substring(valstr, l - 1, 1) == "1")
\r
511 valstr = strcat(valstr, "st");
\r
512 else if(substring(valstr, l - 1, 1) == "2")
\r
513 valstr = strcat(valstr, "nd");
\r
514 else if(substring(valstr, l - 1, 1) == "3")
\r
515 valstr = strcat(valstr, "rd");
\r
517 valstr = strcat(valstr, "th");
\r
519 else if(pFlags & SFL_TIME)
\r
520 valstr = TIME_ENCODED_TOSTRING(pValue);
\r
522 valstr = ftos(pValue);
\r
527 vector cross(vector a, vector b)
\r
530 '1 0 0' * (a_y * b_z - a_z * b_y)
\r
531 + '0 1 0' * (a_z * b_x - a_x * b_z)
\r
532 + '0 0 1' * (a_x * b_y - a_y * b_x);
\r
535 // compressed vector format:
\r
536 // like MD3, just even shorter
\r
537 // 4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90
\r
538 // 5 bit yaw (32 angles), 0=0, 8=90, 16=180, 24=270
\r
539 // 7 bit length (logarithmic encoding), 1/8 .. about 7844
\r
540 // length = 2^(length_encoded/8) / 8
\r
541 // if pitch is 90, yaw does nothing and therefore indicates the sign (yaw is then either 11111 or 11110); 11111 is pointing DOWN
\r
542 // thus, valid values are from 0000.11110.0000000 to 1111.11111.1111111
\r
543 // the special value 0 indicates the zero vector
\r
545 float lengthLogTable[128];
\r
547 float invertLengthLog(float x)
\r
549 float l, r, m, lerr, rerr;
\r
551 if(x >= lengthLogTable[127])
\r
553 if(x <= lengthLogTable[0])
\r
561 m = floor((l + r) / 2);
\r
562 if(lengthLogTable[m] < x)
\r
568 // now: r is >=, l is <
\r
569 lerr = (x - lengthLogTable[l]);
\r
570 rerr = (lengthLogTable[r] - x);
\r
576 vector decompressShortVector(float data)
\r
579 float pitch, yaw, len;
\r
582 pitch = (data & 0xF000) / 0x1000;
\r
583 yaw = (data & 0x0F80) / 0x80;
\r
584 len = (data & 0x007F);
\r
586 //print("\ndecompress: pitch ", ftos(pitch)); print("yaw ", ftos(yaw)); print("len ", ftos(len), "\n");
\r
599 yaw = .19634954084936207740 * yaw;
\r
600 pitch = .19634954084936207740 * pitch - 1.57079632679489661922;
\r
601 out_x = cos(yaw) * cos(pitch);
\r
602 out_y = sin(yaw) * cos(pitch);
\r
603 out_z = -sin(pitch);
\r
606 //print("decompressed: ", vtos(out), "\n");
\r
608 return out * lengthLogTable[len];
\r
611 float compressShortVector(vector vec)
\r
614 float pitch, yaw, len;
\r
617 //print("compress: ", vtos(vec), "\n");
\r
618 ang = vectoangles(vec);
\r
622 if(ang_x < -90 && ang_x > +90)
\r
623 error("BOGUS vectoangles");
\r
624 //print("angles: ", vtos(ang), "\n");
\r
626 pitch = floor(0.5 + (ang_x + 90) * 16 / 180) & 15; // -90..90 to 0..14
\r
635 yaw = floor(0.5 + ang_y * 32 / 360) & 31; // 0..360 to 0..32
\r
636 len = invertLengthLog(vlen(vec));
\r
638 //print("compressed: pitch ", ftos(pitch)); print("yaw ", ftos(yaw)); print("len ", ftos(len), "\n");
\r
640 return (pitch * 0x1000) + (yaw * 0x80) + len;
\r
643 void compressShortVector_init()
\r
648 for(i = 0; i < 128; ++i)
\r
650 lengthLogTable[i] = l;
\r
654 if(cvar("developer"))
\r
656 print("Verifying vector compression table...\n");
\r
657 for(i = 0x0F00; i < 0xFFFF; ++i)
\r
658 if(i != compressShortVector(decompressShortVector(i)))
\r
660 print("BROKEN vector compression: ", ftos(i));
\r
661 print(" -> ", vtos(decompressShortVector(i)));
\r
662 print(" -> ", ftos(compressShortVector(decompressShortVector(i))));
\r
671 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
\r
673 traceline(v0, v0 + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
\r
674 traceline(v0, v0 + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
\r
675 traceline(v0, v0 + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
\r
676 traceline(v0 + dvx, v0 + dvx + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
\r
677 traceline(v0 + dvx, v0 + dvx + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
\r
678 traceline(v0 + dvy, v0 + dvy + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
\r
679 traceline(v0 + dvy, v0 + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
\r
680 traceline(v0 + dvz, v0 + dvz + dvx, TRUE, forent); if(trace_fraction < 1) return 0;
\r
681 traceline(v0 + dvz, v0 + dvz + dvy, TRUE, forent); if(trace_fraction < 1) return 0;
\r
682 traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
\r
683 traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
\r
684 traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, TRUE, forent); if(trace_fraction < 1) return 0;
\r
689 string fixPriorityList(string order, float from, float to, float subtract, float complete)
\r
694 n = tokenize_console(order);
\r
696 for(i = 0; i < n; ++i)
\r
701 if(w >= from && w <= to)
\r
702 neworder = strcat(neworder, ftos(w), " ");
\r
706 if(w >= from && w <= to)
\r
707 neworder = strcat(neworder, ftos(w), " ");
\r
714 n = tokenize_console(neworder);
\r
715 for(w = to; w >= from; --w)
\r
717 for(i = 0; i < n; ++i)
\r
718 if(stof(argv(i)) == w)
\r
720 if(i == n) // not found
\r
721 neworder = strcat(neworder, ftos(w), " ");
\r
725 return substring(neworder, 0, strlen(neworder) - 1);
\r
728 string mapPriorityList(string order, string(string) mapfunc)
\r
733 n = tokenize_console(order);
\r
735 for(i = 0; i < n; ++i)
\r
736 neworder = strcat(neworder, mapfunc(argv(i)), " ");
\r
738 return substring(neworder, 0, strlen(neworder) - 1);
\r
741 string swapInPriorityList(string order, float i, float j)
\r
746 n = tokenize_console(order);
\r
748 if(i >= 0 && i < n && j >= 0 && j < n && i != j)
\r
751 for(w = 0; w < n; ++w)
\r
754 s = strcat(s, argv(j), " ");
\r
756 s = strcat(s, argv(i), " ");
\r
758 s = strcat(s, argv(w), " ");
\r
760 return substring(s, 0, strlen(s) - 1);
\r
766 float cvar_value_issafe(string s)
\r
768 if(strstrofs(s, "\"", 0) >= 0)
\r
770 if(strstrofs(s, "\\", 0) >= 0)
\r
772 if(strstrofs(s, ";", 0) >= 0)
\r
774 if(strstrofs(s, "$", 0) >= 0)
\r
776 if(strstrofs(s, "\r", 0) >= 0)
\r
778 if(strstrofs(s, "\n", 0) >= 0)
\r
784 void get_mi_min_max(float mode)
\r
789 strunzone(mi_shortname);
\r
790 mi_shortname = mapname;
\r
791 if(!strcasecmp(substring(mi_shortname, 0, 5), "maps/"))
\r
792 mi_shortname = substring(mi_shortname, 5, strlen(mi_shortname) - 5);
\r
793 if(!strcasecmp(substring(mi_shortname, strlen(mi_shortname) - 4, 4), ".bsp"))
\r
794 mi_shortname = substring(mi_shortname, 0, strlen(mi_shortname) - 4);
\r
795 mi_shortname = strzone(mi_shortname);
\r
807 MapInfo_Get_ByName(mi_shortname, 0, 0);
\r
808 if(MapInfo_Map_mins_x < MapInfo_Map_maxs_x)
\r
810 mi_min = MapInfo_Map_mins;
\r
811 mi_max = MapInfo_Map_maxs;
\r
819 tracebox('1 0 0' * mi_x,
\r
820 '0 1 0' * mi_y + '0 0 1' * mi_z,
\r
821 '0 1 0' * ma_y + '0 0 1' * ma_z,
\r
825 if(!trace_startsolid)
\r
826 mi_min_x = trace_endpos_x;
\r
828 tracebox('0 1 0' * mi_y,
\r
829 '1 0 0' * mi_x + '0 0 1' * mi_z,
\r
830 '1 0 0' * ma_x + '0 0 1' * ma_z,
\r
834 if(!trace_startsolid)
\r
835 mi_min_y = trace_endpos_y;
\r
837 tracebox('0 0 1' * mi_z,
\r
838 '1 0 0' * mi_x + '0 1 0' * mi_y,
\r
839 '1 0 0' * ma_x + '0 1 0' * ma_y,
\r
843 if(!trace_startsolid)
\r
844 mi_min_z = trace_endpos_z;
\r
846 tracebox('1 0 0' * ma_x,
\r
847 '0 1 0' * mi_y + '0 0 1' * mi_z,
\r
848 '0 1 0' * ma_y + '0 0 1' * ma_z,
\r
852 if(!trace_startsolid)
\r
853 mi_max_x = trace_endpos_x;
\r
855 tracebox('0 1 0' * ma_y,
\r
856 '1 0 0' * mi_x + '0 0 1' * mi_z,
\r
857 '1 0 0' * ma_x + '0 0 1' * ma_z,
\r
861 if(!trace_startsolid)
\r
862 mi_max_y = trace_endpos_y;
\r
864 tracebox('0 0 1' * ma_z,
\r
865 '1 0 0' * mi_x + '0 1 0' * mi_y,
\r
866 '1 0 0' * ma_x + '0 1 0' * ma_y,
\r
870 if(!trace_startsolid)
\r
871 mi_max_z = trace_endpos_z;
\r
876 void get_mi_min_max_texcoords(float mode)
\r
880 get_mi_min_max(mode);
\r
882 mi_picmin = mi_min;
\r
883 mi_picmax = mi_max;
\r
885 // extend mi_picmax to get a square aspect ratio
\r
886 // center the map in that area
\r
887 extend = mi_picmax - mi_picmin;
\r
888 if(extend_y > extend_x)
\r
890 mi_picmin_x -= (extend_y - extend_x) * 0.5;
\r
891 mi_picmax_x += (extend_y - extend_x) * 0.5;
\r
895 mi_picmin_y -= (extend_x - extend_y) * 0.5;
\r
896 mi_picmax_y += (extend_x - extend_y) * 0.5;
\r
899 // add another some percent
\r
900 extend = (mi_picmax - mi_picmin) * (1 / 64.0);
\r
901 mi_picmin -= extend;
\r
902 mi_picmax += extend;
\r
904 // calculate the texcoords
\r
905 mi_pictexcoord0 = mi_pictexcoord1 = mi_pictexcoord2 = mi_pictexcoord3 = '0 0 0';
\r
906 // first the two corners of the origin
\r
907 mi_pictexcoord0_x = (mi_min_x - mi_picmin_x) / (mi_picmax_x - mi_picmin_x);
\r
908 mi_pictexcoord0_y = (mi_min_y - mi_picmin_y) / (mi_picmax_y - mi_picmin_y);
\r
909 mi_pictexcoord2_x = (mi_max_x - mi_picmin_x) / (mi_picmax_x - mi_picmin_x);
\r
910 mi_pictexcoord2_y = (mi_max_y - mi_picmin_y) / (mi_picmax_y - mi_picmin_y);
\r
911 // then the other corners
\r
912 mi_pictexcoord1_x = mi_pictexcoord0_x;
\r
913 mi_pictexcoord1_y = mi_pictexcoord2_y;
\r
914 mi_pictexcoord3_x = mi_pictexcoord2_x;
\r
915 mi_pictexcoord3_y = mi_pictexcoord0_y;
\r
920 void cvar_settemp(string pKey, string pValue)
\r
922 error("cvar_settemp called from CSQC - use cvar_clientsettemp instead!");
\r
924 void cvar_settemp_restore()
\r
926 error("cvar_settemp_restore called from CSQC - use cvar_clientsettemp instead!");
\r
929 void cvar_settemp(string pKey, string pValue)
\r
932 string settemp_var;
\r
933 if(cvar_string(pKey) == pValue)
\r
935 i = cvar("settemp_idx");
\r
936 cvar_set("settemp_idx", ftos(i+1));
\r
937 settemp_var = strcat("_settemp_x", ftos(i));
\r
939 registercvar(settemp_var, "", 0);
\r
941 registercvar(settemp_var, "");
\r
943 cvar_set("settemp_list", strcat("1 ", pKey, " ", settemp_var, " ", cvar_string("settemp_list")));
\r
944 cvar_set(settemp_var, cvar_string(pKey));
\r
945 cvar_set(pKey, pValue);
\r
948 void cvar_settemp_restore()
\r
950 // undo what cvar_settemp did
\r
952 n = tokenize_console(cvar_string("settemp_list"));
\r
953 for(i = 0; i < n - 3; i += 3)
\r
954 cvar_set(argv(i + 1), cvar_string(argv(i + 2)));
\r
955 cvar_set("settemp_list", "0");
\r
959 float almost_equals(float a, float b)
\r
962 eps = (max(a, -a) + max(b, -b)) * 0.001;
\r
963 if(a - b < eps && b - a < eps)
\r
968 float almost_in_bounds(float a, float b, float c)
\r
971 eps = (max(a, -a) + max(c, -c)) * 0.001;
\r
972 return b == median(a - eps, b, c + eps);
\r
975 float power2of(float e)
\r
979 float log2of(float x)
\r
981 // NOTE: generated code
\r
1054 float rgb_mi_ma_to_hue(vector rgb, float mi, float ma)
\r
1058 else if(ma == rgb_x)
\r
1060 if(rgb_y >= rgb_z)
\r
1061 return (rgb_y - rgb_z) / (ma - mi);
\r
1063 return (rgb_y - rgb_z) / (ma - mi) + 6;
\r
1065 else if(ma == rgb_y)
\r
1066 return (rgb_z - rgb_x) / (ma - mi) + 2;
\r
1067 else // if(ma == rgb_z)
\r
1068 return (rgb_x - rgb_y) / (ma - mi) + 4;
\r
1071 vector hue_mi_ma_to_rgb(float hue, float mi, float ma)
\r
1075 hue -= 6 * floor(hue / 6);
\r
1077 //else if(ma == rgb_x)
\r
1078 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
\r
1082 rgb_y = hue * (ma - mi) + mi;
\r
1085 //else if(ma == rgb_y)
\r
1086 // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120;
\r
1089 rgb_x = (2 - hue) * (ma - mi) + mi;
\r
1097 rgb_z = (hue - 2) * (ma - mi) + mi;
\r
1099 //else // if(ma == rgb_z)
\r
1100 // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240;
\r
1104 rgb_y = (4 - hue) * (ma - mi) + mi;
\r
1109 rgb_x = (hue - 4) * (ma - mi) + mi;
\r
1113 //else if(ma == rgb_x)
\r
1114 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
\r
1115 else // if(hue <= 6)
\r
1119 rgb_z = (6 - hue) * (ma - mi) + mi;
\r
1125 vector rgb_to_hsv(vector rgb)
\r
1130 mi = min3(rgb_x, rgb_y, rgb_z);
\r
1131 ma = max3(rgb_x, rgb_y, rgb_z);
\r
1133 hsv_x = rgb_mi_ma_to_hue(rgb, mi, ma);
\r
1139 hsv_y = 1 - mi/ma;
\r
1144 vector hsv_to_rgb(vector hsv)
\r
1146 return hue_mi_ma_to_rgb(hsv_x, hsv_z * (1 - hsv_y), hsv_z);
\r
1149 vector rgb_to_hsl(vector rgb)
\r
1154 mi = min3(rgb_x, rgb_y, rgb_z);
\r
1155 ma = max3(rgb_x, rgb_y, rgb_z);
\r
1157 hsl_x = rgb_mi_ma_to_hue(rgb, mi, ma);
\r
1159 hsl_z = 0.5 * (mi + ma);
\r
1162 else if(hsl_z <= 0.5)
\r
1163 hsl_y = (ma - mi) / (2*hsl_z);
\r
1164 else // if(hsl_z > 0.5)
\r
1165 hsl_y = (ma - mi) / (2 - 2*hsl_z);
\r
1170 vector hsl_to_rgb(vector hsl)
\r
1172 float mi, ma, maminusmi;
\r
1175 maminusmi = hsl_y * 2 * hsl_z;
\r
1177 maminusmi = hsl_y * (2 - 2 * hsl_z);
\r
1179 // hsl_z = 0.5 * mi + 0.5 * ma
\r
1180 // maminusmi = - mi + ma
\r
1181 mi = hsl_z - 0.5 * maminusmi;
\r
1182 ma = hsl_z + 0.5 * maminusmi;
\r
1184 return hue_mi_ma_to_rgb(hsl_x, mi, ma);
\r
1187 string rgb_to_hexcolor(vector rgb)
\r
1192 DEC_TO_HEXDIGIT(floor(rgb_x * 15 + 0.5)),
\r
1193 DEC_TO_HEXDIGIT(floor(rgb_y * 15 + 0.5)),
\r
1194 DEC_TO_HEXDIGIT(floor(rgb_z * 15 + 0.5))
\r
1198 // requires that m2>m1 in all coordinates, and that m4>m3
\r
1199 float boxesoverlap(vector m1, vector m2, vector m3, vector m4) {return m2_x >= m3_x && m1_x <= m4_x && m2_y >= m3_y && m1_y <= m4_y && m2_z >= m3_z && m1_z <= m4_z;};
\r
1201 // requires the same, but is a stronger condition
\r
1202 float boxinsidebox(vector smins, vector smaxs, vector bmins, vector bmaxs) {return smins_x >= bmins_x && smaxs_x <= bmaxs_x && smins_y >= bmins_y && smaxs_y <= bmaxs_y && smins_z >= bmins_z && smaxs_z <= bmaxs_z;};
\r
1207 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
\r
1209 float ICanHasKallerz;
\r
1211 // detect color codes support in the width function
\r
1212 ICanHasKallerz = (w("^7", theSize) == 0);
\r
1215 // The following function is SLOW.
\r
1216 // For your safety and for the protection of those around you...
\r
1217 // DO NOT CALL THIS AT HOME.
\r
1218 // No really, don't.
\r
1219 if(w(theText, theSize) <= maxWidth)
\r
1220 return strlen(theText); // yeah!
\r
1222 // binary search for right place to cut string
\r
1224 float left, right, middle; // this always works
\r
1226 right = strlen(theText); // this always fails
\r
1229 middle = floor((left + right) / 2);
\r
1230 if(w(substring(theText, 0, middle), theSize) <= maxWidth)
\r
1235 while(left < right - 1);
\r
1237 if(ICanHasKallerz)
\r
1239 // NOTE: when color codes are involved, this binary search is,
\r
1240 // mathematically, BROKEN. However, it is obviously guaranteed to
\r
1241 // terminate, as the range still halves each time - but nevertheless, it is
\r
1242 // guaranteed that it finds ONE valid cutoff place (where "left" is in
\r
1243 // range, and "right" is outside).
\r
1245 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
\r
1246 // and decrease left on the basis of the chars detected of the truncated tag
\r
1247 // Even if the ^xrgb tag is not complete/correct, left is decreased
\r
1248 // (sometimes too much but with a correct result)
\r
1249 // it fixes also ^[0-9]
\r
1250 while(left >= 1 && substring(theText, left-1, 1) == "^")
\r
1253 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
\r
1255 else if (left >= 3 && substring(theText, left-3, 2) == "^x")
\r
1257 ch = str2chr(theText, left-1);
\r
1258 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
\r
1261 else if (left >= 4 && substring(theText, left-4, 2) == "^x")
\r
1263 ch = str2chr(theText, left-2);
\r
1264 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
\r
1266 ch = str2chr(theText, left-1);
\r
1267 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
\r
1276 float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w)
\r
1278 float ICanHasKallerz;
\r
1280 // detect color codes support in the width function
\r
1281 ICanHasKallerz = (w("^7") == 0);
\r
1284 // The following function is SLOW.
\r
1285 // For your safety and for the protection of those around you...
\r
1286 // DO NOT CALL THIS AT HOME.
\r
1287 // No really, don't.
\r
1288 if(w(theText) <= maxWidth)
\r
1289 return strlen(theText); // yeah!
\r
1291 // binary search for right place to cut string
\r
1293 float left, right, middle; // this always works
\r
1295 right = strlen(theText); // this always fails
\r
1298 middle = floor((left + right) / 2);
\r
1299 if(w(substring(theText, 0, middle)) <= maxWidth)
\r
1304 while(left < right - 1);
\r
1306 if(ICanHasKallerz)
\r
1308 // NOTE: when color codes are involved, this binary search is,
\r
1309 // mathematically, BROKEN. However, it is obviously guaranteed to
\r
1310 // terminate, as the range still halves each time - but nevertheless, it is
\r
1311 // guaranteed that it finds ONE valid cutoff place (where "left" is in
\r
1312 // range, and "right" is outside).
\r
1314 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
\r
1315 // and decrease left on the basis of the chars detected of the truncated tag
\r
1316 // Even if the ^xrgb tag is not complete/correct, left is decreased
\r
1317 // (sometimes too much but with a correct result)
\r
1318 // it fixes also ^[0-9]
\r
1319 while(left >= 1 && substring(theText, left-1, 1) == "^")
\r
1322 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
\r
1324 else if (left >= 3 && substring(theText, left-3, 2) == "^x")
\r
1326 ch = str2chr(theText, left-1);
\r
1327 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
\r
1330 else if (left >= 4 && substring(theText, left-4, 2) == "^x")
\r
1332 ch = str2chr(theText, left-2);
\r
1333 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
\r
1335 ch = str2chr(theText, left-1);
\r
1336 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
\r
1345 string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
\r
1351 s = getWrappedLine_remaining;
\r
1353 cantake = textLengthUpToWidth(s, w, theFontSize, tw);
\r
1354 if(cantake > 0 && cantake < strlen(s))
\r
1356 take = cantake - 1;
\r
1357 while(take > 0 && substring(s, take, 1) != " ")
\r
1361 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
\r
1362 if(getWrappedLine_remaining == "")
\r
1363 getWrappedLine_remaining = string_null;
\r
1364 return substring(s, 0, cantake);
\r
1368 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
\r
1369 if(getWrappedLine_remaining == "")
\r
1370 getWrappedLine_remaining = string_null;
\r
1371 return substring(s, 0, take);
\r
1376 getWrappedLine_remaining = string_null;
\r
1381 string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw)
\r
1387 s = getWrappedLine_remaining;
\r
1389 cantake = textLengthUpToLength(s, w, tw);
\r
1390 if(cantake > 0 && cantake < strlen(s))
\r
1392 take = cantake - 1;
\r
1393 while(take > 0 && substring(s, take, 1) != " ")
\r
1397 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
\r
1398 if(getWrappedLine_remaining == "")
\r
1399 getWrappedLine_remaining = string_null;
\r
1400 return substring(s, 0, cantake);
\r
1404 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
\r
1405 if(getWrappedLine_remaining == "")
\r
1406 getWrappedLine_remaining = string_null;
\r
1407 return substring(s, 0, take);
\r
1412 getWrappedLine_remaining = string_null;
\r
1417 string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
\r
1419 if(tw(theText, theFontSize) <= maxWidth)
\r
1422 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("...", theFontSize), theFontSize, tw)), "...");
\r
1425 string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw)
\r
1427 if(tw(theText) <= maxWidth)
\r
1430 return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
\r
1433 float isGametypeInFilter(float gt, float tp, string pattern)
\r
1435 string subpattern, subpattern2, subpattern3;
\r
1436 subpattern = strcat(",", GametypeNameFromType(gt), ",");
\r
1438 subpattern2 = ",teams,";
\r
1440 subpattern2 = ",noteams,";
\r
1441 if(gt == GAME_RACE || gt == GAME_CTS)
\r
1442 subpattern3 = ",race,";
\r
1444 subpattern3 = string_null;
\r
1446 if(substring(pattern, 0, 1) == "-")
\r
1448 pattern = substring(pattern, 1, strlen(pattern) - 1);
\r
1449 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
\r
1451 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0)
\r
1453 if(subpattern3 && strstrofs(strcat(",", pattern, ","), subpattern3, 0) >= 0)
\r
1458 if(substring(pattern, 0, 1) == "+")
\r
1459 pattern = substring(pattern, 1, strlen(pattern) - 1);
\r
1460 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
\r
1461 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0)
\r
1462 if((!subpattern3) || strstrofs(strcat(",", pattern, ","), subpattern3, 0) < 0)
\r
1468 void shuffle(float n, swapfunc_t swap, entity pass)
\r
1471 for(i = 1; i < n; ++i)
\r
1473 // swap i-th item at a random position from 0 to i
\r
1474 // proof for even distribution:
\r
1477 // item n+1 gets at any position with chance 1/(n+1)
\r
1478 // all others will get their 1/n chance reduced by factor n/(n+1)
\r
1479 // to be on place n+1, their chance will be 1/(n+1)
\r
1480 // 1/n * n/(n+1) = 1/(n+1)
\r
1482 j = floor(random() * (i + 1));
\r
1488 string substring_range(string s, float b, float e)
\r
1490 return substring(s, b, e - b);
\r
1493 string swapwords(string str, float i, float j)
\r
1496 string s1, s2, s3, s4, s5;
\r
1497 float si, ei, sj, ej, s0, en;
\r
1498 n = tokenizebyseparator(str, " "); // must match g_maplist processing in ShuffleMaplist and "shuffle"
\r
1499 si = argv_start_index(i);
\r
1500 sj = argv_start_index(j);
\r
1501 ei = argv_end_index(i);
\r
1502 ej = argv_end_index(j);
\r
1503 s0 = argv_start_index(0);
\r
1504 en = argv_end_index(n-1);
\r
1505 s1 = substring_range(str, s0, si);
\r
1506 s2 = substring_range(str, si, ei);
\r
1507 s3 = substring_range(str, ei, sj);
\r
1508 s4 = substring_range(str, sj, ej);
\r
1509 s5 = substring_range(str, ej, en);
\r
1510 return strcat(s1, s4, s3, s2, s5);
\r
1513 string _shufflewords_str;
\r
1514 void _shufflewords_swapfunc(float i, float j, entity pass)
\r
1516 _shufflewords_str = swapwords(_shufflewords_str, i, j);
\r
1518 string shufflewords(string str)
\r
1521 _shufflewords_str = str;
\r
1522 n = tokenizebyseparator(str, " ");
\r
1523 shuffle(n, _shufflewords_swapfunc, world);
\r
1524 str = _shufflewords_str;
\r
1525 _shufflewords_str = string_null;
\r
1529 vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0
\r
1538 v_x = v_y = -c / b;
\r
1545 // actually, every number solves the equation!
\r
1556 if(a > 0) // put the smaller solution first
\r
1558 v_x = ((-b)-D) / (2*a);
\r
1559 v_y = ((-b)+D) / (2*a);
\r
1563 v_x = (-b+D) / (2*a);
\r
1564 v_y = (-b-D) / (2*a);
\r
1570 // complex solutions!
\r
1584 float _unacceptable_compiler_bug_1_a(float b, float c) { return b == c; }
\r
1585 float _unacceptable_compiler_bug_1_b() { return 1; }
\r
1586 float _unacceptable_compiler_bug_1_c(float d) { return 2 * d; }
\r
1587 float _unacceptable_compiler_bug_1_d() { return 1; }
\r
1589 void check_unacceptable_compiler_bugs()
\r
1591 if(cvar("_allow_unacceptable_compiler_bugs"))
\r
1593 tokenize_console("foo bar");
\r
1594 if(strcat(argv(0), substring("foo bar", 4, 7 - argv_start_index(1))) == "barbar")
\r
1595 error("fteqcc bug introduced with revision 3178 detected. Please upgrade fteqcc to a later revision, downgrade fteqcc to revision 3177, or pester Spike until he fixes it. You can set _allow_unacceptable_compiler_bugs 1 to skip this check, but expect stuff to be horribly broken then.");
\r
1598 float compressShotOrigin(vector v)
\r
1601 x = rint(v_x * 2);
\r
1602 y = rint(v_y * 4) + 128;
\r
1603 z = rint(v_z * 4) + 128;
\r
1604 if(x > 255 || x < 0)
\r
1606 print("shot origin ", vtos(v), " x out of bounds\n");
\r
1607 x = bound(0, x, 255);
\r
1609 if(y > 255 || y < 0)
\r
1611 print("shot origin ", vtos(v), " y out of bounds\n");
\r
1612 y = bound(0, y, 255);
\r
1614 if(z > 255 || z < 0)
\r
1616 print("shot origin ", vtos(v), " z out of bounds\n");
\r
1617 z = bound(0, z, 255);
\r
1619 return x * 0x10000 + y * 0x100 + z;
\r
1621 vector decompressShotOrigin(float f)
\r
1624 v_x = ((f & 0xFF0000) / 0x10000) / 2;
\r
1625 v_y = ((f & 0xFF00) / 0x100 - 128) / 4;
\r
1626 v_z = ((f & 0xFF) - 128) / 4;
\r
1630 void heapsort(float n, swapfunc_t swap, comparefunc_t cmp, entity pass)
\r
1632 float start, end, root, child;
\r
1635 start = floor((n - 2) / 2);
\r
1638 // siftdown(start, count-1);
\r
1640 while(root * 2 + 1 <= n-1)
\r
1642 child = root * 2 + 1;
\r
1644 if(cmp(child, child+1, pass) < 0)
\r
1646 if(cmp(root, child, pass) < 0)
\r
1648 swap(root, child, pass);
\r
1654 // end of siftdown
\r
1662 swap(0, end, pass);
\r
1664 // siftdown(0, end);
\r
1666 while(root * 2 + 1 <= end)
\r
1668 child = root * 2 + 1;
\r
1669 if(child < end && cmp(child, child+1, pass) < 0)
\r
1671 if(cmp(root, child, pass) < 0)
\r
1673 swap(root, child, pass);
\r
1679 // end of siftdown
\r
1683 void RandomSelection_Init()
\r
1685 RandomSelection_totalweight = 0;
\r
1686 RandomSelection_chosen_ent = world;
\r
1687 RandomSelection_chosen_float = 0;
\r
1688 RandomSelection_chosen_string = string_null;
\r
1689 RandomSelection_best_priority = -1;
\r
1691 void RandomSelection_Add(entity e, float f, string s, float weight, float priority)
\r
1693 if(priority > RandomSelection_best_priority)
\r
1695 RandomSelection_best_priority = priority;
\r
1696 RandomSelection_chosen_ent = e;
\r
1697 RandomSelection_chosen_float = f;
\r
1698 RandomSelection_chosen_string = s;
\r
1699 RandomSelection_totalweight = weight;
\r
1701 else if(priority == RandomSelection_best_priority)
\r
1703 RandomSelection_totalweight += weight;
\r
1704 if(random() * RandomSelection_totalweight <= weight)
\r
1706 RandomSelection_chosen_ent = e;
\r
1707 RandomSelection_chosen_float = f;
\r
1708 RandomSelection_chosen_string = s;
\r
1713 vector healtharmor_maxdamage(float h, float a, float armorblock)
\r
1715 // NOTE: we'll always choose the SMALLER value...
\r
1716 float healthdamage, armordamage, armorideal;
\r
1718 healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health
\r
1719 armordamage = a + (h - 1); // damage we can take if we could use more armor
\r
1720 armorideal = healthdamage * armorblock;
\r
1722 if(armordamage < healthdamage)
\r
1724 v_x = armordamage;
\r
1729 v_x = healthdamage;
\r
1735 vector healtharmor_applydamage(float a, float armorblock, float damage)
\r
1738 v_y = bound(0, damage * armorblock, a); // save
\r
1739 v_x = bound(0, damage - v_y, damage); // take
\r
1744 string getcurrentmod()
\r
1748 m = cvar_string("fs_gamedir");
\r
1749 n = tokenize_console(m);
\r
1753 return argv(n - 1);
\r
1758 float ReadInt24_t()
\r
1761 v = ReadShort() * 256; // note: this is signed
\r
1762 v += ReadByte(); // note: this is unsigned
\r
1766 void WriteInt24_t(float dest, float val)
\r
1769 WriteShort(dest, (v = floor(val / 256)));
\r
1770 WriteByte(dest, val - v * 256); // 0..255
\r
1775 float float2range11(float f)
\r
1777 // continuous function mapping all reals into -1..1
\r
1778 return f / (fabs(f) + 1);
\r
1781 float float2range01(float f)
\r
1783 // continuous function mapping all reals into 0..1
\r
1784 return 0.5 + 0.5 * float2range11(f);
\r
1787 // from the GNU Scientific Library
\r
1788 float gsl_ran_gaussian_lastvalue;
\r
1789 float gsl_ran_gaussian_lastvalue_set;
\r
1790 float gsl_ran_gaussian(float sigma)
\r
1793 if(gsl_ran_gaussian_lastvalue_set)
\r
1795 gsl_ran_gaussian_lastvalue_set = 0;
\r
1796 return sigma * gsl_ran_gaussian_lastvalue;
\r
1800 a = random() * 2 * M_PI;
\r
1801 b = sqrt(-2 * log(random()));
\r
1802 gsl_ran_gaussian_lastvalue = cos(a) * b;
\r
1803 gsl_ran_gaussian_lastvalue_set = 1;
\r
1804 return sigma * sin(a) * b;
\r
1808 string car(string s)
\r
1811 o = strstrofs(s, " ", 0);
\r
1814 return substring(s, 0, o);
\r
1816 string cdr(string s)
\r
1819 o = strstrofs(s, " ", 0);
\r
1821 return string_null;
\r
1822 return substring(s, o + 1, strlen(s) - (o + 1));
\r
1824 float matchacl(string acl, string str)
\r
1831 t = car(acl); acl = cdr(acl);
\r
1833 if(substring(t, 0, 1) == "-")
\r
1836 t = substring(t, 1, strlen(t) - 1);
\r
1838 else if(substring(t, 0, 1) == "+")
\r
1839 t = substring(t, 1, strlen(t) - 1);
\r
1840 if(substring(t, -1, 1) == "*")
\r
1842 t = substring(t, 0, strlen(t) - 1);
\r
1843 s = substring(s, 0, strlen(t));
\r
1855 float startsWith(string haystack, string needle)
\r
1857 return substring(haystack, 0, strlen(needle)) == needle;
\r
1859 float startsWithNocase(string haystack, string needle)
\r
1861 return strcasecmp(substring(haystack, 0, strlen(needle)), needle) == 0;
\r