openmohaa/code/client/cl_uiview3d.cpp
smallmodel 1ef1bad9ae
Add network profiling tool from Spearhead and Breakthrough
Profiling can be enabled with `cl_netprofile` and `sv_netprofile`, the overlay can be shown using `cl_netprofileoverlay` and `sv_netprofileoverlay` and the server can dump network profile using the `netprofiledump` command
2024-11-09 21:38:16 +01:00

875 lines
26 KiB
C++

/*
===========================================================================
Copyright (C) 2024 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 02110-1301 USA
===========================================================================
*/
#include "cl_ui.h"
#include "../qcommon/localization.h"
#include "../server/server.h"
CLASS_DECLARATION(UIWidget, View3D, NULL) {
{&W_Activated, &View3D::OnActivate },
{&W_Deactivated, &View3D::OnDeactivate},
{&W_LeftMouseDown, &View3D::Pressed },
{NULL, NULL }
};
cvar_t *subs[MAX_SUBTITLES];
cvar_t *teams[MAX_SUBTITLES];
float fadeTime[MAX_SUBTITLES];
float subLife[MAX_SUBTITLES];
float alpha[MAX_SUBTITLES];
char oldStrings[MAX_SUBTITLES][2048];
View3D::View3D()
{
// set as transparent
setBackgroundColor(UClear, true);
// no border
setBorderStyle(border_none);
AllowActivate(true);
m_printfadetime = 0.0;
m_print_mat = NULL;
m_locationprint = qfalse;
}
void View3D::UpdateCenterPrint(const char *s, float alpha)
{
m_printstring = s;
if (s[0] == '@') {
m_print_mat = uWinMan.RegisterShader(s + 1);
} else {
m_print_mat = NULL;
}
m_printalpha = alpha;
m_printfadetime = 4000.0;
m_locationprint = qfalse;
}
void View3D::UpdateLocationPrint(int x, int y, const char *s, float alpha)
{
m_printstring = s;
m_printalpha = alpha;
m_printfadetime = 4000.0;
m_x_coord = x;
m_y_coord = y;
m_locationprint = qtrue;
}
void View3D::FrameInitialized(void)
{
Connect(this, W_Activated, W_Activated);
Connect(this, W_Deactivated, W_Deactivated);
}
void View3D::Pressed(Event *ev)
{
IN_MouseOff();
OnActivate(ev);
}
void View3D::OnActivate(Event *ev)
{
UIWidget *wid;
UList<UIWidget *> widgets;
UI_CloseInventory();
Key_SetCatcher(Key_GetCatcher() & ~KEYCATCH_UI);
for (wid = getParent()->getFirstChild(); wid; wid = getParent()->getNextChild(wid)) {
if (wid->getAlwaysOnBottom() && wid != this) {
widgets.AddTail(wid);
}
}
widgets.IterateFromHead();
while (widgets.IsCurrentValid()) {
widgets.getCurrent()->BringToFrontPropogated();
widgets.IterateNext();
}
}
void View3D::OnDeactivate(Event *ev)
{
Key_SetCatcher(Key_GetCatcher() | KEYCATCH_UI);
}
void View3D::DrawFPS(void)
{
char string[128];
setFont("verdana-14");
if (fps->integer == 2) {
re.SetColor(UBlack);
re.DrawBox(
0.0,
m_frame.pos.y + m_frame.size.height - m_font->getHeight(qfalse) * 4.0,
m_frame.pos.x + m_frame.size.width,
m_font->getHeight(qfalse) * 4.0
);
}
Com_sprintf(string, sizeof(string), "FPS %4.1f", currentfps);
if (currentfps > 23.94) {
if (cl_greenfps->integer) {
m_font->setColor(UGreen);
} else {
m_font->setColor(UWhite);
}
} else if (currentfps > 18.0) {
m_font->setColor(UYellow);
} else {
// low fps
m_font->setColor(URed);
}
m_font->Print(
m_font->getHeight(qfalse) * 10.0,
m_frame.pos.y + m_frame.size.height - m_font->getHeight(qfalse) * 3.0,
string,
-1,
qfalse
);
// Draw elements count
if (cl_greenfps->integer) {
m_font->setColor(UGreen);
} else {
m_font->setColor(UWhite);
}
Com_sprintf(string, sizeof(string), "wt%5d wv%5d cl%d", cls.world_tris, cls.world_verts, cls.character_lights);
m_font->Print(
m_font->getHeight(qfalse) * 10.0,
m_frame.pos.y + m_frame.size.height - m_font->getHeight(qfalse) * 2.0,
string,
-1,
qfalse
);
Com_sprintf(
string,
sizeof(string),
"t%5d v%5d Mtex%5.2f",
cls.total_tris,
cls.total_verts,
(float)cls.total_texels * 0.00000095367432
);
m_font->Print(
m_font->getHeight(qfalse) * 10.0,
m_frame.pos.y + m_frame.size.height - m_font->getHeight(qfalse),
string,
-1,
qfalse
);
m_font->setColor(UBlack);
}
/*
void ProfPrint(UIFont* m_font, float minY, int line, char* label, prof_var_t* var, int level)
{
}
*/
void View3D::DrawProf(void)
{
// FIXME: unimplemented
}
void View3D::PrintSound(int channel, const char *name, float vol, int rvol, float pitch, float base, int& line)
{
char buf[255];
float x;
float xStep;
float height;
height = m_font->getHeight(false);
xStep = height;
x = 0;
Com_sprintf(buf, sizeof(buf), "%d", channel);
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, false);
x += xStep + xStep;
Com_sprintf(buf, sizeof(buf), "%s", name);
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, false);
x += xStep * 30.0;
Com_sprintf(buf, sizeof(buf), "vol:%.2f", vol);
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, false);
x += xStep * 8;
Com_sprintf(buf, sizeof(buf), "rvol:%.2f", (float)(rvol / 128.f));
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, false);
x += xStep * 5;
Com_sprintf(buf, sizeof(buf), "pit:%.2f", pitch);
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, false);
x += xStep * 5;
Com_sprintf(buf, sizeof(buf), "base:%d", (int)base);
m_font->Print(x, height * line + m_frame.pos.y, buf, -1, false);
line++;
}
void View3D::DrawSoundOverlay(void)
{
setFont("verdana-14");
m_font->setColor(UWhite);
// FIXME: Unimplemented
if (sound_overlay->integer) {
Com_Printf("sound_overlay isn't supported with OpenAL/SDL right now.\n");
Cvar_Set("sound_overlay", "0");
}
}
void DisplayServerNetProfileInfo(UIFont *font, float y, netprofclient_t *netprofile)
{
font->Print(104, y, va("%i", netprofile->inPackets.packetsPerSec));
font->Print(144, y, va("%i", netprofile->outPackets.packetsPerSec));
font->Print(184, y, va("%i", netprofile->inPackets.packetsPerSec + netprofile->outPackets.packetsPerSec));
font->Print(234, y, va("%i", netprofile->inPackets.percentFragmented));
font->Print(264, y, va("%i", netprofile->outPackets.percentFragmented));
font->Print(
294,
y,
va("%i",
(unsigned int)((float)(netprofile->outPackets.numFragmented + netprofile->inPackets.numFragmented)
/ (float)(netprofile->inPackets.totalProcessed + netprofile->outPackets.totalProcessed)
)),
-1,
qfalse
);
font->Print(334, y, va("%i", netprofile->inPackets.percentDropped));
font->Print(364, y, va("%i", netprofile->outPackets.percentDropped));
font->Print(
394,
y,
va("%i",
(unsigned int)((float)(netprofile->outPackets.numDropped + netprofile->inPackets.numDropped)
/ (float)(netprofile->inPackets.totalProcessed + netprofile->outPackets.totalProcessed)
)),
-1,
qfalse
);
font->Print(434, y, va("%i", netprofile->inPackets.percentDropped));
font->Print(464, y, va("%i", netprofile->outPackets.percentDropped));
font->Print(
494,
y,
va("%i",
(unsigned int)((float)(netprofile->outPackets.totalLengthConnectionLess
+ netprofile->inPackets.totalLengthConnectionLess)
/ (float)(netprofile->outPackets.totalSize + netprofile->inPackets.totalSize))),
-1,
qfalse
);
font->Print(534, y, va("%i", netprofile->inPackets.bytesPerSec));
font->Print(594, y, va("%i", netprofile->outPackets.bytesPerSec));
font->Print(654, y, va("%i", netprofile->outPackets.bytesPerSec + netprofile->inPackets.bytesPerSec));
font->Print(714, y, va("%i", netprofile->rate));
}
void DisplayClientNetProfile(UIFont *font, float x, float y, netprofclient_t *netprofile)
{
float columns[5];
float fontHeight;
float columnHeight;
fontHeight = font->getHeight(qfalse);
columns[0] = x + 120;
columns[1] = x + 230;
columns[2] = x + 330;
columns[3] = x + 430;
columns[4] = x + 530;
columnHeight = y;
font->Print(x, y, va("Rate: %i", netprofile->rate));
columnHeight += fontHeight * 1.5;
font->Print(x, columnHeight, "Data Type");
font->Print(columns[0], columnHeight, "Packets per Sec");
font->Print(columns[1], columnHeight, "% Fragmented");
font->Print(columns[2], columnHeight, "% Dropped");
font->Print(columns[3], columnHeight, "% OOB data");
font->Print(columns[4], columnHeight, "Data per Sec");
columnHeight += fontHeight * 0.5;
font->Print(x, columnHeight, "----------");
font->Print(columns[0], columnHeight, "----------");
font->Print(columns[1], columnHeight, "----------");
font->Print(columns[2], columnHeight, "----------");
font->Print(columns[3], columnHeight, "----------");
font->Print(columns[4], columnHeight, "----------");
columnHeight += fontHeight;
font->Print(x, columnHeight, "Data In");
font->Print(columns[0], columnHeight, va("%i", netprofile->outPackets.packetsPerSec));
font->Print(columns[1], columnHeight, va("%i%%", netprofile->outPackets.percentFragmented));
font->Print(columns[2], columnHeight, va("%i%%", netprofile->outPackets.percentDropped));
font->Print(columns[3], columnHeight, va("%i%%", netprofile->outPackets.percentConnectionLess));
font->Print(columns[4], columnHeight, va("%i", netprofile->outPackets.bytesPerSec));
columnHeight += fontHeight;
font->Print(x, columnHeight, "Data Out");
font->Print(columns[0], columnHeight, va("%i", netprofile->inPackets.packetsPerSec));
font->Print(columns[1], columnHeight, va("%i%%", netprofile->inPackets.percentFragmented));
font->Print(columns[2], columnHeight, va("%i%%", netprofile->inPackets.percentDropped));
font->Print(columns[3], columnHeight, va("%i%%", netprofile->inPackets.percentConnectionLess));
font->Print(columns[4], columnHeight, va("%i", netprofile->inPackets.bytesPerSec));
columnHeight += fontHeight;
font->Print(x, columnHeight, "Total Data");
font->Print(
columns[0],
columnHeight,
va("%i", netprofile->inPackets.packetsPerSec + netprofile->outPackets.packetsPerSec)
);
font->Print(
columns[1],
columnHeight,
va("%i%%",
(unsigned int)((float)(netprofile->outPackets.numFragmented + netprofile->inPackets.numFragmented)
/ (float)(netprofile->inPackets.totalProcessed + netprofile->outPackets.totalProcessed)
))
);
font->Print(
columns[2],
columnHeight,
va("%i%%",
(unsigned int)((float)(netprofile->outPackets.numDropped + netprofile->inPackets.numDropped)
/ (double)(netprofile->inPackets.totalProcessed + netprofile->outPackets.totalProcessed
)))
);
font->Print(
columns[3],
columnHeight,
va("%i%%",
(unsigned int)((float)(netprofile->outPackets.totalLengthConnectionLess
+ netprofile->inPackets.totalLengthConnectionLess)
/ (float)(netprofile->outPackets.totalSize + netprofile->inPackets.totalSize)))
);
font->Print(
columns[4],
columnHeight,
va("%i", netprofile->inPackets.bytesPerSec + netprofile->outPackets.bytesPerSec)
);
}
void View3D::DrawNetProfile(void)
{
float fontHeight;
float yOffset;
int i;
if (sv_netprofileoverlay->integer && sv_netprofile->integer && com_sv_running->integer) {
float columnHeight;
float valueHeight;
float separatorHeight;
float categoryHeight;
float currentHeight;
netprofclient_t netproftotal;
setFont("verdana-14");
m_font->setColor(UWhite);
fontHeight = m_font->getHeight(qfalse);
yOffset = sv_netprofileoverlay->integer + 8;
if (svs.netprofile.rate) {
m_font->Print(8, yOffset, va("Server Net Profile Max Rate: %i", svs.netprofile.rate), -1, qfalse);
} else {
m_font->Print(8, yOffset, "Server Net Profile Max Rate: none", -1, qfalse);
}
columnHeight = fontHeight + fontHeight + yOffset;
valueHeight = columnHeight + fontHeight;
separatorHeight = fontHeight * 1.5 + columnHeight;
categoryHeight = fontHeight * 0.5 + columnHeight;
m_font->Print(8, categoryHeight, "Data Source");
m_font->Print(8, separatorHeight, "---------------");
m_font->Print(104, columnHeight, "Packets per Sec");
m_font->Print(104, valueHeight, "In");
m_font->Print(144, valueHeight, "Out");
m_font->Print(184, valueHeight, "Total");
m_font->Print(104, separatorHeight, "---");
m_font->Print(144, separatorHeight, "-----");
m_font->Print(184, separatorHeight, "------");
m_font->Print(234, columnHeight, "% Fragmented");
m_font->Print(234, valueHeight, "In");
m_font->Print(264, valueHeight, "Out");
m_font->Print(294, valueHeight, "Total");
m_font->Print(234, separatorHeight, "---");
m_font->Print(264, separatorHeight, "-----");
m_font->Print(294, separatorHeight, "------");
m_font->Print(334, columnHeight, "% Dropped");
m_font->Print(334, valueHeight, "In");
m_font->Print(364, valueHeight, "Out");
m_font->Print(394, valueHeight, "Total");
m_font->Print(334, separatorHeight, "---");
m_font->Print(364, separatorHeight, "-----");
m_font->Print(394, separatorHeight, "------");
m_font->Print(434, columnHeight, "% OOB Data");
m_font->Print(434, valueHeight, "In");
m_font->Print(464, valueHeight, "Out");
m_font->Print(494, valueHeight, "Total");
m_font->Print(434, separatorHeight, "---");
m_font->Print(464, separatorHeight, "-----");
m_font->Print(494, separatorHeight, "------");
m_font->Print(534, columnHeight, "Data per Sec");
m_font->Print(534, valueHeight, "In");
m_font->Print(594, valueHeight, "Out");
m_font->Print(654, valueHeight, "Total");
m_font->Print(534, separatorHeight, "---");
m_font->Print(594, separatorHeight, "-----");
m_font->Print(654, separatorHeight, "------");
m_font->Print(714, categoryHeight, "Rate");
m_font->Print(714, separatorHeight, "------");
currentHeight = fontHeight * 2.5 + columnHeight;
SV_NET_CalcTotalNetProfile(&netproftotal, qfalse);
m_font->Print(8, columnHeight + fontHeight * 2.5, "Total");
DisplayServerNetProfileInfo(m_font, currentHeight, &netproftotal);
currentHeight += fontHeight * 1.5;
m_font->Print(8, currentHeight, "Clientless");
DisplayServerNetProfileInfo(m_font, currentHeight, &svs.netprofile);
currentHeight += fontHeight;
for (i = 0; i < svs.iNumClients; i++) {
client_t *client = &svs.clients[i];
if (client->state != CS_ACTIVE || !client->gentity) {
continue;
}
if (client->netchan.remoteAddress.type == NA_LOOPBACK) {
m_font->Print(8.0, currentHeight, va("#%i-Loopback", i), -1, 0);
} else {
m_font->Print(8.0, currentHeight, va("Client #%i", i), -1, 0);
}
DisplayServerNetProfileInfo(m_font, currentHeight, &client->netprofile);
currentHeight = currentHeight + fontHeight;
}
} else if (cl_netprofileoverlay->integer && cl_netprofile->integer && com_cl_running->integer) {
setFont("verdana-14");
m_font->setColor(UWhite);
fontHeight = m_font->getHeight(qfalse);
yOffset = cl_netprofileoverlay->integer + 16;
m_font->Print(16, yOffset, "Client Net Profile", -1, qfalse);
NetProfileCalcStats(&cls.netprofile.outPackets, 500);
NetProfileCalcStats(&cls.netprofile.inPackets, 500);
DisplayClientNetProfile(m_font, 16, yOffset + fontHeight * 2, &cls.netprofile);
}
}
void View3D::Draw2D(void)
{
if (!cls.no_menus) {
DrawFades();
}
DrawLetterbox();
if ((cl_debuggraph->integer || cl_timegraph->integer) && !cls.no_menus) {
SCR_DrawDebugGraph();
} else if (!cls.no_menus) {
if (cge) {
cge->CG_Draw2D();
}
if (m_locationprint) {
LocationPrint();
} else {
CenterPrint();
}
if (!cls.no_menus) {
DrawSoundOverlay();
DrawNetProfile();
DrawSubtitleOverlay();
}
}
if (fps->integer && !cls.no_menus) {
DrawFPS();
DrawProf();
}
}
void View3D::CenterPrint(void)
{
float alpha;
const char *p;
qhandle_t mat;
float x, y;
float w, h;
if (!m_printfadetime) {
return;
}
p = Sys_LV_CL_ConvertString(m_printstring);
if (m_printfadetime > 3250) {
alpha = 1.f - (m_printfadetime - 3250.f) / 750.f * m_printalpha;
} else if (m_printfadetime >= 750) {
alpha = 1.f;
} else {
alpha = m_printfadetime / 750.f * m_printalpha;
}
alpha = Q_clamp_float(alpha, 0, 1);
if (!m_print_mat) {
UIRect2D frame;
m_font->setColor(UColor(0, 0, 0, alpha));
frame = getClientFrame();
m_font->PrintJustified(
UIRect2D(frame.pos.x + 1, frame.pos.y + 1, frame.size.width, frame.size.height),
m_iFontAlignmentHorizontal,
m_iFontAlignmentVertical,
p,
m_bVirtual ? m_vVirtualScale : NULL
);
m_font->setColor(UColor(1, 1, 1, alpha));
frame = getClientFrame();
m_font->PrintJustified(
frame, m_iFontAlignmentHorizontal, m_iFontAlignmentVertical, p, m_bVirtual ? m_vVirtualScale : NULL
);
m_font->setColor(UBlack);
} else if ((mat = m_print_mat->GetMaterial())) {
vec4_t col {alpha, alpha, alpha, alpha};
re.SetColor(col);
w = re.GetShaderWidth(mat);
h = re.GetShaderHeight(mat);
x = (m_frame.pos.x + m_frame.size.width - w) * 0.5f;
y = (m_frame.pos.y + m_frame.size.height - h) * 0.5f;
re.DrawStretchPic(x, y, w, h, 0, 0, 1, 1, mat);
}
m_printfadetime -= cls.frametime;
if (m_printfadetime < 0) {
m_printfadetime = 0;
}
}
void View3D::LocationPrint(void)
{
fonthorzjustify_t horiz;
fontvertjustify_t vert;
int x, y;
const char *p;
float alpha;
UIRect2D frame;
if (!m_printfadetime) {
m_locationprint = false;
return;
}
horiz = FONT_JUSTHORZ_LEFT;
vert = FONT_JUSTVERT_TOP;
p = Sys_LV_CL_ConvertString(m_printstring);
if (m_printfadetime > 3250) {
alpha = 1.f - (m_printfadetime - 3250.f) / 750.f * m_printalpha;
} else if (m_printfadetime >= 750) {
alpha = 1.f;
} else {
alpha = m_printfadetime / 750.f * m_printalpha;
}
alpha = Q_clamp_float(alpha, 0, 1);
x = m_x_coord / 640.f * m_screenframe.size.width;
y = (480 - m_font->getHeight(false) - m_y_coord) / 480.f * m_screenframe.size.height;
if (m_x_coord == -1) {
horiz = FONT_JUSTHORZ_CENTER;
x = 0;
}
if (m_y_coord == -1) {
vert = FONT_JUSTVERT_CENTER;
y = 0;
}
m_font->setColor(UColor(0, 0, 0, alpha));
frame = getClientFrame();
m_font->PrintJustified(
UIRect2D(frame.pos.x + x + 1, frame.pos.y + y + 1, frame.size.width, frame.size.height),
horiz,
vert,
p,
m_bVirtual ? m_vVirtualScale : NULL
);
m_font->setColor(UColor(1, 1, 1, alpha));
frame = getClientFrame();
m_font->PrintJustified(
UIRect2D(frame.pos.x + x, frame.pos.y + y, frame.size.width, frame.size.height),
horiz,
vert,
p,
m_bVirtual ? m_vVirtualScale : NULL
);
m_font->setColor(UBlack);
m_printfadetime -= cls.frametime;
if (m_printfadetime < 0) {
m_printfadetime = 0;
}
}
void View3D::DrawLetterbox(void)
{
float frac;
vec4_t col;
col[0] = col[1] = col[2] = 0;
col[3] = 1;
frac = (float)cl.snap.ps.stats[STAT_LETTERBOX] / MAX_LETTERBOX_SIZE;
if (frac <= 0) {
m_letterbox_active = false;
return;
}
m_letterbox_active = true;
re.SetColor(col);
re.DrawBox(0.0, 0.0, m_screenframe.size.width, m_screenframe.size.height * frac);
re.DrawBox(
0.0,
m_screenframe.size.height - m_screenframe.size.height * frac,
m_screenframe.size.width,
m_screenframe.size.height
);
}
void View3D::DrawFades(void)
{
if (cl.snap.ps.blend[3] > 0) {
re.SetColor(cl.snap.ps.blend);
if (cl.snap.ps.stats[STAT_ADDFADE]) {
re.AddBox(0.0, 0.0, m_screenframe.size.width, m_screenframe.size.height);
} else {
re.DrawBox(0.0, 0.0, m_screenframe.size.width, m_screenframe.size.height);
}
}
}
void View3D::Draw(void)
{
if (clc.state != CA_DISCONNECTED) {
SCR_DrawScreenField();
}
set2D();
re.SavePerformanceCounters();
Draw2D();
}
float avWidth = 0.0;
void View3D::InitSubtitle(void)
{
float totalWidth;
for (int i = 0; i < 4; i++) {
subs[i] = Cvar_Get(va("subtitle%d", i), "", 0);
teams[i] = Cvar_Get(va("subteam%d", i), "0", 0);
Q_strncpyz(oldStrings[i], subs[i]->string, sizeof(oldStrings[i]));
fadeTime[i] = 4000.0;
subLife[i] = 4000.0;
}
totalWidth = 0.0;
for (char j = 'A'; j <= 'Z'; j++) {
totalWidth += m_font->getCharWidth(j);
}
avWidth = totalWidth / 26.0;
}
void View3D::DrawSubtitleOverlay(void)
{
cvar_t *subAlpha;
int i;
float minX, maxY;
int line;
subAlpha = Cvar_Get("subAlpha", "0.5", 0);
setFont("facfont-20");
m_font->setColor(URed);
for (i = 0; i < MAX_SUBTITLES; i++) {
if (strcmp(oldStrings[i], subs[i]->string)) {
fadeTime[i] = 2500 * ((strlen(subs[i]->string) / 68) + 1.f) + 1500;
subLife[i] = fadeTime[i];
Q_strncpyz(oldStrings[i], subs[i]->string, sizeof(oldStrings[i]));
}
if (fadeTime[i] > subLife[i] - 750.f) {
alpha[i] = 1.f - (fadeTime[i] - (subLife[i] - 750.f)) / 750.f;
} else if (fadeTime[i] < 750) {
alpha[i] = fadeTime[i] / 750.f;
} else {
alpha[i] = 1.f;
}
fadeTime[i] -= cls.frametime;
if (fadeTime[i] < 0) {
// Clear the subtitle
fadeTime[i] = 0;
oldStrings[i][0] = 0;
if (subs[i]->string && subs[i]->string[0]) {
Cvar_Set(va("subtitle%d", i), "");
}
}
}
minX = m_screenframe.size.height - m_font->getHeight(false) * 10;
maxY = (m_frame.pos.x + m_frame.size.width) - (m_frame.pos.x + m_frame.size.width) * 0.2f;
line = 0;
for (i = 0; i < MAX_SUBTITLES; i++) {
if (fadeTime[i] <= 0) {
continue;
}
if (m_font->getWidth(subs[i]->string, sizeof(oldStrings[i])) > maxY) {
char buf[2048];
char *c;
char *end;
char *start;
float total;
float width;
int blockcount;
c = subs[i]->string;
total = 0;
end = NULL;
start = buf;
while (*c) {
blockcount = m_font->DBCSGetWordBlockCount(c, -1);
if (!blockcount) {
break;
}
width = m_font->getWidth(c, blockcount);
if (total + width > maxY) {
m_font->setColor(UColor(0, 0, 0, alpha[i] * subAlpha->value));
m_font->Print(18, m_font->getHeight(false) * line + minX + 1.f, buf, -1, false);
m_font->setColor(UColor(1, 1, 1, alpha[i] * subAlpha->value));
m_font->Print(20, m_font->getHeight(false) * line + minX, buf, -1, false);
line++;
total = 0;
start = buf;
}
end = start + blockcount + 1;
if (end > buf + MAX_STRING_CHARS) {
Com_DPrintf("ERROR - word longer than possible line\n");
break;
}
memcpy(start, c, blockcount);
start += blockcount;
total += width;
*start = 0;
c += blockcount;
}
m_font->setColor(UColor(0, 0, 0, alpha[i] * subAlpha->value));
m_font->Print(18, m_font->getHeight(false) * line + minX + 1.f, buf, -1, qfalse);
m_font->setColor(UColor(1, 1, 1, alpha[i] * subAlpha->value));
m_font->Print(20, m_font->getHeight(false) * line + minX, buf, -1, qfalse);
line++;
} else {
m_font->setColor(UColor(0, 0, 0, alpha[i] * subAlpha->value));
m_font->Print(18, m_font->getHeight(false) * line + minX + 1.f, subs[i]->string, -1, qfalse);
m_font->setColor(UColor(1, 1, 1, alpha[i] * subAlpha->value));
m_font->Print(20, m_font->getHeight(false) * line + minX, subs[i]->string, -1, qfalse);
line++;
}
}
}
void View3D::ClearCenterPrint(void)
{
m_printfadetime = 0.0;
}
qboolean View3D::LetterboxActive(void)
{
return m_letterbox_active;
}
CLASS_DECLARATION(UIWidget, ConsoleView, NULL) {
{NULL, NULL}
};
void ConsoleView::Draw(void) {}