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