/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team

This file is part of OpenMoHAA source code.

OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110EV_DEFAULT301  USA
===========================================================================
*/

#include "cl_ui.h"
#include "../qcommon/localization.h"

Event EV_Layout_PlayerStat
(
    "playerstat",
    EV_DEFAULT,
    "i",
    "playerstatValue",
    "set playerstat for layout\n"
);
// Added in 2.0
Event EV_Layout_PlayerStatAlpha
(
    "playerstatalpha",
    EV_DEFAULT,
    "i",
    "playerstatValue",
    "set playerstat to control alpha for layout\n"
);
Event EV_Layout_PlayerStatConfigstring
(
    "playerstatconfigstring",
    EV_DEFAULT,
    "i",
    "playerstatValue",
    "set playerstat for layout - will print out the corresponding configstring\n"
);
Event EV_Layout_MaxPlayerStat
(
    "maxplayerstat",
    EV_DEFAULT,
    "i",
    "maxPlayerstatValue",
    "set the playerstat that holds the max value for that stat.  Used for statbars\n"
);
Event EV_Layout_ItemIndex
(
    "itemstat",
    EV_DEFAULT,
    "i",
    "itemstatValue",
    "set itemstat for layout\n"
);
Event EV_Layout_InventoryRenderModelIndex
(
    "invmodelhand",
    EV_DEFAULT,
    "i",
    "handindex",
    "set the specified hand index to render a 3d model from\n"
);
Event EV_Layout_Statbar
(
    "statbar",
    EV_DEFAULT,
    "sII",
    "orientation min max",
    "make this label present the stat using a graphical bar.  Optionally specify a min and max value"
);
Event EV_Layout_StatbarTileShader
(
    "statbar_tileshader",
    EV_DEFAULT,
    "s",
    "shader",
    "set the tile shader for this statbar"
);
Event EV_Layout_StatbarTileShader_Flash
(
    "statbar_tileshader_flash",
    EV_DEFAULT,
    "s",
    "shader",
    "set the flash tile shader for this statbar"
);
Event EV_Layout_StatbarShader
(
    "statbar_shader",
    EV_DEFAULT,
    "s",
    "shader",
    "set the shader for this statbar"
);
Event EV_Layout_StatbarShader_Flash
(
    "statbar_shader_flash",
    EV_DEFAULT,
    "s",
    "shader",
    "set the flash shader for this statbar"
);
// Added in 2.0
Event EV_Layout_StatbarShader_Marker
(
    "statbar_shader_marker",
    EV_DEFAULT,
    "s",
    "shader",
    "set the marker shader for this statbar\nThis is drawn at the end of the status bar"
);
Event EV_Layout_Statbar_EndAngles
(
    "statbar_endangles",
    EV_DEFAULT,
    "ff",
    "startang endang",
    "Sets the start and end angles for a circular stat bar"
);
Event EV_Layout_Statbar_NeedleWidth
(
    "statbar_needlewidth",
    EV_DEFAULT,
    "f",
    "width",
    "Sets the width of the needle for a needle statbar"
);
Event EV_Layout_Statbar_RotatorSize
(
    "statbar_rotatorsize",
    EV_DEFAULT,
    "f",
    "width",
    "Sets the width of the needle for a needle statbar"
);
Event EV_Layout_DrawModelName
(
    "invmodelname",
    EV_DEFAULT,
    "s",
    "name",
    "set the name of the 3d model to render\n"
);
Event EV_Layout_RenderModel
(
    "rendermodel",
    EV_DEFAULT,
    "b",
    "bool",
    "Render the model specified by the cvar."
);
Event EV_Layout_RenderModelOffset
(
    "modeloffset",
    EV_DEFAULT,
    "v",
    "offset",
    "Render model offset"
);
Event EV_Layout_RenderModelRotateOffset
(
    "modelrotateoffset",
    EV_DEFAULT,
    "v",
    "offset",
    "Render model rotation offset"
);
Event EV_Layout_RenderModelAngles
(
    "modelangles",
    EV_DEFAULT,
    "v",
    "angles",
    "Render model angles"
);
Event EV_Layout_RenderModelScale
(
    "modelscale",
    EV_DEFAULT,
    "f",
    "scale",
    "Render model scale"
);
Event EV_Layout_RenderModelAnim
(
    "modelanim",
    EV_DEFAULT,
    "s",
    "anim",
    "Render model anim"
);
Event EV_ClearInvItemReference
(
    "clearinvitemref",
    EV_DEFAULT,
    NULL,
    NULL,
    "used internally when the inventory is reset\n"
);

CLASS_DECLARATION(UILabel, UIFakkLabel, NULL) {
    {&EV_Layout_PlayerStat,                &UIFakkLabel::LayoutPlayerStat             },
    {&EV_Layout_PlayerStatAlpha,           &UIFakkLabel::LayoutPlayerStatAlpha        },
    {&EV_Layout_PlayerStatConfigstring,    &UIFakkLabel::LayoutPlayerStatConfigstring },
    {&EV_Layout_MaxPlayerStat,             &UIFakkLabel::LayoutMaxPlayerStat          },
    {&EV_Layout_ItemIndex,                 &UIFakkLabel::LayoutItemIndex              },
    {&EV_Layout_InventoryRenderModelIndex, &UIFakkLabel::InventoryRenderModelIndex    },
    {&EV_Layout_Statbar,                   &UIFakkLabel::LayoutStatbar                },
    {&EV_Layout_StatbarTileShader,         &UIFakkLabel::LayoutStatbarTileShader      },
    {&EV_Layout_StatbarShader,             &UIFakkLabel::LayoutStatbarShader          },
    {&EV_Layout_StatbarTileShader_Flash,   &UIFakkLabel::LayoutStatbarTileShader_Flash},
    {&EV_Layout_StatbarShader_Marker,      &UIFakkLabel::LayoutStatbarShader_Marker   },
    {&EV_Layout_StatbarShader_Flash,       &UIFakkLabel::LayoutStatbarShader_Flash    },
    {&EV_Layout_Statbar_EndAngles,         &UIFakkLabel::LayoutStatbarEndAngles       },
    {&EV_Layout_Statbar_NeedleWidth,       &UIFakkLabel::LayoutStatbarNeedleWidth     },
    {&EV_Layout_Statbar_RotatorSize,       &UIFakkLabel::LayoutStatbarRotatorSize     },
    {&EV_Layout_DrawModelName,             &UIFakkLabel::LayoutModelName              },
    {&EV_Layout_RenderModel,               &UIFakkLabel::LayoutRenderModel            },
    {&EV_Layout_RenderModelOffset,         &UIFakkLabel::LayoutRenderModelOffset      },
    {&EV_Layout_RenderModelRotateOffset,   &UIFakkLabel::LayoutRenderModelRotateOffset},
    {&EV_Layout_RenderModelAngles,         &UIFakkLabel::LayoutRenderModelAngles      },
    {&EV_Layout_RenderModelScale,          &UIFakkLabel::LayoutRenderModelScale       },
    {&EV_Layout_RenderModelAnim,           &UIFakkLabel::LayoutRenderModelAnim        },
    {&EV_ClearInvItemReference,            &UIFakkLabel::ClearInvItemReference        },
    {NULL,                                 NULL                                       }
};

UIFakkLabel::UIFakkLabel()
{
    m_statbar_min = m_statbar_max = -1.0;
    m_stat                        = -1;
    m_stat_alpha                  = -1;
    m_stat_configstring           = -1;
    m_maxstat                     = -1;
    m_itemindex                   = -1;
    m_inventoryrendermodelindex   = -1;
    m_rendermodel                 = false;
    m_statbar_or                  = L_STATBAR_NONE;
    m_statbar_material            = NULL;
    m_statbar_material_flash      = NULL;
    m_statbar_material_marker     = NULL;
    m_lastitemindex               = -1;
}

void UIFakkLabel::LayoutPlayerStat(Event *ev)
{
    m_stat = ev->GetInteger(1);
}

void UIFakkLabel::LayoutPlayerStatAlpha(Event *ev)
{
    m_stat_alpha = ev->GetInteger(1);
}

void UIFakkLabel::LayoutPlayerStatConfigstring(Event *ev)
{
    m_stat_configstring = ev->GetInteger(1);
}

void UIFakkLabel::LayoutMaxPlayerStat(Event *ev)
{
    m_maxstat = ev->GetInteger(1);
}

void UIFakkLabel::LayoutItemIndex(Event *ev)
{
    m_itemindex = ev->GetInteger(1);
}

void UIFakkLabel::InventoryRenderModelIndex(Event *ev)
{
    m_inventoryrendermodelindex = ev->GetInteger(1);
}

void UIFakkLabel::LayoutModelName(Event *ev)
{
    m_sDrawModelName = ev->GetString(1);
}

void UIFakkLabel::ClearInvItemReference(Event *ev)
{
    m_lastitem      = NULL;
    m_lastitemindex = -1;
}

void UIFakkLabel::LayoutStatbar(Event *ev)
{
    str _or = ev->GetString(1);

    if (ev->NumArgs() > 1) {
        m_statbar_min = ev->GetFloat(2);
        m_statbar_max = ev->GetFloat(3);
    }

    if (!str::icmp(_or, "horizontal")) {
        m_statbar_or = L_STATBAR_HORIZONTAL;
    } else if (!str::icmp(_or, "vertical_stagger_even")) {
        m_statbar_or = L_STATBAR_VERTICAL_STAGGER_EVEN;
    } else if (!str::icmp(_or, "vertical_stagger_odd")) {
        m_statbar_or = L_STATBAR_VERTICAL_STAGGER_ODD;
    } else if (!str::icmp(_or, "vertical")) {
        m_statbar_or = L_STATBAR_VERTICAL;
    } else if (!str::icmp(_or, "vertical_reverse")) {
        m_statbar_or = L_STATBAR_VERTICAL_REVERSE;
    } else if (!str::icmp(_or, "circular")) {
        m_statbar_or = L_STATBAR_CIRCULAR;

        if (!m_statbar_material) {
            m_statbar_material = uWinMan.RegisterShader("blank");
        }
    } else if (!str::icmp(_or, "needle")) {
        m_statbar_or = L_STATBAR_NEEDLE;

        if (!m_statbar_material) {
            m_statbar_material = uWinMan.RegisterShader("blank");
        }
    } else if (!str::icmp(_or, "rotator")) {
        m_statbar_or = L_STATBAR_ROTATOR;

        if (!m_statbar_material) {
            m_statbar_material = uWinMan.RegisterShader("blank");
        }
    } else if (!str::icmp(_or, "compass")) {
        m_statbar_or = L_STATBAR_COMPASS;

        if (!m_statbar_material) {
            m_statbar_material = uWinMan.RegisterShader("blank");
        }
    } else if (!str::icmp(_or, "spinner")) {
        m_statbar_or = L_STATBAR_SPINNER;

        if (!m_statbar_material) {
            m_statbar_material = uWinMan.RegisterShader("blank");
        }
    } else if (!str::icmp(_or, "headingspinner")) {
        m_statbar_or = L_STATBAR_HEADING_SPINNER;

        if (!m_statbar_material) {
            m_statbar_material = uWinMan.RegisterShader("blank");
        }
    } else {
        warning("LayoutPlayerStat", "Invalid statbar orientation: \"%s\"", _or.c_str());
    }
}

void UIFakkLabel::LayoutStatbarShader(Event *ev)
{
    m_statbar_material = uWinMan.RegisterShader(ev->GetString(1));
}

void UIFakkLabel::LayoutStatbarTileShader(Event *ev)
{
    m_statbar_material = uWinMan.RegisterShader(ev->GetString(1));
    AddFlag(WF_TILESHADER);
}

void UIFakkLabel::LayoutStatbarShader_Flash(Event *ev)
{
    m_statbar_material_flash = uWinMan.RegisterShader(ev->GetString(1));
}

void UIFakkLabel::LayoutStatbarShader_Marker(Event *ev)
{
    m_statbar_material_marker = uWinMan.RegisterShader(ev->GetString(1));
}

void UIFakkLabel::LayoutStatbarEndAngles(Event *ev)
{
    m_angles[0] = ev->GetFloat(1);
    m_angles[1] = ev->GetFloat(2);
}

void UIFakkLabel::LayoutStatbarNeedleWidth(Event *ev)
{
    m_angles[2] = ev->GetFloat(1);
}

void UIFakkLabel::LayoutStatbarRotatorSize(Event *ev)
{
    m_angles[2] = ev->GetFloat(1);

    if (ev->NumArgs() > 1) {
        m_scale = ev->GetFloat(2);
    } else {
        m_scale = m_angles[2];
    }
}

void UIFakkLabel::LayoutStatbarTileShader_Flash(Event *ev)
{
    m_statbar_material_flash = uWinMan.RegisterShader(ev->GetString(1));
    AddFlag(WF_TILESHADER);
}

void UIFakkLabel::LayoutRenderModel(Event *ev)
{
    m_rendermodel = ev->GetBoolean(1);
}

void UIFakkLabel::LayoutRenderModelOffset(Event *ev)
{
    Vector vec = ev->GetVector(1);
    VectorCopy(vec, m_offset);
}

void UIFakkLabel::LayoutRenderModelRotateOffset(Event *ev)
{
    Vector vec = ev->GetVector(1);
    VectorCopy(vec, m_rotateoffset);
}

void UIFakkLabel::LayoutRenderModelAngles(Event *ev)
{
    Vector vec = ev->GetVector(1);
    VectorCopy(vec, m_angles);
}

void UIFakkLabel::LayoutRenderModelScale(Event *ev)
{
    m_scale = ev->GetFloat(1);
}

void UIFakkLabel::LayoutRenderModelAnim(Event *ev)
{
    m_anim = ev->GetString(1);
}

void UIFakkLabel::DrawStatbar(float frac)
{
    vec4_t    col;
    float     alpha;
    qhandle_t hMat;
    float     w, h;
    float     fvWidth, fvHeight;

    col[0] = col[1] = col[2] = col[3] = 1.0;

    switch (m_statbar_or) {
    case L_STATBAR_CIRCULAR:
        DrawStatCircle(frac);
        return;
    case L_STATBAR_NEEDLE:
        DrawStatNeedle(frac);
        return;
    case L_STATBAR_ROTATOR:
        DrawStatRotator(frac);
        return;
    case L_STATBAR_COMPASS:
        DrawStatCompass(frac);
        return;
    case L_STATBAR_SPINNER:
    case L_STATBAR_HEADING_SPINNER:
        DrawStatSpinner(frac);
        return;
    default:
        break;
    }

    if (!frac) {
        return;
    }

    if (frac != m_lastfrac) {
        m_flashtime = uid.time;
    }
    m_lastfrac = frac;

    alpha = 1.0 - ((float)uid.time - m_flashtime) / 1500.0;
    alpha = Q_clamp_float(alpha, 0, 1);

    col[3] = alpha;

    if (m_statbar_material) {
        if (m_flags & WF_TILESHADER) {
            switch (m_statbar_or) {
            case L_STATBAR_HORIZONTAL:
                {
                    float width = frac * m_frame.size.width;

                    fvWidth = m_frame.size.width / m_vVirtualScale[0] / uii.Rend_GetShaderWidth(m_statbar_material->GetMaterial());
                    fvHeight = m_frame.size.height / m_vVirtualScale[1] / uii.Rend_GetShaderHeight(m_statbar_material->GetMaterial());

                    re.DrawStretchPic(0.0, 0.0, width, m_frame.size.height, 0, 0, fvWidth, fvHeight, m_statbar_material->GetMaterial());
                    //re.DrawTilePic(0.0, 0.0, width, m_frame.size.height, m_statbar_material->GetMaterial());

                    if (alpha != 0.0 && m_statbar_material_flash) {
                        re.SetColor(col);

                        fvWidth = m_frame.size.width / m_vVirtualScale[0] / uii.Rend_GetShaderWidth(m_statbar_material_flash->GetMaterial());
                        fvHeight = m_frame.size.height / m_vVirtualScale[1] / uii.Rend_GetShaderHeight(m_statbar_material_flash->GetMaterial());

                        re.DrawStretchPic(0.0, 0.0, width, m_frame.size.height, 0, 0, fvWidth, fvHeight, m_statbar_material_flash->GetMaterial());
                        //re.DrawTilePic(0.0, 0.0, width, m_frame.size.height, m_statbar_material_flash->GetMaterial());
                    }

                    if (m_statbar_material_marker) {
                        hMat = m_statbar_material_marker->GetMaterial();
                        w    = re.GetShaderWidth(hMat);
                        h    = re.GetShaderHeight(hMat);

                        re.DrawStretchPic(
                            m_frame.size.width * frac - w * 0.5,
                            m_frame.size.height * 0.5 - h * 0.5,
                            w,
                            h,
                            0,
                            0,
                            1,
                            1,
                            hMat
                        );
                    }
                    break;
                }
            case L_STATBAR_VERTICAL:
            case L_STATBAR_VERTICAL_REVERSE:
            case L_STATBAR_VERTICAL_STAGGER_EVEN:
            case L_STATBAR_VERTICAL_STAGGER_ODD:
                {
                    float y = m_frame.size.height * (1.0 - frac);

                    fvWidth = m_frame.size.width / m_vVirtualScale[0] / uii.Rend_GetShaderWidth(m_statbar_material->GetMaterial());
                    fvHeight = m_frame.size.height / m_vVirtualScale[1] / uii.Rend_GetShaderHeight(m_statbar_material->GetMaterial());

                    re.DrawStretchPic(0.0, y, m_frame.size.width, m_frame.size.height, 0, 0, fvWidth, fvHeight, m_statbar_material->GetMaterial());
                    //re.DrawTilePic(0.0, y, m_frame.size.width, m_frame.size.height, m_statbar_material->GetMaterial());

                    if (alpha != 0.0 && m_statbar_material_flash) {
                        re.SetColor(col);

                        fvWidth = m_frame.size.width / m_vVirtualScale[0] / uii.Rend_GetShaderWidth(m_statbar_material_flash->GetMaterial());
                        fvHeight = m_frame.size.height / m_vVirtualScale[1] / uii.Rend_GetShaderHeight(m_statbar_material_flash->GetMaterial());

                        re.DrawStretchPic(0.0, y, m_frame.size.width, m_frame.size.height, 0, 0, fvWidth, fvHeight, m_statbar_material_flash->GetMaterial());
                        //re.DrawTilePic(
                        //    0.0, y, m_frame.size.width, m_frame.size.height, m_statbar_material_flash->GetMaterial()
                        //);
                    }

                    if (m_statbar_material_marker) {
                        hMat = m_statbar_material_marker->GetMaterial();
                        w    = re.GetShaderWidth(hMat);
                        h    = re.GetShaderHeight(hMat);

                        re.DrawStretchPic(
                            m_frame.size.width * frac - w * 0.5,
                            m_frame.size.height * 0.5 - h * 0.5,
                            w,
                            h,
                            0,
                            0,
                            1,
                            1,
                            hMat
                        );
                    }
                    break;
                }
            default:
                break;
            }
        } else {
            switch (m_statbar_or) {
            case L_STATBAR_HORIZONTAL:
                {
                    float width = frac * m_frame.size.width;

                    re.DrawStretchPic(
                        0.0, 0.0, width, m_frame.size.height, 0.0, 0.0, 1.0, 1.0, m_statbar_material->GetMaterial()
                    );

                    if (alpha != 0.0 && m_statbar_material_flash) {
                        re.SetColor(col);
                        re.DrawStretchPic(
                            0.0,
                            0.0,
                            width,
                            m_frame.size.height,
                            0.0,
                            0.0,
                            1.0,
                            1.0,
                            m_statbar_material_flash->GetMaterial()
                        );
                    }

                    if (m_statbar_material_marker) {
                        hMat = m_statbar_material_marker->GetMaterial();
                        w    = re.GetShaderWidth(hMat);
                        h    = re.GetShaderHeight(hMat);

                        re.DrawStretchPic(
                            m_frame.size.width * frac - w * 0.5,
                            m_frame.size.height * 0.5 - h * 0.5,
                            w,
                            h,
                            0,
                            0,
                            1,
                            1,
                            hMat
                        );
                    }
                    break;
                }
            case L_STATBAR_VERTICAL:
            case L_STATBAR_VERTICAL_STAGGER_EVEN:
            case L_STATBAR_VERTICAL_STAGGER_ODD:
                {
                    float y      = m_frame.size.height * (1.0 - frac);
                    float height = m_frame.size.height * frac;

                    re.DrawStretchPic(
                        0.0, y, m_frame.size.width, height, 0.0, 1.0 - frac, 1.0, 1.0, m_statbar_material->GetMaterial()
                    );

                    if (alpha != 0.0 && m_statbar_material_flash) {
                        re.SetColor(col);
                        re.DrawStretchPic(
                            0.0,
                            y,
                            m_frame.size.width,
                            height,
                            0.0,
                            1.0 - frac,
                            1.0,
                            1.0,
                            m_statbar_material_flash->GetMaterial()
                        );
                    }

                    if (m_statbar_material_marker) {
                        hMat = m_statbar_material_marker->GetMaterial();
                        w    = re.GetShaderWidth(hMat);
                        h    = re.GetShaderHeight(hMat);

                        re.DrawStretchPic(
                            m_frame.size.width * frac - w * 0.5,
                            m_frame.size.height * 0.5 - h * 0.5,
                            w,
                            h,
                            0,
                            0,
                            1,
                            1,
                            hMat
                        );
                    }
                    break;
                }
            case L_STATBAR_VERTICAL_REVERSE:
                {
                    float height = m_frame.size.height * frac;

                    re.DrawStretchPic(
                        0.0, 0.0, m_frame.size.width, height, 0.0, 0.0, 1.0, frac, m_statbar_material->GetMaterial()
                    );

                    if (alpha != 0.0 && m_statbar_material_flash) {
                        re.SetColor(col);
                        re.DrawStretchPic(
                            0.0,
                            0.0,
                            m_frame.size.width,
                            height,
                            0.0,
                            0.0,
                            1.0,
                            frac,
                            m_statbar_material_flash->GetMaterial()
                        );
                    }
                    break;
                }
            default:
                break;
            }
        }
    } else {
        switch (m_statbar_or) {
        case L_STATBAR_VERTICAL:
        case L_STATBAR_VERTICAL_STAGGER_EVEN:
        case L_STATBAR_VERTICAL_STAGGER_ODD:
            DrawBox(
                0.0,
                m_frame.size.height * (1.0 - frac),
                m_frame.size.width,
                m_frame.size.height,
                m_foreground_color,
                1.0
            );
            break;
        case L_STATBAR_HORIZONTAL:
            DrawBox(0.0, 0.0, frac * m_frame.size.width, m_frame.size.height, m_foreground_color, 1.0);
            break;
        default:
            break;
        }
    }
}

void UIFakkLabel::DrawStatCircle(float frac)
{
    vec4_t col;
    float  alpha;
    float  fCurrAng, fEndAng, fNextAng;
    vec2_t vVerts[3];
    vec2_t vTexCoords[3];

    col[0] = 1.f;
    col[1] = 1.f;
    col[2] = 1.f;
    col[3] = 1.f;

    if (m_lastfrac != frac) {
        m_flashtime = uid.time;
    }
    m_lastfrac = frac;

    alpha = 1.f - (uid.time - m_flashtime) / 1500.f;
    if (alpha < 0.f) {
        alpha = 0.f;
    } else if (alpha > 1.f) {
        alpha = 1.f;
    }

    col[3] = alpha;

    vVerts[0][0]     = m_frame.size.width * 0.5f;
    vVerts[0][1]     = m_frame.size.height * 0.5f;
    vTexCoords[0][0] = 0.5f;
    vTexCoords[0][1] = 0.5f;

    if (ui_health_start->value && ui_health_end->value) {
        m_angles[0] = ui_health_start->value;
        m_angles[1] = ui_health_end->value;
    }

    fCurrAng = m_angles[0];
    fEndAng  = frac * (m_angles[1] - fCurrAng) + fCurrAng;
    if (m_angles[1] > fCurrAng) {
        StatCircleTexCoord(fCurrAng, vTexCoords[1]);
        vVerts[1][0] = m_frame.size.width * vTexCoords[1][0];
        vVerts[1][1] = m_frame.size.height * vTexCoords[1][1];

        if ((int)fCurrAng - (((int)fCurrAng - 45) % 90 + 90) % 90
            == (int)fEndAng - (((int)fEndAng - 45) % 90 + 90) % 90) {
            StatCircleTexCoord(fEndAng, vTexCoords[2]);
            vVerts[2][0] = m_frame.size.width * vTexCoords[2][0];
            vVerts[2][1] = m_frame.size.height * vTexCoords[2][1];

            if (alpha && m_statbar_material_flash) {
                re.SetColor(m_foreground_color);
            }

            re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());

            if (alpha && m_statbar_material_flash) {
                re.SetColor(col);
                re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material_flash->GetMaterial());
            }
        } else {
            while (fCurrAng > fEndAng) {
                fNextAng = ((int)fCurrAng - (((int)fCurrAng - 45) % 90 + 90) % 90) + 90.0;
                if (fNextAng > fEndAng) {
                    fNextAng = fEndAng;
                }

                StatCircleTexCoord(fNextAng, vTexCoords[2]);
                vVerts[2][0] = m_frame.size.width * vTexCoords[2][0];
                vVerts[2][1] = m_frame.size.height * vTexCoords[2][1];

                if (alpha && m_statbar_material_flash) {
                    re.SetColor(m_foreground_color);
                }

                re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());

                if (alpha && m_statbar_material_flash) {
                    re.SetColor(col);
                    re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material_flash->GetMaterial());
                }

                vTexCoords[1][0] = vTexCoords[2][0];
                vTexCoords[1][1] = vTexCoords[2][1];
                vVerts[1][0]     = vVerts[2][0];
                vVerts[1][1]     = vVerts[2][1];
                fCurrAng         = fNextAng;
            }
        }
    } else {
        StatCircleTexCoord(fCurrAng, vTexCoords[2]);
        vVerts[2][0] = m_frame.size.width * vTexCoords[2][0];
        vVerts[2][1] = m_frame.size.height * vTexCoords[2][1];
        if ((int)fCurrAng - (((int)fCurrAng - 45) % 90 + 90) % 90
            == (int)fEndAng - (((int)fEndAng - 45) % 90 + 90) % 90) {
            StatCircleTexCoord(fEndAng, vTexCoords[1]);
            vVerts[1][0] = m_frame.size.width * vTexCoords[1][0];
            vVerts[1][1] = m_frame.size.height * vTexCoords[1][1];

            if (alpha && m_statbar_material_flash) {
                re.SetColor(m_foreground_color);
            }

            re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());

            if (alpha && m_statbar_material_flash) {
                re.SetColor(col);
                re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material_flash->GetMaterial());
            }
        } else {
            while (fCurrAng > fEndAng) {
                fNextAng = ((int)fCurrAng - (((int)fCurrAng + 45) % 90 + 90) % 90);
                if (fNextAng == fCurrAng) {
                    fNextAng -= 90.f;
                }

                if (fNextAng < fEndAng) {
                    fNextAng = fEndAng;
                }

                StatCircleTexCoord(fNextAng, vTexCoords[1]);
                vVerts[1][0] = m_frame.size.width * vTexCoords[1][0];
                vVerts[1][1] = m_frame.size.height * vTexCoords[1][1];

                if (alpha && m_statbar_material_flash) {
                    re.SetColor(m_foreground_color);
                }

                re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());

                if (alpha && m_statbar_material_flash) {
                    re.SetColor(col);
                    re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material_flash->GetMaterial());
                }

                vTexCoords[2][0] = vTexCoords[1][0];
                vTexCoords[2][1] = vTexCoords[1][1];
                vVerts[2][0]     = vVerts[1][0];
                vVerts[2][1]     = vVerts[1][1];
                fCurrAng         = fNextAng;
            }
        }
    }
}

void UIFakkLabel::DrawStatNeedle(float frac)
{
    vec4_t col;
    float  alpha;
    float  fTargAng;
    float  fSinVal;
    float  fCosVal;
    vec2_t vCenter;
    vec2_t vVerts[3];
    vec2_t vTexCoords[3];
    vec2_t vNeedleDir;
    vec2_t vSideDir;

    col[0] = 1.f;
    col[1] = 1.f;
    col[2] = 1.f;
    col[3] = 1.f;

    if (m_lastfrac != frac) {
        m_flashtime = uid.time;
    }
    m_lastfrac = frac;

    alpha = 1.f - (uid.time - m_flashtime) / 1500.f;
    if (alpha < 0.f) {
        alpha = 0.f;
    } else if (alpha > 1.f) {
        alpha = 1.f;
    }

    col[3] = alpha;

    fTargAng = DEG2RAD(frac * (m_angles[1] - m_angles[0]) + m_angles[0]);
    fSinVal  = sin(fTargAng);
    fCosVal  = cos(fTargAng);

    vSideDir[0]   = m_frame.size.width * 0.5f;
    vSideDir[1]   = m_frame.size.height * 0.5f;
    vCenter[0]    = m_frame.size.width + fSinVal * vSideDir[0];
    vCenter[1]    = m_frame.size.height + -fCosVal * vSideDir[1];
    vNeedleDir[0] = m_angles[2] * fCosVal;
    vNeedleDir[1] = m_angles[2] * fSinVal;

    vVerts[0][0] = vCenter[0] - vNeedleDir[0];
    vVerts[0][1] = vCenter[1] - vNeedleDir[1];
    vVerts[1][0] = vCenter[0] + vNeedleDir[0];
    vVerts[1][1] = vCenter[1] + vNeedleDir[1];
    vVerts[2][0] = vSideDir[0] - vNeedleDir[1] - vNeedleDir[0];
    vVerts[2][1] = vSideDir[1] + vNeedleDir[0] - vNeedleDir[1];

    vTexCoords[0][0] = 0.f;
    vTexCoords[0][1] = 0.f;
    vTexCoords[1][0] = 1.f;
    vTexCoords[1][1] = 0.f;
    vTexCoords[2][0] = 0.f;
    vTexCoords[2][1] = 1.f;

    if (alpha != 0.f) {
        re.SetColor(m_foreground_color);
    }

    re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());

    if (alpha != 0.f && m_statbar_material_flash) {
        re.SetColor(col);
        re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material_flash->GetMaterial());
    }

    vVerts[0][0] = vVerts[2][0];
    vVerts[0][1] = vVerts[2][1];
    vVerts[2][0] = m_angles[2] * vCenter[0] + m_frame.size.width * 0.5f - m_angles[2] * vCenter[1];
    vVerts[2][1] = m_angles[2] * vCenter[1] + m_frame.size.height * 0.5f - m_angles[2] * -vCenter[0];

    vTexCoords[0][0] = vTexCoords[2][0];
    vTexCoords[0][1] = vTexCoords[2][1];

    if (alpha != 0.f && m_statbar_material_flash) {
        re.SetColor(m_foreground_color);
    }

    re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());

    if (alpha != 0.f && m_statbar_material_flash) {
        re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material_flash->GetMaterial());
    }
}

void UIFakkLabel::DrawStatRotator(float frac)
{
    vec4_t col;
    float  alpha;
    float  fTargAng;
    float  fSinVal, fCosVal;
    vec2_t vCenter;
    vec2_t vVerts[3];
    vec2_t vTexCoords[3];
    vec2_t vNeedleDir;
    vec2_t vSize;

    col[0] = 1.f;
    col[1] = 1.f;
    col[2] = 1.f;
    col[3] = 1.f;

    if (m_lastfrac != frac) {
        m_flashtime = uid.time;
    }
    m_lastfrac = frac;

    alpha = 1.f - (uid.time - m_flashtime) / 1500.f;
    if (alpha < 0.f) {
        alpha = 0.f;
    } else if (alpha > 1.f) {
        alpha = 1.f;
    }

    col[3] = alpha;

    fTargAng      = DEG2RAD(frac * (m_angles[1] - m_angles[0]) + m_angles[0]);
    fSinVal       = sin(fTargAng);
    fCosVal       = cos(fTargAng);
    vNeedleDir[0] = fSinVal;
    vNeedleDir[1] = -fCosVal;

    vSize[0] = (m_frame.size.width + m_frame.size.height) / m_frame.size.width * m_angles[2] * getVirtualScale()[0];
    vSize[1] = (m_frame.size.width + m_frame.size.height) / m_frame.size.height * m_scale * getVirtualScale()[1];

    vCenter[0] = (m_frame.size.width * 0.5f - vSize[0]) * vNeedleDir[0] + m_frame.size.width * 0.5f;
    vCenter[1] = (m_frame.size.height * 0.5f - vSize[1]) * vNeedleDir[1] + m_frame.size.height * 0.5f;

    vVerts[0][0] = vCenter[0] - vSize[0];
    vVerts[0][1] = vCenter[1] - vSize[1];
    vVerts[1][0] = vCenter[0] + vSize[0];
    vVerts[1][1] = vCenter[1] - vSize[1];
    vVerts[2][0] = vCenter[0] - vSize[0];
    vVerts[2][1] = vCenter[1] + vSize[1];

    vTexCoords[0][0] = 0.f;
    vTexCoords[0][1] = 0.f;
    vTexCoords[1][0] = 1.f;
    vTexCoords[1][1] = 0.f;
    vTexCoords[2][0] = 0.f;
    vTexCoords[2][1] = 1.f;

    if (alpha != 0.f && m_statbar_material_flash) {
        re.SetColor(m_foreground_color);
    }

    re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());

    if (alpha != 0.f && m_statbar_material_flash) {
        re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material_flash->GetMaterial());
    }

    vVerts[0][0] = vVerts[2][0];
    vVerts[0][1] = vVerts[2][1];
    vVerts[2][0] = vCenter[0] + vSize[0];
    vVerts[2][1] = vCenter[1] + vSize[1];

    vTexCoords[0][0] = vTexCoords[2][0];
    vTexCoords[0][1] = vTexCoords[2][1];
    vTexCoords[2][0] = 1.f;
    vTexCoords[2][1] = 1.f;

    if (alpha != 0.f && m_statbar_material_flash) {
        re.SetColor(m_foreground_color);
    }

    re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());

    if (alpha != 0.f && m_statbar_material_flash) {
        re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material_flash->GetMaterial());
    }
}

void UIFakkLabel::DrawStatCompass(float frac)
{
    vec4_t col;
    float  fTargAng;
    float  fSinVal, fCosVal;
    vec2_t vCenter;
    vec2_t vVerts[3];
    vec2_t vTexCoords[3];
    vec2_t vCompassDir;
    vec2_t vSideDir;

    col[0] = col[1] = col[2] = col[3] = 1.0;

    if (!cge) {
        return;
    }

    static int   iLastCompassTime = -9999, iLastTimeDelta = 0;
    static float fLastPitch = 0, fLastYaw = 0, fLastYawDelta = 0;
    static float fYawOffset = 0, fYawSpeed = 0;
    static float fNeedleOffset = 0, fNeedleSpeed = 0;
    int          iTimeCount, iTimeDelta;
    float        fYawDelta, fYawDeltaDiff;
    vec3_t       vViewAngles;

    cge->CG_EyeAngles(&vViewAngles);

    vViewAngles[1] -= cl.snap.ps.stats[STAT_COMPASSNORTH] / 182.0f;

    iTimeCount = uid.time - iLastCompassTime;
    if (iTimeCount <= 1000) {
        fYawDelta = AngleSubtract(vViewAngles[1], fLastYaw);
        if (fYawDelta > 0.1) {
            if ((fYawDelta > 0.0 && fLastYawDelta < fYawDelta) || (fYawDelta < 0.0 && fLastYawDelta > fYawDelta)) {
                fYawSpeed += (fYawDelta - fLastYawDelta) * 0.75;
                fYawOffset += (fYawDelta - fLastYawDelta) * 0.75;
            }

            if (fYawSpeed > 90.0) {
                fYawSpeed = 90.0;
            } else if (fYawSpeed < 0.05) {
                fYawSpeed = 0.05;
            }
        }

        while (iTimeCount > 0) {
            iTimeDelta = iTimeCount;
            if (iTimeCount > 15) {
                iTimeDelta = 15;
            }
            iTimeCount -= iTimeDelta;

            if (fabs(fYawOffset) >= 0.1 || fabs(fYawSpeed) >= 0.01) {
                fYawOffset += fYawSpeed * iTimeDelta;
                if (fYawOffset > 0.0) {
                    fYawSpeed -= iTimeDelta * 0.00175;
                } else if (fYawOffset < 0.0) {
                    fYawSpeed += iTimeDelta * 0.00175;
                }

                if (fYawOffset > 40.0) {
                    fYawOffset = 40.0;
                    fYawSpeed  = 0.0;
                } else if (fYawOffset < -40.0) {
                    fYawOffset = -40.0;
                    fYawSpeed  = 0.0;
                }

                fYawSpeed -= iTimeDelta * 0.003;
                if (fYawSpeed > 0.0) {
                    fYawSpeed -= iTimeDelta * 0.0004;
                } else {
                    fYawSpeed += iTimeDelta * 0.0004;
                    if (fYawSpeed > 0.0) {
                        fYawSpeed = 0.0;
                    }
                }
            } else {
                iTimeCount = 0;
                fYawOffset = 0.0;
                fYawSpeed  = 0.0;
            }
        }

        fLastYawDelta  = fYawDelta;
        iLastTimeDelta = uid.time - iLastCompassTime;
    } else {
        fLastYawDelta  = 0;
        fYawOffset     = 0;
        fYawSpeed      = 0;
        iLastTimeDelta = 0;
    }

    fLastPitch       = vViewAngles[0];
    fLastYaw         = vViewAngles[1];
    iLastCompassTime = uid.time;

    if (iLastTimeDelta) {
        if (fabs(fLastYawDelta) > 0.1) {
            fNeedleOffset -= fLastYawDelta;
        }

        iTimeCount = iLastTimeDelta;
        while (iTimeCount > 0) {
            iTimeDelta = iTimeCount;
            if (iTimeCount > 15) {
                iTimeDelta = 15;
            }
            iTimeCount -= iTimeDelta;

            if (fabs(fNeedleOffset) >= 0.1 || fabs(fNeedleSpeed) >= 0.01) {
                fNeedleOffset += iTimeDelta * fNeedleSpeed;
                if (fNeedleOffset > 180.0) {
                    fNeedleOffset -= 360.0;
                } else if (fNeedleOffset < -180.0) {
                    fNeedleOffset += 360.0;
                }

                if (fNeedleOffset > 0.0) {
                    fNeedleSpeed -= iTimeDelta * 0.00175;
                } else if (fNeedleOffset < 0.0) {
                    fNeedleSpeed += iTimeDelta * 0.00175;
                }

                fNeedleSpeed -= fNeedleSpeed * 0.0025 * iTimeDelta;
                if (fNeedleSpeed > 0.0) {
                    fNeedleSpeed -= iTimeDelta * 0.00035;
                    if (fNeedleSpeed < 0.0) {
                        fNeedleSpeed = 0.0;
                    }
                } else {
                    fNeedleSpeed += iTimeDelta * 0.00035;
                    if (fNeedleSpeed > 0.0) {
                        fNeedleSpeed = 0.0;
                    }
                }
            } else {
                iTimeCount    = 0;
                fNeedleOffset = 0.0;
                fNeedleSpeed  = 0.0;
            }
        }
    } else {
        fNeedleOffset = 0.0;
        fNeedleSpeed  = 0.0;
    }

    fSinVal = sin(anglemod(fNeedleOffset + fLastYaw) / (180 / M_PI));
    fCosVal = cos(anglemod(fNeedleOffset + fLastYaw) / (180 / M_PI));

    vCompassDir[0] = fSinVal;
    vCompassDir[1] = -fCosVal;

    vCenter[0]       = m_frame.size.width * 0.5;
    vCenter[1]       = m_frame.size.height * 0.5;
    vTexCoords[0][0] = 0.0;
    vTexCoords[0][1] = 0.0;
    vTexCoords[1][0] = 1.0;
    vTexCoords[1][1] = 0.0;
    vTexCoords[2][0] = 0.0;
    vTexCoords[2][1] = 1.0;

    vVerts[0][0] = vCenter[0] + vCompassDir[0] * vCenter[0] - fCosVal * vCenter[1];
    vVerts[1][0] = vCenter[0] + vCompassDir[0] * vCenter[0] + fCosVal * vCenter[1];
    vVerts[2][0] = vCenter[0] - vCompassDir[0] * vCenter[0] - fCosVal * vCenter[1];
    vVerts[0][1] = vCenter[1] + vCompassDir[1] * vCenter[1] - fSinVal * vCenter[0];
    vVerts[1][1] = vCenter[1] + vCompassDir[1] * vCenter[1] + fSinVal * vCenter[0];
    vVerts[2][1] = vCenter[1] - vCompassDir[1] * vCenter[1] - fSinVal * vCenter[0];

    re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());

    vVerts[0][0]     = vVerts[2][0];
    vVerts[0][1]     = vVerts[2][1];
    vTexCoords[0][0] = vTexCoords[2][0];
    vTexCoords[0][1] = vTexCoords[2][1];
    vTexCoords[2][0] = 1.0;
    vTexCoords[2][1] = 1.0;

    vVerts[2][0] = vCenter[0] + fCosVal * vCenter[1] - vCompassDir[0] * vCenter[0];
    vVerts[2][1] = vCenter[1] * fSinVal + vCenter[0] - vCompassDir[1] * vCenter[1];

    re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());
}

void UIFakkLabel::DrawStatSpinner(float frac)
{
    vec4_t col;
    float  alpha;
    float  fTargAng;
    float  fSinVal, fCosVal;
    vec2_t vCenter;
    vec2_t vVerts[3];
    vec2_t vTexCoords[3];
    vec2_t vCompassDir;
    vec2_t vSideDir;

    col[0] = 1.f;
    col[1] = 1.f;
    col[2] = 1.f;
    col[3] = 1.f;

    if (m_lastfrac != frac) {
        m_flashtime = uid.time;
    }
    m_lastfrac = frac;

    alpha = 1.f - (uid.time - m_flashtime) / 1500.f;
    if (alpha < 0.f) {
        alpha = 0.f;
    } else if (alpha > 1.f) {
        alpha = 1.f;
    }

    col[3] = alpha;

    if (m_statbar_or == L_STATBAR_HEADING_SPINNER) {
        if (cge) {
            vec3_t vViewAngles;
            float  fAngAlpha;
            cge->CG_EyeAngles(&vViewAngles);

            frac = (AngleSubtract(vViewAngles[1], frac * 360.f + 180.f) + 180.f) / 360.f;
            if (frac < 0.f || frac > 1.f) {
                frac = 0.f;
            }
        }
    }

    fTargAng = DEG2RAD(frac * (m_angles[1] - m_angles[0]) + m_angles[0]);
    fSinVal  = sin(fTargAng);
    fCosVal  = cos(fTargAng);

    vSideDir[0]    = fCosVal;
    vSideDir[1]    = fSinVal;
    vCompassDir[0] = fSinVal;
    vCompassDir[1] = -fCosVal;
    vCenter[0]     = m_frame.size.width * 0.5f;
    vCenter[1]     = m_frame.size.height * 0.5f;

    vVerts[0][0] = (fSinVal * vCenter[0] + vCenter[0]) - (fCosVal * vCenter[1]);
    vVerts[0][1] = (vCompassDir[1] * vCenter[1] + vCenter[1]) - (vSideDir[1] * vCenter[0]);
    vVerts[1][0] = (fSinVal * vCenter[0] + vCenter[0]) + (fCosVal * vCenter[1]);
    vVerts[1][1] = (vSideDir[1] * vCenter[0]) + (vCompassDir[1] * vCenter[1] + vCenter[1]);
    vVerts[2][0] = vCenter[0] - fSinVal * vCenter[0] - fCosVal * vCenter[1];
    vVerts[2][1] = vCenter[1] - vCompassDir[1] * vCenter[1] - vSideDir[1] * vCenter[0];

    vTexCoords[0][0] = 0.f;
    vTexCoords[0][1] = 0.f;
    vTexCoords[1][0] = 1.f;
    vTexCoords[1][1] = 0.f;
    vTexCoords[2][0] = 0.f;
    vTexCoords[2][1] = 1.f;

    if (alpha != 0.f && m_statbar_material_flash) {
        re.SetColor(m_foreground_color);
    }

    re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());

    if (alpha != 0.f && m_statbar_material_flash) {
        re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material_flash->GetMaterial());
    }

    vVerts[0][0] = vVerts[2][0];
    vVerts[0][1] = vVerts[2][1];
    vVerts[2][0] = vCenter[1] * fCosVal + vCenter[0] - vCompassDir[0] * vCenter[0];
    vVerts[2][1] = vCenter[0] * vSideDir[1] + vCenter[1] - vCompassDir[1] * vCenter[1];

    vTexCoords[0][0] = vTexCoords[2][0];
    vTexCoords[0][1] = vTexCoords[2][1];
    vTexCoords[2][0] = 1.f;
    vTexCoords[2][1] = 1.f;

    if (alpha != 0.f && m_statbar_material_flash) {
        re.SetColor(m_foreground_color);
    }

    re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material->GetMaterial());

    if (alpha != 0.f && m_statbar_material_flash) {
        re.DrawTrianglePic(vVerts, vTexCoords, m_statbar_material_flash->GetMaterial());
    }
}

void UIFakkLabel::StatCircleTexCoord(float fAng, vec3_t vTexCoord)
{
    int   iSector;
    float fSinVal, fCosVal;

    iSector = (AngleMod(fAng) + 45.f) / 90;
    fSinVal = sin(DEG2RAD(fAng));
    fCosVal = cos(DEG2RAD(fAng));

    if (iSector >= 0) {
        if (iSector == 0) {
            vTexCoord[0] = 0.f;
            vTexCoord[1] = 0.5f / fCosVal * fSinVal + 0.5f;
        } else if (iSector == 1) {
            vTexCoord[0] = 1.f;
            vTexCoord[1] = -0.5f / fSinVal * fCosVal + 0.5f;
        } else if (iSector == 2) {
            vTexCoord[0] = fSinVal * (-0.5f * fCosVal) + 0.5f;
            vTexCoord[1] = 1.f;
        } else if (iSector == 4) {
            vTexCoord[0] = 0.5f / fCosVal * fSinVal + 0.5f;
            vTexCoord[1] = 0.f;
        } else {
            vTexCoord[0] = 0.f;
            vTexCoord[1] = 0.5f / fSinVal * fCosVal + 0.5f;
        }
    } else {
        vTexCoord[0] = 0.f;
        vTexCoord[1] = 0.5f / fSinVal * fCosVal + 0.5f;
    }
}

void UIFakkLabel::Draw(void)
{
    if (m_stat_alpha != -1) {
        float frac;

        frac = (cl.snap.ps.stats[m_stat_alpha] + 1) / 100.0;
        frac = Q_clamp_float(frac, 0, 1);

        m_alpha = frac;
    }

    if (m_stat == -1 && m_itemindex == -1 && m_inventoryrendermodelindex == -1 && m_stat_configstring == -1
        && !m_rendermodel && m_statbar_or == L_STATBAR_NONE) {
        if (!m_sDrawModelName.length()) {
            UILabel::Draw();
            return;
        }
    }

    if (m_stat_configstring != -1) {
        m_font->setColor(m_foreground_color);

        if (m_bOutlinedText) {
            m_font->PrintOutlinedJustified(
                getClientFrame(),
                m_iFontAlignmentHorizontal,
                m_iFontAlignmentVertical,
                Sys_LV_CL_ConvertString(va("%s", CL_ConfigString(cl.snap.ps.stats[m_stat_configstring]))),
                UBlack,
                getVirtualScale()
            );
        } else {
            m_font->PrintJustified(
                getClientFrame(),
                m_iFontAlignmentHorizontal,
                m_iFontAlignmentVertical,
                Sys_LV_CL_ConvertString(va("%s", CL_ConfigString(cl.snap.ps.stats[m_stat_configstring]))),
                getVirtualScale()
            );
        }
        return;
    }

    if (m_stat != -1) {
        int delta = cl.snap.ps.stats[m_stat];

        if (m_statbar_or == L_STATBAR_NONE) {
            m_font->setColor(m_foreground_color);

            if (m_bOutlinedText) {
                m_font->PrintOutlinedJustified(
                    getClientFrame(),
                    m_iFontAlignmentHorizontal,
                    m_iFontAlignmentVertical,
                    va("%d", delta),
                    UBlack,
                    getVirtualScale()
                );
            } else {
                m_font->PrintJustified(
                    getClientFrame(),
                    m_iFontAlignmentHorizontal,
                    m_iFontAlignmentVertical,
                    va("%d", delta),
                    getVirtualScale()
                );
            }
            return;
        }

        if (m_statbar_or == L_STATBAR_VERTICAL_STAGGER_EVEN || m_statbar_or == L_STATBAR_VERTICAL_STAGGER_ODD) {
            int stat;

            if (m_maxstat < 0) {
                stat = m_statbar_max;
            } else {
                stat = cl.snap.ps.stats[m_maxstat];
            }

            delta = stat - cl.snap.ps.stats[m_stat];

            if (delta & 1) {
                if (m_statbar_or == L_STATBAR_VERTICAL_STAGGER_EVEN) {
                    delta--;
                } else {
                    delta++;
                }
            }

            delta = stat - delta;

            if (m_maxstat >= 0 || m_statbar_min <= delta) {
                if (delta < 0) {
                    delta = 0;
                }
            } else {
                delta = m_statbar_min;
            }
        }

        float frac;

        if (m_maxstat >= 0) {
            frac = (float)delta / (float)cl.snap.ps.stats[m_maxstat];
        } else {
            frac = (float)delta / (float)(m_statbar_max - m_statbar_min);
        }

        if (frac > 1.0) {
            frac = 1.0;
        } else if (frac < 0.0) {
            frac = 0.0;
        }

        DrawStatbar(frac);
        return;
    }

    float     scale;
    qhandle_t handle;
    vec3_t    origin;
    vec3_t    mins;
    vec3_t    maxs;
    vec3_t    angles;
    vec3_t    offset;
    vec3_t    rotateoffset;
    vec4_t    color;
    str       sAnimName;
    float     height;

    if (m_itemindex != -1) {
        if (m_itemindex < 0) {
            return;
        }

        if (cl.snap.ps.activeItems[m_itemindex] < 0 || cl.snap.ps.activeItems[m_itemindex] >= MAX_WEAPONS) {
            // Fixed in OPM
            //  Make sure to not overflow configstrings
            return;
        }

        m_font->setColor(m_foreground_color);

        if (m_bOutlinedText) {
            m_font->PrintOutlinedJustified(
                getClientFrame(),
                m_iFontAlignmentHorizontal,
                m_iFontAlignmentVertical,
                Sys_LV_CL_ConvertString(va("%s", CL_ConfigString(CS_WEAPONS + cl.snap.ps.activeItems[m_itemindex]))),
                UBlack,
                getVirtualScale()
            );
        } else {
            m_font->PrintJustified(
                getClientFrame(),
                m_iFontAlignmentHorizontal,
                m_iFontAlignmentVertical,
                Sys_LV_CL_ConvertString(va("%s", CL_ConfigString(CS_WEAPONS + cl.snap.ps.activeItems[m_itemindex]))),
                getVirtualScale()
            );
        }

        return;
    }

    if (m_inventoryrendermodelindex == -1 && !m_sDrawModelName.length() && !m_rendermodel) {
        float frac = 0.0;

        if (m_statbar_or == L_STATBAR_NONE) {
            return;
        }

        if (m_cvarname.length()) {
            frac = Cvar_VariableValue(m_cvarname) / (m_statbar_max - m_statbar_min);
        }

        if (frac > 1.0) {
            frac = 1.0;
        }
        if (frac < 0.0) {
            frac = 0.0;
        }

        DrawStatbar(frac);
        return;
    }

    VectorSet4(color, 255, 255, 255, 255);

    if (m_rendermodel) {
        if (!m_cvarname.length()) {
            return;
        }

        handle = re.RegisterModel(Cvar_VariableString(m_cvarname));
        if (!handle) {
            return;
        }

        VectorCopy(m_angles, angles);
        VectorCopy(m_rotateoffset, rotateoffset);
        VectorCopy(m_offset, offset);
        scale     = m_scale;
        sAnimName = m_anim;

        // Added in 2.11
        //  Tunak easter egg
        if (Cvar_Get("tunak", "0", 0)->integer) {
            // Since 2.15, those animations start has 'zz' prefix
            if (strstr(m_anim, "american")) {
                sAnimName = "zzamericanspecialidle";
            } else {
                // Added in 2.15
                sAnimName = "zzgermanspecialidle";
            }
        }
    } else {
        if (m_sDrawModelName.length()) {
            if (!m_lastitem) {
                m_lastitem = CL_GetInvItemByName(&client_inv, m_sDrawModelName);
            }
        } else {
            if (m_inventoryrendermodelindex >= 0
                && m_lastitemindex != cl.snap.ps.activeItems[m_inventoryrendermodelindex]) {
                m_lastitem = CL_GetInvItemByName(
                    &client_inv, CL_ConfigString(CS_WEAPONS + cl.snap.ps.activeItems[m_inventoryrendermodelindex])
                );
                m_lastitemindex = cl.snap.ps.activeItems[m_inventoryrendermodelindex];
            }
        }

        if (!m_lastitem) {
            return;
        }

        VectorCopy(m_lastitem->hudprops.rotateoffset, rotateoffset);
        VectorCopy(m_lastitem->hudprops.offset, offset);
        scale  = m_lastitem->hudprops.scale;
        handle = re.RegisterModel(m_lastitem->hudprops.model);
        VectorCopy(m_lastitem->hudprops.angledeltas, angles);

        if (m_lastitem->hudprops.move == INV_MOVE_BOB) {
            float frac = uid.time / 600.0;
            angles[0] += sin(frac) * m_lastitem->hudprops.angledeltas[0];
            angles[1] += sin(frac) * m_lastitem->hudprops.angledeltas[1];
            angles[2] += sin(frac) * m_lastitem->hudprops.angledeltas[2];
        } else if (m_lastitem->hudprops.model == INV_MOVE_SPIN) {
            float frac = uid.time / 600.0;
            angles[0] += frac * m_lastitem->hudprops.angledeltas[0];
            angles[1] += frac * m_lastitem->hudprops.angledeltas[1];
            angles[2] += frac * m_lastitem->hudprops.angledeltas[2];
        }
    }

    if (m_rendermodel) {
        VectorSet(mins, -16, -16, 0);
        VectorSet(maxs, 16, 16, 96);
    } else {
        re.ModelBounds(handle, mins, maxs);
    }

    origin[1] = (mins[1] + maxs[1]) * 0.5;
    origin[2] = (mins[2] + maxs[2]) * -0.5;

    height = maxs[2] - mins[2];

    if (height < maxs[1] - mins[1]) {
        height = maxs[1] - mins[1];
    }

    if (height < maxs[0] - mins[0]) {
        height = maxs[0] - mins[0];
    }

    origin[0] = height * scale * 0.5 / 0.268f;

    CL_Draw3DModel(
        m_screenframe.pos.x,
        m_screenframe.pos.y,
        m_screenframe.size.width,
        m_screenframe.size.height,
        handle,
        origin,
        rotateoffset,
        offset,
        angles,
        color,
        sAnimName
    );

    set2D();
}
