4 #include "constants.qh"
5 #include <client/mutators/_mod.qh>
7 #include "notifications/all.qh"
9 #include <common/deathtypes/all.qh>
12 #include "constants.qh"
13 #include <server/mutators/_mod.qh>
14 #include "notifications/all.qh"
15 #include <common/deathtypes/all.qh>
21 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity) // returns the number of traces done, for benchmarking
27 //nudge = 2 * cvar("collision_impactnudge"); // why not?
30 dir = normalize(v2 - v1);
32 pos = v1 + dir * nudge;
39 if(pos * dir >= v2 * dir)
47 tracebox(pos, mi, ma, v2, nomonsters, forent);
52 LOG_TRACE("When tracing from ", vtos(v1), " to ", vtos(v2));
53 LOG_TRACE(" Nudging gets us nowhere at ", vtos(pos));
54 LOG_TRACE(" trace_endpos is ", vtos(trace_endpos));
55 LOG_TRACE(" trace distance is ", ftos(vlen(pos - trace_endpos)));
58 stopentity = trace_ent;
62 // we started inside solid.
63 // then trace from endpos to pos
65 tracebox(t, mi, ma, pos, nomonsters, forent);
69 // t is still inside solid? bad
70 // force advance, then, and retry
71 pos = t + dir * nudge;
73 // but if we hit an entity, stop RIGHT before it
74 if(stopatentity && stopentity && stopentity != ignorestopatentity)
76 trace_ent = stopentity;
78 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
84 // we actually LEFT solid!
85 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
91 // pos is outside solid?!? but why?!? never mind, just return it.
93 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
99 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity)
101 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent, stopatentity, ignorestopatentity);
110 Returns a point at least 12 units away from walls
111 (useful for explosion animations, although the blast is performed where it really happened)
115 vector findbetterlocation (vector org, float mindist)
117 vector vec = mindist * '1 0 0';
121 traceline (org, org + vec, true, NULL);
123 if (trace_fraction < 1)
125 vector loc = trace_endpos;
126 traceline (loc, loc + vec, true, NULL);
127 if (trace_fraction >= 1)
144 * Get "real" origin, in worldspace, even if ent is attached to something else.
146 vector real_origin(entity ent)
148 vector v = ((ent.absmin + ent.absmax) * 0.5);
149 entity e = ent.tag_entity;
153 v = v + ((e.absmin + e.absmax) * 0.5);
161 string wordwrap_buffer;
163 void wordwrap_buffer_put(string s)
165 wordwrap_buffer = strcat(wordwrap_buffer, s);
168 string wordwrap(string s, float l)
171 wordwrap_buffer = "";
172 wordwrap_cb(s, l, wordwrap_buffer_put);
174 wordwrap_buffer = "";
179 entity _wordwrap_buffer_sprint_ent;
180 void wordwrap_buffer_sprint(string s)
182 wordwrap_buffer = strcat(wordwrap_buffer, s);
185 sprint(_wordwrap_buffer_sprint_ent, wordwrap_buffer);
186 wordwrap_buffer = "";
190 void wordwrap_sprint(entity to, string s, float l)
192 wordwrap_buffer = "";
193 _wordwrap_buffer_sprint_ent = to;
194 wordwrap_cb(s, l, wordwrap_buffer_sprint);
195 _wordwrap_buffer_sprint_ent = NULL;
196 if(wordwrap_buffer != "")
197 sprint(to, strcat(wordwrap_buffer, "\n"));
198 wordwrap_buffer = "";
204 string draw_UseSkinFor(string pic)
206 if(substring(pic, 0, 1) == "/")
207 return substring(pic, 1, strlen(pic)-1);
209 return strcat(draw_currentSkin, "/", pic);
213 void wordwrap_cb(string s, float l, void(string) callback)
216 float lleft, i, j, wlen;
221 for (i = 0; i < len; ++i)
223 if (substring(s, i, 2) == "\\n")
229 else if (substring(s, i, 1) == "\n")
234 else if (substring(s, i, 1) == " ")
244 for (j = i+1; j < len; ++j)
245 // ^^ this skips over the first character of a word, which
246 // is ALWAYS part of the word
247 // this is safe since if i+1 == strlen(s), i will become
248 // strlen(s)-1 at the end of this block and the function
249 // will terminate. A space can't be the first character we
250 // read here, and neither can a \n be the start, since these
251 // two cases have been handled above.
253 c = substring(s, j, 1);
260 // we need to keep this tempstring alive even if substring is
261 // called repeatedly, so call strcat even though we're not
271 callback(substring(s, i, wlen));
279 void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
309 string ScoreString(int pFlags, float pValue)
314 pValue = floor(pValue + 0.5); // round
316 if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME)))
318 else if(pFlags & SFL_RANK)
319 valstr = count_ordinal(pValue);
320 else if(pFlags & SFL_TIME)
321 valstr = TIME_ENCODED_TOSTRING(pValue);
323 valstr = ftos(pValue);
329 // compressed vector format:
330 // like MD3, just even shorter
331 // 4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90
332 // 5 bit yaw (32 angles), 0=0, 8=90, 16=180, 24=270
333 // 7 bit length (logarithmic encoding), 1/8 .. about 7844
334 // length = 2^(length_encoded/8) / 8
335 // if pitch is 90, yaw does nothing and therefore indicates the sign (yaw is then either 11111 or 11110); 11111 is pointing DOWN
336 // thus, valid values are from 0000.11110.0000000 to 1111.11111.1111111
337 // the special value 0 indicates the zero vector
339 float lengthLogTable[128];
341 float invertLengthLog(float dist)
345 if(dist >= lengthLogTable[127])
347 if(dist <= lengthLogTable[0])
355 m = floor((l + r) / 2);
356 if(lengthLogTable[m] < dist)
362 // now: r is >=, l is <
363 float lerr = (dist - lengthLogTable[l]);
364 float rerr = (lengthLogTable[r] - dist);
370 vector decompressShortVector(int data)
375 float p = (data & 0xF000) / 0x1000;
376 float q = (data & 0x0F80) / 0x80;
377 int len = (data & 0x007F);
379 //print("\ndecompress: p ", ftos(p)); print("q ", ftos(q)); print("len ", ftos(len), "\n");
392 q = .19634954084936207740 * q;
393 p = .19634954084936207740 * p - 1.57079632679489661922;
394 out.x = cos(q) * cos(p);
395 out.y = sin(q) * cos(p);
399 //print("decompressed: ", vtos(out), "\n");
401 return out * lengthLogTable[len];
404 float compressShortVector(vector vec)
410 //print("compress: ", vtos(vec), "\n");
411 ang = vectoangles(vec);
415 if(ang.x < -90 && ang.x > +90)
416 error("BOGUS vectoangles");
417 //print("angles: ", vtos(ang), "\n");
419 p = floor(0.5 + (ang.x + 90) * 16 / 180) & 15; // -90..90 to 0..14
428 y = floor(0.5 + ang.y * 32 / 360) & 31; // 0..360 to 0..32
429 len = invertLengthLog(vlen(vec));
431 //print("compressed: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n");
433 return (p * 0x1000) + (y * 0x80) + len;
436 STATIC_INIT(compressShortVector)
439 float f = (2 ** (1/8));
441 for(i = 0; i < 128; ++i)
443 lengthLogTable[i] = l;
447 if(cvar("developer") > 0)
449 LOG_TRACE("Verifying vector compression table...");
450 for(i = 0x0F00; i < 0xFFFF; ++i)
451 if(i != compressShortVector(decompressShortVector(i)))
454 "BROKEN vector compression: %s -> %s -> %s",
456 vtos(decompressShortVector(i)),
457 ftos(compressShortVector(decompressShortVector(i)))
465 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
467 traceline(v0, v0 + dvx, true, forent); if(trace_fraction < 1) return 0;
468 traceline(v0, v0 + dvy, true, forent); if(trace_fraction < 1) return 0;
469 traceline(v0, v0 + dvz, true, forent); if(trace_fraction < 1) return 0;
470 traceline(v0 + dvx, v0 + dvx + dvy, true, forent); if(trace_fraction < 1) return 0;
471 traceline(v0 + dvx, v0 + dvx + dvz, true, forent); if(trace_fraction < 1) return 0;
472 traceline(v0 + dvy, v0 + dvy + dvx, true, forent); if(trace_fraction < 1) return 0;
473 traceline(v0 + dvy, v0 + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
474 traceline(v0 + dvz, v0 + dvz + dvx, true, forent); if(trace_fraction < 1) return 0;
475 traceline(v0 + dvz, v0 + dvz + dvy, true, forent); if(trace_fraction < 1) return 0;
476 traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
477 traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
478 traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
483 string fixPriorityList(string order, float from, float to, float subtract, float complete)
488 n = tokenize_console(order);
490 for(i = 0; i < n; ++i)
495 if(w >= from && w <= to)
496 neworder = strcat(neworder, ftos(w), " ");
500 if(w >= from && w <= to)
501 neworder = strcat(neworder, ftos(w), " ");
508 n = tokenize_console(neworder);
509 for(w = to; w >= from; --w)
511 int wflags = Weapons_from(w).spawnflags;
512 if(wflags & WEP_FLAG_SPECIALATTACK)
514 for(i = 0; i < n; ++i)
515 if(stof(argv(i)) == w)
517 if(i == n) // not found
518 neworder = strcat(neworder, ftos(w), " ");
522 return substring(neworder, 0, strlen(neworder) - 1);
525 string mapPriorityList(string order, string(string) mapfunc)
530 n = tokenize_console(order);
532 for(float i = 0; i < n; ++i)
533 neworder = strcat(neworder, mapfunc(argv(i)), " ");
535 return substring(neworder, 0, strlen(neworder) - 1);
538 string swapInPriorityList(string order, float i, float j)
540 float n = tokenize_console(order);
542 if(i >= 0 && i < n && j >= 0 && j < n && i != j)
545 for(float w = 0; w < n; ++w)
548 s = strcat(s, argv(j), " ");
550 s = strcat(s, argv(i), " ");
552 s = strcat(s, argv(w), " ");
554 return substring(s, 0, strlen(s) - 1);
561 void get_mi_min_max(float mode)
566 if(!strcasecmp(substring(s, 0, 5), "maps/"))
567 s = substring(s, 5, strlen(s) - 5);
568 if(!strcasecmp(substring(s, strlen(s) - 4, 4), ".bsp"))
569 s = substring(s, 0, strlen(s) - 4);
570 strcpy(mi_shortname, s);
582 MapInfo_Get_ByName(mi_shortname, 0, NULL);
583 if(MapInfo_Map_mins.x < MapInfo_Map_maxs.x)
585 mi_min = MapInfo_Map_mins;
586 mi_max = MapInfo_Map_maxs;
594 tracebox('1 0 0' * mi.x,
595 '0 1 0' * mi.y + '0 0 1' * mi.z,
596 '0 1 0' * ma.y + '0 0 1' * ma.z,
600 if(!trace_startsolid)
601 mi_min.x = trace_endpos.x;
603 tracebox('0 1 0' * mi.y,
604 '1 0 0' * mi.x + '0 0 1' * mi.z,
605 '1 0 0' * ma.x + '0 0 1' * ma.z,
609 if(!trace_startsolid)
610 mi_min.y = trace_endpos.y;
612 tracebox('0 0 1' * mi.z,
613 '1 0 0' * mi.x + '0 1 0' * mi.y,
614 '1 0 0' * ma.x + '0 1 0' * ma.y,
618 if(!trace_startsolid)
619 mi_min.z = trace_endpos.z;
621 tracebox('1 0 0' * ma.x,
622 '0 1 0' * mi.y + '0 0 1' * mi.z,
623 '0 1 0' * ma.y + '0 0 1' * ma.z,
627 if(!trace_startsolid)
628 mi_max.x = trace_endpos.x;
630 tracebox('0 1 0' * ma.y,
631 '1 0 0' * mi.x + '0 0 1' * mi.z,
632 '1 0 0' * ma.x + '0 0 1' * ma.z,
636 if(!trace_startsolid)
637 mi_max.y = trace_endpos.y;
639 tracebox('0 0 1' * ma.z,
640 '1 0 0' * mi.x + '0 1 0' * mi.y,
641 '1 0 0' * ma.x + '0 1 0' * ma.y,
645 if(!trace_startsolid)
646 mi_max.z = trace_endpos.z;
651 void get_mi_min_max_texcoords(float mode)
655 get_mi_min_max(mode);
660 // extend mi_picmax to get a square aspect ratio
661 // center the map in that area
662 extend = mi_picmax - mi_picmin;
663 if(extend.y > extend.x)
665 mi_picmin.x -= (extend.y - extend.x) * 0.5;
666 mi_picmax.x += (extend.y - extend.x) * 0.5;
670 mi_picmin.y -= (extend.x - extend.y) * 0.5;
671 mi_picmax.y += (extend.x - extend.y) * 0.5;
674 // add another some percent
675 extend = (mi_picmax - mi_picmin) * (1 / 64.0);
679 // calculate the texcoords
680 mi_pictexcoord0 = mi_pictexcoord1 = mi_pictexcoord2 = mi_pictexcoord3 = '0 0 0';
681 // first the two corners of the origin
682 mi_pictexcoord0_x = (mi_min.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x);
683 mi_pictexcoord0_y = (mi_min.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y);
684 mi_pictexcoord2_x = (mi_max.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x);
685 mi_pictexcoord2_y = (mi_max.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y);
686 // then the other corners
687 mi_pictexcoord1_x = mi_pictexcoord0_x;
688 mi_pictexcoord1_y = mi_pictexcoord2_y;
689 mi_pictexcoord3_x = mi_pictexcoord2_x;
690 mi_pictexcoord3_y = mi_pictexcoord0_y;
694 float cvar_settemp(string tmp_cvar, string tmp_value)
696 float created_saved_value;
698 created_saved_value = 0;
700 if (!(tmp_cvar || tmp_value))
702 LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !");
706 if(!cvar_type(tmp_cvar))
708 LOG_INFOF("Error: cvar %s doesn't exist!", tmp_cvar);
712 IL_EACH(g_saved_cvars, it.netname == tmp_cvar,
714 created_saved_value = -1; // skip creation
715 break; // no need to continue
718 if(created_saved_value != -1)
720 // creating a new entity to keep track of this cvar
721 entity e = new_pure(saved_cvar_value);
722 IL_PUSH(g_saved_cvars, e);
723 e.netname = strzone(tmp_cvar);
724 e.message = strzone(cvar_string(tmp_cvar));
725 created_saved_value = 1;
728 // update the cvar to the value given
729 cvar_set(tmp_cvar, tmp_value);
731 return created_saved_value;
734 int cvar_settemp_restore()
737 // FIXME this new-style loop fails!
739 FOREACH_ENTITY_CLASS("saved_cvar_value", true,
741 if(cvar_type(it.netname))
743 cvar_set(it.netname, it.message);
744 strunzone(it.netname);
745 strunzone(it.message);
750 LOG_INFOF("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.", it.netname);
755 while((e = find(e, classname, "saved_cvar_value")))
757 if(cvar_type(e.netname))
759 cvar_set(e.netname, e.message);
764 print(sprintf("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.", e.netname));
771 bool isCaretEscaped(string theText, float pos)
774 while(pos - i >= 1 && substring(theText, pos - i - 1, 1) == "^")
779 int skipIncompleteTag(string theText, float pos, int len)
783 if(substring(theText, pos - 1, 1) == "^")
785 if(isCaretEscaped(theText, pos - 1) || pos >= len)
788 int ch = str2chr(theText, pos);
789 if(ch >= '0' && ch <= '9')
790 return 1; // ^[0-9] color code found
792 tag_start = pos - 1; // ^x tag found
798 for(int i = 2; pos - i >= 0 && i <= 4; ++i)
800 if(substring(theText, pos - i, 2) == "^x")
802 tag_start = pos - i; // ^x tag found
810 if(tag_start + 5 < len)
811 if(IS_HEXDIGIT(substring(theText, tag_start + 2, 1)))
812 if(IS_HEXDIGIT(substring(theText, tag_start + 3, 1)))
813 if(IS_HEXDIGIT(substring(theText, tag_start + 4, 1)))
815 if(!isCaretEscaped(theText, tag_start))
816 return 5 - (pos - tag_start); // ^xRGB color code found
822 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
825 // The following function is SLOW.
826 // For your safety and for the protection of those around you...
827 // DO NOT CALL THIS AT HOME.
829 if(w(theText, theSize) <= maxWidth)
830 return strlen(theText); // yeah!
832 bool colors = (w("^7", theSize) == 0);
834 // binary search for right place to cut string
835 int len, left, right, middle;
837 right = len = strlen(theText);
841 middle = floor((left + right) / 2);
843 ofs = skipIncompleteTag(theText, middle, len);
844 if(w(substring(theText, 0, middle + ofs), theSize) <= maxWidth)
849 while(left < right - 1);
854 float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w)
857 // The following function is SLOW.
858 // For your safety and for the protection of those around you...
859 // DO NOT CALL THIS AT HOME.
861 if(w(theText) <= maxWidth)
862 return strlen(theText); // yeah!
864 bool colors = (w("^7") == 0);
866 // binary search for right place to cut string
867 int len, left, right, middle;
869 right = len = strlen(theText);
873 middle = floor((left + right) / 2);
875 ofs = skipIncompleteTag(theText, middle, len);
876 if(w(substring(theText, 0, middle + ofs)) <= maxWidth)
881 while(left < right - 1);
886 string find_last_color_code(string s)
888 int start = strstrofs(s, "^", 0);
889 if (start == -1) // no caret found
891 int len = strlen(s)-1;
892 for(int i = len; i >= start; --i)
894 if(substring(s, i, 1) != "^")
898 while (i-carets >= start && substring(s, i-carets, 1) == "^")
901 // check if carets aren't all escaped
905 if(IS_DIGIT(substring(s, i+1, 1)))
906 return substring(s, i, 2);
909 if(substring(s, i+1, 1) == "x")
910 if(IS_HEXDIGIT(substring(s, i + 2, 1)))
911 if(IS_HEXDIGIT(substring(s, i + 3, 1)))
912 if(IS_HEXDIGIT(substring(s, i + 4, 1)))
913 return substring(s, i, 5);
915 i -= carets; // this also skips one char before the carets
921 string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
927 s = getWrappedLine_remaining;
931 getWrappedLine_remaining = string_null;
932 return s; // the line has no size ANYWAY, nothing would be displayed.
935 cantake = textLengthUpToWidth(s, w, theFontSize, tw);
936 if(cantake > 0 && cantake < strlen(s))
939 while(take > 0 && substring(s, take, 1) != " ")
943 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
944 if(getWrappedLine_remaining == "")
945 getWrappedLine_remaining = string_null;
946 else if (tw("^7", theFontSize) == 0)
947 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining);
948 return substring(s, 0, cantake);
952 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
953 if(getWrappedLine_remaining == "")
954 getWrappedLine_remaining = string_null;
955 else if (tw("^7", theFontSize) == 0)
956 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining);
957 return substring(s, 0, take);
962 getWrappedLine_remaining = string_null;
967 string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw)
973 s = getWrappedLine_remaining;
977 getWrappedLine_remaining = string_null;
978 return s; // the line has no size ANYWAY, nothing would be displayed.
981 cantake = textLengthUpToLength(s, w, tw);
982 if(cantake > 0 && cantake < strlen(s))
985 while(take > 0 && substring(s, take, 1) != " ")
989 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
990 if(getWrappedLine_remaining == "")
991 getWrappedLine_remaining = string_null;
992 else if (tw("^7") == 0)
993 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining);
994 return substring(s, 0, cantake);
998 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
999 if(getWrappedLine_remaining == "")
1000 getWrappedLine_remaining = string_null;
1001 else if (tw("^7") == 0)
1002 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining);
1003 return substring(s, 0, take);
1008 getWrappedLine_remaining = string_null;
1013 string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1015 if(tw(theText, theFontSize) <= maxWidth)
1018 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("...", theFontSize), theFontSize, tw)), "...");
1021 string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw)
1023 if(tw(theText) <= maxWidth)
1026 return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
1029 float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern)
1031 string subpattern, subpattern2, subpattern3, subpattern4;
1032 subpattern = strcat(",", MapInfo_Type_ToString(gt), ",");
1034 subpattern2 = ",teams,";
1036 subpattern2 = ",noteams,";
1038 subpattern3 = ",teamspawns,";
1040 subpattern3 = ",noteamspawns,";
1041 if(gt == MAPINFO_TYPE_RACE || gt == MAPINFO_TYPE_CTS)
1042 subpattern4 = ",race,";
1044 subpattern4 = string_null;
1046 if(substring(pattern, 0, 1) == "-")
1048 pattern = substring(pattern, 1, strlen(pattern) - 1);
1049 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
1051 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0)
1053 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) >= 0)
1055 if(subpattern4 && strstrofs(strcat(",", pattern, ","), subpattern4, 0) >= 0)
1060 if(substring(pattern, 0, 1) == "+")
1061 pattern = substring(pattern, 1, strlen(pattern) - 1);
1062 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
1063 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0)
1064 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) < 0)
1068 if(strstrofs(strcat(",", pattern, ","), subpattern4, 0) < 0)
1075 vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style)
1079 // make origin and speed relative
1084 // now solve for ret, ret normalized:
1085 // eorg + t * evel == t * ret * spd
1086 // or, rather, solve for t:
1087 // |eorg + t * evel| == t * spd
1088 // eorg^2 + t^2 * evel^2 + 2 * t * (eorg * evel) == t^2 * spd^2
1089 // t^2 * (evel^2 - spd^2) + t * (2 * (eorg * evel)) + eorg^2 == 0
1090 vector solution = solve_quadratic(evel * evel - spd * spd, 2 * (eorg * evel), eorg * eorg);
1091 // p = 2 * (eorg * evel) / (evel * evel - spd * spd)
1092 // q = (eorg * eorg) / (evel * evel - spd * spd)
1093 if(!solution.z) // no real solution
1096 // (eorg * evel)^2 < (evel^2 - spd^2) * eorg^2
1097 // (eorg * evel)^2 / eorg^2 < evel^2 - spd^2
1098 // spd^2 < ((evel^2 * eorg^2) - (eorg * evel)^2) / eorg^2
1099 // spd^2 < evel^2 * (1 - cos^2 angle(evel, eorg))
1100 // spd^2 < evel^2 * sin^2 angle(evel, eorg)
1101 // spd < |evel| * sin angle(evel, eorg)
1104 else if(solution.x > 0)
1106 // both solutions > 0: take the smaller one
1107 // happens if p < 0 and q > 0
1108 ret = normalize(eorg + solution.x * evel);
1110 else if(solution.y > 0)
1112 // one solution > 0: take the larger one
1113 // happens if q < 0 or q == 0 and p < 0
1114 ret = normalize(eorg + solution.y * evel);
1118 // no solution > 0: reject
1119 // happens if p > 0 and q >= 0
1120 // 2 * (eorg * evel) / (evel * evel - spd * spd) > 0
1121 // (eorg * eorg) / (evel * evel - spd * spd) >= 0
1126 // "Enemy is moving away from me at more than spd"
1130 // NOTE: we always got a solution if spd > |evel|
1132 if(newton_style == 2)
1133 ret = normalize(ret * spd + myvel);
1138 vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_style, float mi, float ma)
1143 if(newton_style == 2)
1145 // true Newtonian projectiles with automatic aim adjustment
1147 // solve: |outspeed * mydir - myvel| = spd
1148 // outspeed^2 - 2 * outspeed * (mydir * myvel) + myvel^2 - spd^2 = 0
1149 // outspeed = (mydir * myvel) +- sqrt((mydir * myvel)^2 - myvel^2 + spd^2)
1153 // myvel^2 - (mydir * myvel)^2 > spd^2
1154 // velocity without mydir component > spd
1155 // fire at smallest possible spd that works?
1156 // |(mydir * myvel) * myvel - myvel| = spd
1158 vector solution = solve_quadratic(1, -2 * (mydir * myvel), myvel * myvel - spd * spd);
1162 outspeed = solution.y; // the larger one
1165 //outspeed = 0; // slowest possible shot
1166 outspeed = solution.x; // the real part (that is, the average!)
1167 //dprint("impossible shot, adjusting\n");
1170 outspeed = bound(spd * mi, outspeed, spd * ma);
1171 return mydir * outspeed;
1175 return myvel + spd * mydir;
1178 float compressShotOrigin(vector v)
1180 float rx = rint(v.x * 2);
1181 float ry = rint(v.y * 4) + 128;
1182 float rz = rint(v.z * 4) + 128;
1183 if(rx > 255 || rx < 0)
1185 LOG_DEBUG("shot origin ", vtos(v), " x out of bounds\n");
1186 rx = bound(0, rx, 255);
1188 if(ry > 255 || ry < 0)
1190 LOG_DEBUG("shot origin ", vtos(v), " y out of bounds\n");
1191 ry = bound(0, ry, 255);
1193 if(rz > 255 || rz < 0)
1195 LOG_DEBUG("shot origin ", vtos(v), " z out of bounds\n");
1196 rz = bound(0, rz, 255);
1198 return rx * 0x10000 + ry * 0x100 + rz;
1200 vector decompressShotOrigin(int f)
1203 v.x = ((f & 0xFF0000) / 0x10000) / 2;
1204 v.y = ((f & 0xFF00) / 0x100 - 128) / 4;
1205 v.z = ((f & 0xFF) - 128) / 4;
1210 vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
1212 // NOTE: we'll always choose the SMALLER value...
1213 float healthdamage, armordamage, armorideal;
1214 if (DEATH_IS(deathtype, DEATH_DROWN)) // Why should armor help here...
1217 healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health
1218 armordamage = a + (h - 1); // damage we can take if we could use more armor
1219 armorideal = healthdamage * armorblock;
1221 if(armordamage < healthdamage)
1234 vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage)
1237 if (DEATH_IS(deathtype, DEATH_DROWN)) // Why should armor help here...
1239 if (deathtype & HITTYPE_ARMORPIERCE)
1241 v.y = bound(0, damage * armorblock, a); // save
1242 v.x = bound(0, damage - v.y, damage); // take
1248 string getcurrentmod()
1252 m = cvar_string("fs_gamedir");
1253 n = tokenize_console(m);
1260 float matchacl(string acl, string str)
1267 t = car(acl); acl = cdr(acl);
1270 if(substring(t, 0, 1) == "-")
1273 t = substring(t, 1, strlen(t) - 1);
1275 else if(substring(t, 0, 1) == "+")
1276 t = substring(t, 1, strlen(t) - 1);
1278 if(substring(t, -1, 1) == "*")
1280 t = substring(t, 0, strlen(t) - 1);
1281 s = substring(str, 0, strlen(t));
1289 break; // if we found a killing case, apply it! other settings may be allowed in the future, but this one is caught
1296 void write_String_To_File(int fh, string str, bool alsoprint)
1299 if (alsoprint) LOG_INFO(str);
1302 string get_model_datafilename(string m, float sk, string fil)
1307 m = "models/player/*_";
1309 m = strcat(m, ftos(sk));
1312 return strcat(m, ".", fil);
1315 float get_model_parameters(string m, float sk)
1317 get_model_parameters_modelname = string_null;
1318 get_model_parameters_modelskin = -1;
1319 get_model_parameters_name = string_null;
1320 get_model_parameters_species = -1;
1321 get_model_parameters_sex = string_null;
1322 get_model_parameters_weight = -1;
1323 get_model_parameters_age = -1;
1324 get_model_parameters_desc = string_null;
1325 get_model_parameters_bone_upperbody = string_null;
1326 get_model_parameters_bone_weapon = string_null;
1327 for(int i = 0; i < MAX_AIM_BONES; ++i)
1329 get_model_parameters_bone_aim[i] = string_null;
1330 get_model_parameters_bone_aimweight[i] = 0;
1332 get_model_parameters_fixbone = 0;
1333 get_model_parameters_hidden = false;
1336 MUTATOR_CALLHOOK(ClearModelParams);
1342 if(substring(m, -9, 5) == "_lod1" || substring(m, -9, 5) == "_lod2")
1343 m = strcat(substring(m, 0, -10), substring(m, -4, -1));
1347 if(substring(m, -4, -1) != ".txt")
1349 if(substring(m, -6, 1) != "_")
1351 sk = stof(substring(m, -5, 1));
1352 m = substring(m, 0, -7);
1355 string fn = get_model_datafilename(m, sk, "txt");
1356 int fh = fopen(fn, FILE_READ);
1360 fn = get_model_datafilename(m, sk, "txt");
1361 fh = fopen(fn, FILE_READ);
1366 get_model_parameters_modelname = m;
1367 get_model_parameters_modelskin = sk;
1369 while((s = fgets(fh)))
1372 break; // next lines will be description
1376 get_model_parameters_name = s;
1380 case "human": get_model_parameters_species = SPECIES_HUMAN; break;
1381 case "alien": get_model_parameters_species = SPECIES_ALIEN; break;
1382 case "robot_shiny": get_model_parameters_species = SPECIES_ROBOT_SHINY; break;
1383 case "robot_rusty": get_model_parameters_species = SPECIES_ROBOT_RUSTY; break;
1384 case "robot_solid": get_model_parameters_species = SPECIES_ROBOT_SOLID; break;
1385 case "animal": get_model_parameters_species = SPECIES_ANIMAL; break;
1386 case "reserved": get_model_parameters_species = SPECIES_RESERVED; break;
1390 if (s == "Male") s = _("Male");
1391 else if (s == "Female") s = _("Female");
1392 else if (s == "Undisclosed") s = _("Undisclosed");
1393 get_model_parameters_sex = s;
1396 get_model_parameters_weight = stof(s);
1398 get_model_parameters_age = stof(s);
1399 if(c == "description")
1400 get_model_parameters_description = s;
1401 if(c == "bone_upperbody")
1402 get_model_parameters_bone_upperbody = s;
1403 if(c == "bone_weapon")
1404 get_model_parameters_bone_weapon = s;
1406 MUTATOR_CALLHOOK(GetModelParams, c, s);
1408 for(int i = 0; i < MAX_AIM_BONES; ++i)
1409 if(c == strcat("bone_aim", ftos(i)))
1411 get_model_parameters_bone_aimweight[i] = stof(car(s));
1412 get_model_parameters_bone_aim[i] = cdr(s);
1415 get_model_parameters_fixbone = stof(s);
1417 get_model_parameters_hidden = stob(s);
1420 while((s = fgets(fh)))
1422 if(get_model_parameters_desc)
1423 get_model_parameters_desc = strcat(get_model_parameters_desc, "\n");
1425 get_model_parameters_desc = strcat(get_model_parameters_desc, s);
1433 string translate_key(string key)
1435 if (prvm_language == "en") return key;
1437 if (substring(key, 0, 1) == "<")
1439 if (key == "<KEY NOT FOUND>") return _("<KEY NOT FOUND>");
1440 if (key == "<UNKNOWN KEYNUM>") return _("<UNKNOWN KEYNUM>");
1445 case "TAB": return _("TAB");
1446 case "ENTER": return _("ENTER");
1447 case "ESCAPE": return _("ESCAPE");
1448 case "SPACE": return _("SPACE");
1450 case "BACKSPACE": return _("BACKSPACE");
1451 case "UPARROW": return _("UPARROW");
1452 case "DOWNARROW": return _("DOWNARROW");
1453 case "LEFTARROW": return _("LEFTARROW");
1454 case "RIGHTARROW": return _("RIGHTARROW");
1456 case "ALT": return _("ALT");
1457 case "CTRL": return _("CTRL");
1458 case "SHIFT": return _("SHIFT");
1460 case "INS": return _("INS");
1461 case "DEL": return _("DEL");
1462 case "PGDN": return _("PGDN");
1463 case "PGUP": return _("PGUP");
1464 case "HOME": return _("HOME");
1465 case "END": return _("END");
1467 case "PAUSE": return _("PAUSE");
1469 case "NUMLOCK": return _("NUMLOCK");
1470 case "CAPSLOCK": return _("CAPSLOCK");
1471 case "SCROLLOCK": return _("SCROLLOCK");
1473 case "SEMICOLON": return _("SEMICOLON");
1474 case "TILDE": return _("TILDE");
1475 case "BACKQUOTE": return _("BACKQUOTE");
1476 case "QUOTE": return _("QUOTE");
1477 case "APOSTROPHE": return _("APOSTROPHE");
1478 case "BACKSLASH": return _("BACKSLASH");
1481 if (substring(key, 0, 1) == "F")
1483 string subkey = substring(key, 1, -1);
1484 if (IS_DIGIT(substring(key, 3, 1))) // check only first digit
1486 return sprintf(_("F%d"), stof(subkey));
1488 // continue in case there is another key name starting with F
1491 if (substring(key, 0, 3) == "KP_")
1493 string subkey = substring(key, 3, -1);
1494 if (IS_DIGIT(substring(key, 3, 1))) // check only first digit
1496 return sprintf(_("KP_%d"), stof(subkey));
1501 case "INS": return sprintf(_("KP_%s"), _("INS"));
1502 case "END": return sprintf(_("KP_%s"), _("END"));
1503 case "DOWNARROW": return sprintf(_("KP_%s"), _("DOWNARROW"));
1504 case "PGDN": return sprintf(_("KP_%s"), _("PGDN"));
1505 case "LEFTARROW": return sprintf(_("KP_%s"), _("LEFTARROW"));
1506 case "RIGHTARROW": return sprintf(_("KP_%s"), _("RIGHTARROW"));
1507 case "HOME": return sprintf(_("KP_%s"), _("HOME"));
1508 case "UPARROW": return sprintf(_("KP_%s"), _("UPARROW"));
1509 case "PGUP": return sprintf(_("KP_%s"), _("PGUP"));
1510 case "PERIOD": return sprintf(_("KP_%s"), _("PERIOD"));
1511 case "DEL": return sprintf(_("KP_%s"), _("DEL"));
1512 case "DIVIDE": return sprintf(_("KP_%s"), _("DIVIDE"));
1513 case "SLASH": return sprintf(_("KP_%s"), _("SLASH"));
1514 case "MULTIPLY": return sprintf(_("KP_%s"), _("MULTIPLY"));
1515 case "MINUS": return sprintf(_("KP_%s"), _("MINUS"));
1516 case "PLUS": return sprintf(_("KP_%s"), _("PLUS"));
1517 case "ENTER": return sprintf(_("KP_%s"), _("ENTER"));
1518 case "EQUALS": return sprintf(_("KP_%s"), _("EQUALS"));
1519 default: return key;
1523 if (key == "PRINTSCREEN") return _("PRINTSCREEN");
1525 if (substring(key, 0, 5) == "MOUSE")
1526 return sprintf(_("MOUSE%d"), stof(substring(key, 5, -1)));
1528 if (key == "MWHEELUP") return _("MWHEELUP");
1529 if (key == "MWHEELDOWN") return _("MWHEELDOWN");
1531 if (substring(key, 0,3) == "JOY")
1532 return sprintf(_("JOY%d"), stof(substring(key, 3, -1)));
1534 if (substring(key, 0,3) == "AUX")
1535 return sprintf(_("AUX%d"), stof(substring(key, 3, -1)));
1537 if (substring(key, 0, 4) == "X360_")
1539 string subkey = substring(key, 4, -1);
1542 case "DPAD_UP": return sprintf(_("X360_%s"), _("DPAD_UP"));
1543 case "DPAD_DOWN": return sprintf(_("X360_%s"), _("DPAD_DOWN"));
1544 case "DPAD_LEFT": return sprintf(_("X360_%s"), _("DPAD_LEFT"));
1545 case "DPAD_RIGHT": return sprintf(_("X360_%s"), _("DPAD_RIGHT"));
1546 case "START": return sprintf(_("X360_%s"), _("START"));
1547 case "BACK": return sprintf(_("X360_%s"), _("BACK"));
1548 case "LEFT_THUMB": return sprintf(_("X360_%s"), _("LEFT_THUMB"));
1549 case "RIGHT_THUMB": return sprintf(_("X360_%s"), _("RIGHT_THUMB"));
1550 case "LEFT_SHOULDER": return sprintf(_("X360_%s"), _("LEFT_SHOULDER"));
1551 case "RIGHT_SHOULDER": return sprintf(_("X360_%s"), _("RIGHT_SHOULDER"));
1552 case "LEFT_TRIGGER": return sprintf(_("X360_%s"), _("LEFT_TRIGGER"));
1553 case "RIGHT_TRIGGER": return sprintf(_("X360_%s"), _("RIGHT_TRIGGER"));
1554 case "LEFT_THUMB_UP": return sprintf(_("X360_%s"), _("LEFT_THUMB_UP"));
1555 case "LEFT_THUMB_DOWN": return sprintf(_("X360_%s"), _("LEFT_THUMB_DOWN"));
1556 case "LEFT_THUMB_LEFT": return sprintf(_("X360_%s"), _("LEFT_THUMB_LEFT"));
1557 case "LEFT_THUMB_RIGHT": return sprintf(_("X360_%s"), _("LEFT_THUMB_RIGHT"));
1558 case "RIGHT_THUMB_UP": return sprintf(_("X360_%s"), _("RIGHT_THUMB_UP"));
1559 case "RIGHT_THUMB_DOWN": return sprintf(_("X360_%s"), _("RIGHT_THUMB_DOWN"));
1560 case "RIGHT_THUMB_LEFT": return sprintf(_("X360_%s"), _("RIGHT_THUMB_LEFT"));
1561 case "RIGHT_THUMB_RIGHT": return sprintf(_("X360_%s"), _("RIGHT_THUMB_RIGHT"));
1562 default: return key;
1566 if (substring(key, 0, 4) == "JOY_")
1568 string subkey = substring(key, 4, -1);
1571 case "UP": return sprintf(_("JOY_%s"), _("UP"));
1572 case "DOWN": return sprintf(_("JOY_%s"), _("DOWN"));
1573 case "LEFT": return sprintf(_("JOY_%s"), _("LEFT"));
1574 case "RIGHT": return sprintf(_("JOY_%s"), _("RIGHT"));
1575 default: return key;
1579 if (substring(key, 0, 8) == "MIDINOTE")
1580 return sprintf(_("MIDINOTE%d"), stof(substring(key, 8, -1)));
1585 // x-encoding (encoding as zero length invisible string)
1586 const string XENCODE_2 = "xX";
1587 const string XENCODE_22 = "0123456789abcdefABCDEF";
1588 string xencode(int f)
1591 d = f % 22; f = floor(f / 22);
1592 c = f % 22; f = floor(f / 22);
1593 b = f % 22; f = floor(f / 22);
1594 a = f % 2; // f = floor(f / 2);
1597 substring(XENCODE_2, a, 1),
1598 substring(XENCODE_22, b, 1),
1599 substring(XENCODE_22, c, 1),
1600 substring(XENCODE_22, d, 1)
1603 float xdecode(string s)
1606 if(substring(s, 0, 1) != "^")
1610 a = strstrofs(XENCODE_2, substring(s, 1, 1), 0);
1611 b = strstrofs(XENCODE_22, substring(s, 2, 1), 0);
1612 c = strstrofs(XENCODE_22, substring(s, 3, 1), 0);
1613 d = strstrofs(XENCODE_22, substring(s, 4, 1), 0);
1614 if(a < 0 || b < 0 || c < 0 || d < 0)
1616 return ((a * 22 + b) * 22 + c) * 22 + d;
1620 string strlimitedlen(string input, string truncation, float strip_colors, float limit)
1622 if(strlen((strip_colors ? strdecolorize(input) : input)) <= limit)
1625 return strcat(substring(input, 0, (strlen(input) - strlen(truncation))), truncation);
1628 float shutdown_running;
1633 void CSQC_Shutdown()
1639 if(shutdown_running)
1641 LOG_INFO("Recursive shutdown detected! Only restoring cvars...");
1645 shutdown_running = 1;
1649 cvar_settemp_restore(); // this must be done LAST, but in any case
1653 .float skeleton_bones_index;
1654 void Skeleton_SetBones(entity e)
1656 // set skeleton_bones to the total number of bones on the model
1657 if(e.skeleton_bones_index == e.modelindex)
1658 return; // same model, nothing to update
1661 skelindex = skel_create(e.modelindex);
1662 e.skeleton_bones = skel_get_numbones(skelindex);
1663 skel_delete(skelindex);
1664 e.skeleton_bones_index = e.modelindex;
1668 string to_execute_next_frame;
1669 void execute_next_frame()
1671 if(to_execute_next_frame)
1673 localcmd("\n", to_execute_next_frame, "\n");
1674 strfree(to_execute_next_frame);
1677 void queue_to_execute_next_frame(string s)
1679 if(to_execute_next_frame)
1681 s = strcat(s, "\n", to_execute_next_frame);
1683 strcpy(to_execute_next_frame, s);
1686 .float FindConnectedComponent_processing;
1687 void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass)
1689 entity queue_start, queue_end;
1691 // we build a queue of to-be-processed entities.
1692 // queue_start is the next entity to be checked for neighbors
1693 // queue_end is the last entity added
1695 if(e.FindConnectedComponent_processing)
1696 error("recursion or broken cleanup");
1698 // start with a 1-element queue
1699 queue_start = queue_end = e;
1700 queue_end.(fld) = NULL;
1701 queue_end.FindConnectedComponent_processing = 1;
1703 // for each queued item:
1704 for (; queue_start; queue_start = queue_start.(fld))
1706 // find all neighbors of queue_start
1708 for(t = NULL; (t = nxt(t, queue_start, pass)); )
1710 if(t.FindConnectedComponent_processing)
1712 if(iscon(t, queue_start, pass))
1714 // it is connected? ADD IT. It will look for neighbors soon too.
1715 queue_end.(fld) = t;
1717 queue_end.(fld) = NULL;
1718 queue_end.FindConnectedComponent_processing = 1;
1724 for (queue_start = e; queue_start; queue_start = queue_start.(fld))
1725 queue_start.FindConnectedComponent_processing = 0;
1729 vector animfixfps(entity e, vector a, vector b)
1731 // multi-frame anim: keep as-is
1734 float dur = frameduration(e.modelindex, a.x);
1735 if (dur <= 0 && b.y)
1738 dur = frameduration(e.modelindex, a.x);
1748 Notification Announcer_PickNumber(int type, int num)
1757 case 10: return ANNCE_NUM_GAMESTART_10;
1758 case 9: return ANNCE_NUM_GAMESTART_9;
1759 case 8: return ANNCE_NUM_GAMESTART_8;
1760 case 7: return ANNCE_NUM_GAMESTART_7;
1761 case 6: return ANNCE_NUM_GAMESTART_6;
1762 case 5: return ANNCE_NUM_GAMESTART_5;
1763 case 4: return ANNCE_NUM_GAMESTART_4;
1764 case 3: return ANNCE_NUM_GAMESTART_3;
1765 case 2: return ANNCE_NUM_GAMESTART_2;
1766 case 1: return ANNCE_NUM_GAMESTART_1;
1774 case 10: return ANNCE_NUM_IDLE_10;
1775 case 9: return ANNCE_NUM_IDLE_9;
1776 case 8: return ANNCE_NUM_IDLE_8;
1777 case 7: return ANNCE_NUM_IDLE_7;
1778 case 6: return ANNCE_NUM_IDLE_6;
1779 case 5: return ANNCE_NUM_IDLE_5;
1780 case 4: return ANNCE_NUM_IDLE_4;
1781 case 3: return ANNCE_NUM_IDLE_3;
1782 case 2: return ANNCE_NUM_IDLE_2;
1783 case 1: return ANNCE_NUM_IDLE_1;
1791 case 10: return ANNCE_NUM_KILL_10;
1792 case 9: return ANNCE_NUM_KILL_9;
1793 case 8: return ANNCE_NUM_KILL_8;
1794 case 7: return ANNCE_NUM_KILL_7;
1795 case 6: return ANNCE_NUM_KILL_6;
1796 case 5: return ANNCE_NUM_KILL_5;
1797 case 4: return ANNCE_NUM_KILL_4;
1798 case 3: return ANNCE_NUM_KILL_3;
1799 case 2: return ANNCE_NUM_KILL_2;
1800 case 1: return ANNCE_NUM_KILL_1;
1808 case 10: return ANNCE_NUM_RESPAWN_10;
1809 case 9: return ANNCE_NUM_RESPAWN_9;
1810 case 8: return ANNCE_NUM_RESPAWN_8;
1811 case 7: return ANNCE_NUM_RESPAWN_7;
1812 case 6: return ANNCE_NUM_RESPAWN_6;
1813 case 5: return ANNCE_NUM_RESPAWN_5;
1814 case 4: return ANNCE_NUM_RESPAWN_4;
1815 case 3: return ANNCE_NUM_RESPAWN_3;
1816 case 2: return ANNCE_NUM_RESPAWN_2;
1817 case 1: return ANNCE_NUM_RESPAWN_1;
1821 case CNT_ROUNDSTART:
1825 case 10: return ANNCE_NUM_ROUNDSTART_10;
1826 case 9: return ANNCE_NUM_ROUNDSTART_9;
1827 case 8: return ANNCE_NUM_ROUNDSTART_8;
1828 case 7: return ANNCE_NUM_ROUNDSTART_7;
1829 case 6: return ANNCE_NUM_ROUNDSTART_6;
1830 case 5: return ANNCE_NUM_ROUNDSTART_5;
1831 case 4: return ANNCE_NUM_ROUNDSTART_4;
1832 case 3: return ANNCE_NUM_ROUNDSTART_3;
1833 case 2: return ANNCE_NUM_ROUNDSTART_2;
1834 case 1: return ANNCE_NUM_ROUNDSTART_1;
1842 case 10: return ANNCE_NUM_10;
1843 case 9: return ANNCE_NUM_9;
1844 case 8: return ANNCE_NUM_8;
1845 case 7: return ANNCE_NUM_7;
1846 case 6: return ANNCE_NUM_6;
1847 case 5: return ANNCE_NUM_5;
1848 case 4: return ANNCE_NUM_4;
1849 case 3: return ANNCE_NUM_3;
1850 case 2: return ANNCE_NUM_2;
1851 case 1: return ANNCE_NUM_1;
1860 int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents)
1862 switch(nativecontents)
1867 return DPCONTENTS_SOLID | DPCONTENTS_OPAQUE;
1869 return DPCONTENTS_WATER;
1871 return DPCONTENTS_SLIME;
1873 return DPCONTENTS_LAVA | DPCONTENTS_NODROP;
1875 return DPCONTENTS_SKY | DPCONTENTS_NODROP | DPCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
1880 int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents)
1882 if(supercontents & (DPCONTENTS_SOLID | DPCONTENTS_BODY))
1883 return CONTENT_SOLID;
1884 if(supercontents & DPCONTENTS_SKY)
1886 if(supercontents & DPCONTENTS_LAVA)
1887 return CONTENT_LAVA;
1888 if(supercontents & DPCONTENTS_SLIME)
1889 return CONTENT_SLIME;
1890 if(supercontents & DPCONTENTS_WATER)
1891 return CONTENT_WATER;
1892 return CONTENT_EMPTY;