Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
util.qc
Go to the documentation of this file.
1#include "util.qh"
2
3#if defined(CSQC)
5 #include <common/constants.qh>
8 #include <common/mapinfo.qh>
10 #include <common/scores.qh>
11#elif defined(MENUQC)
12#elif defined(SVQC)
13 #include <common/constants.qh>
16 #include <common/mapinfo.qh>
18 #include <common/scores.qh>
20#endif
21
22#ifdef SVQC
23float 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
24{
25 vector pos, dir, t;
26 float nudge;
27 entity stopentity;
28
29 //nudge = 2 * cvar("collision_impactnudge"); // why not?
30 nudge = 0.5;
31
32 dir = normalize(v2 - v1);
33
34 pos = v1 + dir * nudge;
35
36 float c;
37 c = 0;
38
39 for (;;)
40 {
41 if(pos * dir >= v2 * dir)
42 {
43 // went too far
45 trace_endpos = v2;
46 return c;
47 }
48
49 tracebox(pos, mi, ma, v2, nomonsters, forent);
50 ++c;
51
52 if(c == 50)
53 {
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)));
58 }
59
60 stopentity = trace_ent;
61
63 {
64 // we started inside solid.
65 // then trace from endpos to pos
66 t = trace_endpos;
67 tracebox(t, mi, ma, pos, nomonsters, forent);
68 ++c;
70 {
71 // t is still inside solid? bad
72 // force advance, then, and retry
73 pos = t + dir * nudge;
74
75 // but if we hit an entity, stop RIGHT before it
76 if(stopatentity && stopentity && stopentity != ignorestopatentity)
77 {
78 trace_ent = stopentity;
79 trace_endpos = t;
80 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
81 return c;
82 }
83 }
84 else
85 {
86 // we actually LEFT solid!
87 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
88 return c;
89 }
90 }
91 else
92 {
93 // pos is outside solid?!? but why?!? never mind, just return it.
94 trace_endpos = pos;
95 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
96 return c;
97 }
98 }
99}
100
101void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity)
102{
103 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent, stopatentity, ignorestopatentity);
104}
105#endif
106
107#ifdef GAMEQC
108/*
109==================
110findbetterlocation
111
112Returns a point at least 12 units away from walls
113(useful for explosion animations, although the blast is performed where it really happened)
114Ripped from DPMod
115==================
116*/
118{
119 vector vec = mindist * '1 0 0';
120 int c = 0;
121 while (c < 6)
122 {
123 traceline (org, org + vec, true, NULL);
124 vec = vec * -1;
125 if (trace_fraction < 1)
126 {
127 vector loc = trace_endpos;
128 traceline (loc, loc + vec, true, NULL);
129 if (trace_fraction >= 1)
130 org = loc + vec;
131 }
132 if (c & 1)
133 {
134 float h = vec.y;
135 vec.y = vec.x;
136 vec.x = vec.z;
137 vec.z = h;
138 }
139 c = c + 1;
140 }
141
142 return org;
143}
144
145/*
146* Get "real" origin, in worldspace, even if ent is attached to something else.
147*/
149{
150 vector v = ((ent.absmin + ent.absmax) * 0.5);
151 entity e = ent.tag_entity;
152
153 while(e)
154 {
155 v = v + ((e.absmin + e.absmax) * 0.5);
156 e = e.tag_entity;
157 }
158
159 return v;
160}
161#endif
162
164
166{
168}
169
170string wordwrap(string s, float l)
171{
172 string r;
173 wordwrap_buffer = "";
175 r = wordwrap_buffer;
176 wordwrap_buffer = "";
177 return r;
178}
179
180#ifdef SVQC
183{
185 if(s == "\n")
186 {
188 wordwrap_buffer = "";
189 }
190}
191
192void wordwrap_sprint(entity to, string s, float l)
193{
194 wordwrap_buffer = "";
198 if(wordwrap_buffer != "")
199 sprint(to, strcat(wordwrap_buffer, "\n"));
200 wordwrap_buffer = "";
201 return;
202}
203#endif
204
205#ifndef SVQC
206string draw_UseSkinFor(string pic)
207{
208 if(substring(pic, 0, 1) == "/")
209 return substring(pic, 1, strlen(pic)-1);
210 else
211 return strcat(draw_currentSkin, "/", pic);
212}
213
214#ifdef CSQC
215 #define PRECACHE_PIC precache_pic
216 #define SKIN_PATH hud_skin_path
217#endif
218#ifdef MENUQC
219 #define PRECACHE_PIC draw_PreloadPicture
220 #define SKIN_PATH (strreplace("gfx/menu", "gfx/hud", draw_currentSkin))
221#endif
222
225string icon_path_from_HUDskin(string theIcon)
226{
227 if (theIcon == "")
228 return "";
229
230 string img = strcat("/", SKIN_PATH, "/", theIcon);
231 if (PRECACHE_PIC(img) == "")
232 img = sprintf("/gfx/hud/default/%s", theIcon);
233 return img;
234}
235
236#ifdef MENUQC
238string icon_path_from_menuskin(string theIcon)
239{
240 if (theIcon == "")
241 return "";
242
243 string img = strcat("/", draw_currentSkin, "/", theIcon);
244 if (draw_PreloadPicture(img) == "")
245 img = sprintf("/gfx/menu/wickedx/%s", theIcon); // default menu skin
246 return img;
247}
248#endif
249
250#undef PRECACHE_PIC
251#undef SKIN_PATH
252
253
254void mut_set_active(int mut)
255{
256 if (mut >= 24)
257 active_mutators[1] |= BIT(mut - 24);
258 else
259 active_mutators[0] |= BIT(mut);
260}
261
262bool mut_is_active(int mut)
263{
264 if (mut >= 24)
265 return (active_mutators[1] & (BIT(mut - 24)));
266 else
267 return (active_mutators[0] & BIT(mut));
268}
269
270// MENUQC: it builds the mutator list based on local cvars (for the Mutators dialog)
271// CSQC: it translates the mutator list received from server (for the Welcome dialog)
272// MENUQC and CSQC code is merged in order to avoid duplicating and separating strings
273string build_mutator_list(string s)
274{
275 #ifdef CSQC
276 if (s == "") return "";
277 #endif
278
279 #ifdef MENUQC
280 if (s != "") s = "";
281 #endif
282
283 int i = -1, n = 0; // allow only 1 iteration in the following for loop if (s == "")
284 if (s != "")
285 {
286 i = 0;
287 n = tokenizebyseparator(s, ", ");
288 }
289 string s2 = "";
290 for (string arg = ""; i < n; ++i)
291 {
292 if (i >= 0) arg = argv(i);
293 // cond is the condition for showing the mutator enabled in the menu
294 #define X(name, translated_name, mut, cond) \
295 if(arg == name || (!n && (cond))) { s2 = cons_mid(s2, ", ", translated_name); mut_set_active(mut); }
296 X("Dodging" , _("Dodging") , MUT_DODGING , cvar("g_dodging"))
297 X("InstaGib" , _("InstaGib") , MUT_INSTAGIB , cvar("g_instagib"))
298 X("New Toys" , _("New Toys") , MUT_NEW_TOYS , cvar("g_new_toys"))
299 X("NIX" , _("NIX") , MUT_NIX , cvar("g_nix"))
300 X("Rocket Flying" , _("Rocket Flying") , MUT_ROCKET_FLYING , cvar("g_rocket_flying"))
301 X("Invincible Projectiles" , _("Invincible Projectiles") , MUT_INVINCIBLE_PROJECTILES , cvar("g_invincible_projectiles"))
302 X("Low gravity" , _("Low gravity") , MUT_GRAVITY , cvar("sv_gravity") < stof(cvar_defstring("sv_gravity")))
303 X("Cloaked" , _("Cloaked") , MUT_CLOAKED , cvar("g_cloaked"))
304 X("Hook" , _("Hook") , MUT_GRAPPLING_HOOK , cvar("g_grappling_hook"))
305 X("Midair" , _("Midair") , MUT_MIDAIR , cvar("g_midair"))
306 X("Melee only Arena" , _("Melee only Arena") , MUT_MELEE_ONLY , cvar("g_melee_only"))
307 X("Vampire" , _("Vampire") , MUT_VAMPIRE , cvar("g_vampire"))
308 X("Piñata" , _("Piñata") , MUT_PINATA , cvar("g_pinata"))
309 X("Weapons stay" , _("Weapons stay") , MUT_WEAPON_STAY , cvar("g_weapon_stay"))
310 X("Blood loss" , _("Blood loss") , MUT_BLOODLOSS , cvar("g_bloodloss") > 0)
311 X("Jetpack" , _("Jetpack") , MUT_JETPACK , cvar("g_jetpack"))
312 X("Buffs" , _("Buffs") , MUT_BUFFS , cvar("g_buffs") > 0)
313 X("Overkill" , _("Overkill") , MUT_OVERKILL , cvar("g_overkill"))
314 X("No powerups" , _("No powerups") , MUT_NO_POWERUPS , cvar("g_powerups") == 0)
315 X("Powerups" , _("Powerups") , MUT_POWERUPS , cvar("g_powerups") > 0)
316 X("Touch explode" , _("Touch explode") , MUT_TOUCHEXPLODE , cvar("g_touchexplode") > 0)
317 X("Wall jumping" , _("Wall jumping") , MUT_WALLJUMP , cvar("g_walljump"))
318 X("No start weapons" , _("No start weapons") , MUT_NO_START_WEAPONS , cvar_string("g_weaponarena") == "0" && cvar("g_balance_blaster_weaponstartoverride") == 0)
319 X("Nades" , _("Nades") , MUT_NADES , cvar("g_nades"))
320 X("Offhand blaster" , _("Offhand blaster") , MUT_OFFHAND_BLASTER , cvar("g_offhand_blaster"))
321 #undef X
322 }
323 return s2;
324}
325#endif
326
327void wordwrap_cb(string s, float l, void(string) callback)
328{
329 string c;
330 float lleft, i, j, wlen;
331
332 s = strzone(s);
333 lleft = l;
334 int len = strlen(s);
335 for (i = 0; i < len; ++i)
336 {
337 if (substring(s, i, 2) == "\\n")
338 {
339 callback("\n");
340 lleft = l;
341 ++i;
342 }
343 else if (substring(s, i, 1) == "\n")
344 {
345 callback("\n");
346 lleft = l;
347 }
348 else if (substring(s, i, 1) == " ")
349 {
350 if (lleft > 0)
351 {
352 callback(" ");
353 --lleft;
354 }
355 }
356 else
357 {
358 for (j = i+1; j < len; ++j)
359 // ^^ this skips over the first character of a word, which
360 // is ALWAYS part of the word
361 // this is safe since if i+1 == strlen(s), i will become
362 // strlen(s)-1 at the end of this block and the function
363 // will terminate. A space can't be the first character we
364 // read here, and neither can a \n be the start, since these
365 // two cases have been handled above.
366 {
367 c = substring(s, j, 1);
368 if (c == " ")
369 break;
370 if (c == "\\")
371 break;
372 if (c == "\n")
373 break;
374 // we need to keep this tempstring alive even if substring is
375 // called repeatedly, so call strcat even though we're not
376 // doing anything
377 callback("");
378 }
379 wlen = j - i;
380 if (lleft < wlen)
381 {
382 callback("\n");
383 lleft = l;
384 }
385 callback(substring(s, i, wlen));
386 lleft -= wlen;
387 i = j - 1;
388 }
389 }
390 strunzone(s);
391}
392
393void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
394{
395 entity e;
396 e = start;
397 funcPre(pass, e);
398 while (e.(downleft))
399 {
400 e = e.(downleft);
401 funcPre(pass, e);
402 }
403 funcPost(pass, e);
404 while(e != start)
405 {
406 if (e.(right))
407 {
408 e = e.(right);
409 funcPre(pass, e);
410 while (e.(downleft))
411 {
412 e = e.(downleft);
413 funcPre(pass, e);
414 }
415 }
416 else
417 e = e.(up);
418 funcPost(pass, e);
419 }
420}
421
422#ifdef GAMEQC
423string ScoreString(int pFlags, float pValue, int rounds_played)
424{
425 string valstr;
426 float l;
427
428 pValue = floor(pValue + 0.5); // round
429
430 if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME)))
431 valstr = "";
432 else if(pFlags & SFL_RANK)
433 valstr = (pValue < 256 ? count_ordinal(pValue) : _("N/A"));
434 else if(pFlags & SFL_TIME)
435 valstr = TIME_ENCODED_TOSTRING(pValue, true);
436 else if (rounds_played)
437 valstr = sprintf("%.1f", pValue / rounds_played);
438 else
439 valstr = ftos(pValue);
440
441 return valstr;
442}
443#endif
444
445// compressed vector format:
446// like MD3, just even shorter
447// 4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90
448// 5 bit yaw (32 angles), 0=0, 8=90, 16=180, 24=270
449// 7 bit length (logarithmic encoding), 1/8 .. about 7844
450// length = 2^(length_encoded/8) / 8
451// if pitch is 90, yaw does nothing and therefore indicates the sign (yaw is then either 11111 or 11110); 11111 is pointing DOWN
452// thus, valid values are from 0000.11110.0000000 to 1111.11111.1111111
453// the special value 0 indicates the zero vector
454
455float lengthLogTable[128];
456
457float invertLengthLog(float dist)
458{
459 int l, r, m;
460
461 if(dist >= lengthLogTable[127])
462 return 127;
463 if(dist <= lengthLogTable[0])
464 return 0;
465
466 l = 0;
467 r = 127;
468
469 while(r - l > 1)
470 {
471 m = floor((l + r) / 2);
472 if(lengthLogTable[m] < dist)
473 l = m;
474 else
475 r = m;
476 }
477
478 // now: r is >=, l is <
479 float lerr = (dist - lengthLogTable[l]);
480 float rerr = (lengthLogTable[r] - dist);
481 if(lerr < rerr)
482 return l;
483 return r;
484}
485
487{
488 vector out;
489 if(data == 0)
490 return '0 0 0';
491 float p = (data & 0xF000) / 0x1000;
492 float q = (data & 0x0F80) / 0x80;
493 int len = (data & 0x007F);
494
495 //print("\ndecompress: p:", ftos(p)); print(" q:", ftos(q)); print(" len:", ftos(len), "\n");
496
497 if(p == 0)
498 {
499 out.x = 0;
500 out.y = 0;
501 if(q == 31)
502 out.z = -1;
503 else
504 out.z = +1;
505 }
506 else
507 {
508 q = M_PI / 16 * q;
509 p = M_PI / 16 * p - M_PI_2;
510 float cos_p = cos(p);
511 out.x = cos(q) * cos_p;
512 out.y = sin(q) * cos_p;
513 out.z = -sin(p);
514 }
515
516 //print("decompressed: ", vtos(out), "\n");
517
518 return out * lengthLogTable[len];
519}
520
522{
523 vector ang;
524 float p, y, len;
525 if(vec == '0 0 0')
526 return 0;
527 //print("compress: ", vtos(vec), "\n");
528 ang = vectoangles(vec);
529 ang.x = -ang.x;
530 if(ang.x < -90)
531 ang.x += 360;
532 if(ang.x < -90 && ang.x > +90)
533 error("BOGUS vectoangles");
534 //print("angles: ", vtos(ang), "\n");
535
536 p = floor(0.5 + (ang.x + 90) * (16 / 180)) & 15; // -90..90 to 0..14
537 if(p == 0)
538 {
539 if(vec.z < 0)
540 y = 31;
541 else
542 y = 30;
543 }
544 else
545 y = floor(0.5 + ang.y * (32 / 360)) & 31; // 0..360 to 0..32
546 len = invertLengthLog(vlen(vec));
547
548 //print("compressed: p:", ftos(p)); print(" y:", ftos(y)); print(" len:", ftos(len), "\n");
549
550 return (p * 0x1000) + (y * 0x80) + len;
551}
552
554{
555 float l = 1;
556 float f = (2 ** (1/8));
557 int i;
558 for(i = 0; i < 128; ++i)
559 {
560 lengthLogTable[i] = l;
561 l *= f;
562 }
563
564 if(cvar("developer") > 0)
565 {
566 LOG_TRACE("Verifying vector compression table...");
567 for(i = 0x0F00; i < 0xFFFF; ++i)
569 {
571 "BROKEN vector compression: %s -> %s -> %s",
572 ftos(i),
575 );
576 }
577 LOG_TRACE("Done.");
578 }
579}
580
581#ifdef GAMEQC
582float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
583{
584 traceline(v0, v0 + dvx, true, forent); if(trace_fraction < 1) return 0;
585 traceline(v0, v0 + dvy, true, forent); if(trace_fraction < 1) return 0;
586 traceline(v0, v0 + dvz, true, forent); if(trace_fraction < 1) return 0;
587 traceline(v0 + dvx, v0 + dvx + dvy, true, forent); if(trace_fraction < 1) return 0;
588 traceline(v0 + dvx, v0 + dvx + dvz, true, forent); if(trace_fraction < 1) return 0;
589 traceline(v0 + dvy, v0 + dvy + dvx, true, forent); if(trace_fraction < 1) return 0;
590 traceline(v0 + dvy, v0 + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
591 traceline(v0 + dvz, v0 + dvz + dvx, true, forent); if(trace_fraction < 1) return 0;
592 traceline(v0 + dvz, v0 + dvz + dvy, true, forent); if(trace_fraction < 1) return 0;
593 traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
594 traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
595 traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0;
596 return 1;
597}
598#endif
599
600string fixPriorityList(string order, float from, float to, float subtract, float complete)
601{
602 string neworder;
603 float i, n, w;
604
605 n = tokenize_console(order);
606 neworder = "";
607 for(i = 0; i < n; ++i)
608 {
609 w = stof(argv(i));
610 if(w == floor(w))
611 {
612 if(w >= from && w <= to)
613 neworder = strcat(neworder, ftos(w), " ");
614 else
615 {
616 w -= subtract;
617 if(w >= from && w <= to)
618 neworder = strcat(neworder, ftos(w), " ");
619 }
620 }
621 }
622
623 if(complete)
624 {
625 n = tokenize_console(neworder);
626 for(w = to; w >= from; --w)
627 {
628 int wflags = REGISTRY_GET(Weapons, w).spawnflags;
629 if(wflags & WEP_FLAG_SPECIALATTACK)
630 continue;
631 for(i = 0; i < n; ++i)
632 if(stof(argv(i)) == w)
633 break;
634 if(i == n) // not found
635 neworder = strcat(neworder, ftos(w), " ");
636 }
637 }
638
639 return substring(neworder, 0, strlen(neworder) - 1);
640}
641
642string mapPriorityList(string order, string(string) mapfunc)
643{
644 string neworder;
645 float n;
646
647 n = tokenize_console(order);
648 neworder = "";
649 for(float i = 0; i < n; ++i)
650 neworder = strcat(neworder, mapfunc(argv(i)), " ");
651
652 return substring(neworder, 0, strlen(neworder) - 1);
653}
654
655string swapInPriorityList(string order, float i, float j)
656{
657 float n = tokenize_console(order);
658
659 if(i >= 0 && i < n && j >= 0 && j < n && i != j)
660 {
661 string s = "";
662 for(float w = 0; w < n; ++w)
663 {
664 if(w == i)
665 s = strcat(s, argv(j), " ");
666 else if(w == j)
667 s = strcat(s, argv(i), " ");
668 else
669 s = strcat(s, argv(w), " ");
670 }
671 return substring(s, 0, strlen(s) - 1);
672 }
673
674 return order;
675}
676
677#ifdef GAMEQC
678void get_mi_min_max(float mode)
679{
680 vector mi, ma;
681
682 string s = mapname;
683 if(!strcasecmp(substring(s, 0, 5), "maps/"))
684 s = substring(s, 5, strlen(s) - 5);
685 if(!strcasecmp(substring(s, strlen(s) - 4, 4), ".bsp"))
686 s = substring(s, 0, strlen(s) - 4);
688
689#ifdef CSQC
690 mi = world.mins;
691 ma = world.maxs;
692#else
693 mi = world.absmin;
694 ma = world.absmax;
695#endif
696
697 mi_min = mi;
698 mi_max = ma;
701 {
704 }
705 else
706 {
707 // not specified
708 if(mode)
709 {
710 // be clever
711 tracebox('1 0 0' * mi.x,
712 '0 1 0' * mi.y + '0 0 1' * mi.z,
713 '0 1 0' * ma.y + '0 0 1' * ma.z,
714 '1 0 0' * ma.x,
716 NULL);
718 mi_min.x = trace_endpos.x;
719
720 tracebox('0 1 0' * mi.y,
721 '1 0 0' * mi.x + '0 0 1' * mi.z,
722 '1 0 0' * ma.x + '0 0 1' * ma.z,
723 '0 1 0' * ma.y,
725 NULL);
727 mi_min.y = trace_endpos.y;
728
729 tracebox('0 0 1' * mi.z,
730 '1 0 0' * mi.x + '0 1 0' * mi.y,
731 '1 0 0' * ma.x + '0 1 0' * ma.y,
732 '0 0 1' * ma.z,
734 NULL);
736 mi_min.z = trace_endpos.z;
737
738 tracebox('1 0 0' * ma.x,
739 '0 1 0' * mi.y + '0 0 1' * mi.z,
740 '0 1 0' * ma.y + '0 0 1' * ma.z,
741 '1 0 0' * mi.x,
743 NULL);
745 mi_max.x = trace_endpos.x;
746
747 tracebox('0 1 0' * ma.y,
748 '1 0 0' * mi.x + '0 0 1' * mi.z,
749 '1 0 0' * ma.x + '0 0 1' * ma.z,
750 '0 1 0' * mi.y,
752 NULL);
754 mi_max.y = trace_endpos.y;
755
756 tracebox('0 0 1' * ma.z,
757 '1 0 0' * mi.x + '0 1 0' * mi.y,
758 '1 0 0' * ma.x + '0 1 0' * ma.y,
759 '0 0 1' * mi.z,
761 NULL);
763 mi_max.z = trace_endpos.z;
764 }
765 }
766}
767
769{
770 vector extend;
771
772 get_mi_min_max(mode);
773
776
777 // extend mi_picmax to get a square aspect ratio
778 // center the map in that area
779 extend = mi_picmax - mi_picmin;
780 if(extend.y > extend.x)
781 {
782 mi_picmin.x -= (extend.y - extend.x) * 0.5;
783 mi_picmax.x += (extend.y - extend.x) * 0.5;
784 }
785 else
786 {
787 mi_picmin.y -= (extend.x - extend.y) * 0.5;
788 mi_picmax.y += (extend.x - extend.y) * 0.5;
789 }
790
791 // add another some percent
792 extend = (mi_picmax - mi_picmin) * (1 / 64.0);
793 mi_picmin -= extend;
794 mi_picmax += extend;
795
796 // calculate the texcoords
798 // first the two corners of the origin
799 mi_pictexcoord0_x = (mi_min.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x);
800 mi_pictexcoord0_y = (mi_min.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y);
801 mi_pictexcoord2_x = (mi_max.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x);
802 mi_pictexcoord2_y = (mi_max.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y);
803 // then the other corners
804 mi_pictexcoord1_x = mi_pictexcoord0_x;
805 mi_pictexcoord1_y = mi_pictexcoord2_y;
806 mi_pictexcoord3_x = mi_pictexcoord2_x;
807 mi_pictexcoord3_y = mi_pictexcoord0_y;
808}
809#endif
810
811float cvar_settemp(string tmp_cvar, string tmp_value)
812{
813 float created_saved_value;
814
815 created_saved_value = 0;
816
817 if (!(tmp_cvar || tmp_value))
818 {
819 LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !");
820 return 0;
821 }
822
823 if(!cvar_type(tmp_cvar))
824 {
825 LOG_INFOF("Error: cvar %s doesn't exist!", tmp_cvar);
826 return 0;
827 }
828
829 IL_EACH(g_saved_cvars, it.netname == tmp_cvar,
830 {
831 created_saved_value = -1; // skip creation
832 break; // no need to continue
833 });
834
835 if(created_saved_value != -1)
836 {
837 // creating a new entity to keep track of this cvar
838 entity e = new_pure(saved_cvar_value);
840 e.netname = strzone(tmp_cvar);
841 e.message = strzone(cvar_string(tmp_cvar));
842 created_saved_value = 1;
843 }
844
845 // update the cvar to the value given
846 cvar_set(tmp_cvar, tmp_value);
847
848 return created_saved_value;
849}
850
852{
853 int j = 0;
854 // FIXME this new-style loop fails!
855#if 0
856 FOREACH_ENTITY_CLASS("saved_cvar_value", true,
857 {
858 if(cvar_type(it.netname))
859 {
860 cvar_set(it.netname, it.message);
861 strunzone(it.netname);
862 strunzone(it.message);
863 delete(it);
864 ++j;
865 }
866 else
867 LOG_INFOF("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.", it.netname);
868 });
869
870#else
871 entity e = NULL;
872 while((e = find(e, classname, "saved_cvar_value")))
873 {
874 if(cvar_type(e.netname))
875 {
876 cvar_set(e.netname, e.message);
877 delete(e);
878 ++j;
879 }
880 else
881 print(sprintf("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.", e.netname));
882 }
883#endif
884
885 return j;
886}
887
888float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
889{
890 // STOP.
891 // The following function is SLOW.
892 // For your safety and for the protection of those around you...
893 // DO NOT CALL THIS AT HOME.
894 // No really, don't.
895 if(w(theText, theSize) <= maxWidth)
896 return strlen(theText); // yeah!
897
898 bool colors = (w("^7", theSize) == 0);
899
900 // binary search for right place to cut string
901 int len, left, right, middle;
902 left = 0;
903 right = len = strlen(theText);
904 int ofs = 0;
905 do
906 {
907 middle = floor((left + right) / 2);
908 if(colors)
909 {
910 vector res = checkColorCode(theText, len, middle, false);
911 ofs = (res.x) ? res.x - res.y : 0;
912 }
913
914 if(w(substring(theText, 0, middle + ofs), theSize) <= maxWidth)
915 left = middle + ofs;
916 else
917 right = middle;
918 }
919 while(left < right - 1);
920
921 return left;
922}
923
924float textLengthUpToLength(string theText, int maxLength, textLengthUpToLength_lenFunction_t w)
925{
926 // STOP.
927 // The following function is SLOW.
928 // For your safety and for the protection of those around you...
929 // DO NOT CALL THIS AT HOME.
930 // No really, don't.
931 if(w(theText) <= maxLength)
932 return strlen(theText); // yeah!
933
934 bool colors = (w("^7") == 0);
935
936 // binary search for right place to cut string
937 int len, left, right, middle;
938 left = 0;
939 right = len = strlen(theText);
940 int ofs = 0;
941 do
942 {
943 middle = floor((left + right) / 2);
944 if(colors)
945 {
946 vector res = checkColorCode(theText, len, middle, true);
947 ofs = (!res.x) ? 0 : res.x - res.y;
948 }
949
950 if(w(substring(theText, 0, middle + ofs)) <= maxLength)
951 left = middle + ofs;
952 else
953 right = middle;
954 }
955 while(left < right - 1);
956
957 return left;
958}
959
960string find_last_color_code(string s)
961{
962 int start = strstrofs(s, "^", 0);
963 if (start == -1) // no caret found
964 return "";
965 int len = strlen(s)-1;
966 for(int i = len; i >= start; --i)
967 {
968 if(substring(s, i, 1) != "^")
969 continue;
970
971 int carets = 1;
972 while (i-carets >= start && substring(s, i-carets, 1) == "^")
973 ++carets;
974
975 // check if carets aren't all escaped
976 if (carets & 1)
977 {
978 if(i+1 <= len)
979 if(IS_DIGIT(substring(s, i+1, 1)))
980 {
981 if(start == 0 && len == 1)
982 return "";
983 return substring(s, i, 2);
984 }
985
986 if(i+4 <= len)
987 if(substring(s, i+1, 1) == "x")
988 if(IS_HEXDIGIT(substring(s, i + 2, 1)))
989 if(IS_HEXDIGIT(substring(s, i + 3, 1)))
990 if(IS_HEXDIGIT(substring(s, i + 4, 1)))
991 {
992 if(start == 0 && len == 4)
993 return "";
994 return substring(s, i, 5);
995 }
996 }
997 i -= carets; // this also skips one char before the carets
998 }
999
1000 return "";
1001}
1002
1004 switch (c)
1005 {
1006 case 0x3008: /* 〈 */
1007 case 0x300a: /* 《 */
1008 case 0x300c: /* 「 */
1009 case 0x300e: /* 『 */
1010 case 0x3010: /* 【 */
1011 case 0xff08: /* ( */
1012 /* rarely used */
1013 // case 0x3014: /* 〔 */
1014 // case 0x3016: /* 〖 */
1015 // case 0x3018: /* 〘 */
1016 // case 0x301a: /* 〚 */
1017 // case 0x301d: /* 〝 */
1018 // case 0xff3b: /* [ */
1019 // case 0xff5b: /* { */
1020 // case 0xff5f: /* ⦅ */
1021 // case 0xff62: /* 「 */
1022 return true;
1023 }
1024 return false;
1025}
1026
1028 switch (c)
1029 {
1030 case 0x3001: /* 、 */
1031 case 0x3002: /* 。 */
1032 case 0x3009: /* 〉 */
1033 case 0x300b: /* 》 */
1034 case 0x300d: /* 」 */
1035 case 0x300f: /* 』 */
1036 case 0x3011: /* 】 */
1037 case 0xff01: /* ! */
1038 case 0xff09: /* ) */
1039 case 0xff0c: /* , */
1040 case 0xff1a: /* : */
1041 case 0xff1b: /* ; */
1042 case 0xff1f: /* ? */
1043 /* rarely used */
1044 // case 0x3015: /* 〕 */
1045 // case 0x3017: /* 〗 */
1046 // case 0x3019: /* 〙 */
1047 // case 0x301b: /* 〛 */
1048 // case 0x301e: /* 〞 */
1049 // case 0x301f: /* 〟 */
1050 // case 0x3099: /* ゙ */
1051 // case 0x309a: /* ゚ */
1052 // case 0x309b: /* ゛ */
1053 // case 0x309c: /* ゜ */
1054 // case 0xff0e: /* . */
1055 // case 0xff3d: /* ] */
1056 // case 0xff5d: /* } */
1057 // case 0xff60: /* ⦆ */
1058 // case 0xff63: /* 」 */
1059 // case 0xff61: /* 。 */
1060 // case 0xff64: /* 、 */
1061 // case 0xff9e: /* ゙ */
1062 // case 0xff9f: /* ゚ */
1063 return true;
1064 }
1065 return false;
1066}
1067
1068string take_wrapped_line_until(int take_until, float colorlen)
1069{
1070 string s = getWrappedLine_remaining;
1071 int c = 0;
1072 int i = take_until;
1073 int skip = 0;
1074 bool take_next_char = false;
1075 while (i > 0)
1076 {
1077 --i;
1078 c = str2chr(s, i);
1079
1080 // break at ascii space, and skip it
1081 if (c == 0x20)
1082 {
1083 skip = 1;
1084 break;
1085 }
1086
1087 // optimize for many non-CJK languages
1088 if (c < 0x3000)
1089 continue;
1090
1091 if (should_break_before(c))
1092 break;
1093
1094 if (should_break_after(c)
1095 /* Unicode range that can break anywhere */
1096 || (c >= 0x3000 && c <= 0x9fff) // CJK Symbols and Ideographs
1097 || (c >= 0xac00 && c <= 0xd7af) // Hangul Syllables
1098 /* CJK Unified Ideographs Extension, B to G, rarely used */
1099 /*
1100 || (c >= 0x20000 && c <= 0x2a6df)
1101 || (c >= 0x2a700 && c <= 0x2b73f)
1102 || (c >= 0x2b740 && c <= 0x2b81f)
1103 || (c >= 0x2b820 && c <= 0x2ceaf)
1104 || (c >= 0x2ebf0 && c <= 0x2eeff)
1105 || (c >= 0x30000 && c <= 0x303ff)
1106 */
1107 )
1108 {
1109 ++i;
1110 take_next_char = true;
1111 break;
1112 }
1113 }
1114
1115 if (i != 0)
1116 {
1117 // If the next line starts with a punctuation,
1118 // wrap before the last character instead.
1119 if (take_next_char)
1120 {
1121 // str2chr is bound checked in darkplaces.
1122 c = str2chr(s, i);
1123 if (should_break_after(c))
1124 --i;
1125 }
1126 take_until = i;
1127 }
1128
1129 getWrappedLine_remaining = substring(s, take_until + skip, strlen(s) - take_until);
1130 if (getWrappedLine_remaining == "")
1132 else if (colorlen == 0)
1134 return substring(s, 0, take_until);
1135}
1136
1137string getWrappedLine(float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1138{
1139 string s = getWrappedLine_remaining;
1140 if (maxWidth <= 0)
1141 {
1143 return s; // the line has no size ANYWAY, nothing would be displayed.
1144 }
1145
1146 int take_until = textLengthUpToWidth(s, maxWidth, theFontSize, tw);
1147
1148 if (take_until <= 0 || take_until >= strlen(s)) {
1150 return s; // remaining text fits the line
1151 }
1152
1153 float colorlen = tw("^7", theFontSize);
1154 return take_wrapped_line_until(take_until, colorlen);
1155}
1156
1158{
1159 string s = getWrappedLine_remaining;
1160 if (maxLength <= 0)
1161 {
1163 return s; // the line has no size ANYWAY, nothing would be displayed.
1164 }
1165
1166 int take_until = textLengthUpToLength(s, maxLength, tw);
1167
1168 if (take_until <= 0 || take_until >= strlen(s)) {
1170 return s; // remaining text fits the line
1171 }
1172
1173 float colorlen = tw("^7");
1174 return take_wrapped_line_until(take_until, colorlen);
1175}
1176
1177string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
1178{
1179 if(tw(theText, theFontSize) <= maxWidth)
1180 return theText;
1181 else
1182 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("...", theFontSize), theFontSize, tw)), "...");
1183}
1184
1185string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw)
1186{
1187 if(tw(theText) <= maxWidth)
1188 return theText;
1189 else
1190 return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "...");
1191}
1192
1193float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern)
1194{
1195 string subpattern, subpattern2, subpattern3, subpattern4;
1196 subpattern = strcat(",", MapInfo_Type_ToString(gt), ",");
1197 if(tp)
1198 subpattern2 = ",teams,";
1199 else
1200 subpattern2 = ",noteams,";
1201 if(ts)
1202 subpattern3 = ",teamspawns,";
1203 else
1204 subpattern3 = ",noteamspawns,";
1205 if(gt == MAPINFO_TYPE_RACE || gt == MAPINFO_TYPE_CTS)
1206 subpattern4 = ",race,";
1207 else
1208 subpattern4 = string_null;
1209
1210 if(substring(pattern, 0, 1) == "-")
1211 {
1212 pattern = substring(pattern, 1, strlen(pattern) - 1);
1213 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0)
1214 return 0;
1215 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0)
1216 return 0;
1217 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) >= 0)
1218 return 0;
1219 if(subpattern4 && strstrofs(strcat(",", pattern, ","), subpattern4, 0) >= 0)
1220 return 0;
1221 }
1222 else
1223 {
1224 if(substring(pattern, 0, 1) == "+")
1225 pattern = substring(pattern, 1, strlen(pattern) - 1);
1226 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0)
1227 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0)
1228 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) < 0)
1229 {
1230 if (!subpattern4)
1231 return 0;
1232 if(strstrofs(strcat(",", pattern, ","), subpattern4, 0) < 0)
1233 return 0;
1234 }
1235 }
1236 return 1;
1237}
1238
1239vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style)
1240{
1241 vector ret;
1242
1243 // make origin and speed relative
1244 eorg -= myorg;
1245 if(newton_style)
1246 evel -= myvel;
1247
1248 // now solve for ret, ret normalized:
1249 // eorg + t * evel == t * ret * spd
1250 // or, rather, solve for t:
1251 // |eorg + t * evel| == t * spd
1252 // eorg^2 + t^2 * evel^2 + 2 * t * (eorg * evel) == t^2 * spd^2
1253 // t^2 * (evel^2 - spd^2) + t * (2 * (eorg * evel)) + eorg^2 == 0
1254 vector solution = solve_quadratic(evel * evel - spd * spd, 2 * (eorg * evel), eorg * eorg);
1255 // p = 2 * (eorg * evel) / (evel * evel - spd * spd)
1256 // q = (eorg * eorg) / (evel * evel - spd * spd)
1257 if(!solution.z) // no real solution
1258 {
1259 // happens if D < 0
1260 // (eorg * evel)^2 < (evel^2 - spd^2) * eorg^2
1261 // (eorg * evel)^2 / eorg^2 < evel^2 - spd^2
1262 // spd^2 < ((evel^2 * eorg^2) - (eorg * evel)^2) / eorg^2
1263 // spd^2 < evel^2 * (1 - cos^2 angle(evel, eorg))
1264 // spd^2 < evel^2 * sin^2 angle(evel, eorg)
1265 // spd < |evel| * sin angle(evel, eorg)
1266 return '0 0 0';
1267 }
1268 else if(solution.x > 0)
1269 {
1270 // both solutions > 0: take the smaller one
1271 // happens if p < 0 and q > 0
1272 ret = normalize(eorg + solution.x * evel);
1273 }
1274 else if(solution.y > 0)
1275 {
1276 // one solution > 0: take the larger one
1277 // happens if q < 0 or q == 0 and p < 0
1278 ret = normalize(eorg + solution.y * evel);
1279 }
1280 else
1281 {
1282 // no solution > 0: reject
1283 // happens if p > 0 and q >= 0
1284 // 2 * (eorg * evel) / (evel * evel - spd * spd) > 0
1285 // (eorg * eorg) / (evel * evel - spd * spd) >= 0
1286 //
1287 // |evel| >= spd
1288 // eorg * evel > 0
1289 //
1290 // "Enemy is moving away from me at more than spd"
1291 return '0 0 0';
1292 }
1293
1294 // NOTE: we always got a solution if spd > |evel|
1295
1296 if(newton_style == 2)
1297 ret = normalize(ret * spd + myvel);
1298
1299 return ret;
1300}
1301
1302vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_style, float mi, float ma)
1303{
1304 if(!newton_style)
1305 return spd * mydir;
1306
1307 if(newton_style == 2)
1308 {
1309 // true Newtonian projectiles with automatic aim adjustment
1310 //
1311 // solve: |outspeed * mydir - myvel| = spd
1312 // outspeed^2 - 2 * outspeed * (mydir * myvel) + myvel^2 - spd^2 = 0
1313 // outspeed = (mydir * myvel) +- sqrt((mydir * myvel)^2 - myvel^2 + spd^2)
1314 // PLUS SIGN!
1315 // not defined?
1316 // then...
1317 // myvel^2 - (mydir * myvel)^2 > spd^2
1318 // velocity without mydir component > spd
1319 // fire at smallest possible spd that works?
1320 // |(mydir * myvel) * myvel - myvel| = spd
1321
1322 vector solution = solve_quadratic(1, -2 * (mydir * myvel), myvel * myvel - spd * spd);
1323
1324 float outspeed;
1325 if(solution.z)
1326 outspeed = solution.y; // the larger one
1327 else
1328 {
1329 //outspeed = 0; // slowest possible shot
1330 outspeed = solution.x; // the real part (that is, the average!)
1331 //dprint("impossible shot, adjusting\n");
1332 }
1333
1334 outspeed = bound(spd * mi, outspeed, spd * ma);
1335 return mydir * outspeed;
1336 }
1337
1338 // real Newtonian
1339 return myvel + spd * mydir;
1340}
1341
1342// compresses the shot origin offset vector to an int with the following format:
1343// xxxxxxxx SSyyyyyy SUzzzzzz
1344// 32109876 54321098 76543210
1345// 1st byte: x component (it uses all 8 bits)
1346// 2nd byte: y component in the last 6 bits and the signs of the x and y components
1347// 3rd byte: z component in the last 6 bits and the sign of the z component (the 2nd bit is unused)
1348// all values are doubled on compression and halved on decompression
1349// so the precision for all components is 0.5
1350// values are bound to the following ranges:
1351// x: -127.5 +127.5
1352// y: -31.5 +31.5
1353// z: -31.5 +31.5
1355{
1356 int rx_neg = (v.x < 0) ? 1 : 0;
1357 int ry_neg = (v.y < 0) ? 1 : 0;
1358 int rz_neg = (v.z < 0) ? 1 : 0;
1359 int rx = rint(fabs(v.x) * 2);
1360 int ry = rint(fabs(v.y) * 2);
1361 int rz = rint(fabs(v.z) * 2);
1362 if(rx > 255) // 128 * 2 - 1
1363 {
1364 LOG_DEBUG("shot origin ", vtos(v), " x out of bounds\n");
1365 rx = bound(0, rx, 255);
1366 }
1367 if(ry > 63) // 32 * 2 - 1
1368 {
1369 LOG_DEBUG("shot origin ", vtos(v), " y out of bounds\n");
1370 ry = bound(0, ry, 63);
1371 }
1372 if(rz > 63) // 32 * 2 - 1
1373 {
1374 LOG_DEBUG("shot origin ", vtos(v), " z out of bounds\n");
1375 rz = bound(0, rz, 63);
1376 }
1377 ry |= ry_neg * BIT(6) + rx_neg * BIT(7);
1378 rz |= rz_neg * BIT(6); // BIT(7) unused
1379 return rx * 0x10000 + ry * 0x100 + rz;
1380}
1382{
1383 vector v;
1384 v.x = f >> 16;
1385 v.y = (f & 0xFF00) >> 8;
1386 v.z = f & 0xFF;
1387 // remove sign bits and apply sign
1388 if (v.y & BIT(7)) { v.y &= ~BIT(7); v.x *= -1; }
1389 if (v.y & BIT(6)) { v.y &= ~BIT(6); v.y *= -1; }
1390 if (v.z & BIT(6)) { v.z &= ~BIT(6); v.z *= -1; }
1391 return v * 0.5;
1392}
1393
1394#ifdef GAMEQC
1395vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
1396{
1397 // NOTE: we'll always choose the SMALLER value...
1398 float healthdamage, armordamage, armorideal;
1399 if (DEATH_IS(deathtype, DEATH_DROWN)) // Why should armor help here...
1400 armorblock = 0;
1401 vector v;
1402 healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health
1403 armordamage = a + (h - 1); // damage we can take if we could use more armor
1404 armorideal = healthdamage * armorblock;
1405 v.y = armorideal;
1406 if(armordamage < healthdamage)
1407 {
1408 v.x = armordamage;
1409 v.z = 1;
1410 }
1411 else
1412 {
1413 v.x = healthdamage;
1414 v.z = 0;
1415 }
1416 return v;
1417}
1418
1419vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage)
1420{
1421 vector v;
1422 if (DEATH_IS(deathtype, DEATH_DROWN)) // Why should armor help here...
1423 armorblock = 0;
1424 if (deathtype & HITTYPE_ARMORPIERCE)
1425 armorblock = 0;
1426 v.y = bound(0, damage * armorblock, a); // save
1427 v.x = bound(0, damage - v.y, damage); // take
1428 v.z = 0;
1429 return v;
1430}
1431#endif
1432
1434{
1435 float n;
1436 string m;
1437 m = cvar_string("fs_gamedir");
1438 n = tokenize_console(m);
1439 if(n == 0)
1440 return "data";
1441 else
1442 return argv(n - 1);
1443}
1444
1445float matchacl(string acl, string str)
1446{
1447 string t, s;
1448 float r, d;
1449 r = 0;
1450 while(acl)
1451 {
1452 t = car(acl); acl = cdr(acl);
1453
1454 d = 1;
1455 if(substring(t, 0, 1) == "-")
1456 {
1457 d = -1;
1458 t = substring(t, 1, strlen(t) - 1);
1459 }
1460 else if(substring(t, 0, 1) == "+")
1461 t = substring(t, 1, strlen(t) - 1);
1462
1463 if(substring(t, -1, 1) == "*")
1464 {
1465 t = substring(t, 0, strlen(t) - 1);
1466 s = substring(str, 0, strlen(t));
1467 }
1468 else
1469 s = str;
1470
1471 if(s == t)
1472 {
1473 r = d;
1474 break; // if we found a killing case, apply it! other settings may be allowed in the future, but this one is caught
1475 }
1476 }
1477 return r;
1478}
1479
1481void write_String_To_File(int fh, string str, bool alsoprint)
1482{
1483 fputs(fh, str);
1484 if (alsoprint) LOG_HELP(str);
1485}
1486
1487string get_model_datafilename(string m, float sk, string fil)
1488{
1489 if(m)
1490 m = strcat(m, "_");
1491 else
1492 m = "models/player/*_";
1493 if(sk >= 0)
1494 m = strcat(m, ftos(sk));
1495 else
1496 m = strcat(m, "*");
1497 return strcat(m, ".", fil);
1498}
1499
1500float get_model_parameters(string m, float sk)
1501{
1512 for(int i = 0; i < MAX_AIM_BONES; ++i)
1513 {
1516 }
1519
1520#ifdef GAMEQC
1521 MUTATOR_CALLHOOK(ClearModelParams);
1522#endif
1523
1524 if (!m)
1525 return 1;
1526
1527 if(substring(m, -9, 5) == "_lod1" || substring(m, -9, 5) == "_lod2")
1528 m = strcat(substring(m, 0, -10), substring(m, -4, -1));
1529
1530 if(sk < 0)
1531 {
1532 if(substring(m, -4, -1) != ".txt")
1533 return 0;
1534 if(substring(m, -6, 1) != "_")
1535 return 0;
1536 sk = stof(substring(m, -5, 1));
1537 m = substring(m, 0, -7);
1538 }
1539
1540 string fn = get_model_datafilename(m, sk, "txt");
1541 int fh = fopen(fn, FILE_READ);
1542 if(fh < 0)
1543 {
1544 sk = 0;
1545 fn = get_model_datafilename(m, sk, "txt");
1546 fh = fopen(fn, FILE_READ);
1547 if(fh < 0)
1548 return 0;
1549 }
1550
1553 string s, c;
1554 while((s = fgets(fh)))
1555 {
1556 if(s == "")
1557 break; // next lines will be description
1558 c = car(s);
1559 s = cdr(s);
1560 if(c == "name")
1562 if(c == "species")
1563 switch(s)
1564 {
1565 case "human": get_model_parameters_species = SPECIES_HUMAN; break;
1566 case "alien": get_model_parameters_species = SPECIES_ALIEN; break;
1567 case "robot_shiny": get_model_parameters_species = SPECIES_ROBOT_SHINY; break;
1568 case "robot_rusty": get_model_parameters_species = SPECIES_ROBOT_RUSTY; break;
1569 case "robot_solid": get_model_parameters_species = SPECIES_ROBOT_SOLID; break;
1570 case "animal": get_model_parameters_species = SPECIES_ANIMAL; break;
1571 case "reserved": get_model_parameters_species = SPECIES_RESERVED; break;
1572 }
1573 if(c == "sex")
1574 {
1575 if (s == "Male") s = _("Male");
1576 else if (s == "Female") s = _("Female");
1577 else if (s == "Undisclosed") s = _("Undisclosed");
1579 }
1580 if(c == "weight")
1582 if(c == "age")
1584 if(c == "description")
1586 if(c == "bone_upperbody")
1588 if(c == "bone_weapon")
1590 #ifdef GAMEQC
1591 MUTATOR_CALLHOOK(GetModelParams, c, s);
1592 #endif
1593 for(int i = 0; i < MAX_AIM_BONES; ++i)
1594 if(c == strcat("bone_aim", ftos(i)))
1595 {
1598 }
1599 if(c == "fixbone")
1601 if(c == "hidden")
1603 }
1604
1605 while((s = fgets(fh)))
1606 {
1609 if(s != "")
1611 }
1612
1613 fclose(fh);
1614
1615 return 1;
1616}
1617
1618string translate_key(string key)
1619{
1620 if (prvm_language == "en") return key;
1621
1622 if (substring(key, 0, 1) == "<")
1623 {
1624 if (key == "<KEY NOT FOUND>") return _("<KEY NOT FOUND>");
1625 if (key == "<UNKNOWN KEYNUM>") return _("<UNKNOWN KEYNUM>");
1626 }
1627
1628 switch(key)
1629 {
1630 case "TAB": return _("TAB");
1631 case "ENTER": return _("ENTER");
1632 case "ESCAPE": return _("ESCAPE");
1633 case "SPACE": return _("SPACE");
1634
1635 case "BACKSPACE": return _("BACKSPACE");
1636 case "UPARROW": return _("UPARROW");
1637 case "DOWNARROW": return _("DOWNARROW");
1638 case "LEFTARROW": return _("LEFTARROW");
1639 case "RIGHTARROW": return _("RIGHTARROW");
1640
1641 case "ALT": return _("ALT");
1642 case "CTRL": return _("CTRL");
1643 case "SHIFT": return _("SHIFT");
1644
1645 case "INS": return _("INS");
1646 case "DEL": return _("DEL");
1647 case "PGDN": return _("PGDN");
1648 case "PGUP": return _("PGUP");
1649 case "HOME": return _("HOME");
1650 case "END": return _("END");
1651
1652 case "PAUSE": return _("PAUSE");
1653
1654 case "NUMLOCK": return _("NUMLOCK");
1655 case "CAPSLOCK": return _("CAPSLOCK");
1656 case "SCROLLOCK": return _("SCROLLOCK");
1657
1658 case "SEMICOLON": return _("SEMICOLON");
1659 case "TILDE": return _("TILDE");
1660 case "BACKQUOTE": return _("BACKQUOTE");
1661 case "QUOTE": return _("QUOTE");
1662 case "APOSTROPHE": return _("APOSTROPHE");
1663 case "BACKSLASH": return _("BACKSLASH");
1664 }
1665
1666 if (substring(key, 0, 1) == "F")
1667 {
1668 string subkey = substring(key, 1, -1);
1669 if (IS_DIGIT(substring(key, 3, 1))) // check only first digit
1670 {
1671 return sprintf(_("F%d"), stof(subkey));
1672 }
1673 // continue in case there is another key name starting with F
1674 }
1675
1676 if (substring(key, 0, 3) == "KP_")
1677 {
1678 string subkey = substring(key, 3, -1);
1679 if (IS_DIGIT(substring(key, 3, 1))) // check only first digit
1680 {
1681 return sprintf(_("KP_%d"), stof(subkey));
1682 }
1683
1684 switch(subkey)
1685 {
1686 case "INS": return sprintf(_("KP_%s"), _("INS"));
1687 case "END": return sprintf(_("KP_%s"), _("END"));
1688 case "DOWNARROW": return sprintf(_("KP_%s"), _("DOWNARROW"));
1689 case "PGDN": return sprintf(_("KP_%s"), _("PGDN"));
1690 case "LEFTARROW": return sprintf(_("KP_%s"), _("LEFTARROW"));
1691 case "RIGHTARROW": return sprintf(_("KP_%s"), _("RIGHTARROW"));
1692 case "HOME": return sprintf(_("KP_%s"), _("HOME"));
1693 case "UPARROW": return sprintf(_("KP_%s"), _("UPARROW"));
1694 case "PGUP": return sprintf(_("KP_%s"), _("PGUP"));
1695 case "PERIOD": return sprintf(_("KP_%s"), _("PERIOD"));
1696 case "DEL": return sprintf(_("KP_%s"), _("DEL"));
1697 case "DIVIDE": return sprintf(_("KP_%s"), _("DIVIDE"));
1698 case "SLASH": return sprintf(_("KP_%s"), _("SLASH"));
1699 case "MULTIPLY": return sprintf(_("KP_%s"), _("MULTIPLY"));
1700 case "MINUS": return sprintf(_("KP_%s"), _("MINUS"));
1701 case "PLUS": return sprintf(_("KP_%s"), _("PLUS"));
1702 case "ENTER": return sprintf(_("KP_%s"), _("ENTER"));
1703 case "EQUALS": return sprintf(_("KP_%s"), _("EQUALS"));
1704 default: return key;
1705 }
1706 }
1707
1708 if (key == "PRINTSCREEN") return _("PRINTSCREEN");
1709
1710 if (substring(key, 0, 5) == "MOUSE")
1711 return sprintf(_("MOUSE%d"), stof(substring(key, 5, -1)));
1712
1713 if (key == "MWHEELUP") return _("MWHEELUP");
1714 if (key == "MWHEELDOWN") return _("MWHEELDOWN");
1715
1716 if (substring(key, 0,3) == "JOY")
1717 return sprintf(_("JOY%d"), stof(substring(key, 3, -1)));
1718
1719 if (substring(key, 0,3) == "AUX")
1720 return sprintf(_("AUX%d"), stof(substring(key, 3, -1)));
1721
1722 if (substring(key, 0, 4) == "X360_")
1723 {
1724 string subkey = substring(key, 4, -1);
1725 switch(subkey)
1726 {
1727 case "DPAD_UP": return sprintf(_("X360_%s"), _("DPAD_UP"));
1728 case "DPAD_DOWN": return sprintf(_("X360_%s"), _("DPAD_DOWN"));
1729 case "DPAD_LEFT": return sprintf(_("X360_%s"), _("DPAD_LEFT"));
1730 case "DPAD_RIGHT": return sprintf(_("X360_%s"), _("DPAD_RIGHT"));
1731 case "START": return sprintf(_("X360_%s"), _("START"));
1732 case "BACK": return sprintf(_("X360_%s"), _("BACK"));
1733 case "LEFT_THUMB": return sprintf(_("X360_%s"), _("LEFT_THUMB"));
1734 case "RIGHT_THUMB": return sprintf(_("X360_%s"), _("RIGHT_THUMB"));
1735 case "LEFT_SHOULDER": return sprintf(_("X360_%s"), _("LEFT_SHOULDER"));
1736 case "RIGHT_SHOULDER": return sprintf(_("X360_%s"), _("RIGHT_SHOULDER"));
1737 case "LEFT_TRIGGER": return sprintf(_("X360_%s"), _("LEFT_TRIGGER"));
1738 case "RIGHT_TRIGGER": return sprintf(_("X360_%s"), _("RIGHT_TRIGGER"));
1739 case "LEFT_THUMB_UP": return sprintf(_("X360_%s"), _("LEFT_THUMB_UP"));
1740 case "LEFT_THUMB_DOWN": return sprintf(_("X360_%s"), _("LEFT_THUMB_DOWN"));
1741 case "LEFT_THUMB_LEFT": return sprintf(_("X360_%s"), _("LEFT_THUMB_LEFT"));
1742 case "LEFT_THUMB_RIGHT": return sprintf(_("X360_%s"), _("LEFT_THUMB_RIGHT"));
1743 case "RIGHT_THUMB_UP": return sprintf(_("X360_%s"), _("RIGHT_THUMB_UP"));
1744 case "RIGHT_THUMB_DOWN": return sprintf(_("X360_%s"), _("RIGHT_THUMB_DOWN"));
1745 case "RIGHT_THUMB_LEFT": return sprintf(_("X360_%s"), _("RIGHT_THUMB_LEFT"));
1746 case "RIGHT_THUMB_RIGHT": return sprintf(_("X360_%s"), _("RIGHT_THUMB_RIGHT"));
1747 default: return key;
1748 }
1749 }
1750
1751 if (substring(key, 0, 4) == "JOY_")
1752 {
1753 string subkey = substring(key, 4, -1);
1754 switch(subkey)
1755 {
1756 case "UP": return sprintf(_("JOY_%s"), _("UP"));
1757 case "DOWN": return sprintf(_("JOY_%s"), _("DOWN"));
1758 case "LEFT": return sprintf(_("JOY_%s"), _("LEFT"));
1759 case "RIGHT": return sprintf(_("JOY_%s"), _("RIGHT"));
1760 default: return key;
1761 }
1762 }
1763
1764 if (substring(key, 0, 8) == "MIDINOTE")
1765 return sprintf(_("MIDINOTE%d"), stof(substring(key, 8, -1)));
1766
1767 return key;
1768}
1769
1770// x-encoding (encoding as zero length invisible string)
1771const string XENCODE_2 = "xX";
1772const string XENCODE_22 = "0123456789abcdefABCDEF";
1773string xencode(int f)
1774{
1775 float a, b, c, d;
1776 d = f % 22; f = floor(f / 22);
1777 c = f % 22; f = floor(f / 22);
1778 b = f % 22; f = floor(f / 22);
1779 a = f % 2; // f = floor(f / 2);
1780 return strcat(
1781 "^",
1782 substring(XENCODE_2, a, 1),
1783 substring(XENCODE_22, b, 1),
1784 substring(XENCODE_22, c, 1),
1785 substring(XENCODE_22, d, 1)
1786 );
1787}
1788float xdecode(string s)
1789{
1790 float a, b, c, d;
1791 if(substring(s, 0, 1) != "^")
1792 return -1;
1793 if(strlen(s) < 5)
1794 return -1;
1795 a = strstrofs(XENCODE_2, substring(s, 1, 1), 0);
1796 b = strstrofs(XENCODE_22, substring(s, 2, 1), 0);
1797 c = strstrofs(XENCODE_22, substring(s, 3, 1), 0);
1798 d = strstrofs(XENCODE_22, substring(s, 4, 1), 0);
1799 if(a < 0 || b < 0 || c < 0 || d < 0)
1800 return -1;
1801 return ((a * 22 + b) * 22 + c) * 22 + d;
1802}
1803
1804/*
1805string strlimitedlen(string input, string truncation, float strip_colors, float limit)
1806{
1807 if(strlen((strip_colors ? strdecolorize(input) : input)) <= limit)
1808 return input;
1809 else
1810 return strcat(substring(input, 0, (strlen(input) - strlen(truncation))), truncation);
1811}*/
1812
1814#ifdef SVQC
1816#endif
1817#ifdef CSQC
1818void CSQC_Shutdown()
1819#endif
1820#ifdef MENUQC
1821void m_shutdown()
1822#endif
1823{
1825 {
1826 LOG_INFO("Recursive shutdown detected! Only restoring cvars...");
1827 }
1828 else
1829 {
1830 shutdown_running = 1;
1831 Shutdown();
1832 shutdownhooks();
1833 }
1834 cvar_settemp_restore(); // this must be done LAST, but in any case
1835}
1836
1837#ifdef GAMEQC
1840{
1841 // set skeleton_bones to the total number of bones on the model
1842 if(e.skeleton_bones_index == e.modelindex)
1843 return; // same model, nothing to update
1844
1845 float skelindex;
1846 skelindex = skel_create(e.modelindex);
1847 e.skeleton_bones = skel_get_numbones(skelindex);
1848 skel_delete(skelindex);
1849 e.skeleton_bones_index = e.modelindex;
1850}
1851#endif
1852
1855{
1857 {
1858 localcmd("\n", to_execute_next_frame, "\n");
1860 }
1861}
1863{
1865 {
1866 s = strcat(s, "\n", to_execute_next_frame);
1867 }
1869}
1870
1873{
1874 entity queue_start, queue_end;
1875
1876 // we build a queue of to-be-processed entities.
1877 // queue_start is the next entity to be checked for neighbors
1878 // queue_end is the last entity added
1879
1880 if(e.FindConnectedComponent_processing)
1881 error("recursion or broken cleanup");
1882
1883 // start with a 1-element queue
1884 queue_start = queue_end = e;
1885 queue_end.(fld) = NULL;
1886 queue_end.FindConnectedComponent_processing = 1;
1887
1888 // for each queued item:
1889 for (; queue_start; queue_start = queue_start.(fld))
1890 {
1891 // find all neighbors of queue_start
1892 entity t;
1893 for(t = NULL; (t = nxt(t, queue_start, pass)); )
1894 {
1895 if(t.FindConnectedComponent_processing)
1896 continue;
1897 if(iscon(t, queue_start, pass))
1898 {
1899 // it is connected? ADD IT. It will look for neighbors soon too.
1900 queue_end.(fld) = t;
1901 queue_end = t;
1902 queue_end.(fld) = NULL;
1903 queue_end.FindConnectedComponent_processing = 1;
1904 }
1905 }
1906 }
1907
1908 // unmark
1909 for (queue_start = e; queue_start; queue_start = queue_start.(fld))
1910 queue_start.FindConnectedComponent_processing = 0;
1911}
1912
1913#ifdef GAMEQC
1915{
1916 // multi-frame anim: keep as-is
1917 if(a.y == 1)
1918 {
1919 float dur = frameduration(e.modelindex, a.x);
1920 if (dur <= 0 && b.y)
1921 {
1922 a = b;
1923 dur = frameduration(e.modelindex, a.x);
1924 }
1925 if (dur > 0)
1926 a.z = 1.0 / dur;
1927 }
1928 return a;
1929}
1930#endif
1931
1932#ifdef GAMEQC
1934{
1935 return = NULL;
1936 switch (type)
1937 {
1938 case CNT_GAMESTART:
1939 {
1940 switch(num)
1941 {
1942 case 10: return ANNCE_NUM_GAMESTART_10;
1943 case 9: return ANNCE_NUM_GAMESTART_9;
1944 case 8: return ANNCE_NUM_GAMESTART_8;
1945 case 7: return ANNCE_NUM_GAMESTART_7;
1946 case 6: return ANNCE_NUM_GAMESTART_6;
1947 case 5: return ANNCE_NUM_GAMESTART_5;
1948 case 4: return ANNCE_NUM_GAMESTART_4;
1949 case 3: return ANNCE_NUM_GAMESTART_3;
1950 case 2: return ANNCE_NUM_GAMESTART_2;
1951 case 1: return ANNCE_NUM_GAMESTART_1;
1952 }
1953 break;
1954 }
1955 case CNT_KILL:
1956 {
1957 switch(num)
1958 {
1959 case 10: return ANNCE_NUM_KILL_10;
1960 case 9: return ANNCE_NUM_KILL_9;
1961 case 8: return ANNCE_NUM_KILL_8;
1962 case 7: return ANNCE_NUM_KILL_7;
1963 case 6: return ANNCE_NUM_KILL_6;
1964 case 5: return ANNCE_NUM_KILL_5;
1965 case 4: return ANNCE_NUM_KILL_4;
1966 case 3: return ANNCE_NUM_KILL_3;
1967 case 2: return ANNCE_NUM_KILL_2;
1968 case 1: return ANNCE_NUM_KILL_1;
1969 }
1970 break;
1971 }
1972 case CNT_RESPAWN:
1973 {
1974 switch(num)
1975 {
1976 case 10: return ANNCE_NUM_RESPAWN_10;
1977 case 9: return ANNCE_NUM_RESPAWN_9;
1978 case 8: return ANNCE_NUM_RESPAWN_8;
1979 case 7: return ANNCE_NUM_RESPAWN_7;
1980 case 6: return ANNCE_NUM_RESPAWN_6;
1981 case 5: return ANNCE_NUM_RESPAWN_5;
1982 case 4: return ANNCE_NUM_RESPAWN_4;
1983 case 3: return ANNCE_NUM_RESPAWN_3;
1984 case 2: return ANNCE_NUM_RESPAWN_2;
1985 case 1: return ANNCE_NUM_RESPAWN_1;
1986 }
1987 break;
1988 }
1989 case CNT_ROUNDSTART:
1990 {
1991 switch(num)
1992 {
1993 case 10: return ANNCE_NUM_ROUNDSTART_10;
1994 case 9: return ANNCE_NUM_ROUNDSTART_9;
1995 case 8: return ANNCE_NUM_ROUNDSTART_8;
1996 case 7: return ANNCE_NUM_ROUNDSTART_7;
1997 case 6: return ANNCE_NUM_ROUNDSTART_6;
1998 case 5: return ANNCE_NUM_ROUNDSTART_5;
1999 case 4: return ANNCE_NUM_ROUNDSTART_4;
2000 case 3: return ANNCE_NUM_ROUNDSTART_3;
2001 case 2: return ANNCE_NUM_ROUNDSTART_2;
2002 case 1: return ANNCE_NUM_ROUNDSTART_1;
2003 }
2004 break;
2005 }
2006 case CNT_NORMAL:
2007 default:
2008 {
2009 switch(num)
2010 {
2011 case 10: return ANNCE_NUM_10;
2012 case 9: return ANNCE_NUM_9;
2013 case 8: return ANNCE_NUM_8;
2014 case 7: return ANNCE_NUM_7;
2015 case 6: return ANNCE_NUM_6;
2016 case 5: return ANNCE_NUM_5;
2017 case 4: return ANNCE_NUM_4;
2018 case 3: return ANNCE_NUM_3;
2019 case 2: return ANNCE_NUM_2;
2020 case 1: return ANNCE_NUM_1;
2021 }
2022 break;
2023 }
2024 }
2025}
2026#endif
2027
2028#ifdef GAMEQC
2030{
2031 switch(nativecontents)
2032 {
2033 case CONTENT_EMPTY:
2034 return 0;
2035 case CONTENT_SOLID:
2037 case CONTENT_WATER:
2038 return DPCONTENTS_WATER;
2039 case CONTENT_SLIME:
2040 return DPCONTENTS_SLIME;
2041 case CONTENT_LAVA:
2043 case CONTENT_SKY:
2044 return DPCONTENTS_SKY | DPCONTENTS_NODROP | DPCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque
2045 }
2046 return 0;
2047}
2048
2050{
2051 if(supercontents & (DPCONTENTS_SOLID | DPCONTENTS_BODY))
2052 return CONTENT_SOLID;
2053 if(supercontents & DPCONTENTS_SKY)
2054 return CONTENT_SKY;
2055 if(supercontents & DPCONTENTS_LAVA)
2056 return CONTENT_LAVA;
2057 if(supercontents & DPCONTENTS_SLIME)
2058 return CONTENT_SLIME;
2059 if(supercontents & DPCONTENTS_WATER)
2060 return CONTENT_WATER;
2061 return CONTENT_EMPTY;
2062}
2063#endif
2064
2065#ifdef SVQC
2066void attach_sameorigin(entity e, entity to, string tag)
2067{
2068 vector org, t_forward, t_left, t_up, e_forward, e_up;
2069 float tagscale;
2070
2071 org = e.origin - gettaginfo(to, gettagindex(to, tag));
2072 tagscale = (vlen(v_forward) ** -2); // undo a scale on the tag
2073 t_forward = v_forward * tagscale;
2074 t_left = v_right * -tagscale;
2075 t_up = v_up * tagscale;
2076
2077 e.origin_x = org * t_forward;
2078 e.origin_y = org * t_left;
2079 e.origin_z = org * t_up;
2080
2081 // current forward and up directions
2082 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2083 e.angles = AnglesTransform_FromVAngles(e.angles);
2084 else
2085 e.angles = AnglesTransform_FromAngles(e.angles);
2086 fixedmakevectors(e.angles);
2087
2088 // untransform forward, up!
2089 e_forward.x = v_forward * t_forward;
2090 e_forward.y = v_forward * t_left;
2091 e_forward.z = v_forward * t_up;
2092 e_up.x = v_up * t_forward;
2093 e_up.y = v_up * t_left;
2094 e_up.z = v_up * t_up;
2095
2096 e.angles = fixedvectoangles2(e_forward, e_up);
2097 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2098 e.angles = AnglesTransform_ToVAngles(e.angles);
2099 else
2100 e.angles = AnglesTransform_ToAngles(e.angles);
2101
2102 setattachment(e, to, tag);
2103 setorigin(e, e.origin);
2104}
2105
2107{
2108 vector org;
2109 org = gettaginfo(e, 0);
2110 e.angles = fixedvectoangles2(v_forward, v_up);
2111 if (substring(e.model, 0, 1) == "*") // bmodels have their own rules
2112 e.angles = AnglesTransform_ToVAngles(e.angles);
2113 else
2114 e.angles = AnglesTransform_ToAngles(e.angles);
2115 setorigin(e, org);
2116 setattachment(e, NULL, "");
2117 setorigin(e, e.origin);
2118}
2119
2121{
2122 set_movetype(e, MOVETYPE_FOLLOW); // make the hole follow
2123 e.aiment = to; // make the hole follow bmodel
2124 e.punchangle = to.angles; // the original angles of bmodel
2125 e.view_ofs = e.origin - to.origin; // relative origin
2126 e.v_angle = e.angles - to.angles; // relative angles
2127}
2128
2129#if 0
2130// TODO: unused, likely for a reason, possibly needs extensions (allow setting the new movetype as a parameter?)
2131void unfollow_sameorigin(entity e)
2132{
2134}
2135#endif
2136
2140{
2141 set_movetype(ent, MOVETYPE_FOLLOW); // make the hole follow
2142 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.
2143 ent.aiment = e; // make the hole follow bmodel
2144 ent.punchangle = e.angles; // the original angles of bmodel
2145 ent.view_ofs = ent.origin - e.origin; // relative origin
2146 ent.v_angle = ent.angles - e.angles; // relative angles
2147 ent.aiment_classname = e.classname;
2148 ent.aiment_deadflag = e.deadflag;
2149
2150 if(IS_PLAYER(ent.aiment))
2151 {
2152 entity pl = ent.aiment;
2153 ent.view_ofs.x = bound(pl.mins.x + 4, ent.view_ofs.x, pl.maxs.x - 4);
2154 ent.view_ofs.y = bound(pl.mins.y + 4, ent.view_ofs.y, pl.maxs.y - 4);
2155 ent.view_ofs.z = bound(pl.mins.z + 4, ent.view_ofs.z, pl.maxs.z - 4);
2156 }
2157}
2158
2160{
2163 ent.aiment_classname = string_null;
2164 // FIXME: engine bug?
2165 // resetting aiment the engine will set orb's origin close to world's origin
2166 //ent.aiment = NULL;
2167}
2168
2170{
2171/*
2172 if(ent.move_movetype != MOVETYPE_FOLLOW)
2173 if(ent.aiment)
2174 error("???");
2175*/
2176 // FIXME: engine bug?
2177 // when aiment disconnects the engine will set orb's origin close to world's origin
2178 if(!ent.aiment)
2179 return 2;
2180 if(ent.aiment.classname != ent.aiment_classname || ent.aiment.deadflag != ent.aiment_deadflag)
2181 return 1;
2182 return 0;
2183}
2184#endif
2185
2186#ifdef GAMEQC
2187// decolorizes and team colors the player name when needed
2188string playername(string thename, int teamid, bool team_colorize)
2189{
2190 TC(int, teamid);
2191 bool do_colorize = (teamplay && team_colorize);
2192#ifdef SVQC
2193 if(do_colorize && !intermission_running)
2194#else
2195 if(do_colorize)
2196#endif
2197 {
2198 string t = Team_ColorCode(teamid);
2199 return strcat(t, strdecolorize(thename));
2200 }
2201 else
2202 return thename;
2203}
2204
2206
2207float trace_hits_box_1d(float end, float thmi, float thma)
2208{
2209 if (end == 0)
2210 {
2211 // just check if x is in range
2212 if (0 < thmi)
2213 return false;
2214 if (0 > thma)
2215 return false;
2216 }
2217 else
2218 {
2219 // do the trace with respect to x
2220 // 0 -> end has to stay in thmi -> thma
2221 trace_hits_box_a0 = max(trace_hits_box_a0, min(thmi / end, thma / end));
2222 trace_hits_box_a1 = min(trace_hits_box_a1, max(thmi / end, thma / end));
2224 return false;
2225 }
2226 return true;
2227}
2228
2229float trace_hits_box(vector start, vector end, vector thmi, vector thma)
2230{
2231 end -= start;
2232 thmi -= start;
2233 thma -= start;
2234 // now it is a trace from 0 to end
2235
2238
2239 if (!trace_hits_box_1d(end.x, thmi.x, thma.x))
2240 return false;
2241 if (!trace_hits_box_1d(end.y, thmi.y, thma.y))
2242 return false;
2243 if (!trace_hits_box_1d(end.z, thmi.z, thma.z))
2244 return false;
2245
2246 return true;
2247}
2248
2249float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
2250{
2251 return trace_hits_box(start, end, thmi - ma, thma - mi);
2252}
2253#endif
2254
2256float cvar_or(string cv, float v)
2257{
2258 string s = cvar_string(cv);
2259 if(s == "")
2260 return v;
2261 else
2262 return stof(s);
2263}
2264
2265// NOTE base is the central value
2266// freq: circle frequency, = 2*pi*frequency in hertz
2267// start_pos:
2268// -1 start from the lower value
2269// 0 start from the base value
2270// 1 start from the higher value
2272float blink_synced(float base, float range, float freq, float start_time, int start_pos)
2273{
2274 // note:
2275 // RMS = sqrt(base^2 + 0.5 * range^2)
2276 // thus
2277 // base = sqrt(RMS^2 - 0.5 * range^2)
2278 // ensure RMS == 1
2279
2280 return base + range * sin((time - start_time - (M_PI / 2) * start_pos) * freq);
2281}
2282
2284float blink(float base, float range, float freq)
2285{
2286 return blink_synced(base, range, freq, 0, 0);
2287}
vector AnglesTransform_ToAngles(vector v)
vector AnglesTransform_ToVAngles(vector v)
vector AnglesTransform_FromAngles(vector v)
vector AnglesTransform_FromVAngles(vector v)
#define fixedvectoangles2
void fixedmakevectors(vector a)
#define MUTATOR_CALLHOOK(id,...)
Definition base.qh:143
#define BIT(n)
Only ever assign into the first 24 bits in QC (so max is BIT(23)).
Definition bits.qh:8
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
void Shutdown()
Definition main.qc:162
#define IS_PLAYER(s)
Definition player.qh:242
const int SFL_TIME
Display as mm:ss.s, value is stored as 10ths of a second (AND 0 is the worst possible value!...
Definition scores.qh:122
const int SFL_RANK
Display as a rank (with st, nd, rd, th suffix)
Definition scores.qh:117
const int SFL_HIDE_ZERO
Don't show zero values as scores.
Definition scores.qh:107
int rounds_played
Definition stats.qh:379
ERASEABLE void write_String_To_File(int fh, string str, bool alsoprint)
Definition util.qc:1481
float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz)
Definition util.qc:582
void SetMovetypeFollow(entity ent, entity e)
Definition util.qc:2139
bool should_break_before(int c)
Definition util.qc:1003
string playername(string thename, int teamid, bool team_colorize)
Definition util.qc:2188
void FindConnectedComponent(entity e,.entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass)
Definition util.qc:1872
void queue_to_execute_next_frame(string s)
Definition util.qc:1862
float shutdown_running
Definition util.qc:1813
float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern)
Definition util.qc:1193
void execute_next_frame()
Definition util.qc:1854
void get_mi_min_max_texcoords(float mode)
Definition util.qc:768
void wordwrap_cb(string s, float l, void(string) callback)
Definition util.qc:327
string fixPriorityList(string order, float from, float to, float subtract, float complete)
Definition util.qc:600
float compressShortVector(vector vec)
Definition util.qc:521
int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents)
Definition util.qc:2029
float skeleton_bones_index
Definition util.qc:1838
void wordwrap_buffer_sprint(string s)
Definition util.qc:182
float tracebox_inverted(vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity)
Definition util.qc:23
float xdecode(string s)
Definition util.qc:1788
float tracebox_hits_box(vector start, vector mi, vector ma, vector end, vector thmi, vector thma)
Definition util.qc:2249
vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype)
Definition util.qc:1395
vector decompressShortVector(int data)
Definition util.qc:486
void depthfirst(entity start,.entity up,.entity downleft,.entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass)
Definition util.qc:393
float trace_hits_box_a1
Definition util.qc:2205
vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage)
Definition util.qc:1419
float matchacl(string acl, string str)
Definition util.qc:1445
vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style)
Definition util.qc:1239
Notification Announcer_PickNumber(int type, int num)
Definition util.qc:1933
string find_last_color_code(string s)
Definition util.qc:960
entity _wordwrap_buffer_sprint_ent
Definition util.qc:181
string aiment_classname
Definition util.qc:2137
int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents)
Definition util.qc:2049
float lengthLogTable[128]
Definition util.qc:455
string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw)
Definition util.qc:1185
string getcurrentmod()
Definition util.qc:1433
float aiment_deadflag
Definition util.qc:2138
float FindConnectedComponent_processing
Definition util.qc:1871
string to_execute_next_frame
Definition util.qc:1853
void SV_Shutdown() void CSQC_Shutdown() void m_shutdown()
Definition util.qc:1815
ERASEABLE float blink_synced(float base, float range, float freq, float start_time, int start_pos)
Definition util.qc:2272
int cvar_settemp_restore()
Definition util.qc:851
string xencode(int f)
Definition util.qc:1773
string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:1177
float compressShotOrigin(vector v)
Definition util.qc:1354
void get_mi_min_max(float mode)
Definition util.qc:678
string ScoreString(int pFlags, float pValue, int rounds_played)
Definition util.qc:423
vector real_origin(entity ent)
Definition util.qc:148
const string XENCODE_22
Definition util.qc:1772
float trace_hits_box_a0
Definition util.qc:2205
float trace_hits_box_1d(float end, float thmi, float thma)
Definition util.qc:2207
void UnsetMovetypeFollow(entity ent)
Definition util.qc:2159
string translate_key(string key)
Definition util.qc:1618
string getWrappedLineLen(int maxLength, textLengthUpToLength_lenFunction_t tw)
Definition util.qc:1157
string take_wrapped_line_until(int take_until, float colorlen)
Definition util.qc:1068
int LostMovetypeFollow(entity ent)
Definition util.qc:2169
string get_model_datafilename(string m, float sk, string fil)
Definition util.qc:1487
float textLengthUpToLength(string theText, int maxLength, textLengthUpToLength_lenFunction_t w)
Definition util.qc:924
void wordwrap_buffer_put(string s)
Definition util.qc:165
float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w)
Definition util.qc:888
bool should_break_after(int c)
Definition util.qc:1027
const string XENCODE_2
Definition util.qc:1771
vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_style, float mi, float ma)
Definition util.qc:1302
float trace_hits_box(vector start, vector end, vector thmi, vector thma)
Definition util.qc:2229
string swapInPriorityList(string order, float i, float j)
Definition util.qc:655
void attach_sameorigin(entity e, entity to, string tag)
Definition util.qc:2066
float cvar_settemp(string tmp_cvar, string tmp_value)
Definition util.qc:811
string getWrappedLine(float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw)
Definition util.qc:1137
ERASEABLE float cvar_or(string cv, float v)
Definition util.qc:2256
void Skeleton_SetBones(entity e)
Definition util.qc:1839
void detach_sameorigin(entity e)
Definition util.qc:2106
string wordwrap(string s, float l)
Definition util.qc:170
void follow_sameorigin(entity e, entity to)
Definition util.qc:2120
vector findbetterlocation(vector org, float mindist)
Definition util.qc:117
float get_model_parameters(string m, float sk)
Definition util.qc:1500
string mapPriorityList(string order, string(string) mapfunc)
Definition util.qc:642
ERASEABLE float blink(float base, float range, float freq)
Definition util.qc:2284
void traceline_inverted(vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity)
Definition util.qc:101
string wordwrap_buffer
Definition util.qc:163
vector animfixfps(entity e, vector a, vector b)
Definition util.qc:1914
float invertLengthLog(float dist)
Definition util.qc:457
void wordwrap_sprint(entity to, string s, float l)
Definition util.qc:192
vector decompressShotOrigin(int f)
Definition util.qc:1381
const int MAX_AIM_BONES
Definition util.qh:188
string mi_shortname
Definition util.qh:126
float(string s) textLengthUpToLength_lenFunction_t
Definition util.qh:141
vector mi_picmin
Definition util.qh:131
const int CNT_NORMAL
Definition util.qh:251
float get_model_parameters_weight
Definition util.qh:182
float get_model_parameters_fixbone
Definition util.qh:191
string get_model_parameters_sex
Definition util.qh:181
vector mi_pictexcoord2
Definition util.qh:135
vector mi_min
Definition util.qh:127
float(entity a, entity b, entity pass) isConnectedFunction_t
Definition util.qh:224
float get_model_parameters_species
Definition util.qh:180
string getWrappedLine_remaining
Definition util.qh:147
float get_model_parameters_bone_aimweight[MAX_AIM_BONES]
Definition util.qh:190
float get_model_parameters_modelskin
Definition util.qh:178
string get_model_parameters_name
Definition util.qh:179
string get_model_parameters_bone_weapon
Definition util.qh:187
vector mi_pictexcoord3
Definition util.qh:136
#define TIME_ENCODED_TOSTRING(n, compact)
Definition util.qh:96
string get_model_parameters_description
Definition util.qh:185
vector mi_pictexcoord0
Definition util.qh:133
bool get_model_parameters_hidden
Definition util.qh:184
string get_model_parameters_modelname
Definition util.qh:177
string get_model_parameters_bone_upperbody
Definition util.qh:186
entity(entity cur, entity near, entity pass) findNextEntityNearFunction_t
Definition util.qh:223
string get_model_parameters_bone_aim[MAX_AIM_BONES]
Definition util.qh:189
float(string s, vector size) textLengthUpToWidth_widthFunction_t
Definition util.qh:140
vector mi_max
Definition util.qh:128
IntrusiveList g_saved_cvars
Definition util.qh:33
float get_model_parameters_age
Definition util.qh:183
const int CNT_RESPAWN
Definition util.qh:255
vector mi_picmax
Definition util.qh:132
const int CNT_KILL
Definition util.qh:254
const int CNT_ROUNDSTART
Definition util.qh:256
vector mi_pictexcoord1
Definition util.qh:134
string get_model_parameters_desc
Definition util.qh:192
const int CNT_GAMESTART
Definition util.qh:252
const int SPECIES_ANIMAL
Definition constants.qh:25
const int SPECIES_ROBOT_SOLID
Definition constants.qh:23
const int SPECIES_ROBOT_RUSTY
Definition constants.qh:26
const int SPECIES_RESERVED
Definition constants.qh:28
const int SPECIES_ROBOT_SHINY
Definition constants.qh:27
const int SPECIES_ALIEN
Definition constants.qh:24
const int SPECIES_HUMAN
Definition constants.qh:22
ERASEABLE string count_ordinal(int interval)
Definition counting.qh:66
vector v_up
string classname
float DPCONTENTS_SKY
float DPCONTENTS_NODROP
entity trace_ent
const float CONTENT_SKY
float DPCONTENTS_SOLID
const float CONTENT_SOLID
string mapname
float DPCONTENTS_BODY
const float CONTENT_WATER
const float SOLID_NOT
const float FILE_READ
float DPCONTENTS_SLIME
float time
float DPCONTENTS_OPAQUE
vector v_right
vector trace_endpos
float trace_startsolid
float MOVE_WORLDONLY
vector v_forward
float trace_fraction
float DPCONTENTS_LAVA
const float CONTENT_LAVA
float DPCONTENTS_WATER
const float CONTENT_EMPTY
const float CONTENT_SLIME
const int HITTYPE_ARMORPIERCE
Definition all.qh:32
#define DEATH_IS(t, dt)
Definition all.qh:40
#define str2chr
#define strstrofs
#define gettagindex
#define strlen
#define tokenize_console
#define tokenizebyseparator
#define strcasecmp
#define pass(name, colormin, colormax)
v y
Definition ent_cs.qc:121
#define X()
Weapons
Definition guide.qh:113
string prvm_language
Definition i18n.qh:8
#define stob(s)
Definition int.qh:5
bool intermission_running
ERASEABLE entity IL_PUSH(IntrusiveList this, entity it)
Push to tail.
#define IL_EACH(this, cond, body)
#define FOREACH_ENTITY_CLASS(class, cond, body)
Definition iter.qh:189
#define ERASEABLE
Definition _all.inc:37
#define CSQC_Shutdown
Definition _all.inc:293
#define m_shutdown
Definition _all.inc:184
#define TC(T, sym)
Definition _all.inc:82
#define LOG_HELP(...)
Definition log.qh:85
#define LOG_FATALF(...)
Definition log.qh:54
#define LOG_INFO(...)
Definition log.qh:65
#define LOG_TRACE(...)
Definition log.qh:76
#define LOG_DEBUG(...)
Definition log.qh:80
#define LOG_INFOF(...)
Definition log.qh:66
string MapInfo_Type_ToString(Gametype t)
Definition mapinfo.qc:656
int MapInfo_Get_ByName(string pFilename, float pAllowGenerate, Gametype pGametypeToSet)
Definition mapinfo.qc:1385
vector MapInfo_Map_maxs
Definition mapinfo.qh:17
vector MapInfo_Map_mins
Definition mapinfo.qh:16
ERASEABLE vector solve_quadratic(float a, float b, float c)
ax^2 + bx + c = 0
Definition math.qh:304
const float M_PI_2
Definition mathlib.qh:109
#define M_PI
Definition mathlib.qh:108
string draw_PreloadPicture(string pic)
Definition draw.qc:60
void localcmd(string command,...)
void cvar_set(string name, string value)
string fgets(float fhandle)
void fclose(float fhandle)
float stof(string val,...)
void fputs(float fhandle, string s)
float bound(float min, float value, float max)
string substring(string s, float start, float length)
float cvar(string name)
void sprint(float clientnum, string text,...)
float fopen(string filename, float mode)
entity find(entity start,.string field, string match)
float cos(float f)
const string cvar_string(string name)
float vlen(vector v)
vector vectoangles(vector v)
float sin(float f)
string vtos(vector v)
float min(float f,...)
void strunzone(string s)
float rint(float f)
vector normalize(vector v)
string ftos(float f)
float fabs(float f)
void print(string text,...)
float floor(float f)
const string cvar_defstring(string name)
string strzone(string s)
string argv(float n)
float max(float f,...)
void set_movetype(entity this, int mt)
Definition movetypes.qc:4
const int MOVETYPE_NONE
Definition movetypes.qh:129
const int MOVETYPE_FOLLOW
Definition movetypes.qh:141
const int MOVETYPE_FLY
Definition movetypes.qh:134
string string_null
Definition nil.qh:9
spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 spree_inf s1 s2 s3loc s2 s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2loc s1 s2 f1points s1 s2
Definition all.inc:469
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
entity Notification
always last
Definition all.qh:81
#define new_pure(class)
purely logical entities (not linked to the area grid)
Definition oo.qh:67
float freq
Definition pendulum.qc:3
#define NULL
Definition post.qh:14
#define world
Definition post.qh:15
#define gettaginfo
Definition post.qh:32
#define error
Definition pre.qh:6
#define REGISTRY_GET(id, i)
Definition registry.qh:43
vector
Definition self.qh:92
vector org
Definition self.qh:92
vector vector ang
Definition self.qh:92
int dir
Definition impulse.qc:89
#define PROJECTILE_MAKETRIGGER(e)
Definition common.qh:34
#define STATIC_INIT(func)
during worldspawn
Definition static.qh:32
#define shutdownhooks()
Definition static.qh:50
ERASEABLE string car(string s)
returns first word
Definition string.qh:259
#define IS_DIGIT(d)
Definition string.qh:576
ERASEABLE string cdr(string s)
returns all but first word
Definition string.qh:268
#define strfree(this)
Definition string.qh:59
#define strcpy(this, s)
Definition string.qh:52
ERASEABLE vector checkColorCode(string theText, int text_len, int pos, bool check_at_the_end)
Definition string.qh:609
#define IS_HEXDIGIT(d)
Definition string.qh:573
bool teamplay
Definition teams.qh:59
string Team_ColorCode(int teamid)
Definition teams.qh:63
const int WEP_FLAG_SPECIALATTACK
Definition weapon.qh:227