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)
320 valstr = ftos(pValue);
322 if((l >= 2) && (substring(valstr, l - 2, 1) == "1"))
323 valstr = strcat(valstr, "th");
324 else if(substring(valstr, l - 1, 1) == "1")
325 valstr = strcat(valstr, "st");
326 else if(substring(valstr, l - 1, 1) == "2")
327 valstr = strcat(valstr, "nd");
328 else if(substring(valstr, l - 1, 1) == "3")
329 valstr = strcat(valstr, "rd");
331 valstr = strcat(valstr, "th");
333 else if(pFlags & SFL_TIME)
334 valstr = TIME_ENCODED_TOSTRING(pValue);
336 valstr = ftos(pValue);
342 // compressed vector format:
343 // like MD3, just even shorter
344 // 4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90
345 // 5 bit yaw (32 angles), 0=0, 8=90, 16=180, 24=270
346 // 7 bit length (logarithmic encoding), 1/8 .. about 7844
347 // length = 2^(length_encoded/8) / 8
348 // if pitch is 90, yaw does nothing and therefore indicates the sign (yaw is then either 11111 or 11110); 11111 is pointing DOWN
349 // thus, valid values are from 0000.11110.0000000 to 1111.11111.1111111
350 // the special value 0 indicates the zero vector
352 float lengthLogTable[128];
354 float invertLengthLog(float dist)
358 if(dist >= lengthLogTable[127])
360 if(dist <= lengthLogTable[0])
368 m = floor((l + r) / 2);
369 if(lengthLogTable[m] < dist)
375 // now: r is >=, l is <
376 float lerr = (dist - lengthLogTable[l]);
377 float rerr = (lengthLogTable[r] - dist);
383 vector decompressShortVector(int data)
388 float p = (data & 0xF000) / 0x1000;
389 float q = (data & 0x0F80) / 0x80;
390 int len = (data & 0x007F);
392 //print("\ndecompress: p ", ftos(p)); print("q ", ftos(q)); print("len ", ftos(len), "\n");
405 q = .19634954084936207740 * q;
406 p = .19634954084936207740 * p - 1.57079632679489661922;
407 out.x = cos(q) * cos(p);
408 out.y = sin(q) * cos(p);
412 //print("decompressed: ", vtos(out), "\n");
414 return out * lengthLogTable[len];
417 float compressShortVector(vector vec)
423 //print("compress: ", vtos(vec), "\n");
424 ang = vectoangles(vec);
428 if(ang.x < -90 && ang.x > +90)
429 error("BOGUS vectoangles");
430 //print("angles: ", vtos(ang), "\n");
432 p = floor(0.5 + (ang.x + 90) * 16 / 180) & 15; // -90..90 to 0..14
441 y = floor(0.5 + ang.y * 32 / 360) & 31; // 0..360 to 0..32
442 len = invertLengthLog(vlen(vec));
444 //print("compressed: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n");
446 return (p * 0x1000) + (y * 0x80) + len;
449 STATIC_INIT(compressShortVector)
452 float f = (2 ** (1/8));
454 for(i = 0; i < 128; ++i)
456 lengthLogTable[i] = l;
460 if(cvar("developer"))
462 LOG_TRACE("Verifying vector compression table...");
463 for(i = 0x0F00; i < 0xFFFF; ++i)
464 if(i != compressShortVector(decompressShortVector(i)))
467 "BROKEN vector compression: %s -> %s -> %s",
469 vtos(decompressShortVector(i)),
470 ftos(compressShortVector(decompressShortVector(i)))
478 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
480 traceline(v0, v0 + dvx, true, forent); if(trace_fraction < 1) return 0;
481 traceline(v0, v0 + dvy, true, forent); if(trace_fraction < 1) return 0;
482 traceline(v0, v0 + dvz, true, forent); if(trace_fraction < 1) return 0;
483 traceline(v0 + dvx, v0 + dvx + dvy, true, forent); if(trace_fraction < 1) return 0;
484 traceline(v0 + dvx, v0 + dvx + dvz, true, forent); if(trace_fraction < 1) return 0;
485 traceline(v0 + dvy, v0 + dvy + dvx, true, forent); if(trace_fraction < 1) return 0;
486 traceline(v0 + dvy, v0 + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
487 traceline(v0 + dvz, v0 + dvz + dvx, true, forent); if(trace_fraction < 1) return 0;
488 traceline(v0 + dvz, v0 + dvz + dvy, true, forent); if(trace_fraction < 1) return 0;
489 traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
490 traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
491 traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
496 string fixPriorityList(string order, float from, float to, float subtract, float complete)
501 n = tokenize_console(order);
503 for(i = 0; i < n; ++i)
508 if(w >= from && w <= to)
509 neworder = strcat(neworder, ftos(w), " ");
513 if(w >= from && w <= to)
514 neworder = strcat(neworder, ftos(w), " ");
521 n = tokenize_console(neworder);
522 for(w = to; w >= from; --w)
524 int wflags = Weapons_from(w).spawnflags;
525 if((wflags & WEP_FLAG_HIDDEN) && (wflags & WEP_FLAG_MUTATORBLOCKED) && !(wflags & WEP_FLAG_NORMAL))
527 for(i = 0; i < n; ++i)
528 if(stof(argv(i)) == w)
530 if(i == n) // not found
531 neworder = strcat(neworder, ftos(w), " ");
535 return substring(neworder, 0, strlen(neworder) - 1);
538 string mapPriorityList(string order, string(string) mapfunc)
543 n = tokenize_console(order);
545 for(float i = 0; i < n; ++i)
546 neworder = strcat(neworder, mapfunc(argv(i)), " ");
548 return substring(neworder, 0, strlen(neworder) - 1);
551 string swapInPriorityList(string order, float i, float j)
553 float n = tokenize_console(order);
555 if(i >= 0 && i < n && j >= 0 && j < n && i != j)
558 for(float w = 0; w < n; ++w)
561 s = strcat(s, argv(j), " ");
563 s = strcat(s, argv(i), " ");
565 s = strcat(s, argv(w), " ");
567 return substring(s, 0, strlen(s) - 1);
574 void get_mi_min_max(float mode)
579 if(!strcasecmp(substring(s, 0, 5), "maps/"))
580 s = substring(s, 5, strlen(s) - 5);
581 if(!strcasecmp(substring(s, strlen(s) - 4, 4), ".bsp"))
582 s = substring(s, 0, strlen(s) - 4);
583 strcpy(mi_shortname, s);
595 MapInfo_Get_ByName(mi_shortname, 0, NULL);
596 if(MapInfo_Map_mins.x < MapInfo_Map_maxs.x)
598 mi_min = MapInfo_Map_mins;
599 mi_max = MapInfo_Map_maxs;
607 tracebox('1 0 0' * mi.x,
608 '0 1 0' * mi.y + '0 0 1' * mi.z,
609 '0 1 0' * ma.y + '0 0 1' * ma.z,
613 if(!trace_startsolid)
614 mi_min.x = trace_endpos.x;
616 tracebox('0 1 0' * mi.y,
617 '1 0 0' * mi.x + '0 0 1' * mi.z,
618 '1 0 0' * ma.x + '0 0 1' * ma.z,
622 if(!trace_startsolid)
623 mi_min.y = trace_endpos.y;
625 tracebox('0 0 1' * mi.z,
626 '1 0 0' * mi.x + '0 1 0' * mi.y,
627 '1 0 0' * ma.x + '0 1 0' * ma.y,
631 if(!trace_startsolid)
632 mi_min.z = trace_endpos.z;
634 tracebox('1 0 0' * ma.x,
635 '0 1 0' * mi.y + '0 0 1' * mi.z,
636 '0 1 0' * ma.y + '0 0 1' * ma.z,
640 if(!trace_startsolid)
641 mi_max.x = trace_endpos.x;
643 tracebox('0 1 0' * ma.y,
644 '1 0 0' * mi.x + '0 0 1' * mi.z,
645 '1 0 0' * ma.x + '0 0 1' * ma.z,
649 if(!trace_startsolid)
650 mi_max.y = trace_endpos.y;
652 tracebox('0 0 1' * ma.z,
653 '1 0 0' * mi.x + '0 1 0' * mi.y,
654 '1 0 0' * ma.x + '0 1 0' * ma.y,
658 if(!trace_startsolid)
659 mi_max.z = trace_endpos.z;
664 void get_mi_min_max_texcoords(float mode)
668 get_mi_min_max(mode);
673 // extend mi_picmax to get a square aspect ratio
674 // center the map in that area
675 extend = mi_picmax - mi_picmin;
676 if(extend.y > extend.x)
678 mi_picmin.x -= (extend.y - extend.x) * 0.5;
679 mi_picmax.x += (extend.y - extend.x) * 0.5;
683 mi_picmin.y -= (extend.x - extend.y) * 0.5;
684 mi_picmax.y += (extend.x - extend.y) * 0.5;
687 // add another some percent
688 extend = (mi_picmax - mi_picmin) * (1 / 64.0);
692 // calculate the texcoords
693 mi_pictexcoord0 = mi_pictexcoord1 = mi_pictexcoord2 = mi_pictexcoord3 = '0 0 0';
694 // first the two corners of the origin
695 mi_pictexcoord0_x = (mi_min.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x);
696 mi_pictexcoord0_y = (mi_min.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y);
697 mi_pictexcoord2_x = (mi_max.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x);
698 mi_pictexcoord2_y = (mi_max.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y);
699 // then the other corners
700 mi_pictexcoord1_x = mi_pictexcoord0_x;
701 mi_pictexcoord1_y = mi_pictexcoord2_y;
702 mi_pictexcoord3_x = mi_pictexcoord2_x;
703 mi_pictexcoord3_y = mi_pictexcoord0_y;
707 float cvar_settemp(string tmp_cvar, string tmp_value)
709 float created_saved_value;
711 created_saved_value = 0;
713 if (!(tmp_cvar || tmp_value))
715 LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !");
719 if(!cvar_type(tmp_cvar))
721 LOG_INFOF("Error: cvar %s doesn't exist!", tmp_cvar);
725 IL_EACH(g_saved_cvars, it.netname == tmp_cvar,
727 created_saved_value = -1; // skip creation
728 break; // no need to continue
731 if(created_saved_value != -1)
733 // creating a new entity to keep track of this cvar
734 entity e = new_pure(saved_cvar_value);
735 IL_PUSH(g_saved_cvars, e);
736 e.netname = strzone(tmp_cvar);
737 e.message = strzone(cvar_string(tmp_cvar));
738 created_saved_value = 1;
741 // update the cvar to the value given
742 cvar_set(tmp_cvar, tmp_value);
744 return created_saved_value;
747 int cvar_settemp_restore()
750 // FIXME this new-style loop fails!
752 FOREACH_ENTITY_CLASS("saved_cvar_value", true,
754 if(cvar_type(it.netname))
756 cvar_set(it.netname, it.message);
757 strunzone(it.netname);
758 strunzone(it.message);
763 LOG_INFOF("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.", it.netname);
768 while((e = find(e, classname, "saved_cvar_value")))
770 if(cvar_type(e.netname))
772 cvar_set(e.netname, e.message);
777 print(sprintf("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.", e.netname));
784 bool isCaretEscaped(string theText, float pos)
787 while(pos - i >= 1 && substring(theText, pos - i - 1, 1) == "^")
792 int skipIncompleteTag(string theText, float pos, int len)
796 if(substring(theText, pos - 1, 1) == "^")
798 if(isCaretEscaped(theText, pos - 1) || pos >= len)
801 int ch = str2chr(theText, pos);
802 if(ch >= '0' && ch <= '9')
803 return 1; // ^[0-9] color code found
805 tag_start = pos - 1; // ^x tag found
811 for(int i = 2; pos - i >= 0 && i <= 4; ++i)
813 if(substring(theText, pos - i, 2) == "^x")
815 tag_start = pos - i; // ^x tag found
823 if(tag_start + 5 < len)
824 if(IS_HEXDIGIT(substring(theText, tag_start + 2, 1)))
825 if(IS_HEXDIGIT(substring(theText, tag_start + 3, 1)))
826 if(IS_HEXDIGIT(substring(theText, tag_start + 4, 1)))
828 if(!isCaretEscaped(theText, tag_start))
829 return 5 - (pos - tag_start); // ^xRGB color code found
835 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
838 // The following function is SLOW.
839 // For your safety and for the protection of those around you...
840 // DO NOT CALL THIS AT HOME.
842 if(w(theText, theSize) <= maxWidth)
843 return strlen(theText); // yeah!
845 bool colors = (w("^7", theSize) == 0);
847 // binary search for right place to cut string
848 int len, left, right, middle;
850 right = len = strlen(theText);
854 middle = floor((left + right) / 2);
856 ofs = skipIncompleteTag(theText, middle, len);
857 if(w(substring(theText, 0, middle + ofs), theSize) <= maxWidth)
862 while(left < right - 1);
867 float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w)
870 // The following function is SLOW.
871 // For your safety and for the protection of those around you...
872 // DO NOT CALL THIS AT HOME.
874 if(w(theText) <= maxWidth)
875 return strlen(theText); // yeah!
877 bool colors = (w("^7") == 0);
879 // binary search for right place to cut string
880 int len, left, right, middle;
882 right = len = strlen(theText);
886 middle = floor((left + right) / 2);
888 ofs = skipIncompleteTag(theText, middle, len);
889 if(w(substring(theText, 0, middle + ofs)) <= maxWidth)
894 while(left < right - 1);
899 string find_last_color_code(string s)
901 int start = strstrofs(s, "^", 0);
902 if (start == -1) // no caret found
904 int len = strlen(s)-1;
905 for(int i = len; i >= start; --i)
907 if(substring(s, i, 1) != "^")
911 while (i-carets >= start && substring(s, i-carets, 1) == "^")
914 // check if carets aren't all escaped
918 if(IS_DIGIT(substring(s, i+1, 1)))
919 return substring(s, i, 2);
922 if(substring(s, i+1, 1) == "x")
923 if(IS_HEXDIGIT(substring(s, i + 2, 1)))
924 if(IS_HEXDIGIT(substring(s, i + 3, 1)))
925 if(IS_HEXDIGIT(substring(s, i + 4, 1)))
926 return substring(s, i, 5);
928 i -= carets; // this also skips one char before the carets
934 string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
940 s = getWrappedLine_remaining;
944 getWrappedLine_remaining = string_null;
945 return s; // the line has no size ANYWAY, nothing would be displayed.
948 cantake = textLengthUpToWidth(s, w, theFontSize, tw);
949 if(cantake > 0 && cantake < strlen(s))
952 while(take > 0 && substring(s, take, 1) != " ")
956 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
957 if(getWrappedLine_remaining == "")
958 getWrappedLine_remaining = string_null;
959 else if (tw("^7", theFontSize) == 0)
960 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining);
961 return substring(s, 0, cantake);
965 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
966 if(getWrappedLine_remaining == "")
967 getWrappedLine_remaining = string_null;
968 else if (tw("^7", theFontSize) == 0)
969 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining);
970 return substring(s, 0, take);
975 getWrappedLine_remaining = string_null;
980 string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw)
986 s = getWrappedLine_remaining;
990 getWrappedLine_remaining = string_null;
991 return s; // the line has no size ANYWAY, nothing would be displayed.
994 cantake = textLengthUpToLength(s, w, tw);
995 if(cantake > 0 && cantake < strlen(s))
998 while(take > 0 && substring(s, take, 1) != " ")
1002 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake);
1003 if(getWrappedLine_remaining == "")
1004 getWrappedLine_remaining = string_null;
1005 else if (tw("^7") == 0)
1006 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining);
1007 return substring(s, 0, cantake);
1011 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take);
1012 if(getWrappedLine_remaining == "")
1013 getWrappedLine_remaining = string_null;
1014 else if (tw("^7") == 0)
1015 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining);
1016 return substring(s, 0, take);
1021 getWrappedLine_remaining = string_null;
1026 string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1028 if(tw(theText, theFontSize) <= maxWidth)
1031 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("...", theFontSize), theFontSize, tw)), "...");
1034 string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw)
1036 if(tw(theText) <= maxWidth)
1039 return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
1042 float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern)
1044 string subpattern, subpattern2, subpattern3, subpattern4;
1045 subpattern = strcat(",", MapInfo_Type_ToString(gt), ",");
1047 subpattern2 = ",teams,";
1049 subpattern2 = ",noteams,";
1051 subpattern3 = ",teamspawns,";
1053 subpattern3 = ",noteamspawns,";
1054 if(gt == MAPINFO_TYPE_RACE || gt == MAPINFO_TYPE_CTS)
1055 subpattern4 = ",race,";
1057 subpattern4 = string_null;
1059 if(substring(pattern, 0, 1) == "-")
1061 pattern = substring(pattern, 1, strlen(pattern) - 1);
1062 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
1064 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0)
1066 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) >= 0)
1068 if(subpattern4 && strstrofs(strcat(",", pattern, ","), subpattern4, 0) >= 0)
1073 if(substring(pattern, 0, 1) == "+")
1074 pattern = substring(pattern, 1, strlen(pattern) - 1);
1075 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
1076 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0)
1077 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) < 0)
1081 if(strstrofs(strcat(",", pattern, ","), subpattern4, 0) < 0)
1088 vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style)
1092 // make origin and speed relative
1097 // now solve for ret, ret normalized:
1098 // eorg + t * evel == t * ret * spd
1099 // or, rather, solve for t:
1100 // |eorg + t * evel| == t * spd
1101 // eorg^2 + t^2 * evel^2 + 2 * t * (eorg * evel) == t^2 * spd^2
1102 // t^2 * (evel^2 - spd^2) + t * (2 * (eorg * evel)) + eorg^2 == 0
1103 vector solution = solve_quadratic(evel * evel - spd * spd, 2 * (eorg * evel), eorg * eorg);
1104 // p = 2 * (eorg * evel) / (evel * evel - spd * spd)
1105 // q = (eorg * eorg) / (evel * evel - spd * spd)
1106 if(!solution.z) // no real solution
1109 // (eorg * evel)^2 < (evel^2 - spd^2) * eorg^2
1110 // (eorg * evel)^2 / eorg^2 < evel^2 - spd^2
1111 // spd^2 < ((evel^2 * eorg^2) - (eorg * evel)^2) / eorg^2
1112 // spd^2 < evel^2 * (1 - cos^2 angle(evel, eorg))
1113 // spd^2 < evel^2 * sin^2 angle(evel, eorg)
1114 // spd < |evel| * sin angle(evel, eorg)
1117 else if(solution.x > 0)
1119 // both solutions > 0: take the smaller one
1120 // happens if p < 0 and q > 0
1121 ret = normalize(eorg + solution.x * evel);
1123 else if(solution.y > 0)
1125 // one solution > 0: take the larger one
1126 // happens if q < 0 or q == 0 and p < 0
1127 ret = normalize(eorg + solution.y * evel);
1131 // no solution > 0: reject
1132 // happens if p > 0 and q >= 0
1133 // 2 * (eorg * evel) / (evel * evel - spd * spd) > 0
1134 // (eorg * eorg) / (evel * evel - spd * spd) >= 0
1139 // "Enemy is moving away from me at more than spd"
1143 // NOTE: we always got a solution if spd > |evel|
1145 if(newton_style == 2)
1146 ret = normalize(ret * spd + myvel);
1151 vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_style, float mi, float ma)
1156 if(newton_style == 2)
1158 // true Newtonian projectiles with automatic aim adjustment
1160 // solve: |outspeed * mydir - myvel| = spd
1161 // outspeed^2 - 2 * outspeed * (mydir * myvel) + myvel^2 - spd^2 = 0
1162 // outspeed = (mydir * myvel) +- sqrt((mydir * myvel)^2 - myvel^2 + spd^2)
1166 // myvel^2 - (mydir * myvel)^2 > spd^2
1167 // velocity without mydir component > spd
1168 // fire at smallest possible spd that works?
1169 // |(mydir * myvel) * myvel - myvel| = spd
1171 vector solution = solve_quadratic(1, -2 * (mydir * myvel), myvel * myvel - spd * spd);
1175 outspeed = solution.y; // the larger one
1178 //outspeed = 0; // slowest possible shot
1179 outspeed = solution.x; // the real part (that is, the average!)
1180 //dprint("impossible shot, adjusting\n");
1183 outspeed = bound(spd * mi, outspeed, spd * ma);
1184 return mydir * outspeed;
1188 return myvel + spd * mydir;
1191 float compressShotOrigin(vector v)
1193 float rx = rint(v.x * 2);
1194 float ry = rint(v.y * 4) + 128;
1195 float rz = rint(v.z * 4) + 128;
1196 if(rx > 255 || rx < 0)
1198 LOG_DEBUG("shot origin ", vtos(v), " x out of bounds\n");
1199 rx = bound(0, rx, 255);
1201 if(ry > 255 || ry < 0)
1203 LOG_DEBUG("shot origin ", vtos(v), " y out of bounds\n");
1204 ry = bound(0, ry, 255);
1206 if(rz > 255 || rz < 0)
1208 LOG_DEBUG("shot origin ", vtos(v), " z out of bounds\n");
1209 rz = bound(0, rz, 255);
1211 return rx * 0x10000 + ry * 0x100 + rz;
1213 vector decompressShotOrigin(int f)
1216 v.x = ((f & 0xFF0000) / 0x10000) / 2;
1217 v.y = ((f & 0xFF00) / 0x100 - 128) / 4;
1218 v.z = ((f & 0xFF) - 128) / 4;
1223 vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
1225 // NOTE: we'll always choose the SMALLER value...
1226 float healthdamage, armordamage, armorideal;
1227 if (DEATH_IS(deathtype, DEATH_DROWN)) // Why should armor help here...
1230 healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health
1231 armordamage = a + (h - 1); // damage we can take if we could use more armor
1232 armorideal = healthdamage * armorblock;
1234 if(armordamage < healthdamage)
1247 vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage)
1250 if (DEATH_IS(deathtype, DEATH_DROWN)) // Why should armor help here...
1252 if (deathtype & HITTYPE_ARMORPIERCE)
1254 v.y = bound(0, damage * armorblock, a); // save
1255 v.x = bound(0, damage - v.y, damage); // take
1261 string getcurrentmod()
1265 m = cvar_string("fs_gamedir");
1266 n = tokenize_console(m);
1273 float matchacl(string acl, string str)
1280 t = car(acl); acl = cdr(acl);
1283 if(substring(t, 0, 1) == "-")
1286 t = substring(t, 1, strlen(t) - 1);
1288 else if(substring(t, 0, 1) == "+")
1289 t = substring(t, 1, strlen(t) - 1);
1291 if(substring(t, -1, 1) == "*")
1293 t = substring(t, 0, strlen(t) - 1);
1294 s = substring(str, 0, strlen(t));
1302 break; // if we found a killing case, apply it! other settings may be allowed in the future, but this one is caught
1309 void write_String_To_File(int fh, string str, bool alsoprint)
1312 if (alsoprint) LOG_INFO(str);
1315 string get_model_datafilename(string m, float sk, string fil)
1320 m = "models/player/*_";
1322 m = strcat(m, ftos(sk));
1325 return strcat(m, ".", fil);
1328 float get_model_parameters(string m, float sk)
1330 get_model_parameters_modelname = string_null;
1331 get_model_parameters_modelskin = -1;
1332 get_model_parameters_name = string_null;
1333 get_model_parameters_species = -1;
1334 get_model_parameters_sex = string_null;
1335 get_model_parameters_weight = -1;
1336 get_model_parameters_age = -1;
1337 get_model_parameters_desc = string_null;
1338 get_model_parameters_bone_upperbody = string_null;
1339 get_model_parameters_bone_weapon = string_null;
1340 for(int i = 0; i < MAX_AIM_BONES; ++i)
1342 get_model_parameters_bone_aim[i] = string_null;
1343 get_model_parameters_bone_aimweight[i] = 0;
1345 get_model_parameters_fixbone = 0;
1346 get_model_parameters_hidden = false;
1349 MUTATOR_CALLHOOK(ClearModelParams);
1355 if(substring(m, -9, 5) == "_lod1" || substring(m, -9, 5) == "_lod2")
1356 m = strcat(substring(m, 0, -10), substring(m, -4, -1));
1360 if(substring(m, -4, -1) != ".txt")
1362 if(substring(m, -6, 1) != "_")
1364 sk = stof(substring(m, -5, 1));
1365 m = substring(m, 0, -7);
1368 string fn = get_model_datafilename(m, sk, "txt");
1369 int fh = fopen(fn, FILE_READ);
1373 fn = get_model_datafilename(m, sk, "txt");
1374 fh = fopen(fn, FILE_READ);
1379 get_model_parameters_modelname = m;
1380 get_model_parameters_modelskin = sk;
1382 while((s = fgets(fh)))
1385 break; // next lines will be description
1389 get_model_parameters_name = s;
1393 case "human": get_model_parameters_species = SPECIES_HUMAN; break;
1394 case "alien": get_model_parameters_species = SPECIES_ALIEN; break;
1395 case "robot_shiny": get_model_parameters_species = SPECIES_ROBOT_SHINY; break;
1396 case "robot_rusty": get_model_parameters_species = SPECIES_ROBOT_RUSTY; break;
1397 case "robot_solid": get_model_parameters_species = SPECIES_ROBOT_SOLID; break;
1398 case "animal": get_model_parameters_species = SPECIES_ANIMAL; break;
1399 case "reserved": get_model_parameters_species = SPECIES_RESERVED; break;
1403 if (s == "Male") s = _("Male");
1404 else if (s == "Female") s = _("Female");
1405 else if (s == "Undisclosed") s = _("Undisclosed");
1406 get_model_parameters_sex = s;
1409 get_model_parameters_weight = stof(s);
1411 get_model_parameters_age = stof(s);
1412 if(c == "description")
1413 get_model_parameters_description = s;
1414 if(c == "bone_upperbody")
1415 get_model_parameters_bone_upperbody = s;
1416 if(c == "bone_weapon")
1417 get_model_parameters_bone_weapon = s;
1419 MUTATOR_CALLHOOK(GetModelParams, c, s);
1421 for(int i = 0; i < MAX_AIM_BONES; ++i)
1422 if(c == strcat("bone_aim", ftos(i)))
1424 get_model_parameters_bone_aimweight[i] = stof(car(s));
1425 get_model_parameters_bone_aim[i] = cdr(s);
1428 get_model_parameters_fixbone = stof(s);
1430 get_model_parameters_hidden = stob(s);
1433 while((s = fgets(fh)))
1435 if(get_model_parameters_desc)
1436 get_model_parameters_desc = strcat(get_model_parameters_desc, "\n");
1438 get_model_parameters_desc = strcat(get_model_parameters_desc, s);
1446 string translate_key(string key)
1448 if (prvm_language == "en") return key;
1450 if (substring(key, 0, 1) == "<")
1452 if (key == "<KEY NOT FOUND>") return _("<KEY NOT FOUND>");
1453 if (key == "<UNKNOWN KEYNUM>") return _("<UNKNOWN KEYNUM>");
1458 case "TAB": return _("TAB");
1459 case "ENTER": return _("ENTER");
1460 case "ESCAPE": return _("ESCAPE");
1461 case "SPACE": return _("SPACE");
1463 case "BACKSPACE": return _("BACKSPACE");
1464 case "UPARROW": return _("UPARROW");
1465 case "DOWNARROW": return _("DOWNARROW");
1466 case "LEFTARROW": return _("LEFTARROW");
1467 case "RIGHTARROW": return _("RIGHTARROW");
1469 case "ALT": return _("ALT");
1470 case "CTRL": return _("CTRL");
1471 case "SHIFT": return _("SHIFT");
1473 case "INS": return _("INS");
1474 case "DEL": return _("DEL");
1475 case "PGDN": return _("PGDN");
1476 case "PGUP": return _("PGUP");
1477 case "HOME": return _("HOME");
1478 case "END": return _("END");
1480 case "PAUSE": return _("PAUSE");
1482 case "NUMLOCK": return _("NUMLOCK");
1483 case "CAPSLOCK": return _("CAPSLOCK");
1484 case "SCROLLOCK": return _("SCROLLOCK");
1486 case "SEMICOLON": return _("SEMICOLON");
1487 case "TILDE": return _("TILDE");
1488 case "BACKQUOTE": return _("BACKQUOTE");
1489 case "QUOTE": return _("QUOTE");
1490 case "APOSTROPHE": return _("APOSTROPHE");
1491 case "BACKSLASH": return _("BACKSLASH");
1494 if (substring(key, 0, 1) == "F")
1496 string subkey = substring(key, 1, -1);
1497 if (IS_DIGIT(substring(key, 3, 1))) // check only first digit
1499 return sprintf(_("F%d"), stof(subkey));
1501 // continue in case there is another key name starting with F
1504 if (substring(key, 0, 3) == "KP_")
1506 string subkey = substring(key, 3, -1);
1507 if (IS_DIGIT(substring(key, 3, 1))) // check only first digit
1509 return sprintf(_("KP_%d"), stof(subkey));
1514 case "INS": return sprintf(_("KP_%s"), _("INS"));
1515 case "END": return sprintf(_("KP_%s"), _("END"));
1516 case "DOWNARROW": return sprintf(_("KP_%s"), _("DOWNARROW"));
1517 case "PGDN": return sprintf(_("KP_%s"), _("PGDN"));
1518 case "LEFTARROW": return sprintf(_("KP_%s"), _("LEFTARROW"));
1519 case "RIGHTARROW": return sprintf(_("KP_%s"), _("RIGHTARROW"));
1520 case "HOME": return sprintf(_("KP_%s"), _("HOME"));
1521 case "UPARROW": return sprintf(_("KP_%s"), _("UPARROW"));
1522 case "PGUP": return sprintf(_("KP_%s"), _("PGUP"));
1523 case "PERIOD": return sprintf(_("KP_%s"), _("PERIOD"));
1524 case "DEL": return sprintf(_("KP_%s"), _("DEL"));
1525 case "DIVIDE": return sprintf(_("KP_%s"), _("DIVIDE"));
1526 case "SLASH": return sprintf(_("KP_%s"), _("SLASH"));
1527 case "MULTIPLY": return sprintf(_("KP_%s"), _("MULTIPLY"));
1528 case "MINUS": return sprintf(_("KP_%s"), _("MINUS"));
1529 case "PLUS": return sprintf(_("KP_%s"), _("PLUS"));
1530 case "ENTER": return sprintf(_("KP_%s"), _("ENTER"));
1531 case "EQUALS": return sprintf(_("KP_%s"), _("EQUALS"));
1532 default: return key;
1536 if (key == "PRINTSCREEN") return _("PRINTSCREEN");
1538 if (substring(key, 0, 5) == "MOUSE")
1539 return sprintf(_("MOUSE%d"), stof(substring(key, 5, -1)));
1541 if (key == "MWHEELUP") return _("MWHEELUP");
1542 if (key == "MWHEELDOWN") return _("MWHEELDOWN");
1544 if (substring(key, 0,3) == "JOY")
1545 return sprintf(_("JOY%d"), stof(substring(key, 3, -1)));
1547 if (substring(key, 0,3) == "AUX")
1548 return sprintf(_("AUX%d"), stof(substring(key, 3, -1)));
1550 if (substring(key, 0, 4) == "X360_")
1552 string subkey = substring(key, 4, -1);
1555 case "DPAD_UP": return sprintf(_("X360_%s"), _("DPAD_UP"));
1556 case "DPAD_DOWN": return sprintf(_("X360_%s"), _("DPAD_DOWN"));
1557 case "DPAD_LEFT": return sprintf(_("X360_%s"), _("DPAD_LEFT"));
1558 case "DPAD_RIGHT": return sprintf(_("X360_%s"), _("DPAD_RIGHT"));
1559 case "START": return sprintf(_("X360_%s"), _("START"));
1560 case "BACK": return sprintf(_("X360_%s"), _("BACK"));
1561 case "LEFT_THUMB": return sprintf(_("X360_%s"), _("LEFT_THUMB"));
1562 case "RIGHT_THUMB": return sprintf(_("X360_%s"), _("RIGHT_THUMB"));
1563 case "LEFT_SHOULDER": return sprintf(_("X360_%s"), _("LEFT_SHOULDER"));
1564 case "RIGHT_SHOULDER": return sprintf(_("X360_%s"), _("RIGHT_SHOULDER"));
1565 case "LEFT_TRIGGER": return sprintf(_("X360_%s"), _("LEFT_TRIGGER"));
1566 case "RIGHT_TRIGGER": return sprintf(_("X360_%s"), _("RIGHT_TRIGGER"));
1567 case "LEFT_THUMB_UP": return sprintf(_("X360_%s"), _("LEFT_THUMB_UP"));
1568 case "LEFT_THUMB_DOWN": return sprintf(_("X360_%s"), _("LEFT_THUMB_DOWN"));
1569 case "LEFT_THUMB_LEFT": return sprintf(_("X360_%s"), _("LEFT_THUMB_LEFT"));
1570 case "LEFT_THUMB_RIGHT": return sprintf(_("X360_%s"), _("LEFT_THUMB_RIGHT"));
1571 case "RIGHT_THUMB_UP": return sprintf(_("X360_%s"), _("RIGHT_THUMB_UP"));
1572 case "RIGHT_THUMB_DOWN": return sprintf(_("X360_%s"), _("RIGHT_THUMB_DOWN"));
1573 case "RIGHT_THUMB_LEFT": return sprintf(_("X360_%s"), _("RIGHT_THUMB_LEFT"));
1574 case "RIGHT_THUMB_RIGHT": return sprintf(_("X360_%s"), _("RIGHT_THUMB_RIGHT"));
1575 default: return key;
1579 if (substring(key, 0, 4) == "JOY_")
1581 string subkey = substring(key, 4, -1);
1584 case "UP": return sprintf(_("JOY_%s"), _("UP"));
1585 case "DOWN": return sprintf(_("JOY_%s"), _("DOWN"));
1586 case "LEFT": return sprintf(_("JOY_%s"), _("LEFT"));
1587 case "RIGHT": return sprintf(_("JOY_%s"), _("RIGHT"));
1588 default: return key;
1592 if (substring(key, 0, 8) == "MIDINOTE")
1593 return sprintf(_("MIDINOTE%d"), stof(substring(key, 8, -1)));
1598 // x-encoding (encoding as zero length invisible string)
1599 const string XENCODE_2 = "xX";
1600 const string XENCODE_22 = "0123456789abcdefABCDEF";
1601 string xencode(int f)
1604 d = f % 22; f = floor(f / 22);
1605 c = f % 22; f = floor(f / 22);
1606 b = f % 22; f = floor(f / 22);
1607 a = f % 2; // f = floor(f / 2);
1610 substring(XENCODE_2, a, 1),
1611 substring(XENCODE_22, b, 1),
1612 substring(XENCODE_22, c, 1),
1613 substring(XENCODE_22, d, 1)
1616 float xdecode(string s)
1619 if(substring(s, 0, 1) != "^")
1623 a = strstrofs(XENCODE_2, substring(s, 1, 1), 0);
1624 b = strstrofs(XENCODE_22, substring(s, 2, 1), 0);
1625 c = strstrofs(XENCODE_22, substring(s, 3, 1), 0);
1626 d = strstrofs(XENCODE_22, substring(s, 4, 1), 0);
1627 if(a < 0 || b < 0 || c < 0 || d < 0)
1629 return ((a * 22 + b) * 22 + c) * 22 + d;
1633 string strlimitedlen(string input, string truncation, float strip_colors, float limit)
1635 if(strlen((strip_colors ? strdecolorize(input) : input)) <= limit)
1638 return strcat(substring(input, 0, (strlen(input) - strlen(truncation))), truncation);
1641 float shutdown_running;
1646 void CSQC_Shutdown()
1652 if(shutdown_running)
1654 LOG_INFO("Recursive shutdown detected! Only restoring cvars...");
1658 shutdown_running = 1;
1662 cvar_settemp_restore(); // this must be done LAST, but in any case
1666 .float skeleton_bones_index;
1667 void Skeleton_SetBones(entity e)
1669 // set skeleton_bones to the total number of bones on the model
1670 if(e.skeleton_bones_index == e.modelindex)
1671 return; // same model, nothing to update
1674 skelindex = skel_create(e.modelindex);
1675 e.skeleton_bones = skel_get_numbones(skelindex);
1676 skel_delete(skelindex);
1677 e.skeleton_bones_index = e.modelindex;
1681 string to_execute_next_frame;
1682 void execute_next_frame()
1684 if(to_execute_next_frame)
1686 localcmd("\n", to_execute_next_frame, "\n");
1687 strfree(to_execute_next_frame);
1690 void queue_to_execute_next_frame(string s)
1692 if(to_execute_next_frame)
1694 s = strcat(s, "\n", to_execute_next_frame);
1696 strcpy(to_execute_next_frame, s);
1699 .float FindConnectedComponent_processing;
1700 void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass)
1702 entity queue_start, queue_end;
1704 // we build a queue of to-be-processed entities.
1705 // queue_start is the next entity to be checked for neighbors
1706 // queue_end is the last entity added
1708 if(e.FindConnectedComponent_processing)
1709 error("recursion or broken cleanup");
1711 // start with a 1-element queue
1712 queue_start = queue_end = e;
1713 queue_end.(fld) = NULL;
1714 queue_end.FindConnectedComponent_processing = 1;
1716 // for each queued item:
1717 for (; queue_start; queue_start = queue_start.(fld))
1719 // find all neighbors of queue_start
1721 for(t = NULL; (t = nxt(t, queue_start, pass)); )
1723 if(t.FindConnectedComponent_processing)
1725 if(iscon(t, queue_start, pass))
1727 // it is connected? ADD IT. It will look for neighbors soon too.
1728 queue_end.(fld) = t;
1730 queue_end.(fld) = NULL;
1731 queue_end.FindConnectedComponent_processing = 1;
1737 for (queue_start = e; queue_start; queue_start = queue_start.(fld))
1738 queue_start.FindConnectedComponent_processing = 0;
1742 vector animfixfps(entity e, vector a, vector b)
1744 // multi-frame anim: keep as-is
1747 float dur = frameduration(e.modelindex, a.x);
1748 if (dur <= 0 && b.y)
1751 dur = frameduration(e.modelindex, a.x);
1761 Notification Announcer_PickNumber(int type, int num)
1770 case 10: return ANNCE_NUM_GAMESTART_10;
1771 case 9: return ANNCE_NUM_GAMESTART_9;
1772 case 8: return ANNCE_NUM_GAMESTART_8;
1773 case 7: return ANNCE_NUM_GAMESTART_7;
1774 case 6: return ANNCE_NUM_GAMESTART_6;
1775 case 5: return ANNCE_NUM_GAMESTART_5;
1776 case 4: return ANNCE_NUM_GAMESTART_4;
1777 case 3: return ANNCE_NUM_GAMESTART_3;
1778 case 2: return ANNCE_NUM_GAMESTART_2;
1779 case 1: return ANNCE_NUM_GAMESTART_1;
1787 case 10: return ANNCE_NUM_IDLE_10;
1788 case 9: return ANNCE_NUM_IDLE_9;
1789 case 8: return ANNCE_NUM_IDLE_8;
1790 case 7: return ANNCE_NUM_IDLE_7;
1791 case 6: return ANNCE_NUM_IDLE_6;
1792 case 5: return ANNCE_NUM_IDLE_5;
1793 case 4: return ANNCE_NUM_IDLE_4;
1794 case 3: return ANNCE_NUM_IDLE_3;
1795 case 2: return ANNCE_NUM_IDLE_2;
1796 case 1: return ANNCE_NUM_IDLE_1;
1804 case 10: return ANNCE_NUM_KILL_10;
1805 case 9: return ANNCE_NUM_KILL_9;
1806 case 8: return ANNCE_NUM_KILL_8;
1807 case 7: return ANNCE_NUM_KILL_7;
1808 case 6: return ANNCE_NUM_KILL_6;
1809 case 5: return ANNCE_NUM_KILL_5;
1810 case 4: return ANNCE_NUM_KILL_4;
1811 case 3: return ANNCE_NUM_KILL_3;
1812 case 2: return ANNCE_NUM_KILL_2;
1813 case 1: return ANNCE_NUM_KILL_1;
1821 case 10: return ANNCE_NUM_RESPAWN_10;
1822 case 9: return ANNCE_NUM_RESPAWN_9;
1823 case 8: return ANNCE_NUM_RESPAWN_8;
1824 case 7: return ANNCE_NUM_RESPAWN_7;
1825 case 6: return ANNCE_NUM_RESPAWN_6;
1826 case 5: return ANNCE_NUM_RESPAWN_5;
1827 case 4: return ANNCE_NUM_RESPAWN_4;
1828 case 3: return ANNCE_NUM_RESPAWN_3;
1829 case 2: return ANNCE_NUM_RESPAWN_2;
1830 case 1: return ANNCE_NUM_RESPAWN_1;
1834 case CNT_ROUNDSTART:
1838 case 10: return ANNCE_NUM_ROUNDSTART_10;
1839 case 9: return ANNCE_NUM_ROUNDSTART_9;
1840 case 8: return ANNCE_NUM_ROUNDSTART_8;
1841 case 7: return ANNCE_NUM_ROUNDSTART_7;
1842 case 6: return ANNCE_NUM_ROUNDSTART_6;
1843 case 5: return ANNCE_NUM_ROUNDSTART_5;
1844 case 4: return ANNCE_NUM_ROUNDSTART_4;
1845 case 3: return ANNCE_NUM_ROUNDSTART_3;
1846 case 2: return ANNCE_NUM_ROUNDSTART_2;
1847 case 1: return ANNCE_NUM_ROUNDSTART_1;
1855 case 10: return ANNCE_NUM_10;
1856 case 9: return ANNCE_NUM_9;
1857 case 8: return ANNCE_NUM_8;
1858 case 7: return ANNCE_NUM_7;
1859 case 6: return ANNCE_NUM_6;
1860 case 5: return ANNCE_NUM_5;
1861 case 4: return ANNCE_NUM_4;
1862 case 3: return ANNCE_NUM_3;
1863 case 2: return ANNCE_NUM_2;
1864 case 1: return ANNCE_NUM_1;
1873 int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents)
1875 switch(nativecontents)
1880 return DPCONTENTS_SOLID | DPCONTENTS_OPAQUE;
1882 return DPCONTENTS_WATER;
1884 return DPCONTENTS_SLIME;
1886 return DPCONTENTS_LAVA | DPCONTENTS_NODROP;
1888 return DPCONTENTS_SKY | DPCONTENTS_NODROP | DPCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
1893 int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents)
1895 if(supercontents & (DPCONTENTS_SOLID | DPCONTENTS_BODY))
1896 return CONTENT_SOLID;
1897 if(supercontents & DPCONTENTS_SKY)
1899 if(supercontents & DPCONTENTS_LAVA)
1900 return CONTENT_LAVA;
1901 if(supercontents & DPCONTENTS_SLIME)
1902 return CONTENT_SLIME;
1903 if(supercontents & DPCONTENTS_WATER)
1904 return CONTENT_WATER;
1905 return CONTENT_EMPTY;