1#include "util.qh" 2 3#if defined(CSQC) 4 #include "../client/defs.qh" 5 #include "constants.qh" 6 #include "../client/mutators/events.qh" 7 #include "mapinfo.qh" 8 #include "notifications/all.qh" 9 #include <common/deathtypes/all.qh> 10#elif defined(MENUQC) 11#elif defined(SVQC) 12 #include "constants.qh" 13 #include "../server/autocvars.qh" 14 #include "../server/defs.qh" 15 #include "../server/mutators/events.qh" 16 #include "notifications/all.qh" 17 #include <common/deathtypes/all.qh> 18 #include "mapinfo.qh" 19#endif 20 21#ifdef GAMEQC 22/* 23* Get "real" origin, in worldspace, even if ent is attached to something else. 24*/ 25vector real_origin(entity ent) 26{ 27 entity e; 28 vector v = ((ent.absmin + ent.absmax) * 0.5); 29 30 e = ent.tag_entity; 31 while(e) 32 { 33 v = v + ((e.absmin + e.absmax) * 0.5); 34 e = e.tag_entity; 35 } 36 37 return v; 38} 39#endif 40 41string wordwrap_buffer; 42 43void wordwrap_buffer_put(string s) 44{ 45 wordwrap_buffer = strcat(wordwrap_buffer, s); 46} 47 48string wordwrap(string s, float l) 49{ 50 string r; 51 wordwrap_buffer = ""; 52 wordwrap_cb(s, l, wordwrap_buffer_put); 53 r = wordwrap_buffer; 54 wordwrap_buffer = ""; 55 return r; 56} 57 58#ifdef SVQC 59entity _wordwrap_buffer_sprint_ent; 60void wordwrap_buffer_sprint(string s) 61{ 62 wordwrap_buffer = strcat(wordwrap_buffer, s); 63 if(s == "\n") 64 { 65 sprint(_wordwrap_buffer_sprint_ent, wordwrap_buffer); 66 wordwrap_buffer = ""; 67 } 68} 69 70void wordwrap_sprint(entity to, string s, float l) 71{ 72 wordwrap_buffer = ""; 73 _wordwrap_buffer_sprint_ent = to; 74 wordwrap_cb(s, l, wordwrap_buffer_sprint); 75 _wordwrap_buffer_sprint_ent = NULL; 76 if(wordwrap_buffer != "") 77 sprint(to, strcat(wordwrap_buffer, "\n")); 78 wordwrap_buffer = ""; 79 return; 80} 81#endif 82 83#ifndef SVQC 84string draw_UseSkinFor(string pic) 85{ 86 if(substring(pic, 0, 1) == "/") 87 return substring(pic, 1, strlen(pic)-1); 88 else 89 return strcat(draw_currentSkin, "/", pic); 90} 91#endif 92 93void wordwrap_cb(string s, float l, void(string) callback) 94{ 95 string c; 96 float lleft, i, j, wlen; 97 98 s = strzone(s); 99 lleft = l; 100 for (i = 0;i < strlen(s);++i) 101 { 102 if (substring(s, i, 2) == "\\n") 103 { 104 callback("\n"); 105 lleft = l; 106 ++i; 107 } 108 else if (substring(s, i, 1) == "\n") 109 { 110 callback("\n"); 111 lleft = l; 112 } 113 else if (substring(s, i, 1) == " ") 114 { 115 if (lleft > 0) 116 { 117 callback(" "); 118 lleft = lleft - 1; 119 } 120 } 121 else 122 { 123 for (j = i+1;j < strlen(s);++j) 124 // ^^ this skips over the first character of a word, which 125 // is ALWAYS part of the word 126 // this is safe since if i+1 == strlen(s), i will become 127 // strlen(s)-1 at the end of this block and the function 128 // will terminate. A space can't be the first character we 129 // read here, and neither can a \n be the start, since these 130 // two cases have been handled above. 131 { 132 c = substring(s, j, 1); 133 if (c == " ") 134 break; 135 if (c == "\\") 136 break; 137 if (c == "\n") 138 break; 139 // we need to keep this tempstring alive even if substring is 140 // called repeatedly, so call strcat even though we're not 141 // doing anything 142 callback(""); 143 } 144 wlen = j - i; 145 if (lleft < wlen) 146 { 147 callback("\n"); 148 lleft = l; 149 } 150 callback(substring(s, i, wlen)); 151 lleft = lleft - wlen; 152 i = j - 1; 153 } 154 } 155 strunzone(s); 156} 157 158void depthfirst(entity start, .entity up, .entity downleft, .entity right, void(entity, entity) funcPre, void(entity, entity) funcPost, entity pass) 159{ 160 entity e; 161 e = start; 162 funcPre(pass, e); 163 while (e.(downleft)) 164 { 165 e = e.(downleft); 166 funcPre(pass, e); 167 } 168 funcPost(pass, e); 169 while(e != start) 170 { 171 if (e.(right)) 172 { 173 e = e.(right); 174 funcPre(pass, e); 175 while (e.(downleft)) 176 { 177 e = e.(downleft); 178 funcPre(pass, e); 179 } 180 } 181 else 182 e = e.(up); 183 funcPost(pass, e); 184 } 185} 186 187string ScoreString(int pFlags, float pValue) 188{ 189 string valstr; 190 float l; 191 192 pValue = floor(pValue + 0.5); // round 193 194 if((pValue == 0) && (pFlags & (SFL_HIDE_ZERO | SFL_RANK | SFL_TIME))) 195 valstr = ""; 196 else if(pFlags & SFL_RANK) 197 { 198 valstr = ftos(pValue); 199 l = strlen(valstr); 200 if((l >= 2) && (substring(valstr, l - 2, 1) == "1")) 201 valstr = strcat(valstr, "th"); 202 else if(substring(valstr, l - 1, 1) == "1") 203 valstr = strcat(valstr, "st"); 204 else if(substring(valstr, l - 1, 1) == "2") 205 valstr = strcat(valstr, "nd"); 206 else if(substring(valstr, l - 1, 1) == "3") 207 valstr = strcat(valstr, "rd"); 208 else 209 valstr = strcat(valstr, "th"); 210 } 211 else if(pFlags & SFL_TIME) 212 valstr = TIME_ENCODED_TOSTRING(pValue); 213 else 214 valstr = ftos(pValue); 215 216 return valstr; 217} 218 219// compressed vector format: 220// like MD3, just even shorter 221// 4 bit pitch (16 angles), 0 is -90, 8 is 0, 16 would be 90 222// 5 bit yaw (32 angles), 0=0, 8=90, 16=180, 24=270 223// 7 bit length (logarithmic encoding), 1/8 .. about 7844 224// length = 2^(length_encoded/8) / 8 225// if pitch is 90, yaw does nothing and therefore indicates the sign (yaw is then either 11111 or 11110); 11111 is pointing DOWN 226// thus, valid values are from 0000.11110.0000000 to 1111.11111.1111111 227// the special value 0 indicates the zero vector 228 229float lengthLogTable[128]; 230 231float invertLengthLog(float dist) 232{ 233 int l, r, m; 234 235 if(dist >= lengthLogTable[127]) 236 return 127; 237 if(dist <= lengthLogTable[0]) 238 return 0; 239 240 l = 0; 241 r = 127; 242 243 while(r - l > 1) 244 { 245 m = floor((l + r) / 2); 246 if(lengthLogTable[m] < dist) 247 l = m; 248 else 249 r = m; 250 } 251 252 // now: r is >=, l is < 253 float lerr = (dist - lengthLogTable[l]); 254 float rerr = (lengthLogTable[r] - dist); 255 if(lerr < rerr) 256 return l; 257 return r; 258} 259 260vector decompressShortVector(int data) 261{ 262 vector out; 263 if(data == 0) 264 return '0 0 0'; 265 float p = (data & 0xF000) / 0x1000; 266 float q = (data & 0x0F80) / 0x80; 267 int len = (data & 0x007F); 268 269 //print("\ndecompress: p ", ftos(p)); print("q ", ftos(q)); print("len ", ftos(len), "\n"); 270 271 if(p == 0) 272 { 273 out.x = 0; 274 out.y = 0; 275 if(q == 31) 276 out.z = -1; 277 else 278 out.z = +1; 279 } 280 else 281 { 282 q = .19634954084936207740 * q; 283 p = .19634954084936207740 * p - 1.57079632679489661922; 284 out.x = cos(q) * cos(p); 285 out.y = sin(q) * cos(p); 286 out.z = -sin(p); 287 } 288 289 //print("decompressed: ", vtos(out), "\n"); 290 291 return out * lengthLogTable[len]; 292} 293 294float compressShortVector(vector vec) 295{ 296 vector ang; 297 float p, y, len; 298 if(vec == '0 0 0') 299 return 0; 300 //print("compress: ", vtos(vec), "\n"); 301 ang = vectoangles(vec); 302 ang.x = -ang.x; 303 if(ang.x < -90) 304 ang.x += 360; 305 if(ang.x < -90 && ang.x > +90) 306 error("BOGUS vectoangles"); 307 //print("angles: ", vtos(ang), "\n"); 308 309 p = floor(0.5 + (ang.x + 90) * 16 / 180) & 15; // -90..90 to 0..14 310 if(p == 0) 311 { 312 if(vec.z < 0) 313 y = 31; 314 else 315 y = 30; 316 } 317 else 318 y = floor(0.5 + ang.y * 32 / 360) & 31; // 0..360 to 0..32 319 len = invertLengthLog(vlen(vec)); 320 321 //print("compressed: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n"); 322 323 return (p * 0x1000) + (y * 0x80) + len; 324} 325 326STATIC_INIT(compressShortVector) 327{ 328 float l = 1; 329 float f = (2 ** (1/8)); 330 int i; 331 for(i = 0; i < 128; ++i) 332 { 333 lengthLogTable[i] = l; 334 l *= f; 335 } 336 337 if(cvar("developer")) 338 { 339 LOG_INFO("Verifying vector compression table...\n"); 340 for(i = 0x0F00; i < 0xFFFF; ++i) 341 if(i != compressShortVector(decompressShortVector(i))) 342 { 343 LOG_INFO("BROKEN vector compression: ", ftos(i)); 344 LOG_INFO(" -> ", vtos(decompressShortVector(i))); 345 LOG_INFO(" -> ", ftos(compressShortVector(decompressShortVector(i)))); 346 LOG_INFO("\n"); 347 error("b0rk"); 348 } 349 LOG_INFO("Done.\n"); 350 } 351} 352 353#ifdef GAMEQC 354float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz) 355{ 356 traceline(v0, v0 + dvx, true, forent); if(trace_fraction < 1) return 0; 357 traceline(v0, v0 + dvy, true, forent); if(trace_fraction < 1) return 0; 358 traceline(v0, v0 + dvz, true, forent); if(trace_fraction < 1) return 0; 359 traceline(v0 + dvx, v0 + dvx + dvy, true, forent); if(trace_fraction < 1) return 0; 360 traceline(v0 + dvx, v0 + dvx + dvz, true, forent); if(trace_fraction < 1) return 0; 361 traceline(v0 + dvy, v0 + dvy + dvx, true, forent); if(trace_fraction < 1) return 0; 362 traceline(v0 + dvy, v0 + dvy + dvz, true, forent); if(trace_fraction < 1) return 0; 363 traceline(v0 + dvz, v0 + dvz + dvx, true, forent); if(trace_fraction < 1) return 0; 364 traceline(v0 + dvz, v0 + dvz + dvy, true, forent); if(trace_fraction < 1) return 0; 365 traceline(v0 + dvx + dvy, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0; 366 traceline(v0 + dvx + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0; 367 traceline(v0 + dvy + dvz, v0 + dvx + dvy + dvz, true, forent); if(trace_fraction < 1) return 0; 368 return 1; 369} 370#endif 371 372string fixPriorityList(string order, float from, float to, float subtract, float complete) 373{ 374 string neworder; 375 float i, n, w; 376 377 n = tokenize_console(order); 378 neworder = ""; 379 for(i = 0; i < n; ++i) 380 { 381 w = stof(argv(i)); 382 if(w == floor(w)) 383 { 384 if(w >= from && w <= to) 385 neworder = strcat(neworder, ftos(w), " "); 386 else 387 { 388 w -= subtract; 389 if(w >= from && w <= to) 390 neworder = strcat(neworder, ftos(w), " "); 391 } 392 } 393 } 394 395 if(complete) 396 { 397 n = tokenize_console(neworder); 398 for(w = to; w >= from; --w) 399 { 400 int wflags = Weapons_from(w).spawnflags; 401 if((wflags & WEP_FLAG_HIDDEN) && (wflags & WEP_FLAG_MUTATORBLOCKED) && !(wflags & WEP_FLAG_NORMAL)) 402 continue; 403 for(i = 0; i < n; ++i) 404 if(stof(argv(i)) == w) 405 break; 406 if(i == n) // not found 407 neworder = strcat(neworder, ftos(w), " "); 408 } 409 } 410 411 return substring(neworder, 0, strlen(neworder) - 1); 412} 413 414string mapPriorityList(string order, string(string) mapfunc) 415{ 416 string neworder; 417 float i, n; 418 419 n = tokenize_console(order); 420 neworder = ""; 421 for(i = 0; i < n; ++i) 422 neworder = strcat(neworder, mapfunc(argv(i)), " "); 423 424 return substring(neworder, 0, strlen(neworder) - 1); 425} 426 427string swapInPriorityList(string order, float i, float j) 428{ 429 string s; 430 float w, n; 431 432 n = tokenize_console(order); 433 434 if(i >= 0 && i < n && j >= 0 && j < n && i != j) 435 { 436 s = ""; 437 for(w = 0; w < n; ++w) 438 { 439 if(w == i) 440 s = strcat(s, argv(j), " "); 441 else if(w == j) 442 s = strcat(s, argv(i), " "); 443 else 444 s = strcat(s, argv(w), " "); 445 } 446 return substring(s, 0, strlen(s) - 1); 447 } 448 449 return order; 450} 451 452#ifdef GAMEQC 453void get_mi_min_max(float mode) 454{ 455 vector mi, ma; 456 457 if(mi_shortname) 458 strunzone(mi_shortname); 459 mi_shortname = mapname; 460 if(!strcasecmp(substring(mi_shortname, 0, 5), "maps/")) 461 mi_shortname = substring(mi_shortname, 5, strlen(mi_shortname) - 5); 462 if(!strcasecmp(substring(mi_shortname, strlen(mi_shortname) - 4, 4), ".bsp")) 463 mi_shortname = substring(mi_shortname, 0, strlen(mi_shortname) - 4); 464 mi_shortname = strzone(mi_shortname); 465 466#ifdef CSQC 467 mi = world.mins; 468 ma = world.maxs; 469#else 470 mi = world.absmin; 471 ma = world.absmax; 472#endif 473 474 mi_min = mi; 475 mi_max = ma; 476 MapInfo_Get_ByName(mi_shortname, 0, NULL); 477 if(MapInfo_Map_mins.x < MapInfo_Map_maxs.x) 478 { 479 mi_min = MapInfo_Map_mins; 480 mi_max = MapInfo_Map_maxs; 481 } 482 else 483 { 484 // not specified 485 if(mode) 486 { 487 // be clever 488 tracebox('1 0 0' * mi.x, 489 '0 1 0' * mi.y + '0 0 1' * mi.z, 490 '0 1 0' * ma.y + '0 0 1' * ma.z, 491 '1 0 0' * ma.x, 492 MOVE_WORLDONLY, 493 NULL); 494 if(!trace_startsolid) 495 mi_min.x = trace_endpos.x; 496 497 tracebox('0 1 0' * mi.y, 498 '1 0 0' * mi.x + '0 0 1' * mi.z, 499 '1 0 0' * ma.x + '0 0 1' * ma.z, 500 '0 1 0' * ma.y, 501 MOVE_WORLDONLY, 502 NULL); 503 if(!trace_startsolid) 504 mi_min.y = trace_endpos.y; 505 506 tracebox('0 0 1' * mi.z, 507 '1 0 0' * mi.x + '0 1 0' * mi.y, 508 '1 0 0' * ma.x + '0 1 0' * ma.y, 509 '0 0 1' * ma.z, 510 MOVE_WORLDONLY, 511 NULL); 512 if(!trace_startsolid) 513 mi_min.z = trace_endpos.z; 514 515 tracebox('1 0 0' * ma.x, 516 '0 1 0' * mi.y + '0 0 1' * mi.z, 517 '0 1 0' * ma.y + '0 0 1' * ma.z, 518 '1 0 0' * mi.x, 519 MOVE_WORLDONLY, 520 NULL); 521 if(!trace_startsolid) 522 mi_max.x = trace_endpos.x; 523 524 tracebox('0 1 0' * ma.y, 525 '1 0 0' * mi.x + '0 0 1' * mi.z, 526 '1 0 0' * ma.x + '0 0 1' * ma.z, 527 '0 1 0' * mi.y, 528 MOVE_WORLDONLY, 529 NULL); 530 if(!trace_startsolid) 531 mi_max.y = trace_endpos.y; 532 533 tracebox('0 0 1' * ma.z, 534 '1 0 0' * mi.x + '0 1 0' * mi.y, 535 '1 0 0' * ma.x + '0 1 0' * ma.y, 536 '0 0 1' * mi.z, 537 MOVE_WORLDONLY, 538 NULL); 539 if(!trace_startsolid) 540 mi_max.z = trace_endpos.z; 541 } 542 } 543} 544 545void get_mi_min_max_texcoords(float mode) 546{ 547 vector extend; 548 549 get_mi_min_max(mode); 550 551 mi_picmin = mi_min; 552 mi_picmax = mi_max; 553 554 // extend mi_picmax to get a square aspect ratio 555 // center the map in that area 556 extend = mi_picmax - mi_picmin; 557 if(extend.y > extend.x) 558 { 559 mi_picmin.x -= (extend.y - extend.x) * 0.5; 560 mi_picmax.x += (extend.y - extend.x) * 0.5; 561 } 562 else 563 { 564 mi_picmin.y -= (extend.x - extend.y) * 0.5; 565 mi_picmax.y += (extend.x - extend.y) * 0.5; 566 } 567 568 // add another some percent 569 extend = (mi_picmax - mi_picmin) * (1 / 64.0); 570 mi_picmin -= extend; 571 mi_picmax += extend; 572 573 // calculate the texcoords 574 mi_pictexcoord0 = mi_pictexcoord1 = mi_pictexcoord2 = mi_pictexcoord3 = '0 0 0'; 575 // first the two corners of the origin 576 mi_pictexcoord0_x = (mi_min.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x); 577 mi_pictexcoord0_y = (mi_min.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y); 578 mi_pictexcoord2_x = (mi_max.x - mi_picmin.x) / (mi_picmax.x - mi_picmin.x); 579 mi_pictexcoord2_y = (mi_max.y - mi_picmin.y) / (mi_picmax.y - mi_picmin.y); 580 // then the other corners 581 mi_pictexcoord1_x = mi_pictexcoord0_x; 582 mi_pictexcoord1_y = mi_pictexcoord2_y; 583 mi_pictexcoord3_x = mi_pictexcoord2_x; 584 mi_pictexcoord3_y = mi_pictexcoord0_y; 585} 586#endif 587 588float cvar_settemp(string tmp_cvar, string tmp_value) 589{ 590 float created_saved_value; 591 592 created_saved_value = 0; 593 594 if (!(tmp_cvar || tmp_value)) 595 { 596 LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !"); 597 return 0; 598 } 599 600 if(!cvar_type(tmp_cvar)) 601 { 602 LOG_INFOF("Error: cvar %s doesn't exist!\n", tmp_cvar); 603 return 0; 604 } 605 606 IL_EACH(g_saved_cvars, it.netname == tmp_cvar, 607 { 608 created_saved_value = -1; // skip creation 609 break; // no need to continue 610 }); 611 612 if(created_saved_value != -1) 613 { 614 // creating a new entity to keep track of this cvar 615 entity e = new_pure(saved_cvar_value); 616 IL_PUSH(g_saved_cvars, e); 617 e.netname = strzone(tmp_cvar); 618 e.message = strzone(cvar_string(tmp_cvar)); 619 created_saved_value = 1; 620 } 621 622 // update the cvar to the value given 623 cvar_set(tmp_cvar, tmp_value); 624 625 return created_saved_value; 626} 627 628int cvar_settemp_restore() 629{ 630 int j = 0; 631 // FIXME this new-style loop fails! 632#if 0 633 FOREACH_ENTITY_CLASS("saved_cvar_value", true, 634 { 635 if(cvar_type(it.netname)) 636 { 637 cvar_set(it.netname, it.message); 638 strunzone(it.netname); 639 strunzone(it.message); 640 delete(it); 641 ++j; 642 } 643 else 644 LOG_INFOF("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.\n", it.netname); 645 }); 646 647#else 648 entity e = NULL; 649 while((e = find(e, classname, "saved_cvar_value"))) 650 { 651 if(cvar_type(e.netname)) 652 { 653 cvar_set(e.netname, e.message); 654 delete(e); 655 ++j; 656 } 657 else 658 print(sprintf("Error: cvar %s doesn't exist anymore! It can still be restored once it's manually recreated.\n", e.netname)); 659 } 660#endif 661 662 return j; 663} 664 665bool isCaretEscaped(string theText, float pos) 666{ 667 int i = 0; 668 while(pos - i >= 1 && substring(theText, pos - i - 1, 1) == "^") 669 ++i; 670 return (i & 1); 671} 672 673int skipIncompleteTag(string theText, float pos, int len) 674{ 675 int i = 0, ch = 0; 676 int tag_start = -1; 677 678 if(substring(theText, pos - 1, 1) == "^") 679 { 680 if(isCaretEscaped(theText, pos - 1) || pos >= len) 681 return 0; 682 683 ch = str2chr(theText, pos); 684 if(ch >= '0' && ch <= '9') 685 return 1; // ^[0-9] color code found 686 else if (ch == 'x') 687 tag_start = pos - 1; // ^x tag found 688 else 689 return 0; 690 } 691 else 692 { 693 for(i = 2; pos - i >= 0 && i <= 4; ++i) 694 { 695 if(substring(theText, pos - i, 2) == "^x") 696 { 697 tag_start = pos - i; // ^x tag found 698 break; 699 } 700 } 701 } 702 703 if(tag_start >= 0) 704 { 705 if(tag_start + 5 < len) 706 if(IS_HEXDIGIT(substring(theText, tag_start + 2, 1))) 707 if(IS_HEXDIGIT(substring(theText, tag_start + 3, 1))) 708 if(IS_HEXDIGIT(substring(theText, tag_start + 4, 1))) 709 { 710 if(!isCaretEscaped(theText, tag_start)) 711 return 5 - (pos - tag_start); // ^xRGB color code found 712 } 713 } 714 return 0; 715} 716 717float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w) 718{ 719 // STOP. 720 // The following function is SLOW. 721 // For your safety and for the protection of those around you... 722 // DO NOT CALL THIS AT HOME. 723 // No really, don't. 724 if(w(theText, theSize) <= maxWidth) 725 return strlen(theText); // yeah! 726 727 bool colors = (w("^7", theSize) == 0); 728 729 // binary search for right place to cut string 730 int len, left, right, middle; 731 left = 0; 732 right = len = strlen(theText); 733 int ofs = 0; 734 do 735 { 736 middle = floor((left + right) / 2); 737 if(colors) 738 ofs = skipIncompleteTag(theText, middle, len); 739 if(w(substring(theText, 0, middle + ofs), theSize) <= maxWidth) 740 left = middle + ofs; 741 else 742 right = middle; 743 } 744 while(left < right - 1); 745 746 return left; 747} 748 749float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t w) 750{ 751 // STOP. 752 // The following function is SLOW. 753 // For your safety and for the protection of those around you... 754 // DO NOT CALL THIS AT HOME. 755 // No really, don't. 756 if(w(theText) <= maxWidth) 757 return strlen(theText); // yeah! 758 759 bool colors = (w("^7") == 0); 760 761 // binary search for right place to cut string 762 int len, left, right, middle; 763 left = 0; 764 right = len = strlen(theText); 765 int ofs = 0; 766 do 767 { 768 middle = floor((left + right) / 2); 769 if(colors) 770 ofs = skipIncompleteTag(theText, middle, len); 771 if(w(substring(theText, 0, middle + ofs)) <= maxWidth) 772 left = middle + ofs; 773 else 774 right = middle; 775 } 776 while(left < right - 1); 777 778 return left; 779} 780 781string find_last_color_code(string s) 782{ 783 int start = strstrofs(s, "^", 0); 784 if (start == -1) // no caret found 785 return ""; 786 int len = strlen(s)-1; 787 int i; 788 for(i = len; i >= start; --i) 789 { 790 if(substring(s, i, 1) != "^") 791 continue; 792 793 int carets = 1; 794 while (i-carets >= start && substring(s, i-carets, 1) == "^") 795 ++carets; 796 797 // check if carets aren't all escaped 798 if (carets & 1) 799 { 800 if(i+1 <= len) 801 if(IS_DIGIT(substring(s, i+1, 1))) 802 return substring(s, i, 2); 803 804 if(i+4 <= len) 805 if(substring(s, i+1, 1) == "x") 806 if(IS_HEXDIGIT(substring(s, i + 2, 1))) 807 if(IS_HEXDIGIT(substring(s, i + 3, 1))) 808 if(IS_HEXDIGIT(substring(s, i + 4, 1))) 809 return substring(s, i, 5); 810 } 811 i -= carets; // this also skips one char before the carets 812 } 813 814 return ""; 815} 816 817string getWrappedLine(float w, vector theFontSize, textLengthUpToWidth_widthFunction_t tw) 818{ 819 float cantake; 820 float take; 821 string s; 822 823 s = getWrappedLine_remaining; 824 825 if(w <= 0) 826 { 827 getWrappedLine_remaining = string_null; 828 return s; // the line has no size ANYWAY, nothing would be displayed. 829 } 830 831 cantake = textLengthUpToWidth(s, w, theFontSize, tw); 832 if(cantake > 0 && cantake < strlen(s)) 833 { 834 take = cantake - 1; 835 while(take > 0 && substring(s, take, 1) != " ") 836 --take; 837 if(take == 0) 838 { 839 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake); 840 if(getWrappedLine_remaining == "") 841 getWrappedLine_remaining = string_null; 842 else if (tw("^7", theFontSize) == 0) 843 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining); 844 return substring(s, 0, cantake); 845 } 846 else 847 { 848 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take); 849 if(getWrappedLine_remaining == "") 850 getWrappedLine_remaining = string_null; 851 else if (tw("^7", theFontSize) == 0) 852 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining); 853 return substring(s, 0, take); 854 } 855 } 856 else 857 { 858 getWrappedLine_remaining = string_null; 859 return s; 860 } 861} 862 863string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw) 864{ 865 float cantake; 866 float take; 867 string s; 868 869 s = getWrappedLine_remaining; 870 871 if(w <= 0) 872 { 873 getWrappedLine_remaining = string_null; 874 return s; // the line has no size ANYWAY, nothing would be displayed. 875 } 876 877 cantake = textLengthUpToLength(s, w, tw); 878 if(cantake > 0 && cantake < strlen(s)) 879 { 880 take = cantake - 1; 881 while(take > 0 && substring(s, take, 1) != " ") 882 --take; 883 if(take == 0) 884 { 885 getWrappedLine_remaining = substring(s, cantake, strlen(s) - cantake); 886 if(getWrappedLine_remaining == "") 887 getWrappedLine_remaining = string_null; 888 else if (tw("^7") == 0) 889 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, cantake)), getWrappedLine_remaining); 890 return substring(s, 0, cantake); 891 } 892 else 893 { 894 getWrappedLine_remaining = substring(s, take + 1, strlen(s) - take); 895 if(getWrappedLine_remaining == "") 896 getWrappedLine_remaining = string_null; 897 else if (tw("^7") == 0) 898 getWrappedLine_remaining = strcat(find_last_color_code(substring(s, 0, take)), getWrappedLine_remaining); 899 return substring(s, 0, take); 900 } 901 } 902 else 903 { 904 getWrappedLine_remaining = string_null; 905 return s; 906 } 907} 908 909string textShortenToWidth(string theText, float maxWidth, vector theFontSize, textLengthUpToWidth_widthFunction_t tw) 910{ 911 if(tw(theText, theFontSize) <= maxWidth) 912 return theText; 913 else 914 return strcat(substring(theText, 0, textLengthUpToWidth(theText, maxWidth - tw("...", theFontSize), theFontSize, tw)), "..."); 915} 916 917string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_lenFunction_t tw) 918{ 919 if(tw(theText) <= maxWidth) 920 return theText; 921 else 922 return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "..."); 923} 924 925float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern) 926{ 927 string subpattern, subpattern2, subpattern3, subpattern4; 928 subpattern = strcat(",", MapInfo_Type_ToString(gt), ","); 929 if(tp) 930 subpattern2 = ",teams,"; 931 else 932 subpattern2 = ",noteams,"; 933 if(ts) 934 subpattern3 = ",teamspawns,"; 935 else 936 subpattern3 = ",noteamspawns,"; 937 if(gt == MAPINFO_TYPE_RACE || gt == MAPINFO_TYPE_CTS) 938 subpattern4 = ",race,"; 939 else 940 subpattern4 = string_null; 941 942 if(substring(pattern, 0, 1) == "-") 943 { 944 pattern = substring(pattern, 1, strlen(pattern) - 1); 945 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) >= 0) 946 return 0; 947 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) >= 0) 948 return 0; 949 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) >= 0) 950 return 0; 951 if(subpattern4 && strstrofs(strcat(",", pattern, ","), subpattern4, 0) >= 0) 952 return 0; 953 } 954 else 955 { 956 if(substring(pattern, 0, 1) == "+") 957 pattern = substring(pattern, 1, strlen(pattern) - 1); 958 if(strstrofs(strcat(",", pattern, ","), subpattern, 0) < 0) 959 if(strstrofs(strcat(",", pattern, ","), subpattern2, 0) < 0) 960 if(strstrofs(strcat(",", pattern, ","), subpattern3, 0) < 0) 961 { 962 if (!subpattern4) 963 return 0; 964 if(strstrofs(strcat(",", pattern, ","), subpattern4, 0) < 0) 965 return 0; 966 } 967 } 968 return 1; 969} 970 971vector solve_shotdirection(vector myorg, vector myvel, vector eorg, vector evel, float spd, float newton_style) 972{ 973 vector ret; 974 975 // make origin and speed relative 976 eorg -= myorg; 977 if(newton_style) 978 evel -= myvel; 979 980 // now solve for ret, ret normalized: 981 // eorg + t * evel == t * ret * spd 982 // or, rather, solve for t: 983 // |eorg + t * evel| == t * spd 984 // eorg^2 + t^2 * evel^2 + 2 * t * (eorg * evel) == t^2 * spd^2 985 // t^2 * (evel^2 - spd^2) + t * (2 * (eorg * evel)) + eorg^2 == 0 986 vector solution = solve_quadratic(evel * evel - spd * spd, 2 * (eorg * evel), eorg * eorg); 987 // p = 2 * (eorg * evel) / (evel * evel - spd * spd) 988 // q = (eorg * eorg) / (evel * evel - spd * spd) 989 if(!solution.z) // no real solution 990 { 991 // happens if D < 0 992 // (eorg * evel)^2 < (evel^2 - spd^2) * eorg^2 993 // (eorg * evel)^2 / eorg^2 < evel^2 - spd^2 994 // spd^2 < ((evel^2 * eorg^2) - (eorg * evel)^2) / eorg^2 995 // spd^2 < evel^2 * (1 - cos^2 angle(evel, eorg)) 996 // spd^2 < evel^2 * sin^2 angle(evel, eorg) 997 // spd < |evel| * sin angle(evel, eorg) 998 return '0 0 0'; 999 } 1000 else if(solution.x > 0) 1001 { 1002 // both solutions > 0: take the smaller one 1003 // happens if p < 0 and q > 0 1004 ret = normalize(eorg + solution.x * evel); 1005 } 1006 else if(solution.y > 0) 1007 { 1008 // one solution > 0: take the larger one 1009 // happens if q < 0 or q == 0 and p < 0 1010 ret = normalize(eorg + solution.y * evel); 1011 } 1012 else 1013 { 1014 // no solution > 0: reject 1015 // happens if p > 0 and q >= 0 1016 // 2 * (eorg * evel) / (evel * evel - spd * spd) > 0 1017 // (eorg * eorg) / (evel * evel - spd * spd) >= 0 1018 // 1019 // |evel| >= spd 1020 // eorg * evel > 0 1021 // 1022 // "Enemy is moving away from me at more than spd" 1023 return '0 0 0'; 1024 } 1025 1026 // NOTE: we always got a solution if spd > |evel| 1027 1028 if(newton_style == 2) 1029 ret = normalize(ret * spd + myvel); 1030 1031 return ret; 1032} 1033 1034vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_style, float mi, float ma) 1035{ 1036 if(!newton_style) 1037 return spd * mydir; 1038 1039 if(newton_style == 2) 1040 { 1041 // true Newtonian projectiles with automatic aim adjustment 1042 // 1043 // solve: |outspeed * mydir - myvel| = spd 1044 // outspeed^2 - 2 * outspeed * (mydir * myvel) + myvel^2 - spd^2 = 0 1045 // outspeed = (mydir * myvel) +- sqrt((mydir * myvel)^2 - myvel^2 + spd^2) 1046 // PLUS SIGN! 1047 // not defined? 1048 // then... 1049 // myvel^2 - (mydir * myvel)^2 > spd^2 1050 // velocity without mydir component > spd 1051 // fire at smallest possible spd that works? 1052 // |(mydir * myvel) * myvel - myvel| = spd 1053 1054 vector solution = solve_quadratic(1, -2 * (mydir * myvel), myvel * myvel - spd * spd); 1055 1056 float outspeed; 1057 if(solution.z) 1058 outspeed = solution.y; // the larger one 1059 else 1060 { 1061 //outspeed = 0; // slowest possible shot 1062 outspeed = solution.x; // the real part (that is, the average!) 1063 //dprint("impossible shot, adjusting\n"); 1064 } 1065 1066 outspeed = bound(spd * mi, outspeed, spd * ma); 1067 return mydir * outspeed; 1068 } 1069 1070 // real Newtonian 1071 return myvel + spd * mydir; 1072} 1073 1074float compressShotOrigin(vector v) 1075{ 1076 float rx = rint(v.x * 2); 1077 float ry = rint(v.y * 4) + 128; 1078 float rz = rint(v.z * 4) + 128; 1079 if(rx > 255 || rx < 0) 1080 { 1081 LOG_DEBUG("shot origin ", vtos(v), " x out of bounds\n"); 1082 rx = bound(0, rx, 255); 1083 } 1084 if(ry > 255 || ry < 0) 1085 { 1086 LOG_DEBUG("shot origin ", vtos(v), " y out of bounds\n"); 1087 ry = bound(0, ry, 255); 1088 } 1089 if(rz > 255 || rz < 0) 1090 { 1091 LOG_DEBUG("shot origin ", vtos(v), " z out of bounds\n"); 1092 rz = bound(0, rz, 255); 1093 } 1094 return rx * 0x10000 + ry * 0x100 + rz; 1095} 1096vector decompressShotOrigin(int f) 1097{ 1098 vector v; 1099 v.x = ((f & 0xFF0000) / 0x10000) / 2; 1100 v.y = ((f & 0xFF00) / 0x100 - 128) / 4; 1101 v.z = ((f & 0xFF) - 128) / 4; 1102 return v; 1103} 1104 1105#ifdef GAMEQC 1106vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype) 1107{ 1108 // NOTE: we'll always choose the SMALLER value... 1109 float healthdamage, armordamage, armorideal; 1110 if (DEATH_IS(deathtype, DEATH_DROWN)) // Why should armor help here... 1111 armorblock = 0; 1112 vector v; 1113 healthdamage = (h - 1) / (1 - armorblock); // damage we can take if we could use more health 1114 armordamage = a + (h - 1); // damage we can take if we could use more armor 1115 armorideal = healthdamage * armorblock; 1116 v.y = armorideal; 1117 if(armordamage < healthdamage) 1118 { 1119 v.x = armordamage; 1120 v.z = 1; 1121 } 1122 else 1123 { 1124 v.x = healthdamage; 1125 v.z = 0; 1126 } 1127 return v; 1128} 1129 1130vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage) 1131{ 1132 vector v; 1133 if (DEATH_IS(deathtype, DEATH_DROWN)) // Why should armor help here... 1134 armorblock = 0; 1135 v.y = bound(0, damage * armorblock, a); // save 1136 v.x = bound(0, damage - v.y, damage); // take 1137 v.z = 0; 1138 return v; 1139} 1140#endif 1141 1142string getcurrentmod() 1143{ 1144 float n; 1145 string m; 1146 m = cvar_string("fs_gamedir"); 1147 n = tokenize_console(m); 1148 if(n == 0) 1149 return "data"; 1150 else 1151 return argv(n - 1); 1152} 1153 1154float matchacl(string acl, string str) 1155{ 1156 string t, s; 1157 float r, d; 1158 r = 0; 1159 while(acl) 1160 { 1161 t = car(acl); acl = cdr(acl); 1162 1163 d = 1; 1164 if(substring(t, 0, 1) == "-") 1165 { 1166 d = -1; 1167 t = substring(t, 1, strlen(t) - 1); 1168 } 1169 else if(substring(t, 0, 1) == "+") 1170 t = substring(t, 1, strlen(t) - 1); 1171 1172 if(substring(t, -1, 1) == "*") 1173 { 1174 t = substring(t, 0, strlen(t) - 1); 1175 s = substring(str, 0, strlen(t)); 1176 } 1177 else 1178 s = str; 1179 1180 if(s == t) 1181 { 1182 r = d; 1183 } 1184 } 1185 return r; 1186} 1187 1188string get_model_datafilename(string m, float sk, string fil) 1189{ 1190 if(m) 1191 m = strcat(m, "_"); 1192 else 1193 m = "models/player/*_"; 1194 if(sk >= 0) 1195 m = strcat(m, ftos(sk)); 1196 else 1197 m = strcat(m, "*"); 1198 return strcat(m, ".", fil); 1199} 1200 1201float get_model_parameters(string m, float sk) 1202{ 1203 get_model_parameters_modelname = string_null; 1204 get_model_parameters_modelskin = -1; 1205 get_model_parameters_name = string_null; 1206 get_model_parameters_species = -1; 1207 get_model_parameters_sex = string_null; 1208 get_model_parameters_weight = -1; 1209 get_model_parameters_age = -1; 1210 get_model_parameters_desc = string_null; 1211 get_model_parameters_bone_upperbody = string_null; 1212 get_model_parameters_bone_weapon = string_null; 1213 for(int i = 0; i < MAX_AIM_BONES; ++i) 1214 { 1215 get_model_parameters_bone_aim[i] = string_null; 1216 get_model_parameters_bone_aimweight[i] = 0; 1217 } 1218 get_model_parameters_fixbone = 0; 1219 get_model_parameters_hidden = false; 1220 1221#ifdef GAMEQC 1222 MUTATOR_CALLHOOK(ClearModelParams); 1223#endif 1224 1225 if (!m) 1226 return 1; 1227 1228 if(substring(m, -9, 5) == "_lod1" || substring(m, -9, 5) == "_lod2") 1229 m = strcat(substring(m, 0, -10), substring(m, -4, -1)); 1230 1231 if(sk < 0) 1232 { 1233 if(substring(m, -4, -1) != ".txt") 1234 return 0; 1235 if(substring(m, -6, 1) != "_") 1236 return 0; 1237 sk = stof(substring(m, -5, 1)); 1238 m = substring(m, 0, -7); 1239 } 1240 1241 string fn = get_model_datafilename(m, sk, "txt"); 1242 int fh = fopen(fn, FILE_READ); 1243 if(fh < 0) 1244 { 1245 sk = 0; 1246 fn = get_model_datafilename(m, sk, "txt"); 1247 fh = fopen(fn, FILE_READ); 1248 if(fh < 0) 1249 return 0; 1250 } 1251 1252 get_model_parameters_modelname = m; 1253 get_model_parameters_modelskin = sk; 1254 string s, c; 1255 while((s = fgets(fh))) 1256 { 1257 if(s == "") 1258 break; // next lines will be description 1259 c = car(s); 1260 s = cdr(s); 1261 if(c == "name") 1262 get_model_parameters_name = s; 1263 if(c == "species") 1264 switch(s) 1265 { 1266 case "human": get_model_parameters_species = SPECIES_HUMAN; break; 1267 case "alien": get_model_parameters_species = SPECIES_ALIEN; break; 1268 case "robot_shiny": get_model_parameters_species = SPECIES_ROBOT_SHINY; break; 1269 case "robot_rusty": get_model_parameters_species = SPECIES_ROBOT_RUSTY; break; 1270 case "robot_solid": get_model_parameters_species = SPECIES_ROBOT_SOLID; break; 1271 case "animal": get_model_parameters_species = SPECIES_ANIMAL; break; 1272 case "reserved": get_model_parameters_species = SPECIES_RESERVED; break; 1273 } 1274 if(c == "sex") 1275 get_model_parameters_sex = s; 1276 if(c == "weight") 1277 get_model_parameters_weight = stof(s); 1278 if(c == "age") 1279 get_model_parameters_age = stof(s); 1280 if(c == "description") 1281 get_model_parameters_description = s; 1282 if(c == "bone_upperbody") 1283 get_model_parameters_bone_upperbody = s; 1284 if(c == "bone_weapon") 1285 get_model_parameters_bone_weapon = s; 1286 #ifdef GAMEQC 1287 MUTATOR_CALLHOOK(GetModelParams, c, s); 1288 #endif 1289 for(int i = 0; i < MAX_AIM_BONES; ++i) 1290 if(c == strcat("bone_aim", ftos(i))) 1291 { 1292 get_model_parameters_bone_aimweight[i] = stof(car(s)); 1293 get_model_parameters_bone_aim[i] = cdr(s); 1294 } 1295 if(c == "fixbone") 1296 get_model_parameters_fixbone = stof(s); 1297 if(c == "hidden") 1298 get_model_parameters_hidden = stob(s); 1299 } 1300 1301 while((s = fgets(fh))) 1302 { 1303 if(get_model_parameters_desc) 1304 get_model_parameters_desc = strcat(get_model_parameters_desc, "\n"); 1305 if(s != "") 1306 get_model_parameters_desc = strcat(get_model_parameters_desc, s); 1307 } 1308 1309 fclose(fh); 1310 1311 return 1; 1312} 1313 1314// x-encoding (encoding as zero length invisible string) 1315const string XENCODE_2 = "xX"; 1316const string XENCODE_22 = "0123456789abcdefABCDEF"; 1317string xencode(int f) 1318{ 1319 float a, b, c, d; 1320 d = f % 22; f = floor(f / 22); 1321 c = f % 22; f = floor(f / 22); 1322 b = f % 22; f = floor(f / 22); 1323 a = f % 2; // f = floor(f / 2); 1324 return strcat( 1325 "^", 1326 substring(XENCODE_2, a, 1), 1327 substring(XENCODE_22, b, 1), 1328 substring(XENCODE_22, c, 1), 1329 substring(XENCODE_22, d, 1) 1330 ); 1331} 1332float xdecode(string s) 1333{ 1334 float a, b, c, d; 1335 if(substring(s, 0, 1) != "^") 1336 return -1; 1337 if(strlen(s) < 5) 1338 return -1; 1339 a = strstrofs(XENCODE_2, substring(s, 1, 1), 0); 1340 b = strstrofs(XENCODE_22, substring(s, 2, 1), 0); 1341 c = strstrofs(XENCODE_22, substring(s, 3, 1), 0); 1342 d = strstrofs(XENCODE_22, substring(s, 4, 1), 0); 1343 if(a < 0 || b < 0 || c < 0 || d < 0) 1344 return -1; 1345 return ((a * 22 + b) * 22 + c) * 22 + d; 1346} 1347 1348/* 1349string strlimitedlen(string input, string truncation, float strip_colors, float limit) 1350{ 1351 if(strlen((strip_colors ? strdecolorize(input) : input)) <= limit) 1352 return input; 1353 else 1354 return strcat(substring(input, 0, (strlen(input) - strlen(truncation))), truncation); 1355}*/ 1356 1357float shutdown_running; 1358#ifdef SVQC 1359void SV_Shutdown() 1360#endif 1361#ifdef CSQC 1362void CSQC_Shutdown() 1363#endif 1364#ifdef MENUQC 1365void m_shutdown() 1366#endif 1367{ 1368 if(shutdown_running) 1369 { 1370 LOG_INFO("Recursive shutdown detected! Only restoring cvars...\n"); 1371 } 1372 else 1373 { 1374 shutdown_running = 1; 1375 Shutdown(); 1376 shutdownhooks(); 1377 } 1378 cvar_settemp_restore(); // this must be done LAST, but in any case 1379} 1380 1381#ifdef GAMEQC 1382.float skeleton_bones_index; 1383void Skeleton_SetBones(entity e) 1384{ 1385 // set skeleton_bones to the total number of bones on the model 1386 if(e.skeleton_bones_index == e.modelindex) 1387 return; // same model, nothing to update 1388 1389 float skelindex; 1390 skelindex = skel_create(e.modelindex); 1391 e.skeleton_bones = skel_get_numbones(skelindex); 1392 skel_delete(skelindex); 1393 e.skeleton_bones_index = e.modelindex; 1394} 1395#endif 1396 1397string to_execute_next_frame; 1398void execute_next_frame() 1399{ 1400 if(to_execute_next_frame) 1401 { 1402 localcmd("\n", to_execute_next_frame, "\n"); 1403 strunzone(to_execute_next_frame); 1404 to_execute_next_frame = string_null; 1405 } 1406} 1407void queue_to_execute_next_frame(string s) 1408{ 1409 if(to_execute_next_frame) 1410 { 1411 s = strcat(s, "\n", to_execute_next_frame); 1412 strunzone(to_execute_next_frame); 1413 } 1414 to_execute_next_frame = strzone(s); 1415} 1416 1417.float FindConnectedComponent_processing; 1418void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t nxt, isConnectedFunction_t iscon, entity pass) 1419{ 1420 entity queue_start, queue_end; 1421 1422 // we build a queue of to-be-processed entities. 1423 // queue_start is the next entity to be checked for neighbors 1424 // queue_end is the last entity added 1425 1426 if(e.FindConnectedComponent_processing) 1427 error("recursion or broken cleanup"); 1428 1429 // start with a 1-element queue 1430 queue_start = queue_end = e; 1431 queue_end.(fld) = NULL; 1432 queue_end.FindConnectedComponent_processing = 1; 1433 1434 // for each queued item: 1435 for (; queue_start; queue_start = queue_start.(fld)) 1436 { 1437 // find all neighbors of queue_start 1438 entity t; 1439 for(t = NULL; (t = nxt(t, queue_start, pass)); ) 1440 { 1441 if(t.FindConnectedComponent_processing) 1442 continue; 1443 if(iscon(t, queue_start, pass)) 1444 { 1445 // it is connected? ADD IT. It will look for neighbors soon too. 1446 queue_end.(fld) = t; 1447 queue_end = t; 1448 queue_end.(fld) = NULL; 1449 queue_end.FindConnectedComponent_processing = 1; 1450 } 1451 } 1452 } 1453 1454 // unmark 1455 for (queue_start = e; queue_start; queue_start = queue_start.(fld)) 1456 queue_start.FindConnectedComponent_processing = 0; 1457} 1458 1459#ifdef GAMEQC 1460vector animfixfps(entity e, vector a, vector b) 1461{ 1462 // multi-frame anim: keep as-is 1463 if(a.y == 1) 1464 { 1465 float dur = frameduration(e.modelindex, a.x); 1466 if (dur <= 0 && b.y) 1467 { 1468 a = b; 1469 dur = frameduration(e.modelindex, a.x); 1470 } 1471 if (dur > 0) 1472 a.z = 1.0 / dur; 1473 } 1474 return a; 1475} 1476#endif 1477 1478#ifdef GAMEQC 1479Notification Announcer_PickNumber(int type, int num) 1480{ 1481 return = NULL; 1482 switch (type) 1483 { 1484 case CNT_GAMESTART: 1485 { 1486 switch(num) 1487 { 1488 case 10: return ANNCE_NUM_GAMESTART_10; 1489 case 9: return ANNCE_NUM_GAMESTART_9; 1490 case 8: return ANNCE_NUM_GAMESTART_8; 1491 case 7: return ANNCE_NUM_GAMESTART_7; 1492 case 6: return ANNCE_NUM_GAMESTART_6; 1493 case 5: return ANNCE_NUM_GAMESTART_5; 1494 case 4: return ANNCE_NUM_GAMESTART_4; 1495 case 3: return ANNCE_NUM_GAMESTART_3; 1496 case 2: return ANNCE_NUM_GAMESTART_2; 1497 case 1: return ANNCE_NUM_GAMESTART_1; 1498 } 1499 break; 1500 } 1501 case CNT_IDLE: 1502 { 1503 switch(num) 1504 { 1505 case 10: return ANNCE_NUM_IDLE_10; 1506 case 9: return ANNCE_NUM_IDLE_9; 1507 case 8: return ANNCE_NUM_IDLE_8; 1508 case 7: return ANNCE_NUM_IDLE_7; 1509 case 6: return ANNCE_NUM_IDLE_6; 1510 case 5: return ANNCE_NUM_IDLE_5; 1511 case 4: return ANNCE_NUM_IDLE_4; 1512 case 3: return ANNCE_NUM_IDLE_3; 1513 case 2: return ANNCE_NUM_IDLE_2; 1514 case 1: return ANNCE_NUM_IDLE_1; 1515 } 1516 break; 1517 } 1518 case CNT_KILL: 1519 { 1520 switch(num) 1521 { 1522 case 10: return ANNCE_NUM_KILL_10; 1523 case 9: return ANNCE_NUM_KILL_9; 1524 case 8: return ANNCE_NUM_KILL_8; 1525 case 7: return ANNCE_NUM_KILL_7; 1526 case 6: return ANNCE_NUM_KILL_6; 1527 case 5: return ANNCE_NUM_KILL_5; 1528 case 4: return ANNCE_NUM_KILL_4; 1529 case 3: return ANNCE_NUM_KILL_3; 1530 case 2: return ANNCE_NUM_KILL_2; 1531 case 1: return ANNCE_NUM_KILL_1; 1532 } 1533 break; 1534 } 1535 case CNT_RESPAWN: 1536 { 1537 switch(num) 1538 { 1539 case 10: return ANNCE_NUM_RESPAWN_10; 1540 case 9: return ANNCE_NUM_RESPAWN_9; 1541 case 8: return ANNCE_NUM_RESPAWN_8; 1542 case 7: return ANNCE_NUM_RESPAWN_7; 1543 case 6: return ANNCE_NUM_RESPAWN_6; 1544 case 5: return ANNCE_NUM_RESPAWN_5; 1545 case 4: return ANNCE_NUM_RESPAWN_4; 1546 case 3: return ANNCE_NUM_RESPAWN_3; 1547 case 2: return ANNCE_NUM_RESPAWN_2; 1548 case 1: return ANNCE_NUM_RESPAWN_1; 1549 } 1550 break; 1551 } 1552 case CNT_ROUNDSTART: 1553 { 1554 switch(num) 1555 { 1556 case 10: return ANNCE_NUM_ROUNDSTART_10; 1557 case 9: return ANNCE_NUM_ROUNDSTART_9; 1558 case 8: return ANNCE_NUM_ROUNDSTART_8; 1559 case 7: return ANNCE_NUM_ROUNDSTART_7; 1560 case 6: return ANNCE_NUM_ROUNDSTART_6; 1561 case 5: return ANNCE_NUM_ROUNDSTART_5; 1562 case 4: return ANNCE_NUM_ROUNDSTART_4; 1563 case 3: return ANNCE_NUM_ROUNDSTART_3; 1564 case 2: return ANNCE_NUM_ROUNDSTART_2; 1565 case 1: return ANNCE_NUM_ROUNDSTART_1; 1566 } 1567 break; 1568 } 1569 default: 1570 { 1571 switch(num) 1572 { 1573 case 10: return ANNCE_NUM_10; 1574 case 9: return ANNCE_NUM_9; 1575 case 8: return ANNCE_NUM_8; 1576 case 7: return ANNCE_NUM_7; 1577 case 6: return ANNCE_NUM_6; 1578 case 5: return ANNCE_NUM_5; 1579 case 4: return ANNCE_NUM_4; 1580 case 3: return ANNCE_NUM_3; 1581 case 2: return ANNCE_NUM_2; 1582 case 1: return ANNCE_NUM_1; 1583 } 1584 break; 1585 } 1586 } 1587} 1588#endif 1589 1590#ifdef GAMEQC 1591int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents) 1592{ 1593 switch(nativecontents) 1594 { 1595 case CONTENT_EMPTY: 1596 return 0; 1597 case CONTENT_SOLID: 1598 return DPCONTENTS_SOLID | DPCONTENTS_OPAQUE; 1599 case CONTENT_WATER: 1600 return DPCONTENTS_WATER; 1601 case CONTENT_SLIME: 1602 return DPCONTENTS_SLIME; 1603 case CONTENT_LAVA: 1604 return DPCONTENTS_LAVA | DPCONTENTS_NODROP; 1605 case CONTENT_SKY: 1606 return DPCONTENTS_SKY | DPCONTENTS_NODROP | DPCONTENTS_OPAQUE; // to match behaviour of Q3 maps, let sky count as opaque 1607 } 1608 return 0; 1609} 1610 1611int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents) 1612{ 1613 if(supercontents & (DPCONTENTS_SOLID | DPCONTENTS_BODY)) 1614 return CONTENT_SOLID; 1615 if(supercontents & DPCONTENTS_SKY) 1616 return CONTENT_SKY; 1617 if(supercontents & DPCONTENTS_LAVA) 1618 return CONTENT_LAVA; 1619 if(supercontents & DPCONTENTS_SLIME) 1620 return CONTENT_SLIME; 1621 if(supercontents & DPCONTENTS_WATER) 1622 return CONTENT_WATER; 1623 return CONTENT_EMPTY; 1624} 1625#endif 1626