1#include "miscfunctions.qh" 2 3#include "hud/_mod.qh" 4 5#include <common/command/_mod.qh> 6 7#include <common/teams.qh> 8 9#include <lib/csqcmodel/cl_model.qh> 10 11 12void AuditLists() 13{ 14 entity e; 15 entity prev; 16 17 prev = players; 18 for(e = prev.sort_next; e; prev = e, e = e.sort_next) 19 { 20 if(prev != e.sort_prev) 21 error(strcat("sort list chain error\nplease submit the output of 'prvm_edicts client' to the developers")); 22 } 23 24 prev = teams; 25 for(e = prev.sort_next; e; prev = e, e = e.sort_next) 26 { 27 if(prev != e.sort_prev) 28 error(strcat("sort list chain error\nplease submit the output of 'prvm_edicts client' to the developers")); 29 } 30} 31 32 33float RegisterPlayer(entity player) 34{ 35 entity pl; 36 AuditLists(); 37 for(pl = players.sort_next; pl; pl = pl.sort_next) 38 if(pl == player) 39 error("Player already registered!"); 40 player.sort_next = players.sort_next; 41 player.sort_prev = players; 42 if(players.sort_next) 43 players.sort_next.sort_prev = player; 44 players.sort_next = player; 45 AuditLists(); 46 return true; 47} 48 49void RemovePlayer(entity player) 50{ 51 entity pl, parent; 52 AuditLists(); 53 parent = players; 54 for(pl = players.sort_next; pl && pl != player; pl = pl.sort_next) 55 parent = pl; 56 57 if(!pl) 58 { 59 error("Trying to remove a player which is not in the playerlist!"); 60 return; 61 } 62 parent.sort_next = player.sort_next; 63 if(player.sort_next) 64 player.sort_next.sort_prev = parent; 65 AuditLists(); 66} 67 68void MoveToLast(entity e) 69{ 70 AuditLists(); 71 entity ent = e.sort_next; 72 while(ent) 73 { 74 SORT_SWAP(ent, e); 75 ent = e.sort_next; 76 } 77 AuditLists(); 78} 79 80float RegisterTeam(entity Team) 81{ 82 assert_once(Team.team, eprint(Team)); 83 entity tm; 84 AuditLists(); 85 for(tm = teams.sort_next; tm; tm = tm.sort_next) 86 if(tm == Team) 87 error("Team already registered!"); 88 Team.sort_next = teams.sort_next; 89 Team.sort_prev = teams; 90 if(teams.sort_next) 91 teams.sort_next.sort_prev = Team; 92 teams.sort_next = Team; 93 if(Team.team && Team.team != NUM_SPECTATOR) 94 ++team_count; 95 AuditLists(); 96 return true; 97} 98 99void RemoveTeam(entity Team) 100{ 101 entity tm, parent; 102 AuditLists(); 103 parent = teams; 104 for(tm = teams.sort_next; tm && tm != Team; tm = tm.sort_next) 105 parent = tm; 106 107 if(!tm) 108 { 109 LOG_INFO(_("Trying to remove a team which is not in the teamlist!")); 110 return; 111 } 112 parent.sort_next = Team.sort_next; 113 if(Team.sort_next) 114 Team.sort_next.sort_prev = parent; 115 if(Team.team && Team.team != NUM_SPECTATOR) 116 --team_count; 117 AuditLists(); 118} 119 120entity GetTeam(int Team, bool add) 121{ 122 TC(int, Team); TC(bool, add); 123 int num = (Team == NUM_SPECTATOR) ? 16 : Team; 124 if(teamslots[num]) 125 return teamslots[num]; 126 if (!add) 127 return NULL; 128 entity tm = new_pure(team); 129 tm.team = Team; 130 teamslots[num] = tm; 131 RegisterTeam(tm); 132 return tm; 133} 134 135vector HUD_GetFontsize(string cvarname) 136{ 137 vector v; 138 v = stov(cvar_string(cvarname)); 139 if(v.x == 0) 140 v = '8 8 0'; 141 if(v.y == 0) 142 v.y = v.x; 143 v.z = 0; 144 return v; 145} 146 147float PreviewExists(string name) 148{ 149 if(autocvar_cl_readpicture_force) 150 return false; 151 152 if (fexists(strcat(name, ".tga"))) return true; 153 if (fexists(strcat(name, ".png"))) return true; 154 if (fexists(strcat(name, ".jpg"))) return true; 155 if (fexists(strcat(name, ".pcx"))) return true; 156 157 return false; 158} 159 160// decolorizes and team colors the player name when needed 161string playername(string thename, float teamid) 162{ 163 TC(int, teamid); 164 string t; 165 if (teamplay) 166 { 167 t = Team_ColorCode(teamid); 168 return strcat(t, strdecolorize(thename)); 169 } 170 else 171 return strdecolorize(thename); 172} 173 174float cvar_or(string cv, float v) 175{ 176 string s; 177 s = cvar_string(cv); 178 if(s == "") 179 return v; 180 else 181 return stof(s); 182} 183 184vector project_3d_to_2d(vector vec) 185{ 186 vec = cs_project(vec); 187 if(cs_project_is_b0rked > 0) 188 { 189 vec.x *= vid_conwidth / vid_width; 190 vec.y *= vid_conheight / vid_height; 191 } 192 return vec; 193} 194 195float expandingbox_sizefactor_from_fadelerp(float fadelerp) 196{ 197 return 1.2 / (1.2 - fadelerp); 198} 199 200vector expandingbox_resize_centered_box_offset(float sz, vector boxsize, float boxxsizefactor) 201{ 202 boxsize.x *= boxxsizefactor; // easier interface for text 203 return boxsize * (0.5 * (1 - sz)); 204} 205 206void drawborderlines(float thickness, vector pos, vector dim, vector color, float theAlpha, float drawflag) 207{ 208 vector line_dim = '0 0 0'; 209 210 // left and right lines 211 pos.x -= thickness; 212 line_dim.x = thickness; 213 line_dim.y = dim.y; 214 drawfill(pos, line_dim, color, theAlpha, drawflag); 215 drawfill(pos + (dim.x + thickness) * '1 0 0', line_dim, color, theAlpha, drawflag); 216 217 // upper and lower lines 218 pos.y -= thickness; 219 line_dim.x = dim.x + thickness * 2; // make upper and lower lines longer 220 line_dim.y = thickness; 221 drawfill(pos, line_dim, color, theAlpha, drawflag); 222 drawfill(pos + (dim.y + thickness) * '0 1 0', line_dim, color, theAlpha, drawflag); 223} 224 225void drawpic_tiled(vector pos, string pic, vector sz, vector area, vector color, float theAlpha, float drawflag) 226{ 227 pos = HUD_Shift(pos); 228 sz = HUD_Scale(sz); 229 area = HUD_Scale(area); 230 231 vector current_pos = '0 0 0', end_pos, new_size = '0 0 0', ratio = '0 0 0'; 232 end_pos = pos + area; 233 234 current_pos.y = pos.y; 235 while (current_pos.y < end_pos.y) 236 { 237 current_pos.x = pos.x; 238 while (current_pos.x < end_pos.x) 239 { 240 new_size.x = min(sz.x, end_pos.x - current_pos.x); 241 new_size.y = min(sz.y, end_pos.y - current_pos.y); 242 ratio.x = new_size.x / sz.x; 243 ratio.y = new_size.y / sz.y; 244 drawsubpic(current_pos, new_size, pic, '0 0 0', ratio, color, theAlpha, drawflag); 245 current_pos.x += sz.x; 246 } 247 current_pos.y += sz.y; 248 } 249} 250 251void drawpic_aspect_skin_expanding(vector position, string pic, vector theScale, vector rgb, float theAlpha, float flag, float fadelerp) 252{ 253 float sz; 254 sz = expandingbox_sizefactor_from_fadelerp(fadelerp); 255 256 drawpic_aspect_skin(position + expandingbox_resize_centered_box_offset(sz, theScale, 1), pic, theScale * sz, rgb, theAlpha * (1 - fadelerp), flag); 257} 258 259void drawpic_aspect_skin_expanding_two(vector position, string pic, vector theScale, vector rgb, float theAlpha, float flag, float fadelerp) 260{ 261 drawpic_aspect_skin_expanding(position, pic, theScale, rgb, theAlpha, flag, fadelerp); 262 drawpic_skin(position, pic, theScale, rgb, theAlpha * fadelerp, flag); 263} 264 265void HUD_Scale_Disable() 266{ 267 hud_scale = '1 1 0'; 268 hud_shift = '0 0 0'; 269 drawfontscale = hud_scale; 270} 271 272void HUD_Scale_Enable() 273{ 274 hud_scale = hud_scale_current; 275 hud_shift = hud_shift_current; 276 drawfontscale = hud_scale; 277} 278 279vector HUD_Scale(vector v) 280{ 281 v.x = HUD_ScaleX(v.x); 282 v.y = HUD_ScaleY(v.y); 283 return v; 284} 285 286vector HUD_Shift(vector v) 287{ 288 v.x = HUD_ShiftX(v.x); 289 v.y = HUD_ShiftY(v.y); 290 return v; 291} 292 293float stringwidth(string text, float handleColors, vector sz) 294{ 295 vector dfs = drawfontscale; 296 drawfontscale = '1 1 0'; 297 float r = stringwidth_builtin(text, handleColors, sz); 298 drawfontscale = dfs; 299 return r; 300} 301 302// drawstring wrapper to draw a string as large as possible with preserved aspect ratio into a box 303void drawstring_aspect(vector pos, string text, vector sz, vector color, float theAlpha, float drawflag) { 304 SET_POS_AND_SZ_Y_ASPECT(false); 305 drawstring(pos, text, '1 1 0' * sz.y, color, theAlpha, drawflag); 306} 307 308// drawstring wrapper to draw a colorcodedstring as large as possible with preserved aspect ratio into a box 309void drawcolorcodedstring_aspect(vector pos, string text, vector sz, float theAlpha, float drawflag) { 310 SET_POS_AND_SZ_Y_ASPECT(true); 311 drawcolorcodedstring(pos, text, '1 1 0' * sz.y, theAlpha, drawflag); 312} 313 314void drawstring_expanding(vector position, string text, vector theScale, vector rgb, float theAlpha, float flag, float fadelerp) 315{ 316 float sz; 317 sz = expandingbox_sizefactor_from_fadelerp(fadelerp); 318 319 drawfontscale = hud_scale * sz; 320 vector dfs = drawfontscale; 321 drawfontscale = sz * '1 1 0'; 322 float textaspect = stringwidth_builtin(text, false, theScale * (sz / drawfontscale.x)) / (theScale.x * sz); 323 drawfontscale = dfs; 324 drawstring(position + expandingbox_resize_centered_box_offset(sz, theScale, textaspect), text, HUD_Scale(theScale * (sz / drawfontscale.x)), rgb, theAlpha * (1 - fadelerp), flag); 325 // width parameter: 326 // (scale_x * sz / drawfontscale.x) * drawfontscale.x * SIZE1 / (scale_x * sz) 327 // SIZE1 328 drawfontscale = hud_scale; 329} 330 331// drawstring wrapper to draw a string as large as possible with preserved aspect ratio into a box 332void drawstring_aspect_expanding(vector pos, string text, vector sz, vector color, float theAlpha, float drawflag, float fadelerp) { 333 SET_POS_AND_SZ_Y_ASPECT(false); 334 drawstring_expanding(pos, text, '1 1 0' * sz.y, color, theAlpha, drawflag, fadelerp); 335} 336 337void drawcolorcodedstring_expanding(vector position, string text, vector theScale, float theAlpha, float flag, float fadelerp) 338{ 339 float sz; 340 sz = expandingbox_sizefactor_from_fadelerp(fadelerp); 341 342 drawfontscale = hud_scale * sz; 343 // eventually replace with drawcolorcodedstring 344 drawcolorcodedstring(position + expandingbox_resize_centered_box_offset(sz, theScale, stringwidth_builtin(text, true, theScale * (sz / drawfontscale.x)) / (theScale.x * sz)), text, theScale * (sz / drawfontscale.x), theAlpha * (1 - fadelerp), flag); 345 drawfontscale = hud_scale; 346} 347 348void drawcolorcodedstring_aspect_expanding(vector pos, string text, vector sz, float theAlpha, float drawflag, float fadelerp) { 349 SET_POS_AND_SZ_Y_ASPECT(true); 350 drawcolorcodedstring_expanding(pos, text, '1 1 0' * sz.y, theAlpha, drawflag, fadelerp); 351} 352 353void update_mousepos() 354{ 355 mousepos += getmousepos() * autocvar_menu_mouse_speed; 356 mousepos.x = bound(0, mousepos.x, vid_conwidth); 357 mousepos.y = bound(0, mousepos.y, vid_conheight); 358} 359 360// this draws the triangles of a model DIRECTLY. Don't expect high performance, really... 361float PolyDrawModelSurface(entity e, float i_s) 362{ 363 float i_t; 364 float n_t; 365 vector tri; 366 string tex; 367 tex = getsurfacetexture(e, i_s); 368 if (!tex) 369 return 0; // this is beyond the last one 370 n_t = getsurfacenumtriangles(e, i_s); 371 for(i_t = 0; i_t < n_t; ++i_t) 372 { 373 tri = getsurfacetriangle(e, i_s, i_t); 374 R_BeginPolygon(tex, 0); 375 R_PolygonVertex(getsurfacepoint(e, i_s, tri.x), getsurfacepointattribute(e, i_s, tri.x, SPA_TEXCOORDS0), '1 1 1', 1); 376 R_PolygonVertex(getsurfacepoint(e, i_s, tri.y), getsurfacepointattribute(e, i_s, tri.y, SPA_TEXCOORDS0), '1 1 1', 1); 377 R_PolygonVertex(getsurfacepoint(e, i_s, tri.z), getsurfacepointattribute(e, i_s, tri.z, SPA_TEXCOORDS0), '1 1 1', 1); 378 R_EndPolygon(); 379 } 380 return 1; 381} 382void PolyDrawModel(entity e) 383{ 384 float i_s; 385 for(i_s = 0; ; ++i_s) 386 if(!PolyDrawModelSurface(e, i_s)) 387 break; 388} 389 390void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector rgb, float a, float drawflag) 391{ 392 float d; 393 vector ringsize, v, t; 394 ringsize = radi * '1 1 0'; 395 396 float co = cos(f * 2 * M_PI); 397 float si = sin(f * 2 * M_PI); 398 float q = fabs(co) + fabs(si); 399 co /= q; 400 si /= q; 401 402 if(f >= 1) 403 { 404 // draw full rectangle 405 R_BeginPolygon(pic, drawflag); 406 v = centre; t = '0.5 0.5 0'; 407 v.x += 0.5 * ringsize.x; t += '0.5 0.5 0'; 408 R_PolygonVertex(v, t, rgb, a); 409 410 v = centre; t = '0.5 0.5 0'; 411 v.y += 0.5 * ringsize.y; t += '0.5 -0.5 0'; 412 R_PolygonVertex(v, t, rgb, a); 413 414 v = centre; t = '0.5 0.5 0'; 415 v.x -= 0.5 * ringsize.x; t -= '0.5 0.5 0'; 416 R_PolygonVertex(v, t, rgb, a); 417 418 v = centre; t = '0.5 0.5 0'; 419 v.y -= 0.5 * ringsize.y; t -= '0.5 -0.5 0'; 420 R_PolygonVertex(v, t, rgb, a); 421 R_EndPolygon(); 422 423 d = q - 1; 424 if(d > 0) 425 { 426 R_BeginPolygon(pic, drawflag); 427 v = centre; t = '0.5 0.5 0'; 428 R_PolygonVertex(v, t, rgb, a); 429 430 v = centre; t = '0.5 0.5 0'; 431 v.x += 0.5 * ringsize.x; t += '0.5 0.5 0'; 432 R_PolygonVertex(v, t, rgb, a); 433 } 434 } 435 else if(f > 0.75) 436 { 437 // draw upper and first triangle 438 R_BeginPolygon(pic, drawflag); 439 v = centre; t = '0.5 0.5 0'; 440 v.x += 0.5 * ringsize.x; t += '0.5 0.5 0'; 441 R_PolygonVertex(v, t, rgb, a); 442 443 v = centre; t = '0.5 0.5 0'; 444 v.y += 0.5 * ringsize.y; t += '0.5 -0.5 0'; 445 R_PolygonVertex(v, t, rgb, a); 446 447 v = centre; t = '0.5 0.5 0'; 448 v.x -= 0.5 * ringsize.x; t -= '0.5 0.5 0'; 449 R_PolygonVertex(v, t, rgb, a); 450 R_EndPolygon(); 451 R_BeginPolygon(pic, drawflag); 452 v = centre; t = '0.5 0.5 0'; 453 R_PolygonVertex(v, t, rgb, a); 454 455 v = centre; t = '0.5 0.5 0'; 456 v.x -= 0.5 * ringsize.x; t -= '0.5 0.5 0'; 457 R_PolygonVertex(v, t, rgb, a); 458 459 v = centre; t = '0.5 0.5 0'; 460 v.y -= 0.5 * ringsize.y; t -= '0.5 -0.5 0'; 461 R_PolygonVertex(v, t, rgb, a); 462 463 d = q - 0.75; 464 if(d <= 0) 465 R_EndPolygon(); 466 } 467 else if(f > 0.5) 468 { 469 // draw upper triangle 470 R_BeginPolygon(pic, drawflag); 471 v = centre; t = '0.5 0.5 0'; 472 v.x += 0.5 * ringsize.x; t += '0.5 0.5 0'; 473 R_PolygonVertex(v, t, rgb, a); 474 475 v = centre; t = '0.5 0.5 0'; 476 v.y += 0.5 * ringsize.y; t += '0.5 -0.5 0'; 477 R_PolygonVertex(v, t, rgb, a); 478 479 v = centre; t = '0.5 0.5 0'; 480 v.x -= 0.5 * ringsize.x; t -= '0.5 0.5 0'; 481 R_PolygonVertex(v, t, rgb, a); 482 R_EndPolygon(); 483 484 d = q - 0.5; 485 if(d > 0) 486 { 487 R_BeginPolygon(pic, drawflag); 488 v = centre; t = '0.5 0.5 0'; 489 R_PolygonVertex(v, t, rgb, a); 490 491 v = centre; t = '0.5 0.5 0'; 492 v.x -= 0.5 * ringsize.x; t -= '0.5 0.5 0'; 493 R_PolygonVertex(v, t, rgb, a); 494 } 495 } 496 else if(f > 0.25) 497 { 498 // draw first triangle 499 R_BeginPolygon(pic, drawflag); 500 v = centre; t = '0.5 0.5 0'; 501 R_PolygonVertex(v, t, rgb, a); 502 503 v = centre; t = '0.5 0.5 0'; 504 v.x += 0.5 * ringsize.x; t += '0.5 0.5 0'; 505 R_PolygonVertex(v, t, rgb, a); 506 507 v = centre; t = '0.5 0.5 0'; 508 v.y += 0.5 * ringsize.y; t += '0.5 -0.5 0'; 509 R_PolygonVertex(v, t, rgb, a); 510 511 d = q - 0.25; 512 if(d <= 0) 513 R_EndPolygon(); 514 } 515 else 516 { 517 d = q; 518 if(d > 0) 519 { 520 R_BeginPolygon(pic, drawflag); 521 v = centre; t = '0.5 0.5 0'; 522 R_PolygonVertex(v, t, rgb, a); 523 524 v = centre; t = '0.5 0.5 0'; 525 v.x += 0.5 * ringsize.x; t += '0.5 0.5 0'; 526 R_PolygonVertex(v, t, rgb, a); 527 } 528 } 529 530 if(d > 0) 531 { 532 v = centre; t = '0.5 0.5 0'; 533 v.x += co * 0.5 * ringsize.x; t += co * '0.5 0.5 0'; 534 v.y += si * 0.5 * ringsize.y; t += si * '0.5 -0.5 0'; 535 R_PolygonVertex(v, t, rgb, a); 536 R_EndPolygon(); 537 } 538} 539 540/** engine callback */ 541void URI_Get_Callback(int id, int status, string data) 542{ 543 TC(int, id); TC(int, status); 544 if(url_URI_Get_Callback(id, status, data)) 545 { 546 // handled 547 } 548 else if (id == URI_GET_DISCARD) 549 { 550 // discard 551 } 552 else if (id >= URI_GET_CURL && id <= URI_GET_CURL_END) 553 { 554 // sv_cmd curl 555 Curl_URI_Get_Callback(id, status, data); 556 } 557 else 558 { 559 LOG_INFOF("Received HTTP request data for an invalid id %d.\n", id); 560 } 561} 562 563void Accuracy_LoadLevels() 564{ 565 if(autocvar_accuracy_color_levels != acc_color_levels) 566 { 567 if(acc_color_levels) 568 strunzone(acc_color_levels); 569 acc_color_levels = strzone(autocvar_accuracy_color_levels); 570 acc_levels = tokenize_console(acc_color_levels); 571 if(acc_levels > MAX_ACCURACY_LEVELS) 572 acc_levels = MAX_ACCURACY_LEVELS; 573 if(acc_levels < 2) 574 LOG_INFO("Warning: accuracy_color_levels must contain at least 2 values\n"); 575 576 int i; 577 for(i = 0; i < acc_levels; ++i) 578 acc_lev[i] = stof(argv(i)) / 100.0; 579 } 580} 581 582void Accuracy_LoadColors() 583{ 584 if(time > acc_col_loadtime) 585 if(acc_levels >= 2) 586 { 587 int i; 588 for(i = 0; i < acc_levels; ++i) 589 acc_col[i] = stov(cvar_string(strcat("accuracy_color", ftos(i)))); 590 acc_col_loadtime = time + 2; 591 } 592} 593 594vector Accuracy_GetColor(float accuracy) 595{ 596 float factor; 597 vector color; 598 if(acc_levels < 2) 599 return '0 0 0'; // return black, can't determine the right color 600 601 // find the max level lower than acc 602 int j = acc_levels-1; 603 while(j && accuracy < acc_lev[j]) 604 --j; 605 606 // inject color j+1 in color j, how much depending on how much accuracy is higher than level j 607 factor = (accuracy - acc_lev[j]) / (acc_lev[j+1] - acc_lev[j]); 608 color = acc_col[j]; 609 color = color + factor * (acc_col[j+1] - color); 610 return color; 611} 612 613