#include "draw.qh" #include // draw the strafe-o-meter bar // aligns HUD elements perfectly in the hud area // also deals with wrapping around on edges, different HUD styles, etc. void StrafeHUD_DrawStrafeHUD(float startangle, float offsetangle, vector color, float alpha, int type, int gradient_type, float range) { float offset = StrafeHUD_AngleToOffset(startangle % 360, range); float width = StrafeHUD_AngleToWidth(offsetangle, range); float mirror_offset; float mirror_width; if(width <= 0) return; if(StrafeHUD_IsGradient(type)) { if(gradient_type == STRAFEHUD_GRADIENT_NONE) { type = STRAFEHUD_STYLE_DRAWFILL; if(alpha <= 0) return; } } else if(alpha <= 0) return; // how much is hidden by the current hud angle float hidden_width = (360 - range) / range * panel_size.x; float total_width = panel_size.x + hidden_width; float original_width = width; // required for gradient if(offset < 0) { mirror_width = min(fabs(offset), width); mirror_offset = offset + total_width; width += offset; offset = 0; } else { mirror_offset = offset - total_width; mirror_width = min(mirror_offset + width, width); if(mirror_offset < 0) mirror_offset = 0; } float overflow_width = offset + width - panel_size.x; width = max(width, 0); if(overflow_width > 0) width = panel_size.x - offset; else overflow_width = 0; vector size = panel_size; size.x = width; float original_offset = offset; // the accelerated gradient does the projection later if(type != STRAFEHUD_STYLE_GRADIENT) { if(size.x > 0) size.x = StrafeHUD_ProjectWidth(offset, size.x, range); offset = StrafeHUD_ProjectOffset(offset, range, false); } if(mirror_offset < 0) { mirror_width += mirror_offset; mirror_offset = 0; } float overflow_mirror_width = mirror_offset + mirror_width - panel_size.x; mirror_width = max(mirror_width, 0); if(overflow_mirror_width > 0) mirror_width = panel_size.x - mirror_offset; else overflow_mirror_width = 0; vector mirror_size = panel_size; mirror_size.x = mirror_width; float original_mirror_offset = mirror_offset; // the accelerated gradient does the projection later if(type != STRAFEHUD_STYLE_GRADIENT) { if(mirror_size.x > 0) mirror_size.x = StrafeHUD_ProjectWidth(mirror_offset, mirror_size.x, range); mirror_offset = StrafeHUD_ProjectOffset(mirror_offset, range, false); } switch(type) { default: case STRAFEHUD_STYLE_DRAWFILL: // no styling (drawfill) if(mirror_size.x > 0 && mirror_size.y > 0) drawfill(panel_pos + eX * mirror_offset, mirror_size, color, alpha, DRAWFLAG_NORMAL); if(size.x > 0 && size.y > 0) drawfill(panel_pos + eX * offset, size, color, alpha, DRAWFLAG_NORMAL); break; case STRAFEHUD_STYLE_PROGRESSBAR: // progress bar style if(mirror_size.x > 0 && mirror_size.y > 0) HUD_Panel_DrawProgressBar( panel_pos + eX * mirror_offset, mirror_size, "progressbar", 1, 0, 0, color, alpha, DRAWFLAG_NORMAL); if(size.x > 0 && size.y > 0) HUD_Panel_DrawProgressBar( panel_pos + eX * offset, size, "progressbar", 1, 0, 0, color, alpha, DRAWFLAG_NORMAL); break; case STRAFEHUD_STYLE_GRADIENT: // gradient style (types: 1 = left, 2 = right, 3 = both) case STRAFEHUD_STYLE_SOFT_GRADIENT: // determine whether the gradient starts in the mirrored or the non-mirrored area int gradient_start; float gradient_offset, gradient_mirror_offset; if(offset == 0 && mirror_offset == 0) gradient_start = width > mirror_width ? 2 : 1; else if(offset == 0) gradient_start = 2; else if(mirror_offset == 0) gradient_start = 1; else gradient_start = 0; switch(gradient_start) { default: case 0: // no offset required gradient_offset = gradient_mirror_offset = 0; break; case 1: // offset starts in non-mirrored area, mirrored area requires offset gradient_offset = 0; gradient_mirror_offset = original_width - (mirror_width + overflow_mirror_width); break; case 2: // offset starts in mirrored area, non-mirrored area requires offset gradient_offset = original_width - (width + overflow_width); gradient_mirror_offset = 0; } if(type == STRAFEHUD_STYLE_GRADIENT) { if(mirror_size.x > 0) StrafeHUD_DrawGradient( color, autocvar_hud_panel_strafehud_bar_neutral_color, mirror_size, original_width, mirror_offset, alpha, gradient_mirror_offset, gradient_type, range); if(size.x > 0) StrafeHUD_DrawGradient( color, autocvar_hud_panel_strafehud_bar_neutral_color, size, original_width, offset, alpha, gradient_offset, gradient_type, range); } else { if(mirror_size.x > 0) StrafeHUD_DrawSoftGradient( color, autocvar_hud_panel_strafehud_bar_neutral_color, mirror_size, original_width, mirror_offset, original_mirror_offset, alpha, gradient_mirror_offset, gradient_type, range); if(size.x > 0) StrafeHUD_DrawSoftGradient( color, autocvar_hud_panel_strafehud_bar_neutral_color, size, original_width, offset, original_offset, alpha, gradient_offset, gradient_type, range); } } } // accelerated gradient, does not support non-linear projection of the color and opacity within individual segments void StrafeHUD_DrawGradient( vector color1, vector color2, vector size, float original_width, float offset, float alpha, float gradient_offset, int gradient_type, float range) { if(gradient_type == STRAFEHUD_GRADIENT_BOTH) { original_width /= 2; vector size1 = size; size1.x = bound(0, original_width - gradient_offset, size.x); vector size2 = size; size2.x = size.x - size1.x; if(size1.x > 0) StrafeHUD_DrawGradient(color1, color2, size1, original_width, offset, alpha, gradient_offset, STRAFEHUD_GRADIENT_LEFT, range); if(size2.x > 0) StrafeHUD_DrawGradient(color1, color2, size2, original_width, offset + size1.x, alpha, max(0, gradient_offset - original_width), STRAFEHUD_GRADIENT_RIGHT, range); return; } vector gradient_start = eX * offset; float gradient_width = StrafeHUD_ProjectWidth(gradient_start.x, size.x, range); gradient_start.x = StrafeHUD_ProjectOffset(gradient_start.x, range, false); vector gradient_end = gradient_start + eX * gradient_width; vector gradient_height = eY * size.y; float alpha1 = bound(0, alpha, 1); float alpha2 = bound(0, autocvar_hud_panel_strafehud_bar_neutral_alpha * panel_fg_alpha, 1); if(alpha1 + alpha2 == 0) return; float ratio1 = gradient_offset / original_width; float ratio2 = (gradient_offset + size.x) / original_width; if(gradient_type == STRAFEHUD_GRADIENT_LEFT) { ratio1 = 1 - ratio1; ratio2 = 1 - ratio2; } vector origin = HUD_Shift(panel_pos); gradient_start.x = HUD_ScaleX(gradient_start.x); gradient_end.x = HUD_ScaleX(gradient_end.x); gradient_height.y = HUD_ScaleY(gradient_height.y); R_BeginPolygon("", DRAWFLAG_NORMAL, true); R_PolygonVertex(origin + gradient_start, '0 0 0', color1, alpha1 * (1 - ratio1)); R_PolygonVertex(origin + gradient_start + gradient_height, '0 0 0', color1, alpha1 * (1 - ratio1)); R_PolygonVertex(origin + gradient_end + gradient_height, '0 0 0', color1, alpha1 * (1 - ratio2)); R_PolygonVertex(origin + gradient_end, '0 0 0', color1, alpha1 * (1 - ratio2)); R_EndPolygon(); R_BeginPolygon("", DRAWFLAG_NORMAL, true); R_PolygonVertex(origin + gradient_start, '0 0 0', color2, alpha2 * ratio1); R_PolygonVertex(origin + gradient_start + gradient_height, '0 0 0', color2, alpha2 * ratio1); R_PolygonVertex(origin + gradient_end + gradient_height, '0 0 0', color2, alpha2 * ratio2); R_PolygonVertex(origin + gradient_end, '0 0 0', color2, alpha2 * ratio2); R_EndPolygon(); } // more expensive gradient rendering which does not rely on vertex gradients (required to properly render the color/opacity of individual segments in non-linear projection modes) void StrafeHUD_DrawSoftGradient( vector color1, vector color2, vector size, float original_width, float offset, float original_offset, float alpha, float gradient_offset, int gradient_type, float range) { float alpha1 = bound(0, alpha, 1); float alpha2 = bound(0, autocvar_hud_panel_strafehud_bar_neutral_alpha * panel_fg_alpha, 1); if(alpha1 + alpha2 == 0) return; float color_ratio = alpha1 / (alpha1 + alpha2); vector segment_size = size; for(int i = 0; i < size.x; ++i) { segment_size.x = min(size.x - i, 1); // each gradient segment is 1 unit wide except if there is less than 1 unit of gradient remaining float segment_offset = offset + i; float ratio_offset = segment_offset + segment_size.x / 2; ratio_offset = StrafeHUD_ProjectOffset(ratio_offset, range, true); ratio_offset += gradient_offset; float ratio = (ratio_offset - original_offset) / original_width * (gradient_type == STRAFEHUD_GRADIENT_BOTH ? 2 : 1); if(ratio > 1) ratio = 2 - ratio; if(gradient_type != STRAFEHUD_GRADIENT_RIGHT) ratio = 1 - ratio; float alpha_ratio = alpha1 - (alpha1 - alpha2) * ratio; float combine_ratio1 = ratio * (1 - color_ratio); float combine_ratio2 = (1 - ratio) * color_ratio; ratio = (combine_ratio1 + combine_ratio2) == 0 ? 1 : combine_ratio1 / (combine_ratio1 + combine_ratio2); if(alpha_ratio > 0) drawfill( panel_pos + eX * segment_offset, segment_size, StrafeHUD_MixColors(color1, color2, ratio), alpha_ratio, DRAWFLAG_NORMAL); } } // draw the strafe arrows (inspired by drawspritearrow() in common/mutators/mutator/waypoints/waypointsprites.qc) void StrafeHUD_DrawStrafeArrow(vector origin, float size, vector color, float alpha, bool flipped, float connection_width) { // alpha and size already checked origin = HUD_Shift(origin); float width = HUD_ScaleX(size * 2 + connection_width); float height = HUD_ScaleY(size); if(flipped) origin.y -= size; R_BeginPolygon("", DRAWFLAG_NORMAL, true); if(connection_width > 0) { R_PolygonVertex(origin + (connection_width / 2 * eX) + (flipped ? height * eY : '0 0 0'), '0 0 0', color, alpha); R_PolygonVertex(origin - (connection_width / 2 * eX) + (flipped ? height * eY : '0 0 0'), '0 0 0', color, alpha); } else { R_PolygonVertex(origin + (flipped ? height * eY : '0 0 0'), '0 0 0', color, alpha); } R_PolygonVertex(origin + (flipped ? '0 0 0' : height * eY) - (width / 2) * eX, '0 0 0', color, alpha); R_PolygonVertex(origin + (flipped ? '0 0 0' : height * eY) + (width / 2) * eX, '0 0 0', color, alpha); R_EndPolygon(); } // draw a fading text indicator above or below the strafe meter void StrafeHUD_DrawTextIndicator( string text, float height, vector color, float fadetime, float lasttime, vector pos, float offset_top, float offset_bottom) { float time_frac = (time - lasttime) / fadetime; if(height <= 0 || lasttime <= 0 || fadetime <= 0 || time_frac > 1) return; float alpha = cos(time_frac * M_PI_2); // fade non-linear like the physics panel does vector size = panel_size; size.y = height; if(pos.y >= 1) { --pos.y; // for calculations the position should not start at +1 pos = StrafeHUD_CalculateTextIndicatorPosition(pos); pos.y += size.y + offset_top; pos.y *= -1; // it's more intuitive for up to be positive } else if(pos.y <= -1) { ++pos.y; // for calculations the position should not start at -1 pos = StrafeHUD_CalculateTextIndicatorPosition(pos); pos.y *= -1; // it's more intuitive for down to be negative pos.y += panel_size.y + offset_bottom; } else return; drawstring_aspect(panel_pos + pos, text, size, color, alpha * panel_fg_alpha, DRAWFLAG_NORMAL); } // checks whether the current style is a gradient style bool StrafeHUD_IsGradient(int style) { return style == STRAFEHUD_STYLE_GRADIENT || style == STRAFEHUD_STYLE_SOFT_GRADIENT; }