4 #include "../dpdefs/csprogsdefs.qh"
5 #include "../client/defs.qh"
6 #include "constants.qh"
7 #include "../client/mutators/events.qh"
9 #include "notifications.qh"
10 #include "deathtypes.qh"
13 #include "../dpdefs/progsdefs.qh"
14 #include "../dpdefs/dpextensions.qh"
15 #include "constants.qh"
16 #include "../server/autocvars.qh"
17 #include "../server/defs.qh"
18 #include "../server/mutators/events.qh"
19 #include "notifications.qh"
20 #include "deathtypes.qh"
24 string wordwrap_buffer;
26 void wordwrap_buffer_put(string s)
28 wordwrap_buffer = strcat(wordwrap_buffer, s);
31 string wordwrap(string s, float l)
35 wordwrap_cb(s, l, wordwrap_buffer_put);
43 void wordwrap_buffer_sprint(string s)
45 wordwrap_buffer = strcat(wordwrap_buffer, s);
48 sprint(self, wordwrap_buffer);
53 void wordwrap_sprint(string s, float l)
56 wordwrap_cb(s, l, wordwrap_buffer_sprint);
57 if(wordwrap_buffer != "")
58 sprint(self, strcat(wordwrap_buffer, "\n"));
66 string draw_UseSkinFor(string pic)
68 if(substring(pic, 0, 1) == "/")
69 return substring(pic, 1, strlen(pic)-1);
71 return strcat(draw_currentSkin, "/", pic);
75 void wordwrap_cb(string s, float l, void(string) callback)
78 float lleft, i, j, wlen;
82 for (i = 0;i < strlen(s);++i)
84 if (substring(s, i, 2) == "\\n")
90 else if (substring(s, i, 1) == "\n")
95 else if (substring(s, i, 1) == " ")
105 for (j = i+1;j < strlen(s);++j)
106 // ^^ this skips over the first character of a word, which
107 // is ALWAYS part of the word
108 // this is safe since if i+1 == strlen(s), i will become
109 // strlen(s)-1 at the end of this block and the function
110 // will terminate. A space can't be the first character we
111 // read here, and neither can a \n be the start, since these
112 // two cases have been handled above.
114 c = substring(s, j, 1);
121 // we need to keep this tempstring alive even if substring is
122 // called repeatedly, so call strcat even though we're not
132 callback(substring(s, i, wlen));
133 lleft = lleft - wlen;
140 void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
169 // converts a number to a string with the indicated number of decimals
170 // works for up to 10 decimals!
171 string ftos_decimals(float number, float decimals)
173 // inhibit stupid negative zero
176 // we have sprintf...
177 return sprintf("%.*f", decimals, number);
180 vector colormapPaletteColor(float c, bool isPants)
184 case 0: return '1.000000 1.000000 1.000000';
185 case 1: return '1.000000 0.333333 0.000000';
186 case 2: return '0.000000 1.000000 0.501961';
187 case 3: return '0.000000 1.000000 0.000000';
188 case 4: return '1.000000 0.000000 0.000000';
189 case 5: return '0.000000 0.666667 1.000000';
190 case 6: return '0.000000 1.000000 1.000000';
191 case 7: return '0.501961 1.000000 0.000000';
192 case 8: return '0.501961 0.000000 1.000000';
193 case 9: return '1.000000 0.000000 1.000000';
194 case 10: return '1.000000 0.000000 0.501961';
195 case 11: return '0.000000 0.000000 1.000000';
196 case 12: return '1.000000 1.000000 0.000000';
197 case 13: return '0.000000 0.333333 1.000000';
198 case 14: return '1.000000 0.666667 0.000000';
202 '1 0 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 0.0000000000))
203 + '0 1 0' * (0.502 + 0.498 * sin(time / 2.7182818285 + 2.0943951024))
204 + '0 0 1' * (0.502 + 0.498 * sin(time / 2.7182818285 + 4.1887902048));
207 '1 0 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 5.2359877560))
208 + '0 1 0' * (0.502 + 0.498 * sin(time / 3.1415926536 + 3.1415926536))
209 + '0 0 1' * (0.502 + 0.498 * sin(time / 3.1415926536 + 1.0471975512));
210 default: return '0.000 0.000 0.000';
214 // Databases (hash tables)
215 const float DB_BUCKETS = 8192;
216 void db_save(float db, string pFilename)
219 fh = fopen(pFilename, FILE_WRITE);
222 LOG_INFO(strcat("^1Can't write DB to ", pFilename));
226 fputs(fh, strcat(ftos(DB_BUCKETS), "\n"));
227 for(i = 0; i < n; ++i)
228 fputs(fh, strcat(bufstr_get(db, i), "\n"));
237 int db_load(string pFilename)
239 float db, fh, i, j, n;
244 fh = fopen(pFilename, FILE_READ);
248 if(stof(l) == DB_BUCKETS)
251 while((l = fgets(fh)))
254 bufstr_set(db, i, l);
260 // different count of buckets, or a dump?
261 // need to reorganize the database then (SLOW)
263 // note: we also parse the first line (l) in case the DB file is
264 // missing the bucket count
267 n = tokenizebyseparator(l, "\\");
268 for(j = 2; j < n; j += 2)
269 db_put(db, argv(j-1), uri_unescape(argv(j)));
271 while((l = fgets(fh)));
277 void db_dump(float db, string pFilename)
279 float fh, i, j, n, m;
280 fh = fopen(pFilename, FILE_WRITE);
282 error(strcat("Can't dump DB to ", pFilename));
285 for(i = 0; i < n; ++i)
287 m = tokenizebyseparator(bufstr_get(db, i), "\\");
288 for(j = 2; j < m; j += 2)
289 fputs(fh, strcat("\\", argv(j-1), "\\", argv(j), "\n"));
294 void db_close(float db)
299 string db_get(float db, string pKey)
302 h = crc16(false, pKey) % DB_BUCKETS;
303 return uri_unescape(infoget(bufstr_get(db, h), pKey));
306 void db_put(float db, string pKey, string pValue)
309 h = crc16(false, pKey) % DB_BUCKETS;
310 bufstr_set(db, h, infoadd(bufstr_get(db, h), pKey, uri_escape(pValue)));
316 LOG_INFO("LOAD...\n");
317 db = db_load("foo.db");
318 LOG_INFO("LOADED. FILL...\n");
319 for(i = 0; i < DB_BUCKETS; ++i)
320 db_put(db, ftos(random()), "X");
321 LOG_INFO("FILLED. SAVE...\n");
322 db_save(db, "foo.db");
323 LOG_INFO("SAVED. CLOSE...\n");
325 LOG_INFO("CLOSED.\n");
328 // Multiline text file buffers
329 int buf_load(string pFilename)
336 fh = fopen(pFilename, FILE_READ);
343 while((l = fgets(fh)))
345 bufstr_set(buf, i, l);
352 void buf_save(float buf, string pFilename)
355 fh = fopen(pFilename, FILE_WRITE);
357 error(strcat("Can't write buf to ", pFilename));
358 n = buf_getsize(buf);
359 for(i = 0; i < n; ++i)
360 fputs(fh, strcat(bufstr_get(buf, i), "\n"));
364 string format_time(float seconds)
366 float days, hours, minutes;
367 seconds = floor(seconds + 0.5);
368 days = floor(seconds / 864000);
369 seconds -= days * 864000;
370 hours = floor(seconds / 36000);
371 seconds -= hours * 36000;
372 minutes = floor(seconds / 600);
373 seconds -= minutes * 600;
375 return sprintf(_("%d days, %02d:%02d:%02d"), days, hours, minutes, seconds);
377 return sprintf(_("%02d:%02d:%02d"), hours, minutes, seconds);
380 string mmsss(float tenths)
384 tenths = floor(tenths + 0.5);
385 minutes = floor(tenths / 600);
386 tenths -= minutes * 600;
387 s = ftos(1000 + tenths);
388 return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 1));
391 string mmssss(float hundredths)
395 hundredths = floor(hundredths + 0.5);
396 minutes = floor(hundredths / 6000);
397 hundredths -= minutes * 6000;
398 s = ftos(10000 + hundredths);
399 return strcat(ftos(minutes), ":", substring(s, 1, 2), ".", substring(s, 3, 2));
402 string ScoreString(int pFlags, float pValue)
407 pValue = floor(pValue + 0.5); // round
409 if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME)))
411 else if(pFlags & SFL_RANK)
413 valstr = ftos(pValue);
415 if((l >= 2) && (substring(valstr, l - 2, 1) == "1"))
416 valstr = strcat(valstr, "th");
417 else if(substring(valstr, l - 1, 1) == "1")
418 valstr = strcat(valstr, "st");
419 else if(substring(valstr, l - 1, 1) == "2")
420 valstr = strcat(valstr, "nd");
421 else if(substring(valstr, l - 1, 1) == "3")
422 valstr = strcat(valstr, "rd");
424 valstr = strcat(valstr, "th");
426 else if(pFlags & SFL_TIME)
427 valstr = TIME_ENCODED_TOSTRING(pValue);
429 valstr = ftos(pValue);
434 // compressed vector format:
435 // like MD3, just even shorter
436 // 4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90
437 // 5 bit yaw (32 angles), 0=0, 8=90, 16=180, 24=270
438 // 7 bit length (logarithmic encoding), 1/8 .. about 7844
439 // length = 2^(length_encoded/8) / 8
440 // if pitch is 90, yaw does nothing and therefore indicates the sign (yaw is then either 11111 or 11110); 11111 is pointing DOWN
441 // thus, valid values are from 0000.11110.0000000 to 1111.11111.1111111
442 // the special value 0 indicates the zero vector
444 float lengthLogTable[128];
446 float invertLengthLog(float x)
450 if(x >= lengthLogTable[127])
452 if(x <= lengthLogTable[0])
460 m = floor((l + r) / 2);
461 if(lengthLogTable[m] < x)
467 // now: r is >=, l is <
468 float lerr = (x - lengthLogTable[l]);
469 float rerr = (lengthLogTable[r] - x);
475 vector decompressShortVector(int data)
480 float p = (data & 0xF000) / 0x1000;
481 float y = (data & 0x0F80) / 0x80;
482 int len = (data & 0x007F);
484 //print("\ndecompress: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n");
497 y = .19634954084936207740 * y;
498 p = .19634954084936207740 * p - 1.57079632679489661922;
499 out.x = cos(y) * cos(p);
500 out.y = sin(y) * cos(p);
504 //print("decompressed: ", vtos(out), "\n");
506 return out * lengthLogTable[len];
509 float compressShortVector(vector vec)
515 //print("compress: ", vtos(vec), "\n");
516 ang = vectoangles(vec);
520 if(ang.x < -90 && ang.x > +90)
521 error("BOGUS vectoangles");
522 //print("angles: ", vtos(ang), "\n");
524 p = floor(0.5 + (ang.x + 90) * 16 / 180) & 15; // -90..90 to 0..14
533 y = floor(0.5 + ang.y * 32 / 360) & 31; // 0..360 to 0..32
534 len = invertLengthLog(vlen(vec));
536 //print("compressed: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n");
538 return (p * 0x1000) + (y * 0x80) + len;
541 void compressShortVector_init()
544 float f = pow(2, 1/8);
546 for(i = 0; i < 128; ++i)
548 lengthLogTable[i] = l;
552 if(cvar("developer"))
554 LOG_INFO("Verifying vector compression table...\n");
555 for(i = 0x0F00; i < 0xFFFF; ++i)
556 if(i != compressShortVector(decompressShortVector(i)))
558 LOG_INFO("BROKEN vector compression: ", ftos(i));
559 LOG_INFO(" -> ", vtos(decompressShortVector(i)));
560 LOG_INFO(" -> ", ftos(compressShortVector(decompressShortVector(i))));
569 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
571 traceline(v0, v0 + dvx, true, forent); if(trace_fraction < 1) return 0;
572 traceline(v0, v0 + dvy, true, forent); if(trace_fraction < 1) return 0;
573 traceline(v0, v0 + dvz, true, forent); if(trace_fraction < 1) return 0;
574 traceline(v0 + dvx, v0 + dvx + dvy, true, forent); if(trace_fraction < 1) return 0;
575 traceline(v0 + dvx, v0 + dvx + dvz, true, forent); if(trace_fraction < 1) return 0;
576 traceline(v0 + dvy, v0 + dvy + dvx, true, forent); if(trace_fraction < 1) return 0;
577 traceline(v0 + dvy, v0 + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
578 traceline(v0 + dvz, v0 + dvz + dvx, true, forent); if(trace_fraction < 1) return 0;
579 traceline(v0 + dvz, v0 + dvz + dvy, true, forent); if(trace_fraction < 1) return 0;
580 traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
581 traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
582 traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
587 string fixPriorityList(string order, float from, float to, float subtract, float complete)
592 n = tokenize_console(order);
594 for(i = 0; i < n; ++i)
599 if(w >= from && w <= to)
600 neworder = strcat(neworder, ftos(w), " ");
604 if(w >= from && w <= to)
605 neworder = strcat(neworder, ftos(w), " ");
612 n = tokenize_console(neworder);
613 for(w = to; w >= from; --w)
615 for(i = 0; i < n; ++i)
616 if(stof(argv(i)) == w)
618 if(i == n) // not found
619 neworder = strcat(neworder, ftos(w), " ");
623 return substring(neworder, 0, strlen(neworder) - 1);
626 string mapPriorityList(string order, string(string) mapfunc)
631 n = tokenize_console(order);
633 for(i = 0; i < n; ++i)
634 neworder = strcat(neworder, mapfunc(argv(i)), " ");
636 return substring(neworder, 0, strlen(neworder) - 1);
639 string swapInPriorityList(string order, float i, float j)
644 n = tokenize_console(order);
646 if(i >= 0 && i < n && j >= 0 && j < n && i != j)
649 for(w = 0; w < n; ++w)
652 s = strcat(s, argv(j), " ");
654 s = strcat(s, argv(i), " ");
656 s = strcat(s, argv(w), " ");
658 return substring(s, 0, strlen(s) - 1);
664 float cvar_value_issafe(string s)
666 if(strstrofs(s, "\"", 0) >= 0)
668 if(strstrofs(s, "\\", 0) >= 0)
670 if(strstrofs(s, ";", 0) >= 0)
672 if(strstrofs(s, "$", 0) >= 0)
674 if(strstrofs(s, "\r", 0) >= 0)
676 if(strstrofs(s, "\n", 0) >= 0)
682 void get_mi_min_max(float mode)
687 strunzone(mi_shortname);
688 mi_shortname = mapname;
689 if(!strcasecmp(substring(mi_shortname, 0, 5), "maps/"))
690 mi_shortname = substring(mi_shortname, 5, strlen(mi_shortname) - 5);
691 if(!strcasecmp(substring(mi_shortname, strlen(mi_shortname) - 4, 4), ".bsp"))
692 mi_shortname = substring(mi_shortname, 0, strlen(mi_shortname) - 4);
693 mi_shortname = strzone(mi_shortname);
705 MapInfo_Get_ByName(mi_shortname, 0, 0);
706 if(MapInfo_Map_mins.x < MapInfo_Map_maxs.x)
708 mi_min = MapInfo_Map_mins;
709 mi_max = MapInfo_Map_maxs;
717 tracebox('1 0 0' * mi.x,
718 '0 1 0' * mi.y + '0 0 1' * mi.z,
719 '0 1 0' * ma.y + '0 0 1' * ma.z,
723 if(!trace_startsolid)
724 mi_min.x = trace_endpos.x;
726 tracebox('0 1 0' * mi.y,
727 '1 0 0' * mi.x + '0 0 1' * mi.z,
728 '1 0 0' * ma.x + '0 0 1' * ma.z,
732 if(!trace_startsolid)
733 mi_min.y = trace_endpos.y;
735 tracebox('0 0 1' * mi.z,
736 '1 0 0' * mi.x + '0 1 0' * mi.y,
737 '1 0 0' * ma.x + '0 1 0' * ma.y,
741 if(!trace_startsolid)
742 mi_min.z = trace_endpos.z;
744 tracebox('1 0 0' * ma.x,
745 '0 1 0' * mi.y + '0 0 1' * mi.z,
746 '0 1 0' * ma.y + '0 0 1' * ma.z,
750 if(!trace_startsolid)
751 mi_max.x = trace_endpos.x;
753 tracebox('0 1 0' * ma.y,
754 '1 0 0' * mi.x + '0 0 1' * mi.z,
755 '1 0 0' * ma.x + '0 0 1' * ma.z,
759 if(!trace_startsolid)
760 mi_max.y = trace_endpos.y;
762 tracebox('0 0 1' * ma.z,
763 '1 0 0' * mi.x + '0 1 0' * mi.y,
764 '1 0 0' * ma.x + '0 1 0' * ma.y,
768 if(!trace_startsolid)
769 mi_max.z = trace_endpos.z;
774 void get_mi_min_max_texcoords(float mode)
778 get_mi_min_max(mode);
783 // extend mi_picmax to get a square aspect ratio
784 // center the map in that area
785 extend = mi_picmax - mi_picmin;
786 if(extend.y > extend.x)
788 mi_picmin.x -= (extend.y - extend.x) * 0.5;
789 mi_picmax.x += (extend.y - extend.x) * 0.5;
793 mi_picmin.y -= (extend.x - extend.y) * 0.5;
794 mi_picmax.y += (extend.x - extend.y) * 0.5;
797 // add another some percent
798 extend = (mi_picmax - mi_picmin) * (1 / 64.0);
802 // calculate the texcoords
803 mi_pictexcoord0 = mi_pictexcoord1 = mi_pictexcoord2 = mi_pictexcoord3 = '0 0 0';
804 // first the two corners of the origin
805 mi_pictexcoord0_x = (mi_min.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x);
806 mi_pictexcoord0_y = (mi_min.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y);
807 mi_pictexcoord2_x = (mi_max.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x);
808 mi_pictexcoord2_y = (mi_max.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y);
809 // then the other corners
810 mi_pictexcoord1_x = mi_pictexcoord0_x;
811 mi_pictexcoord1_y = mi_pictexcoord2_y;
812 mi_pictexcoord3_x = mi_pictexcoord2_x;
813 mi_pictexcoord3_y = mi_pictexcoord0_y;
817 float cvar_settemp(string tmp_cvar, string tmp_value)
819 float created_saved_value;
822 created_saved_value = 0;
824 if (!(tmp_cvar || tmp_value))
826 LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !\n");
830 if(!cvar_type(tmp_cvar))
832 LOG_INFOF("Error: cvar %s doesn't exist!\n", tmp_cvar);
836 for(e = world; (e = find(e, classname, "saved_cvar_value")); )
837 if(e.netname == tmp_cvar)
838 created_saved_value = -1; // skip creation
840 if(created_saved_value != -1)
842 // creating a new entity to keep track of this cvar
844 e.classname = "saved_cvar_value";
845 e.netname = strzone(tmp_cvar);
846 e.message = strzone(cvar_string(tmp_cvar));
847 created_saved_value = 1;
850 // update the cvar to the value given
851 cvar_set(tmp_cvar, tmp_value);
853 return created_saved_value;
856 float cvar_settemp_restore()
860 while((e = find(e, classname, "saved_cvar_value")))
862 if(cvar_type(e.netname))
864 cvar_set(e.netname, e.message);
869 LOG_INFOF("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.\n", e.netname);
875 float rgb_mi_ma_to_hue(vector rgb, float mi, float ma)
882 return (rgb.y - rgb.z) / (ma - mi);
884 return (rgb.y - rgb.z) / (ma - mi) + 6;
887 return (rgb.z - rgb.x) / (ma - mi) + 2;
888 else // if(ma == rgb_z)
889 return (rgb.x - rgb.y) / (ma - mi) + 4;
892 vector hue_mi_ma_to_rgb(float hue, float mi, float ma)
896 hue -= 6 * floor(hue / 6);
898 //else if(ma == rgb_x)
899 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
903 rgb.y = hue * (ma - mi) + mi;
906 //else if(ma == rgb_y)
907 // hue = 60 * (rgb_z - rgb_x) / (ma - mi) + 120;
910 rgb.x = (2 - hue) * (ma - mi) + mi;
918 rgb.z = (hue - 2) * (ma - mi) + mi;
920 //else // if(ma == rgb_z)
921 // hue = 60 * (rgb_x - rgb_y) / (ma - mi) + 240;
925 rgb.y = (4 - hue) * (ma - mi) + mi;
930 rgb.x = (hue - 4) * (ma - mi) + mi;
934 //else if(ma == rgb_x)
935 // hue = 60 * (rgb_y - rgb_z) / (ma - mi);
940 rgb.z = (6 - hue) * (ma - mi) + mi;
946 vector rgb_to_hsv(vector rgb)
951 mi = min(rgb.x, rgb.y, rgb.z);
952 ma = max(rgb.x, rgb.y, rgb.z);
954 hsv.x = rgb_mi_ma_to_hue(rgb, mi, ma);
965 vector hsv_to_rgb(vector hsv)
967 return hue_mi_ma_to_rgb(hsv.x, hsv.z * (1 - hsv.y), hsv.z);
970 vector rgb_to_hsl(vector rgb)
975 mi = min(rgb.x, rgb.y, rgb.z);
976 ma = max(rgb.x, rgb.y, rgb.z);
978 hsl.x = rgb_mi_ma_to_hue(rgb, mi, ma);
980 hsl.z = 0.5 * (mi + ma);
983 else if(hsl.z <= 0.5)
984 hsl.y = (ma - mi) / (2*hsl.z);
985 else // if(hsl_z > 0.5)
986 hsl.y = (ma - mi) / (2 - 2*hsl.z);
991 vector hsl_to_rgb(vector hsl)
993 float mi, ma, maminusmi;
996 maminusmi = hsl.y * 2 * hsl.z;
998 maminusmi = hsl.y * (2 - 2 * hsl.z);
1000 // hsl_z = 0.5 * mi + 0.5 * ma
1001 // maminusmi = - mi + ma
1002 mi = hsl.z - 0.5 * maminusmi;
1003 ma = hsl.z + 0.5 * maminusmi;
1005 return hue_mi_ma_to_rgb(hsl.x, mi, ma);
1008 string rgb_to_hexcolor(vector rgb)
1013 DEC_TO_HEXDIGIT(floor(rgb.x * 15 + 0.5)),
1014 DEC_TO_HEXDIGIT(floor(rgb.y * 15 + 0.5)),
1015 DEC_TO_HEXDIGIT(floor(rgb.z * 15 + 0.5))
1019 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
1022 // The following function is SLOW.
1023 // For your safety and for the protection of those around you...
1024 // DO NOT CALL THIS AT HOME.
1025 // No really, don't.
1026 if(w(theText, theSize) <= maxWidth)
1027 return strlen(theText); // yeah!
1029 // binary search for right place to cut string
1031 float left, right, middle; // this always works
1033 right = strlen(theText); // this always fails
1036 middle = floor((left + right) / 2);
1037 if(w(substring(theText, 0, middle), theSize) <= maxWidth)
1042 while(left < right - 1);
1044 if(w("^7", theSize) == 0) // detect color codes support in the width function
1046 // NOTE: when color codes are involved, this binary search is,
1047 // mathematically, BROKEN. However, it is obviously guaranteed to
1048 // terminate, as the range still halves each time - but nevertheless, it is
1049 // guaranteed that it finds ONE valid cutoff place (where "left" is in
1050 // range, and "right" is outside).
1052 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
1053 // and decrease left on the basis of the chars detected of the truncated tag
1054 // Even if the ^xrgb tag is not complete/correct, left is decreased
1055 // (sometimes too much but with a correct result)
1056 // it fixes also ^[0-9]
1057 while(left >= 1 && substring(theText, left-1, 1) == "^")
1060 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
1062 else if (left >= 3 && substring(theText, left-3, 2) == "^x")
1064 ch = str2chr(theText, left-1);
1065 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
1068 else if (left >= 4 && substring(theText, left-4, 2) == "^x")
1070 ch = str2chr(theText, left-2);
1071 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
1073 ch = str2chr(theText, left-1);
1074 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
1083 float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w)
1086 // The following function is SLOW.
1087 // For your safety and for the protection of those around you...
1088 // DO NOT CALL THIS AT HOME.
1089 // No really, don't.
1090 if(w(theText) <= maxWidth)
1091 return strlen(theText); // yeah!
1093 // binary search for right place to cut string
1095 float left, right, middle; // this always works
1097 right = strlen(theText); // this always fails
1100 middle = floor((left + right) / 2);
1101 if(w(substring(theText, 0, middle)) <= maxWidth)
1106 while(left < right - 1);
1108 if(w("^7") == 0) // detect color codes support in the width function
1110 // NOTE: when color codes are involved, this binary search is,
1111 // mathematically, BROKEN. However, it is obviously guaranteed to
1112 // terminate, as the range still halves each time - but nevertheless, it is
1113 // guaranteed that it finds ONE valid cutoff place (where "left" is in
1114 // range, and "right" is outside).
1116 // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4)
1117 // and decrease left on the basis of the chars detected of the truncated tag
1118 // Even if the ^xrgb tag is not complete/correct, left is decreased
1119 // (sometimes too much but with a correct result)
1120 // it fixes also ^[0-9]
1121 while(left >= 1 && substring(theText, left-1, 1) == "^")
1124 if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/
1126 else if (left >= 3 && substring(theText, left-3, 2) == "^x")
1128 ch = str2chr(theText, left-1);
1129 if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/
1132 else if (left >= 4 && substring(theText, left-4, 2) == "^x")
1134 ch = str2chr(theText, left-2);
1135 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') )
1137 ch = str2chr(theText, left-1);
1138 if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/
1147 string find_last_color_code(string s)
1149 int start = strstrofs(s, "^", 0);
1150 if (start == -1) // no caret found
1152 int len = strlen(s)-1;
1154 for(i = len; i >= start; --i)
1156 if(substring(s, i, 1) != "^")
1160 while (i-carets >= start && substring(s, i-carets, 1) == "^")
1163 // check if carets aren't all escaped
1167 if(strstrofs("0123456789", substring(s, i+1, 1), 0) >= 0)
1168 return substring(s, i, 2);
1171 if(substring(s, i+1, 1) == "x")
1172 if(strstrofs("0123456789abcdefABCDEF", substring(s, i+2, 1), 0) >= 0)
1173 if(strstrofs("0123456789abcdefABCDEF", substring(s, i+3, 1), 0) >= 0)
1174 if(strstrofs("0123456789abcdefABCDEF", substring(s, i+4, 1), 0) >= 0)
1175 return substring(s, i, 5);
1177 i -= carets; // this also skips one char before the carets
1183 string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1189 s = getWrappedLine_remaining;
1193 getWrappedLine_remaining = string_null;
1194 return s; // the line has no size ANYWAY, nothing would be displayed.
1197 cantake = textLengthUpToWidth(s, w, theFontSize, tw);
1198 if(cantake > 0 && cantake < strlen(s))
1201 while(take > 0 && substring(s, take, 1) != " ")
1205 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
1206 if(getWrappedLine_remaining == "")
1207 getWrappedLine_remaining = string_null;
1208 else if (tw("^7", theFontSize) == 0)
1209 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining);
1210 return substring(s, 0, cantake);
1214 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
1215 if(getWrappedLine_remaining == "")
1216 getWrappedLine_remaining = string_null;
1217 else if (tw("^7", theFontSize) == 0)
1218 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining);
1219 return substring(s, 0, take);
1224 getWrappedLine_remaining = string_null;
1229 string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw)
1235 s = getWrappedLine_remaining;
1239 getWrappedLine_remaining = string_null;
1240 return s; // the line has no size ANYWAY, nothing would be displayed.
1243 cantake = textLengthUpToLength(s, w, tw);
1244 if(cantake > 0 && cantake < strlen(s))
1247 while(take > 0 && substring(s, take, 1) != " ")
1251 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
1252 if(getWrappedLine_remaining == "")
1253 getWrappedLine_remaining = string_null;
1254 else if (tw("^7") == 0)
1255 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining);
1256 return substring(s, 0, cantake);
1260 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
1261 if(getWrappedLine_remaining == "")
1262 getWrappedLine_remaining = string_null;
1263 else if (tw("^7") == 0)
1264 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining);
1265 return substring(s, 0, take);
1270 getWrappedLine_remaining = string_null;
1275 string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1277 if(tw(theText, theFontSize) <= maxWidth)
1280 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("...", theFontSize), theFontSize, tw)), "...");
1283 string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw)
1285 if(tw(theText) <= maxWidth)
1288 return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
1291 float isGametypeInFilter(float gt, float tp, float ts, string pattern)
1293 string subpattern, subpattern2, subpattern3, subpattern4;
1294 subpattern = strcat(",", MapInfo_Type_ToString(gt), ",");
1296 subpattern2 = ",teams,";
1298 subpattern2 = ",noteams,";
1300 subpattern3 = ",teamspawns,";
1302 subpattern3 = ",noteamspawns,";
1303 if(gt == MAPINFO_TYPE_RACE || gt == MAPINFO_TYPE_CTS)
1304 subpattern4 = ",race,";
1306 subpattern4 = string_null;
1308 if(substring(pattern, 0, 1) == "-")
1310 pattern = substring(pattern, 1, strlen(pattern) - 1);
1311 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
1313 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0)
1315 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) >= 0)
1317 if(subpattern4 && strstrofs(strcat(",", pattern, ","), subpattern4, 0) >= 0)
1322 if(substring(pattern, 0, 1) == "+")
1323 pattern = substring(pattern, 1, strlen(pattern) - 1);
1324 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
1325 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0)
1326 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) < 0)
1330 if(strstrofs(strcat(",", pattern, ","), subpattern4, 0) < 0)
1337 vector solve_quadratic(float a, float b, float c) // ax^2 + bx + c = 0
1353 // actually, every number solves the equation!
1364 if(a > 0) // put the smaller solution first
1366 v.x = ((-b)-D) / (2*a);
1367 v.y = ((-b)+D) / (2*a);
1371 v.x = (-b+D) / (2*a);
1372 v.y = (-b-D) / (2*a);
1378 // complex solutions!
1391 vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style)
1395 // make origin and speed relative
1400 // now solve for ret, ret normalized:
1401 // eorg + t * evel == t * ret * spd
1402 // or, rather, solve for t:
1403 // |eorg + t * evel| == t * spd
1404 // eorg^2 + t^2 * evel^2 + 2 * t * (eorg * evel) == t^2 * spd^2
1405 // t^2 * (evel^2 - spd^2) + t * (2 * (eorg * evel)) + eorg^2 == 0
1406 vector solution = solve_quadratic(evel * evel - spd * spd, 2 * (eorg * evel), eorg * eorg);
1407 // p = 2 * (eorg * evel) / (evel * evel - spd * spd)
1408 // q = (eorg * eorg) / (evel * evel - spd * spd)
1409 if(!solution.z) // no real solution
1412 // (eorg * evel)^2 < (evel^2 - spd^2) * eorg^2
1413 // (eorg * evel)^2 / eorg^2 < evel^2 - spd^2
1414 // spd^2 < ((evel^2 * eorg^2) - (eorg * evel)^2) / eorg^2
1415 // spd^2 < evel^2 * (1 - cos^2 angle(evel, eorg))
1416 // spd^2 < evel^2 * sin^2 angle(evel, eorg)
1417 // spd < |evel| * sin angle(evel, eorg)
1420 else if(solution.x > 0)
1422 // both solutions > 0: take the smaller one
1423 // happens if p < 0 and q > 0
1424 ret = normalize(eorg + solution.x * evel);
1426 else if(solution.y > 0)
1428 // one solution > 0: take the larger one
1429 // happens if q < 0 or q == 0 and p < 0
1430 ret = normalize(eorg + solution.y * evel);
1434 // no solution > 0: reject
1435 // happens if p > 0 and q >= 0
1436 // 2 * (eorg * evel) / (evel * evel - spd * spd) > 0
1437 // (eorg * eorg) / (evel * evel - spd * spd) >= 0
1442 // "Enemy is moving away from me at more than spd"
1446 // NOTE: we always got a solution if spd > |evel|
1448 if(newton_style == 2)
1449 ret = normalize(ret * spd + myvel);
1454 vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_style, float mi, float ma)
1459 if(newton_style == 2)
1461 // true Newtonian projectiles with automatic aim adjustment
1463 // solve: |outspeed * mydir - myvel| = spd
1464 // outspeed^2 - 2 * outspeed * (mydir * myvel) + myvel^2 - spd^2 = 0
1465 // outspeed = (mydir * myvel) +- sqrt((mydir * myvel)^2 - myvel^2 + spd^2)
1469 // myvel^2 - (mydir * myvel)^2 > spd^2
1470 // velocity without mydir component > spd
1471 // fire at smallest possible spd that works?
1472 // |(mydir * myvel) * myvel - myvel| = spd
1474 vector solution = solve_quadratic(1, -2 * (mydir * myvel), myvel * myvel - spd * spd);
1478 outspeed = solution.y; // the larger one
1481 //outspeed = 0; // slowest possible shot
1482 outspeed = solution.x; // the real part (that is, the average!)
1483 //dprint("impossible shot, adjusting\n");
1486 outspeed = bound(spd * mi, outspeed, spd * ma);
1487 return mydir * outspeed;
1491 return myvel + spd * mydir;
1494 float compressShotOrigin(vector v)
1498 y = rint(v.y * 4) + 128;
1499 z = rint(v.z * 4) + 128;
1500 if(x > 255 || x < 0)
1502 LOG_INFO("shot origin ", vtos(v), " x out of bounds\n");
1503 x = bound(0, x, 255);
1505 if(y > 255 || y < 0)
1507 LOG_INFO("shot origin ", vtos(v), " y out of bounds\n");
1508 y = bound(0, y, 255);
1510 if(z > 255 || z < 0)
1512 LOG_INFO("shot origin ", vtos(v), " z out of bounds\n");
1513 z = bound(0, z, 255);
1515 return x * 0x10000 + y * 0x100 + z;
1517 vector decompressShotOrigin(int f)
1520 v.x = ((f & 0xFF0000) / 0x10000) / 2;
1521 v.y = ((f & 0xFF00) / 0x100 - 128) / 4;
1522 v.z = ((f & 0xFF) - 128) / 4;
1527 vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
1529 // NOTE: we'll always choose the SMALLER value...
1530 float healthdamage, armordamage, armorideal;
1531 if (deathtype == DEATH_DROWN) // Why should armor help here...
1534 healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health
1535 armordamage = a + (h - 1); // damage we can take if we could use more armor
1536 armorideal = healthdamage * armorblock;
1538 if(armordamage < healthdamage)
1551 vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage)
1554 if (deathtype == DEATH_DROWN) // Why should armor help here...
1556 v.y = bound(0, damage * armorblock, a); // save
1557 v.x = bound(0, damage - v.y, damage); // take
1563 string getcurrentmod()
1567 m = cvar_string("fs_gamedir");
1568 n = tokenize_console(m);
1575 // from the GNU Scientific Library
1576 float gsl_ran_gaussian_lastvalue;
1577 float gsl_ran_gaussian_lastvalue_set;
1578 float gsl_ran_gaussian(float sigma)
1581 if(gsl_ran_gaussian_lastvalue_set)
1583 gsl_ran_gaussian_lastvalue_set = 0;
1584 return sigma * gsl_ran_gaussian_lastvalue;
1588 a = random() * 2 * M_PI;
1589 b = sqrt(-2 * log(random()));
1590 gsl_ran_gaussian_lastvalue = cos(a) * b;
1591 gsl_ran_gaussian_lastvalue_set = 1;
1592 return sigma * sin(a) * b;
1596 float matchacl(string acl, string str)
1603 t = car(acl); acl = cdr(acl);
1606 if(substring(t, 0, 1) == "-")
1609 t = substring(t, 1, strlen(t) - 1);
1611 else if(substring(t, 0, 1) == "+")
1612 t = substring(t, 1, strlen(t) - 1);
1614 if(substring(t, -1, 1) == "*")
1616 t = substring(t, 0, strlen(t) - 1);
1617 s = substring(str, 0, strlen(t));
1630 string get_model_datafilename(string m, float sk, string fil)
1635 m = "models/player/*_";
1637 m = strcat(m, ftos(sk));
1640 return strcat(m, ".", fil);
1643 float get_model_parameters(string m, float sk)
1645 get_model_parameters_modelname = string_null;
1646 get_model_parameters_modelskin = -1;
1647 get_model_parameters_name = string_null;
1648 get_model_parameters_species = -1;
1649 get_model_parameters_sex = string_null;
1650 get_model_parameters_weight = -1;
1651 get_model_parameters_age = -1;
1652 get_model_parameters_desc = string_null;
1653 get_model_parameters_bone_upperbody = string_null;
1654 get_model_parameters_bone_weapon = string_null;
1655 for(int i = 0; i < MAX_AIM_BONES; ++i)
1657 get_model_parameters_bone_aim[i] = string_null;
1658 get_model_parameters_bone_aimweight[i] = 0;
1660 get_model_parameters_fixbone = 0;
1663 MUTATOR_CALLHOOK(ClearModelParams);
1669 if(substring(m, -9, 5) == "_lod1" || substring(m, -9, 5) == "_lod2")
1670 m = strcat(substring(m, 0, -10), substring(m, -4, -1));
1674 if(substring(m, -4, -1) != ".txt")
1676 if(substring(m, -6, 1) != "_")
1678 sk = stof(substring(m, -5, 1));
1679 m = substring(m, 0, -7);
1682 string fn = get_model_datafilename(m, sk, "txt");
1683 int fh = fopen(fn, FILE_READ);
1687 fn = get_model_datafilename(m, sk, "txt");
1688 fh = fopen(fn, FILE_READ);
1693 get_model_parameters_modelname = m;
1694 get_model_parameters_modelskin = sk;
1696 while((s = fgets(fh)))
1699 break; // next lines will be description
1703 get_model_parameters_name = s;
1707 case "human": get_model_parameters_species = SPECIES_HUMAN; break;
1708 case "alien": get_model_parameters_species = SPECIES_ALIEN; break;
1709 case "robot_shiny": get_model_parameters_species = SPECIES_ROBOT_SHINY; break;
1710 case "robot_rusty": get_model_parameters_species = SPECIES_ROBOT_RUSTY; break;
1711 case "robot_solid": get_model_parameters_species = SPECIES_ROBOT_SOLID; break;
1712 case "animal": get_model_parameters_species = SPECIES_ANIMAL; break;
1713 case "reserved": get_model_parameters_species = SPECIES_RESERVED; break;
1716 get_model_parameters_sex = s;
1718 get_model_parameters_weight = stof(s);
1720 get_model_parameters_age = stof(s);
1721 if(c == "description")
1722 get_model_parameters_description = s;
1723 if(c == "bone_upperbody")
1724 get_model_parameters_bone_upperbody = s;
1725 if(c == "bone_weapon")
1726 get_model_parameters_bone_weapon = s;
1728 MUTATOR_CALLHOOK(GetModelParams, c, s);
1730 for(int i = 0; i < MAX_AIM_BONES; ++i)
1731 if(c == strcat("bone_aim", ftos(i)))
1733 get_model_parameters_bone_aimweight[i] = stof(car(s));
1734 get_model_parameters_bone_aim[i] = cdr(s);
1737 get_model_parameters_fixbone = stof(s);
1740 while((s = fgets(fh)))
1742 if(get_model_parameters_desc)
1743 get_model_parameters_desc = strcat(get_model_parameters_desc, "\n");
1745 get_model_parameters_desc = strcat(get_model_parameters_desc, s);
1753 float vercmp_recursive(string v1, string v2)
1759 dot1 = strstrofs(v1, ".", 0);
1760 dot2 = strstrofs(v2, ".", 0);
1764 s1 = substring(v1, 0, dot1);
1768 s2 = substring(v2, 0, dot2);
1770 r = stof(s1) - stof(s2);
1774 r = strcasecmp(s1, s2);
1787 return vercmp_recursive(substring(v1, dot1 + 1, 999), substring(v2, dot2 + 1, 999));
1790 float vercmp(string v1, string v2)
1792 if(strcasecmp(v1, v2) == 0) // early out check
1801 return vercmp_recursive(v1, v2);
1804 // x-encoding (encoding as zero length invisible string)
1805 const string XENCODE_2 = "xX";
1806 const string XENCODE_22 = "0123456789abcdefABCDEF";
1807 string xencode(int f)
1810 d = f % 22; f = floor(f / 22);
1811 c = f % 22; f = floor(f / 22);
1812 b = f % 22; f = floor(f / 22);
1813 a = f % 2; // f = floor(f / 2);
1816 substring(XENCODE_2, a, 1),
1817 substring(XENCODE_22, b, 1),
1818 substring(XENCODE_22, c, 1),
1819 substring(XENCODE_22, d, 1)
1822 float xdecode(string s)
1825 if(substring(s, 0, 1) != "^")
1829 a = strstrofs(XENCODE_2, substring(s, 1, 1), 0);
1830 b = strstrofs(XENCODE_22, substring(s, 2, 1), 0);
1831 c = strstrofs(XENCODE_22, substring(s, 3, 1), 0);
1832 d = strstrofs(XENCODE_22, substring(s, 4, 1), 0);
1833 if(a < 0 || b < 0 || c < 0 || d < 0)
1835 return ((a * 22 + b) * 22 + c) * 22 + d;
1839 string strlimitedlen(string input, string truncation, float strip_colors, float limit)
1841 if(strlen((strip_colors ? strdecolorize(input) : input)) <= limit)
1844 return strcat(substring(input, 0, (strlen(input) - strlen(truncation))), truncation);
1848 entity ReadCSQCEntity()
1850 int f = ReadShort();
1853 return findfloat(world, entnum, f);
1857 float shutdown_running;
1862 void CSQC_Shutdown()
1868 if(shutdown_running)
1870 LOG_INFO("Recursive shutdown detected! Only restoring cvars...\n");
1874 shutdown_running = 1;
1877 cvar_settemp_restore(); // this must be done LAST, but in any case
1880 const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05;
1881 #define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
1882 #define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT)
1883 // this will use the value:
1885 // accuracy near zero is APPROXPASTTIME_MAX/(256*255)
1886 // accuracy at x is 1/derivative, i.e.
1887 // APPROXPASTTIME_MAX * (1 + 256 * (dt / APPROXPASTTIME_MAX))^2 / 65536
1889 void WriteApproxPastTime(float dst, float t)
1891 float dt = time - t;
1893 // warning: this is approximate; do not resend when you don't have to!
1894 // be careful with sendflags here!
1895 // we want: 0 -> 0.05, 1 -> 0.1, ..., 255 -> 12.75
1898 dt = 256 * (dt / ((APPROXPASTTIME_MAX / 256) + dt));
1901 dt = rint(bound(0, dt, 255));
1907 float ReadApproxPastTime()
1909 float dt = ReadByte();
1911 // map from range...PPROXPASTTIME_MAX / 256
1912 dt = (APPROXPASTTIME_MAX / 256) * (dt / (256 - dt));
1914 return servertime - dt;
1919 .float skeleton_bones_index;
1920 void Skeleton_SetBones(entity e)
1922 // set skeleton_bones to the total number of bones on the model
1923 if(e.skeleton_bones_index == e.modelindex)
1924 return; // same model, nothing to update
1927 skelindex = skel_create(e.modelindex);
1928 e.skeleton_bones = skel_get_numbones(skelindex);
1929 skel_delete(skelindex);
1930 e.skeleton_bones_index = e.modelindex;
1934 string to_execute_next_frame;
1935 void execute_next_frame()
1937 if(to_execute_next_frame)
1939 localcmd("\n", to_execute_next_frame, "\n");
1940 strunzone(to_execute_next_frame);
1941 to_execute_next_frame = string_null;
1944 void queue_to_execute_next_frame(string s)
1946 if(to_execute_next_frame)
1948 s = strcat(s, "\n", to_execute_next_frame);
1949 strunzone(to_execute_next_frame);
1951 to_execute_next_frame = strzone(s);
1954 .float FindConnectedComponent_processing;
1955 void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass)
1957 entity queue_start, queue_end;
1959 // we build a queue of to-be-processed entities.
1960 // queue_start is the next entity to be checked for neighbors
1961 // queue_end is the last entity added
1963 if(e.FindConnectedComponent_processing)
1964 error("recursion or broken cleanup");
1966 // start with a 1-element queue
1967 queue_start = queue_end = e;
1968 queue_end.(fld) = world;
1969 queue_end.FindConnectedComponent_processing = 1;
1971 // for each queued item:
1972 for (; queue_start; queue_start = queue_start.(fld))
1974 // find all neighbors of queue_start
1976 for(t = world; (t = nxt(t, queue_start, pass)); )
1978 if(t.FindConnectedComponent_processing)
1980 if(iscon(t, queue_start, pass))
1982 // it is connected? ADD IT. It will look for neighbors soon too.
1983 queue_end.(fld) = t;
1985 queue_end.(fld) = world;
1986 queue_end.FindConnectedComponent_processing = 1;
1992 for (queue_start = e; queue_start; queue_start = queue_start.(fld))
1993 queue_start.FindConnectedComponent_processing = 0;
1997 vector animfixfps(entity e, vector a, vector b)
1999 // multi-frame anim: keep as-is
2003 dur = frameduration(e.modelindex, a.x);
2007 dur = frameduration(e.modelindex, a.x);
2017 void dedicated_print(string input) // print(), but only print if the server is not local
2019 if(server_is_dedicated) { LOG_INFO(input); }
2024 float Announcer_PickNumber(float type, float num)
2032 case 10: return ANNCE_NUM_GAMESTART_10;
2033 case 9: return ANNCE_NUM_GAMESTART_9;
2034 case 8: return ANNCE_NUM_GAMESTART_8;
2035 case 7: return ANNCE_NUM_GAMESTART_7;
2036 case 6: return ANNCE_NUM_GAMESTART_6;
2037 case 5: return ANNCE_NUM_GAMESTART_5;
2038 case 4: return ANNCE_NUM_GAMESTART_4;
2039 case 3: return ANNCE_NUM_GAMESTART_3;
2040 case 2: return ANNCE_NUM_GAMESTART_2;
2041 case 1: return ANNCE_NUM_GAMESTART_1;
2049 case 10: return ANNCE_NUM_IDLE_10;
2050 case 9: return ANNCE_NUM_IDLE_9;
2051 case 8: return ANNCE_NUM_IDLE_8;
2052 case 7: return ANNCE_NUM_IDLE_7;
2053 case 6: return ANNCE_NUM_IDLE_6;
2054 case 5: return ANNCE_NUM_IDLE_5;
2055 case 4: return ANNCE_NUM_IDLE_4;
2056 case 3: return ANNCE_NUM_IDLE_3;
2057 case 2: return ANNCE_NUM_IDLE_2;
2058 case 1: return ANNCE_NUM_IDLE_1;
2066 case 10: return ANNCE_NUM_KILL_10;
2067 case 9: return ANNCE_NUM_KILL_9;
2068 case 8: return ANNCE_NUM_KILL_8;
2069 case 7: return ANNCE_NUM_KILL_7;
2070 case 6: return ANNCE_NUM_KILL_6;
2071 case 5: return ANNCE_NUM_KILL_5;
2072 case 4: return ANNCE_NUM_KILL_4;
2073 case 3: return ANNCE_NUM_KILL_3;
2074 case 2: return ANNCE_NUM_KILL_2;
2075 case 1: return ANNCE_NUM_KILL_1;
2083 case 10: return ANNCE_NUM_RESPAWN_10;
2084 case 9: return ANNCE_NUM_RESPAWN_9;
2085 case 8: return ANNCE_NUM_RESPAWN_8;
2086 case 7: return ANNCE_NUM_RESPAWN_7;
2087 case 6: return ANNCE_NUM_RESPAWN_6;
2088 case 5: return ANNCE_NUM_RESPAWN_5;
2089 case 4: return ANNCE_NUM_RESPAWN_4;
2090 case 3: return ANNCE_NUM_RESPAWN_3;
2091 case 2: return ANNCE_NUM_RESPAWN_2;
2092 case 1: return ANNCE_NUM_RESPAWN_1;
2096 case CNT_ROUNDSTART:
2100 case 10: return ANNCE_NUM_ROUNDSTART_10;
2101 case 9: return ANNCE_NUM_ROUNDSTART_9;
2102 case 8: return ANNCE_NUM_ROUNDSTART_8;
2103 case 7: return ANNCE_NUM_ROUNDSTART_7;
2104 case 6: return ANNCE_NUM_ROUNDSTART_6;
2105 case 5: return ANNCE_NUM_ROUNDSTART_5;
2106 case 4: return ANNCE_NUM_ROUNDSTART_4;
2107 case 3: return ANNCE_NUM_ROUNDSTART_3;
2108 case 2: return ANNCE_NUM_ROUNDSTART_2;
2109 case 1: return ANNCE_NUM_ROUNDSTART_1;
2117 case 10: return ANNCE_NUM_10;
2118 case 9: return ANNCE_NUM_9;
2119 case 8: return ANNCE_NUM_8;
2120 case 7: return ANNCE_NUM_7;
2121 case 6: return ANNCE_NUM_6;
2122 case 5: return ANNCE_NUM_5;
2123 case 4: return ANNCE_NUM_4;
2124 case 3: return ANNCE_NUM_3;
2125 case 2: return ANNCE_NUM_2;
2126 case 1: return ANNCE_NUM_1;
2131 return NOTIF_ABORT; // abort sending if none of these numbers were right
2136 int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents)
2138 switch(nativecontents)
2143 return DPCONTENTS_SOLID | DPCONTENTS_OPAQUE;
2145 return DPCONTENTS_WATER;
2147 return DPCONTENTS_SLIME;
2149 return DPCONTENTS_LAVA | DPCONTENTS_NODROP;
2151 return DPCONTENTS_SKY | DPCONTENTS_NODROP | DPCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
2156 int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents)
2158 if(supercontents & (DPCONTENTS_SOLID | DPCONTENTS_BODY))
2159 return CONTENT_SOLID;
2160 if(supercontents & DPCONTENTS_SKY)
2162 if(supercontents & DPCONTENTS_LAVA)
2163 return CONTENT_LAVA;
2164 if(supercontents & DPCONTENTS_SLIME)
2165 return CONTENT_SLIME;
2166 if(supercontents & DPCONTENTS_WATER)
2167 return CONTENT_WATER;
2168 return CONTENT_EMPTY;