4 #include <client/mutators/_mod.qh>
5 #include <common/constants.qh>
6 #include <common/deathtypes/all.qh>
7 #include <common/gamemodes/_mod.qh>
8 #include <common/mapinfo.qh>
9 #include <common/notifications/all.qh>
10 #include <common/scores.qh>
13 #include <common/constants.qh>
14 #include <common/deathtypes/all.qh>
15 #include <common/gamemodes/_mod.qh>
16 #include <common/mapinfo.qh>
17 #include <common/notifications/all.qh>
18 #include <common/scores.qh>
19 #include <server/mutators/_mod.qh>
23 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
29 //nudge = 2 * cvar("collision_impactnudge"); // why not?
32 dir = normalize(v2 - v1);
34 pos = v1 + dir * nudge;
41 if(pos * dir >= v2 * dir)
49 tracebox(pos, mi, ma, v2, nomonsters, forent);
54 LOG_TRACE("When tracing from ", vtos(v1), " to ", vtos(v2));
55 LOG_TRACE(" Nudging gets us nowhere at ", vtos(pos));
56 LOG_TRACE(" trace_endpos is ", vtos(trace_endpos));
57 LOG_TRACE(" trace distance is ", ftos(vlen(pos - trace_endpos)));
60 stopentity = trace_ent;
64 // we started inside solid.
65 // then trace from endpos to pos
67 tracebox(t, mi, ma, pos, nomonsters, forent);
71 // t is still inside solid? bad
72 // force advance, then, and retry
73 pos = t + dir * nudge;
75 // but if we hit an entity, stop RIGHT before it
76 if(stopatentity && stopentity && stopentity != ignorestopatentity)
78 trace_ent = stopentity;
80 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
86 // we actually LEFT solid!
87 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
93 // pos is outside solid?!? but why?!? never mind, just return it.
95 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
101 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity)
103 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent, stopatentity, ignorestopatentity);
112 Returns a point at least 12 units away from walls
113 (useful for explosion animations, although the blast is performed where it really happened)
117 vector findbetterlocation (vector org, float mindist)
119 vector vec = mindist * '1 0 0';
123 traceline (org, org + vec, true, NULL);
125 if (trace_fraction < 1)
127 vector loc = trace_endpos;
128 traceline (loc, loc + vec, true, NULL);
129 if (trace_fraction >= 1)
146 * Get "real" origin, in worldspace, even if ent is attached to something else.
148 vector real_origin(entity ent)
150 vector v = ((ent.absmin + ent.absmax) * 0.5);
151 entity e = ent.tag_entity;
155 v = v + ((e.absmin + e.absmax) * 0.5);
163 string wordwrap_buffer;
165 void wordwrap_buffer_put(string s)
167 wordwrap_buffer = strcat(wordwrap_buffer, s);
170 string wordwrap(string s, float l)
173 wordwrap_buffer = "";
174 wordwrap_cb(s, l, wordwrap_buffer_put);
176 wordwrap_buffer = "";
181 entity _wordwrap_buffer_sprint_ent;
182 void wordwrap_buffer_sprint(string s)
184 wordwrap_buffer = strcat(wordwrap_buffer, s);
187 sprint(_wordwrap_buffer_sprint_ent, wordwrap_buffer);
188 wordwrap_buffer = "";
192 void wordwrap_sprint(entity to, string s, float l)
194 wordwrap_buffer = "";
195 _wordwrap_buffer_sprint_ent = to;
196 wordwrap_cb(s, l, wordwrap_buffer_sprint);
197 _wordwrap_buffer_sprint_ent = NULL;
198 if(wordwrap_buffer != "")
199 sprint(to, strcat(wordwrap_buffer, "\n"));
200 wordwrap_buffer = "";
206 string draw_UseSkinFor(string pic)
208 if(substring(pic, 0, 1) == "/")
209 return substring(pic, 1, strlen(pic)-1);
211 return strcat(draw_currentSkin, "/", pic);
214 // if s == "" (MENUQC) builds the mutator list for the Mutators dialog based on local cvar values
215 // otherwise (CSQC) translates the mutator list (s) that client has received from server
216 // NOTE: this function merges MENUQC and CSQC code in order to avoid duplicating and separating strings
217 string build_mutator_list(string s)
219 int i = -1, n = 0; // allow only 1 iteration in the following for loop if (s == "")
223 n = tokenizebyseparator(s, ", ");
226 for (string arg = ""; i < n; i++)
228 if (i >= 0) arg = argv(i);
229 if(arg == "Dodging" || (!n && cvar("g_dodging"))) s2 = cons_mid(s2, ", ", _("Dodging"));
230 if(arg == "InstaGib" || (!n && cvar("g_instagib"))) s2 = cons_mid(s2, ", ", _("InstaGib"));
231 if(arg == "New Toys" || (!n && cvar("g_new_toys"))) s2 = cons_mid(s2, ", ", _("New Toys"));
232 if(arg == "NIX" || (!n && cvar("g_nix"))) s2 = cons_mid(s2, ", ", _("NIX"));
233 if(arg == "Rocket Flying" || (!n && cvar("g_rocket_flying"))) s2 = cons_mid(s2, ", ", _("Rocket Flying"));
234 if(arg == "Invincible Projectiles" || (!n && cvar("g_invincible_projectiles"))) s2 = cons_mid(s2, ", ", _("Invincible Projectiles"));
235 if(arg == "Low gravity" || (!n && cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))) s2 = cons_mid(s2, ", ", _("Low gravity"));
236 if(arg == "Cloaked" || (!n && cvar("g_cloaked"))) s2 = cons_mid(s2, ", ", _("Cloaked"));
237 if(arg == "Hook" || (!n && cvar("g_grappling_hook"))) s2 = cons_mid(s2, ", ", _("Hook"));
238 if(arg == "Midair" || (!n && cvar("g_midair"))) s2 = cons_mid(s2, ", ", _("Midair"));
239 if(arg == "Melee only" || (!n && cvar("g_melee_only"))) s2 = cons_mid(s2, ", ", _("Melee only"));
240 if(arg == "Vampire" || (!n && cvar("g_vampire"))) s2 = cons_mid(s2, ", ", _("Vampire"));
241 if(arg == "Piñata" || (!n && cvar("g_pinata"))) s2 = cons_mid(s2, ", ", _("Piñata"));
242 if(arg == "Weapons stay" || (!n && cvar("g_weapon_stay"))) s2 = cons_mid(s2, ", ", _("Weapons stay"));
243 if(arg == "Blood loss" || (!n && cvar("g_bloodloss") > 0)) s2 = cons_mid(s2, ", ", _("Blood loss"));
244 if(arg == "Jetpack" || (!n && cvar("g_jetpack"))) s2 = cons_mid(s2, ", ", _("Jetpack"));
245 if(arg == "Buffs" || (!n && cvar("g_buffs") > 0)) s2 = cons_mid(s2, ", ", _("Buffs"));
246 if(arg == "Overkill" || (!n && cvar("g_overkill"))) s2 = cons_mid(s2, ", ", _("Overkill"));
247 if(arg == "No powerups" || (!n && cvar("g_powerups") == 0)) s2 = cons_mid(s2, ", ", _("No powerups"));
248 if(arg == "Powerups" || (!n && cvar("g_powerups") > 0)) s2 = cons_mid(s2, ", ", _("Powerups"));
249 if(arg == "Touch explode" || (!n && cvar("g_touchexplode") > 0)) s2 = cons_mid(s2, ", ", _("Touch explode"));
250 if(arg == "Wall jumping" || (!n && cvar("g_walljump"))) s2 = cons_mid(s2, ", ", _("Wall jumping"));
251 if(arg == "No start weapons" || (!n && cvar_string("g_weaponarena") == "0" && cvar("g_balance_blaster_weaponstartoverride") == 0)) s2 = cons_mid(s2, ", ", _("No start weapons"));
257 void wordwrap_cb(string s, float l, void(string) callback)
260 float lleft, i, j, wlen;
265 for (i = 0; i < len; ++i)
267 if (substring(s, i, 2) == "\\n")
273 else if (substring(s, i, 1) == "\n")
278 else if (substring(s, i, 1) == " ")
288 for (j = i+1; j < len; ++j)
289 // ^^ this skips over the first character of a word, which
290 // is ALWAYS part of the word
291 // this is safe since if i+1 == strlen(s), i will become
292 // strlen(s)-1 at the end of this block and the function
293 // will terminate. A space can't be the first character we
294 // read here, and neither can a \n be the start, since these
295 // two cases have been handled above.
297 c = substring(s, j, 1);
304 // we need to keep this tempstring alive even if substring is
305 // called repeatedly, so call strcat even though we're not
315 callback(substring(s, i, wlen));
323 void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
353 string ScoreString(int pFlags, float pValue)
358 pValue = floor(pValue + 0.5); // round
360 if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME)))
362 else if(pFlags & SFL_RANK)
363 valstr = (pValue < 256 ? count_ordinal(pValue) : _("N/A"));
364 else if(pFlags & SFL_TIME)
365 valstr = TIME_ENCODED_TOSTRING(pValue);
367 valstr = ftos(pValue);
373 // compressed vector format:
374 // like MD3, just even shorter
375 // 4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90
376 // 5 bit yaw (32 angles), 0=0, 8=90, 16=180, 24=270
377 // 7 bit length (logarithmic encoding), 1/8 .. about 7844
378 // length = 2^(length_encoded/8) / 8
379 // if pitch is 90, yaw does nothing and therefore indicates the sign (yaw is then either 11111 or 11110); 11111 is pointing DOWN
380 // thus, valid values are from 0000.11110.0000000 to 1111.11111.1111111
381 // the special value 0 indicates the zero vector
383 float lengthLogTable[128];
385 float invertLengthLog(float dist)
389 if(dist >= lengthLogTable[127])
391 if(dist <= lengthLogTable[0])
399 m = floor((l + r) / 2);
400 if(lengthLogTable[m] < dist)
406 // now: r is >=, l is <
407 float lerr = (dist - lengthLogTable[l]);
408 float rerr = (lengthLogTable[r] - dist);
414 vector decompressShortVector(int data)
419 float p = (data & 0xF000) / 0x1000;
420 float q = (data & 0x0F80) / 0x80;
421 int len = (data & 0x007F);
423 //print("\ndecompress: p ", ftos(p)); print("q ", ftos(q)); print("len ", ftos(len), "\n");
436 q = .19634954084936207740 * q;
437 p = .19634954084936207740 * p - 1.57079632679489661922;
438 out.x = cos(q) * cos(p);
439 out.y = sin(q) * cos(p);
443 //print("decompressed: ", vtos(out), "\n");
445 return out * lengthLogTable[len];
448 float compressShortVector(vector vec)
454 //print("compress: ", vtos(vec), "\n");
455 ang = vectoangles(vec);
459 if(ang.x < -90 && ang.x > +90)
460 error("BOGUS vectoangles");
461 //print("angles: ", vtos(ang), "\n");
463 p = floor(0.5 + (ang.x + 90) * 16 / 180) & 15; // -90..90 to 0..14
472 y = floor(0.5 + ang.y * 32 / 360) & 31; // 0..360 to 0..32
473 len = invertLengthLog(vlen(vec));
475 //print("compressed: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n");
477 return (p * 0x1000) + (y * 0x80) + len;
480 STATIC_INIT(compressShortVector)
483 float f = (2 ** (1/8));
485 for(i = 0; i < 128; ++i)
487 lengthLogTable[i] = l;
491 if(cvar("developer") > 0)
493 LOG_TRACE("Verifying vector compression table...");
494 for(i = 0x0F00; i < 0xFFFF; ++i)
495 if(i != compressShortVector(decompressShortVector(i)))
498 "BROKEN vector compression: %s -> %s -> %s",
500 vtos(decompressShortVector(i)),
501 ftos(compressShortVector(decompressShortVector(i)))
509 float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
511 traceline(v0, v0 + dvx, true, forent); if(trace_fraction < 1) return 0;
512 traceline(v0, v0 + dvy, true, forent); if(trace_fraction < 1) return 0;
513 traceline(v0, v0 + dvz, true, forent); if(trace_fraction < 1) return 0;
514 traceline(v0 + dvx, v0 + dvx + dvy, true, forent); if(trace_fraction < 1) return 0;
515 traceline(v0 + dvx, v0 + dvx + dvz, true, forent); if(trace_fraction < 1) return 0;
516 traceline(v0 + dvy, v0 + dvy + dvx, true, forent); if(trace_fraction < 1) return 0;
517 traceline(v0 + dvy, v0 + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
518 traceline(v0 + dvz, v0 + dvz + dvx, true, forent); if(trace_fraction < 1) return 0;
519 traceline(v0 + dvz, v0 + dvz + dvy, true, forent); if(trace_fraction < 1) return 0;
520 traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
521 traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
522 traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
527 string fixPriorityList(string order, float from, float to, float subtract, float complete)
532 n = tokenize_console(order);
534 for(i = 0; i < n; ++i)
539 if(w >= from && w <= to)
540 neworder = strcat(neworder, ftos(w), " ");
544 if(w >= from && w <= to)
545 neworder = strcat(neworder, ftos(w), " ");
552 n = tokenize_console(neworder);
553 for(w = to; w >= from; --w)
555 int wflags = REGISTRY_GET(Weapons, w).spawnflags;
556 if(wflags & WEP_FLAG_SPECIALATTACK)
558 for(i = 0; i < n; ++i)
559 if(stof(argv(i)) == w)
561 if(i == n) // not found
562 neworder = strcat(neworder, ftos(w), " ");
566 return substring(neworder, 0, strlen(neworder) - 1);
569 string mapPriorityList(string order, string(string) mapfunc)
574 n = tokenize_console(order);
576 for(float i = 0; i < n; ++i)
577 neworder = strcat(neworder, mapfunc(argv(i)), " ");
579 return substring(neworder, 0, strlen(neworder) - 1);
582 string swapInPriorityList(string order, float i, float j)
584 float n = tokenize_console(order);
586 if(i >= 0 && i < n && j >= 0 && j < n && i != j)
589 for(float w = 0; w < n; ++w)
592 s = strcat(s, argv(j), " ");
594 s = strcat(s, argv(i), " ");
596 s = strcat(s, argv(w), " ");
598 return substring(s, 0, strlen(s) - 1);
605 void get_mi_min_max(float mode)
610 if(!strcasecmp(substring(s, 0, 5), "maps/"))
611 s = substring(s, 5, strlen(s) - 5);
612 if(!strcasecmp(substring(s, strlen(s) - 4, 4), ".bsp"))
613 s = substring(s, 0, strlen(s) - 4);
614 strcpy(mi_shortname, s);
626 MapInfo_Get_ByName(mi_shortname, 0, NULL);
627 if(MapInfo_Map_mins.x < MapInfo_Map_maxs.x)
629 mi_min = MapInfo_Map_mins;
630 mi_max = MapInfo_Map_maxs;
638 tracebox('1 0 0' * mi.x,
639 '0 1 0' * mi.y + '0 0 1' * mi.z,
640 '0 1 0' * ma.y + '0 0 1' * ma.z,
644 if(!trace_startsolid)
645 mi_min.x = trace_endpos.x;
647 tracebox('0 1 0' * mi.y,
648 '1 0 0' * mi.x + '0 0 1' * mi.z,
649 '1 0 0' * ma.x + '0 0 1' * ma.z,
653 if(!trace_startsolid)
654 mi_min.y = trace_endpos.y;
656 tracebox('0 0 1' * mi.z,
657 '1 0 0' * mi.x + '0 1 0' * mi.y,
658 '1 0 0' * ma.x + '0 1 0' * ma.y,
662 if(!trace_startsolid)
663 mi_min.z = trace_endpos.z;
665 tracebox('1 0 0' * ma.x,
666 '0 1 0' * mi.y + '0 0 1' * mi.z,
667 '0 1 0' * ma.y + '0 0 1' * ma.z,
671 if(!trace_startsolid)
672 mi_max.x = trace_endpos.x;
674 tracebox('0 1 0' * ma.y,
675 '1 0 0' * mi.x + '0 0 1' * mi.z,
676 '1 0 0' * ma.x + '0 0 1' * ma.z,
680 if(!trace_startsolid)
681 mi_max.y = trace_endpos.y;
683 tracebox('0 0 1' * ma.z,
684 '1 0 0' * mi.x + '0 1 0' * mi.y,
685 '1 0 0' * ma.x + '0 1 0' * ma.y,
689 if(!trace_startsolid)
690 mi_max.z = trace_endpos.z;
695 void get_mi_min_max_texcoords(float mode)
699 get_mi_min_max(mode);
704 // extend mi_picmax to get a square aspect ratio
705 // center the map in that area
706 extend = mi_picmax - mi_picmin;
707 if(extend.y > extend.x)
709 mi_picmin.x -= (extend.y - extend.x) * 0.5;
710 mi_picmax.x += (extend.y - extend.x) * 0.5;
714 mi_picmin.y -= (extend.x - extend.y) * 0.5;
715 mi_picmax.y += (extend.x - extend.y) * 0.5;
718 // add another some percent
719 extend = (mi_picmax - mi_picmin) * (1 / 64.0);
723 // calculate the texcoords
724 mi_pictexcoord0 = mi_pictexcoord1 = mi_pictexcoord2 = mi_pictexcoord3 = '0 0 0';
725 // first the two corners of the origin
726 mi_pictexcoord0_x = (mi_min.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x);
727 mi_pictexcoord0_y = (mi_min.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y);
728 mi_pictexcoord2_x = (mi_max.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x);
729 mi_pictexcoord2_y = (mi_max.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y);
730 // then the other corners
731 mi_pictexcoord1_x = mi_pictexcoord0_x;
732 mi_pictexcoord1_y = mi_pictexcoord2_y;
733 mi_pictexcoord3_x = mi_pictexcoord2_x;
734 mi_pictexcoord3_y = mi_pictexcoord0_y;
738 float cvar_settemp(string tmp_cvar, string tmp_value)
740 float created_saved_value;
742 created_saved_value = 0;
744 if (!(tmp_cvar || tmp_value))
746 LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !");
750 if(!cvar_type(tmp_cvar))
752 LOG_INFOF("Error: cvar %s doesn't exist!", tmp_cvar);
756 IL_EACH(g_saved_cvars, it.netname == tmp_cvar,
758 created_saved_value = -1; // skip creation
759 break; // no need to continue
762 if(created_saved_value != -1)
764 // creating a new entity to keep track of this cvar
765 entity e = new_pure(saved_cvar_value);
766 IL_PUSH(g_saved_cvars, e);
767 e.netname = strzone(tmp_cvar);
768 e.message = strzone(cvar_string(tmp_cvar));
769 created_saved_value = 1;
772 // update the cvar to the value given
773 cvar_set(tmp_cvar, tmp_value);
775 return created_saved_value;
778 int cvar_settemp_restore()
781 // FIXME this new-style loop fails!
783 FOREACH_ENTITY_CLASS("saved_cvar_value", true,
785 if(cvar_type(it.netname))
787 cvar_set(it.netname, it.message);
788 strunzone(it.netname);
789 strunzone(it.message);
794 LOG_INFOF("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.", it.netname);
799 while((e = find(e, classname, "saved_cvar_value")))
801 if(cvar_type(e.netname))
803 cvar_set(e.netname, e.message);
808 print(sprintf("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.", e.netname));
815 float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
818 // The following function is SLOW.
819 // For your safety and for the protection of those around you...
820 // DO NOT CALL THIS AT HOME.
822 if(w(theText, theSize) <= maxWidth)
823 return strlen(theText); // yeah!
825 bool colors = (w("^7", theSize) == 0);
827 // binary search for right place to cut string
828 int len, left, right, middle;
830 right = len = strlen(theText);
834 middle = floor((left + right) / 2);
837 vector res = checkColorCode(theText, len, middle, false);
838 ofs = (res.x) ? res.x - res.y : 0;
841 if(w(substring(theText, 0, middle + ofs), theSize) <= maxWidth)
846 while(left < right - 1);
851 float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w)
854 // The following function is SLOW.
855 // For your safety and for the protection of those around you...
856 // DO NOT CALL THIS AT HOME.
858 if(w(theText) <= maxWidth)
859 return strlen(theText); // yeah!
861 bool colors = (w("^7") == 0);
863 // binary search for right place to cut string
864 int len, left, right, middle;
866 right = len = strlen(theText);
870 middle = floor((left + right) / 2);
873 vector res = checkColorCode(theText, len, middle, true);
874 ofs = (!res.x) ? 0 : res.x - res.y;
877 if(w(substring(theText, 0, middle + ofs)) <= maxWidth)
882 while(left < right - 1);
887 string find_last_color_code(string s)
889 int start = strstrofs(s, "^", 0);
890 if (start == -1) // no caret found
892 int len = strlen(s)-1;
893 for(int i = len; i >= start; --i)
895 if(substring(s, i, 1) != "^")
899 while (i-carets >= start && substring(s, i-carets, 1) == "^")
902 // check if carets aren't all escaped
906 if(IS_DIGIT(substring(s, i+1, 1)))
907 return substring(s, i, 2);
910 if(substring(s, i+1, 1) == "x")
911 if(IS_HEXDIGIT(substring(s, i + 2, 1)))
912 if(IS_HEXDIGIT(substring(s, i + 3, 1)))
913 if(IS_HEXDIGIT(substring(s, i + 4, 1)))
914 return substring(s, i, 5);
916 i -= carets; // this also skips one char before the carets
922 string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
924 string s = getWrappedLine_remaining;
928 getWrappedLine_remaining = string_null;
929 return s; // the line has no size ANYWAY, nothing would be displayed.
932 int take_until = textLengthUpToWidth(s, w, theFontSize, tw);
933 if(take_until > 0 && take_until < strlen(s))
935 int last_word = take_until - 1;
936 while(last_word > 0 && substring(s, last_word, 1) != " ")
942 take_until = last_word;
946 getWrappedLine_remaining = substring(s, take_until + skip, strlen(s) - take_until);
947 if(getWrappedLine_remaining == "")
948 getWrappedLine_remaining = string_null;
949 else if (tw("^7", theFontSize) == 0)
950 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take_until)), getWrappedLine_remaining);
951 return substring(s, 0, take_until);
955 getWrappedLine_remaining = string_null;
960 string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw)
962 string s = getWrappedLine_remaining;
966 getWrappedLine_remaining = string_null;
967 return s; // the line has no size ANYWAY, nothing would be displayed.
970 int take_until = textLengthUpToLength(s, w, tw);
971 if(take_until > 0 && take_until < strlen(s))
973 int last_word = take_until - 1;
974 while(last_word > 0 && substring(s, last_word, 1) != " ")
980 take_until = last_word;
984 getWrappedLine_remaining = substring(s, take_until + skip, strlen(s) - take_until);
985 if(getWrappedLine_remaining == "")
986 getWrappedLine_remaining = string_null;
987 else if (tw("^7") == 0)
988 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take_until)), getWrappedLine_remaining);
989 return substring(s, 0, take_until);
993 getWrappedLine_remaining = string_null;
998 string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1000 if(tw(theText, theFontSize) <= maxWidth)
1003 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("...", theFontSize), theFontSize, tw)), "...");
1006 string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw)
1008 if(tw(theText) <= maxWidth)
1011 return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
1014 float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern)
1016 string subpattern, subpattern2, subpattern3, subpattern4;
1017 subpattern = strcat(",", MapInfo_Type_ToString(gt), ",");
1019 subpattern2 = ",teams,";
1021 subpattern2 = ",noteams,";
1023 subpattern3 = ",teamspawns,";
1025 subpattern3 = ",noteamspawns,";
1026 if(gt == MAPINFO_TYPE_RACE || gt == MAPINFO_TYPE_CTS)
1027 subpattern4 = ",race,";
1029 subpattern4 = string_null;
1031 if(substring(pattern, 0, 1) == "-")
1033 pattern = substring(pattern, 1, strlen(pattern) - 1);
1034 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
1036 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0)
1038 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) >= 0)
1040 if(subpattern4 && strstrofs(strcat(",", pattern, ","), subpattern4, 0) >= 0)
1045 if(substring(pattern, 0, 1) == "+")
1046 pattern = substring(pattern, 1, strlen(pattern) - 1);
1047 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
1048 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0)
1049 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) < 0)
1053 if(strstrofs(strcat(",", pattern, ","), subpattern4, 0) < 0)
1060 vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style)
1064 // make origin and speed relative
1069 // now solve for ret, ret normalized:
1070 // eorg + t * evel == t * ret * spd
1071 // or, rather, solve for t:
1072 // |eorg + t * evel| == t * spd
1073 // eorg^2 + t^2 * evel^2 + 2 * t * (eorg * evel) == t^2 * spd^2
1074 // t^2 * (evel^2 - spd^2) + t * (2 * (eorg * evel)) + eorg^2 == 0
1075 vector solution = solve_quadratic(evel * evel - spd * spd, 2 * (eorg * evel), eorg * eorg);
1076 // p = 2 * (eorg * evel) / (evel * evel - spd * spd)
1077 // q = (eorg * eorg) / (evel * evel - spd * spd)
1078 if(!solution.z) // no real solution
1081 // (eorg * evel)^2 < (evel^2 - spd^2) * eorg^2
1082 // (eorg * evel)^2 / eorg^2 < evel^2 - spd^2
1083 // spd^2 < ((evel^2 * eorg^2) - (eorg * evel)^2) / eorg^2
1084 // spd^2 < evel^2 * (1 - cos^2 angle(evel, eorg))
1085 // spd^2 < evel^2 * sin^2 angle(evel, eorg)
1086 // spd < |evel| * sin angle(evel, eorg)
1089 else if(solution.x > 0)
1091 // both solutions > 0: take the smaller one
1092 // happens if p < 0 and q > 0
1093 ret = normalize(eorg + solution.x * evel);
1095 else if(solution.y > 0)
1097 // one solution > 0: take the larger one
1098 // happens if q < 0 or q == 0 and p < 0
1099 ret = normalize(eorg + solution.y * evel);
1103 // no solution > 0: reject
1104 // happens if p > 0 and q >= 0
1105 // 2 * (eorg * evel) / (evel * evel - spd * spd) > 0
1106 // (eorg * eorg) / (evel * evel - spd * spd) >= 0
1111 // "Enemy is moving away from me at more than spd"
1115 // NOTE: we always got a solution if spd > |evel|
1117 if(newton_style == 2)
1118 ret = normalize(ret * spd + myvel);
1123 vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_style, float mi, float ma)
1128 if(newton_style == 2)
1130 // true Newtonian projectiles with automatic aim adjustment
1132 // solve: |outspeed * mydir - myvel| = spd
1133 // outspeed^2 - 2 * outspeed * (mydir * myvel) + myvel^2 - spd^2 = 0
1134 // outspeed = (mydir * myvel) +- sqrt((mydir * myvel)^2 - myvel^2 + spd^2)
1138 // myvel^2 - (mydir * myvel)^2 > spd^2
1139 // velocity without mydir component > spd
1140 // fire at smallest possible spd that works?
1141 // |(mydir * myvel) * myvel - myvel| = spd
1143 vector solution = solve_quadratic(1, -2 * (mydir * myvel), myvel * myvel - spd * spd);
1147 outspeed = solution.y; // the larger one
1150 //outspeed = 0; // slowest possible shot
1151 outspeed = solution.x; // the real part (that is, the average!)
1152 //dprint("impossible shot, adjusting\n");
1155 outspeed = bound(spd * mi, outspeed, spd * ma);
1156 return mydir * outspeed;
1160 return myvel + spd * mydir;
1163 float compressShotOrigin(vector v)
1165 float rx = rint(v.x * 2);
1166 float ry = rint(v.y * 4) + 128;
1167 float rz = rint(v.z * 4) + 128;
1168 if(rx > 255 || rx < 0)
1170 LOG_DEBUG("shot origin ", vtos(v), " x out of bounds\n");
1171 rx = bound(0, rx, 255);
1173 if(ry > 255 || ry < 0)
1175 LOG_DEBUG("shot origin ", vtos(v), " y out of bounds\n");
1176 ry = bound(0, ry, 255);
1178 if(rz > 255 || rz < 0)
1180 LOG_DEBUG("shot origin ", vtos(v), " z out of bounds\n");
1181 rz = bound(0, rz, 255);
1183 return rx * 0x10000 + ry * 0x100 + rz;
1185 vector decompressShotOrigin(int f)
1188 v.x = ((f & 0xFF0000) / 0x10000) / 2;
1189 v.y = ((f & 0xFF00) / 0x100 - 128) / 4;
1190 v.z = ((f & 0xFF) - 128) / 4;
1195 vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
1197 // NOTE: we'll always choose the SMALLER value...
1198 float healthdamage, armordamage, armorideal;
1199 if (DEATH_IS(deathtype, DEATH_DROWN)) // Why should armor help here...
1202 healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health
1203 armordamage = a + (h - 1); // damage we can take if we could use more armor
1204 armorideal = healthdamage * armorblock;
1206 if(armordamage < healthdamage)
1219 vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage)
1222 if (DEATH_IS(deathtype, DEATH_DROWN)) // Why should armor help here...
1224 if (deathtype & HITTYPE_ARMORPIERCE)
1226 v.y = bound(0, damage * armorblock, a); // save
1227 v.x = bound(0, damage - v.y, damage); // take
1233 string getcurrentmod()
1237 m = cvar_string("fs_gamedir");
1238 n = tokenize_console(m);
1245 float matchacl(string acl, string str)
1252 t = car(acl); acl = cdr(acl);
1255 if(substring(t, 0, 1) == "-")
1258 t = substring(t, 1, strlen(t) - 1);
1260 else if(substring(t, 0, 1) == "+")
1261 t = substring(t, 1, strlen(t) - 1);
1263 if(substring(t, -1, 1) == "*")
1265 t = substring(t, 0, strlen(t) - 1);
1266 s = substring(str, 0, strlen(t));
1274 break; // if we found a killing case, apply it! other settings may be allowed in the future, but this one is caught
1281 void write_String_To_File(int fh, string str, bool alsoprint)
1284 if (alsoprint) LOG_HELP(str);
1287 string get_model_datafilename(string m, float sk, string fil)
1292 m = "models/player/*_";
1294 m = strcat(m, ftos(sk));
1297 return strcat(m, ".", fil);
1300 float get_model_parameters(string m, float sk)
1302 get_model_parameters_modelname = string_null;
1303 get_model_parameters_modelskin = -1;
1304 get_model_parameters_name = string_null;
1305 get_model_parameters_species = -1;
1306 get_model_parameters_sex = string_null;
1307 get_model_parameters_weight = -1;
1308 get_model_parameters_age = -1;
1309 get_model_parameters_desc = string_null;
1310 get_model_parameters_bone_upperbody = string_null;
1311 get_model_parameters_bone_weapon = string_null;
1312 for(int i = 0; i < MAX_AIM_BONES; ++i)
1314 get_model_parameters_bone_aim[i] = string_null;
1315 get_model_parameters_bone_aimweight[i] = 0;
1317 get_model_parameters_fixbone = 0;
1318 get_model_parameters_hidden = false;
1321 MUTATOR_CALLHOOK(ClearModelParams);
1327 if(substring(m, -9, 5) == "_lod1" || substring(m, -9, 5) == "_lod2")
1328 m = strcat(substring(m, 0, -10), substring(m, -4, -1));
1332 if(substring(m, -4, -1) != ".txt")
1334 if(substring(m, -6, 1) != "_")
1336 sk = stof(substring(m, -5, 1));
1337 m = substring(m, 0, -7);
1340 string fn = get_model_datafilename(m, sk, "txt");
1341 int fh = fopen(fn, FILE_READ);
1345 fn = get_model_datafilename(m, sk, "txt");
1346 fh = fopen(fn, FILE_READ);
1351 get_model_parameters_modelname = m;
1352 get_model_parameters_modelskin = sk;
1354 while((s = fgets(fh)))
1357 break; // next lines will be description
1361 get_model_parameters_name = s;
1365 case "human": get_model_parameters_species = SPECIES_HUMAN; break;
1366 case "alien": get_model_parameters_species = SPECIES_ALIEN; break;
1367 case "robot_shiny": get_model_parameters_species = SPECIES_ROBOT_SHINY; break;
1368 case "robot_rusty": get_model_parameters_species = SPECIES_ROBOT_RUSTY; break;
1369 case "robot_solid": get_model_parameters_species = SPECIES_ROBOT_SOLID; break;
1370 case "animal": get_model_parameters_species = SPECIES_ANIMAL; break;
1371 case "reserved": get_model_parameters_species = SPECIES_RESERVED; break;
1375 if (s == "Male") s = _("Male");
1376 else if (s == "Female") s = _("Female");
1377 else if (s == "Undisclosed") s = _("Undisclosed");
1378 get_model_parameters_sex = s;
1381 get_model_parameters_weight = stof(s);
1383 get_model_parameters_age = stof(s);
1384 if(c == "description")
1385 get_model_parameters_description = s;
1386 if(c == "bone_upperbody")
1387 get_model_parameters_bone_upperbody = s;
1388 if(c == "bone_weapon")
1389 get_model_parameters_bone_weapon = s;
1391 MUTATOR_CALLHOOK(GetModelParams, c, s);
1393 for(int i = 0; i < MAX_AIM_BONES; ++i)
1394 if(c == strcat("bone_aim", ftos(i)))
1396 get_model_parameters_bone_aimweight[i] = stof(car(s));
1397 get_model_parameters_bone_aim[i] = cdr(s);
1400 get_model_parameters_fixbone = stof(s);
1402 get_model_parameters_hidden = stob(s);
1405 while((s = fgets(fh)))
1407 if(get_model_parameters_desc)
1408 get_model_parameters_desc = strcat(get_model_parameters_desc, "\n");
1410 get_model_parameters_desc = strcat(get_model_parameters_desc, s);
1418 string translate_key(string key)
1420 if (prvm_language == "en") return key;
1422 if (substring(key, 0, 1) == "<")
1424 if (key == "<KEY NOT FOUND>") return _("<KEY NOT FOUND>");
1425 if (key == "<UNKNOWN KEYNUM>") return _("<UNKNOWN KEYNUM>");
1430 case "TAB": return _("TAB");
1431 case "ENTER": return _("ENTER");
1432 case "ESCAPE": return _("ESCAPE");
1433 case "SPACE": return _("SPACE");
1435 case "BACKSPACE": return _("BACKSPACE");
1436 case "UPARROW": return _("UPARROW");
1437 case "DOWNARROW": return _("DOWNARROW");
1438 case "LEFTARROW": return _("LEFTARROW");
1439 case "RIGHTARROW": return _("RIGHTARROW");
1441 case "ALT": return _("ALT");
1442 case "CTRL": return _("CTRL");
1443 case "SHIFT": return _("SHIFT");
1445 case "INS": return _("INS");
1446 case "DEL": return _("DEL");
1447 case "PGDN": return _("PGDN");
1448 case "PGUP": return _("PGUP");
1449 case "HOME": return _("HOME");
1450 case "END": return _("END");
1452 case "PAUSE": return _("PAUSE");
1454 case "NUMLOCK": return _("NUMLOCK");
1455 case "CAPSLOCK": return _("CAPSLOCK");
1456 case "SCROLLOCK": return _("SCROLLOCK");
1458 case "SEMICOLON": return _("SEMICOLON");
1459 case "TILDE": return _("TILDE");
1460 case "BACKQUOTE": return _("BACKQUOTE");
1461 case "QUOTE": return _("QUOTE");
1462 case "APOSTROPHE": return _("APOSTROPHE");
1463 case "BACKSLASH": return _("BACKSLASH");
1466 if (substring(key, 0, 1) == "F")
1468 string subkey = substring(key, 1, -1);
1469 if (IS_DIGIT(substring(key, 3, 1))) // check only first digit
1471 return sprintf(_("F%d"), stof(subkey));
1473 // continue in case there is another key name starting with F
1476 if (substring(key, 0, 3) == "KP_")
1478 string subkey = substring(key, 3, -1);
1479 if (IS_DIGIT(substring(key, 3, 1))) // check only first digit
1481 return sprintf(_("KP_%d"), stof(subkey));
1486 case "INS": return sprintf(_("KP_%s"), _("INS"));
1487 case "END": return sprintf(_("KP_%s"), _("END"));
1488 case "DOWNARROW": return sprintf(_("KP_%s"), _("DOWNARROW"));
1489 case "PGDN": return sprintf(_("KP_%s"), _("PGDN"));
1490 case "LEFTARROW": return sprintf(_("KP_%s"), _("LEFTARROW"));
1491 case "RIGHTARROW": return sprintf(_("KP_%s"), _("RIGHTARROW"));
1492 case "HOME": return sprintf(_("KP_%s"), _("HOME"));
1493 case "UPARROW": return sprintf(_("KP_%s"), _("UPARROW"));
1494 case "PGUP": return sprintf(_("KP_%s"), _("PGUP"));
1495 case "PERIOD": return sprintf(_("KP_%s"), _("PERIOD"));
1496 case "DEL": return sprintf(_("KP_%s"), _("DEL"));
1497 case "DIVIDE": return sprintf(_("KP_%s"), _("DIVIDE"));
1498 case "SLASH": return sprintf(_("KP_%s"), _("SLASH"));
1499 case "MULTIPLY": return sprintf(_("KP_%s"), _("MULTIPLY"));
1500 case "MINUS": return sprintf(_("KP_%s"), _("MINUS"));
1501 case "PLUS": return sprintf(_("KP_%s"), _("PLUS"));
1502 case "ENTER": return sprintf(_("KP_%s"), _("ENTER"));
1503 case "EQUALS": return sprintf(_("KP_%s"), _("EQUALS"));
1504 default: return key;
1508 if (key == "PRINTSCREEN") return _("PRINTSCREEN");
1510 if (substring(key, 0, 5) == "MOUSE")
1511 return sprintf(_("MOUSE%d"), stof(substring(key, 5, -1)));
1513 if (key == "MWHEELUP") return _("MWHEELUP");
1514 if (key == "MWHEELDOWN") return _("MWHEELDOWN");
1516 if (substring(key, 0,3) == "JOY")
1517 return sprintf(_("JOY%d"), stof(substring(key, 3, -1)));
1519 if (substring(key, 0,3) == "AUX")
1520 return sprintf(_("AUX%d"), stof(substring(key, 3, -1)));
1522 if (substring(key, 0, 4) == "X360_")
1524 string subkey = substring(key, 4, -1);
1527 case "DPAD_UP": return sprintf(_("X360_%s"), _("DPAD_UP"));
1528 case "DPAD_DOWN": return sprintf(_("X360_%s"), _("DPAD_DOWN"));
1529 case "DPAD_LEFT": return sprintf(_("X360_%s"), _("DPAD_LEFT"));
1530 case "DPAD_RIGHT": return sprintf(_("X360_%s"), _("DPAD_RIGHT"));
1531 case "START": return sprintf(_("X360_%s"), _("START"));
1532 case "BACK": return sprintf(_("X360_%s"), _("BACK"));
1533 case "LEFT_THUMB": return sprintf(_("X360_%s"), _("LEFT_THUMB"));
1534 case "RIGHT_THUMB": return sprintf(_("X360_%s"), _("RIGHT_THUMB"));
1535 case "LEFT_SHOULDER": return sprintf(_("X360_%s"), _("LEFT_SHOULDER"));
1536 case "RIGHT_SHOULDER": return sprintf(_("X360_%s"), _("RIGHT_SHOULDER"));
1537 case "LEFT_TRIGGER": return sprintf(_("X360_%s"), _("LEFT_TRIGGER"));
1538 case "RIGHT_TRIGGER": return sprintf(_("X360_%s"), _("RIGHT_TRIGGER"));
1539 case "LEFT_THUMB_UP": return sprintf(_("X360_%s"), _("LEFT_THUMB_UP"));
1540 case "LEFT_THUMB_DOWN": return sprintf(_("X360_%s"), _("LEFT_THUMB_DOWN"));
1541 case "LEFT_THUMB_LEFT": return sprintf(_("X360_%s"), _("LEFT_THUMB_LEFT"));
1542 case "LEFT_THUMB_RIGHT": return sprintf(_("X360_%s"), _("LEFT_THUMB_RIGHT"));
1543 case "RIGHT_THUMB_UP": return sprintf(_("X360_%s"), _("RIGHT_THUMB_UP"));
1544 case "RIGHT_THUMB_DOWN": return sprintf(_("X360_%s"), _("RIGHT_THUMB_DOWN"));
1545 case "RIGHT_THUMB_LEFT": return sprintf(_("X360_%s"), _("RIGHT_THUMB_LEFT"));
1546 case "RIGHT_THUMB_RIGHT": return sprintf(_("X360_%s"), _("RIGHT_THUMB_RIGHT"));
1547 default: return key;
1551 if (substring(key, 0, 4) == "JOY_")
1553 string subkey = substring(key, 4, -1);
1556 case "UP": return sprintf(_("JOY_%s"), _("UP"));
1557 case "DOWN": return sprintf(_("JOY_%s"), _("DOWN"));
1558 case "LEFT": return sprintf(_("JOY_%s"), _("LEFT"));
1559 case "RIGHT": return sprintf(_("JOY_%s"), _("RIGHT"));
1560 default: return key;
1564 if (substring(key, 0, 8) == "MIDINOTE")
1565 return sprintf(_("MIDINOTE%d"), stof(substring(key, 8, -1)));
1570 // x-encoding (encoding as zero length invisible string)
1571 const string XENCODE_2 = "xX";
1572 const string XENCODE_22 = "0123456789abcdefABCDEF";
1573 string xencode(int f)
1576 d = f % 22; f = floor(f / 22);
1577 c = f % 22; f = floor(f / 22);
1578 b = f % 22; f = floor(f / 22);
1579 a = f % 2; // f = floor(f / 2);
1582 substring(XENCODE_2, a, 1),
1583 substring(XENCODE_22, b, 1),
1584 substring(XENCODE_22, c, 1),
1585 substring(XENCODE_22, d, 1)
1588 float xdecode(string s)
1591 if(substring(s, 0, 1) != "^")
1595 a = strstrofs(XENCODE_2, substring(s, 1, 1), 0);
1596 b = strstrofs(XENCODE_22, substring(s, 2, 1), 0);
1597 c = strstrofs(XENCODE_22, substring(s, 3, 1), 0);
1598 d = strstrofs(XENCODE_22, substring(s, 4, 1), 0);
1599 if(a < 0 || b < 0 || c < 0 || d < 0)
1601 return ((a * 22 + b) * 22 + c) * 22 + d;
1605 string strlimitedlen(string input, string truncation, float strip_colors, float limit)
1607 if(strlen((strip_colors ? strdecolorize(input) : input)) <= limit)
1610 return strcat(substring(input, 0, (strlen(input) - strlen(truncation))), truncation);
1613 float shutdown_running;
1618 void CSQC_Shutdown()
1624 if(shutdown_running)
1626 LOG_INFO("Recursive shutdown detected! Only restoring cvars...");
1630 shutdown_running = 1;
1634 cvar_settemp_restore(); // this must be done LAST, but in any case
1638 .float skeleton_bones_index;
1639 void Skeleton_SetBones(entity e)
1641 // set skeleton_bones to the total number of bones on the model
1642 if(e.skeleton_bones_index == e.modelindex)
1643 return; // same model, nothing to update
1646 skelindex = skel_create(e.modelindex);
1647 e.skeleton_bones = skel_get_numbones(skelindex);
1648 skel_delete(skelindex);
1649 e.skeleton_bones_index = e.modelindex;
1653 string to_execute_next_frame;
1654 void execute_next_frame()
1656 if(to_execute_next_frame)
1658 localcmd("\n", to_execute_next_frame, "\n");
1659 strfree(to_execute_next_frame);
1662 void queue_to_execute_next_frame(string s)
1664 if(to_execute_next_frame)
1666 s = strcat(s, "\n", to_execute_next_frame);
1668 strcpy(to_execute_next_frame, s);
1671 .float FindConnectedComponent_processing;
1672 void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass)
1674 entity queue_start, queue_end;
1676 // we build a queue of to-be-processed entities.
1677 // queue_start is the next entity to be checked for neighbors
1678 // queue_end is the last entity added
1680 if(e.FindConnectedComponent_processing)
1681 error("recursion or broken cleanup");
1683 // start with a 1-element queue
1684 queue_start = queue_end = e;
1685 queue_end.(fld) = NULL;
1686 queue_end.FindConnectedComponent_processing = 1;
1688 // for each queued item:
1689 for (; queue_start; queue_start = queue_start.(fld))
1691 // find all neighbors of queue_start
1693 for(t = NULL; (t = nxt(t, queue_start, pass)); )
1695 if(t.FindConnectedComponent_processing)
1697 if(iscon(t, queue_start, pass))
1699 // it is connected? ADD IT. It will look for neighbors soon too.
1700 queue_end.(fld) = t;
1702 queue_end.(fld) = NULL;
1703 queue_end.FindConnectedComponent_processing = 1;
1709 for (queue_start = e; queue_start; queue_start = queue_start.(fld))
1710 queue_start.FindConnectedComponent_processing = 0;
1714 vector animfixfps(entity e, vector a, vector b)
1716 // multi-frame anim: keep as-is
1719 float dur = frameduration(e.modelindex, a.x);
1720 if (dur <= 0 && b.y)
1723 dur = frameduration(e.modelindex, a.x);
1733 Notification Announcer_PickNumber(int type, int num)
1742 case 10: return ANNCE_NUM_GAMESTART_10;
1743 case 9: return ANNCE_NUM_GAMESTART_9;
1744 case 8: return ANNCE_NUM_GAMESTART_8;
1745 case 7: return ANNCE_NUM_GAMESTART_7;
1746 case 6: return ANNCE_NUM_GAMESTART_6;
1747 case 5: return ANNCE_NUM_GAMESTART_5;
1748 case 4: return ANNCE_NUM_GAMESTART_4;
1749 case 3: return ANNCE_NUM_GAMESTART_3;
1750 case 2: return ANNCE_NUM_GAMESTART_2;
1751 case 1: return ANNCE_NUM_GAMESTART_1;
1759 case 10: return ANNCE_NUM_KILL_10;
1760 case 9: return ANNCE_NUM_KILL_9;
1761 case 8: return ANNCE_NUM_KILL_8;
1762 case 7: return ANNCE_NUM_KILL_7;
1763 case 6: return ANNCE_NUM_KILL_6;
1764 case 5: return ANNCE_NUM_KILL_5;
1765 case 4: return ANNCE_NUM_KILL_4;
1766 case 3: return ANNCE_NUM_KILL_3;
1767 case 2: return ANNCE_NUM_KILL_2;
1768 case 1: return ANNCE_NUM_KILL_1;
1776 case 10: return ANNCE_NUM_RESPAWN_10;
1777 case 9: return ANNCE_NUM_RESPAWN_9;
1778 case 8: return ANNCE_NUM_RESPAWN_8;
1779 case 7: return ANNCE_NUM_RESPAWN_7;
1780 case 6: return ANNCE_NUM_RESPAWN_6;
1781 case 5: return ANNCE_NUM_RESPAWN_5;
1782 case 4: return ANNCE_NUM_RESPAWN_4;
1783 case 3: return ANNCE_NUM_RESPAWN_3;
1784 case 2: return ANNCE_NUM_RESPAWN_2;
1785 case 1: return ANNCE_NUM_RESPAWN_1;
1789 case CNT_ROUNDSTART:
1793 case 10: return ANNCE_NUM_ROUNDSTART_10;
1794 case 9: return ANNCE_NUM_ROUNDSTART_9;
1795 case 8: return ANNCE_NUM_ROUNDSTART_8;
1796 case 7: return ANNCE_NUM_ROUNDSTART_7;
1797 case 6: return ANNCE_NUM_ROUNDSTART_6;
1798 case 5: return ANNCE_NUM_ROUNDSTART_5;
1799 case 4: return ANNCE_NUM_ROUNDSTART_4;
1800 case 3: return ANNCE_NUM_ROUNDSTART_3;
1801 case 2: return ANNCE_NUM_ROUNDSTART_2;
1802 case 1: return ANNCE_NUM_ROUNDSTART_1;
1811 case 10: return ANNCE_NUM_10;
1812 case 9: return ANNCE_NUM_9;
1813 case 8: return ANNCE_NUM_8;
1814 case 7: return ANNCE_NUM_7;
1815 case 6: return ANNCE_NUM_6;
1816 case 5: return ANNCE_NUM_5;
1817 case 4: return ANNCE_NUM_4;
1818 case 3: return ANNCE_NUM_3;
1819 case 2: return ANNCE_NUM_2;
1820 case 1: return ANNCE_NUM_1;
1829 int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents)
1831 switch(nativecontents)
1836 return DPCONTENTS_SOLID | DPCONTENTS_OPAQUE;
1838 return DPCONTENTS_WATER;
1840 return DPCONTENTS_SLIME;
1842 return DPCONTENTS_LAVA | DPCONTENTS_NODROP;
1844 return DPCONTENTS_SKY | DPCONTENTS_NODROP | DPCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
1849 int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents)
1851 if(supercontents & (DPCONTENTS_SOLID | DPCONTENTS_BODY))
1852 return CONTENT_SOLID;
1853 if(supercontents & DPCONTENTS_SKY)
1855 if(supercontents & DPCONTENTS_LAVA)
1856 return CONTENT_LAVA;
1857 if(supercontents & DPCONTENTS_SLIME)
1858 return CONTENT_SLIME;
1859 if(supercontents & DPCONTENTS_WATER)
1860 return CONTENT_WATER;
1861 return CONTENT_EMPTY;
1866 void attach_sameorigin(entity e, entity to, string tag)
1868 vector org, t_forward, t_left, t_up, e_forward, e_up;
1871 org = e.origin - gettaginfo(to, gettagindex(to, tag));
1872 tagscale = (vlen(v_forward) ** -2); // undo a scale on the tag
1873 t_forward = v_forward * tagscale;
1874 t_left = v_right * -tagscale;
1875 t_up = v_up * tagscale;
1877 e.origin_x = org * t_forward;
1878 e.origin_y = org * t_left;
1879 e.origin_z = org * t_up;
1881 // current forward and up directions
1882 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1883 e.angles = AnglesTransform_FromVAngles(e.angles);
1885 e.angles = AnglesTransform_FromAngles(e.angles);
1886 fixedmakevectors(e.angles);
1888 // untransform forward, up!
1889 e_forward.x = v_forward * t_forward;
1890 e_forward.y = v_forward * t_left;
1891 e_forward.z = v_forward * t_up;
1892 e_up.x = v_up * t_forward;
1893 e_up.y = v_up * t_left;
1894 e_up.z = v_up * t_up;
1896 e.angles = fixedvectoangles2(e_forward, e_up);
1897 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1898 e.angles = AnglesTransform_ToVAngles(e.angles);
1900 e.angles = AnglesTransform_ToAngles(e.angles);
1902 setattachment(e, to, tag);
1903 setorigin(e, e.origin);
1906 void detach_sameorigin(entity e)
1909 org = gettaginfo(e, 0);
1910 e.angles = fixedvectoangles2(v_forward, v_up);
1911 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
1912 e.angles = AnglesTransform_ToVAngles(e.angles);
1914 e.angles = AnglesTransform_ToAngles(e.angles);
1916 setattachment(e, NULL, "");
1917 setorigin(e, e.origin);
1920 void follow_sameorigin(entity e, entity to)
1922 set_movetype(e, MOVETYPE_FOLLOW); // make the hole follow
1923 e.aiment = to; // make the hole follow bmodel
1924 e.punchangle = to.angles; // the original angles of bmodel
1925 e.view_ofs = e.origin - to.origin; // relative origin
1926 e.v_angle = e.angles - to.angles; // relative angles
1930 // TODO: unused, likely for a reason, possibly needs extensions (allow setting the new movetype as a parameter?)
1931 void unfollow_sameorigin(entity e)
1933 set_movetype(e, MOVETYPE_NONE);
1937 .string aiment_classname;
1938 .float aiment_deadflag;
1939 void SetMovetypeFollow(entity ent, entity e)
1941 // FIXME this may not be warpzone aware
1942 set_movetype(ent, MOVETYPE_FOLLOW); // make the hole follow
1943 ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported.
1944 ent.aiment = e; // make the hole follow bmodel
1945 ent.punchangle = e.angles; // the original angles of bmodel
1946 ent.view_ofs = ent.origin - e.origin; // relative origin
1947 ent.v_angle = ent.angles - e.angles; // relative angles
1948 ent.aiment_classname = strzone(e.classname);
1949 ent.aiment_deadflag = e.deadflag;
1951 if(IS_PLAYER(ent.aiment))
1953 entity pl = ent.aiment;
1954 ent.view_ofs.x = bound(pl.mins.x + 4, ent.view_ofs.x, pl.maxs.x - 4);
1955 ent.view_ofs.y = bound(pl.mins.y + 4, ent.view_ofs.y, pl.maxs.y - 4);
1956 ent.view_ofs.z = bound(pl.mins.z + 4, ent.view_ofs.z, pl.maxs.z - 4);
1960 void UnsetMovetypeFollow(entity ent)
1962 set_movetype(ent, MOVETYPE_FLY);
1963 PROJECTILE_MAKETRIGGER(ent);
1964 if (ent.aiment_classname)
1965 strunzone(ent.classname);
1966 // FIXME: engine bug?
1967 // resetting aiment the engine will set orb's origin close to world's origin
1968 //ent.aiment = NULL;
1971 int LostMovetypeFollow(entity ent)
1974 if(ent.move_movetype != MOVETYPE_FOLLOW)
1978 // FIXME: engine bug?
1979 // when aiment disconnects the engine will set orb's origin close to world's origin
1982 if(ent.aiment.classname != ent.aiment_classname || ent.aiment.deadflag != ent.aiment_deadflag)
1989 // decolorizes and team colors the player name when needed
1990 string playername(string thename, int teamid, bool team_colorize)
1993 bool do_colorize = (teamplay && team_colorize);
1995 if(do_colorize && !intermission_running)
2000 string t = Team_ColorCode(teamid);
2001 return strcat(t, strdecolorize(thename));
2007 float trace_hits_box_a0, trace_hits_box_a1;
2009 float trace_hits_box_1d(float end, float thmi, float thma)
2013 // just check if x is in range
2021 // do the trace with respect to x
2022 // 0 -> end has to stay in thmi -> thma
2023 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
2024 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
2025 if (trace_hits_box_a0 > trace_hits_box_a1)
2031 float trace_hits_box(vector start, vector end, vector thmi, vector thma)
2036 // now it is a trace from 0 to end
2038 trace_hits_box_a0 = 0;
2039 trace_hits_box_a1 = 1;
2041 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
2043 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
2045 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
2051 float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
2053 return trace_hits_box(start, end, thmi - ma, thma - mi);
2058 float cvar_or(string cv, float v)
2060 string s = cvar_string(cv);
2067 // NOTE base is the central value
2068 // freq: circle frequency, = 2*pi*frequency in hertz
2070 // -1 start from the lower value
2071 // 0 start from the base value
2072 // 1 start from the higher value
2074 float blink_synced(float base, float range, float freq, float start_time, int start_pos)
2077 // RMS = sqrt(base^2 + 0.5 * range^2)
2079 // base = sqrt(RMS^2 - 0.5 * range^2)
2082 return base + range * sin((time - start_time - (M_PI / 2) * start_pos) * freq);
2086 float blink(float base, float range, float freq)
2088 return blink_synced(base, range, freq, 0, 0);