openmohaa/code/client/cl_uidmbox.cpp
2025-01-08 21:26:47 +01:00

425 lines
10 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"
Event EV_DMBox_Goin
(
"_dmbox_goin",
EV_DEFAULT,
NULL,
NULL,
"Event to make the dmbox disappear"
);
Event EV_DMBox_Decay
(
"_dmbox_decay",
EV_DEFAULT,
NULL,
NULL,
"Event to make the dmbox console line decay"
);
static float s_dmboxWidth = 384.0;
static float s_dmboxOffsetX = 3.0f;
static float s_dmboxOffsetY = 8.0f;
CLASS_DECLARATION(UIWidget, UIDMBox, NULL) {
{&W_SizeChanged, &UIDMBox::OnSizeChanged},
{&EV_DMBox_Goin, &UIDMBox::MoveInEvent },
{&EV_DMBox_Decay, &UIDMBox::DecayEvent },
{NULL, NULL }
};
UIDMBox::UIDMBox()
{
m_numitems = 0;
m_reallyshown = true;
m_fontbold = NULL;
m_boxstate = boxstate_t::box_out;
m_iBeginDecay = 0;
m_boxtime = uid.time;
m_movespeed = 500;
m_drawoutline = com_target_game->integer >= target_game_e::TG_MOHTA;
}
void UIDMBox::VerifyBoxOut(void)
{
PostMoveinEvent();
if (m_boxstate != boxstate_t::box_moving_out && m_boxstate != boxstate_t::box_out) {
ChangeBoxState(boxstate_t::box_moving_out);
}
}
void UIDMBox::ChangeBoxState(boxstate_t state)
{
m_boxstate = state;
m_boxtime = uid.time;
setShowState();
if (state == box_out) {
PostMoveinEvent();
}
}
void UIDMBox::HandleBoxMoving(void)
{
int delta;
UIRect2D newRect;
if (m_boxstate != boxstate_t::box_moving_out && m_boxstate != boxstate_t::box_moving_in) {
return;
}
delta = m_movespeed * (uid.time - m_boxtime) / 1000;
m_boxtime = 1000 * delta / m_movespeed + m_boxtime;
if (m_boxstate == boxstate_t::box_moving_out) {
newRect.size.width = m_frame.size.width;
newRect.size.height = m_frame.size.height;
newRect.pos.x = m_frame.pos.x;
newRect.pos.y = delta + m_frame.pos.y;
if (newRect.pos.y <= 0.0) {
newRect.pos.y = 0.0;
ChangeBoxState(boxstate_t::box_out);
}
} else if (m_boxstate == boxstate_t::box_moving_in) {
newRect.size.width = m_frame.size.width;
newRect.size.height = m_frame.size.height;
newRect.pos.x = m_frame.pos.x;
newRect.pos.y = delta - m_frame.pos.y;
if (newRect.pos.y <= -newRect.size.height) {
newRect.pos.y = -newRect.size.height;
ChangeBoxState(boxstate_t::box_in);
}
} else {
newRect = m_frame;
}
setFrame(newRect);
}
void UIDMBox::PostMoveinEvent(void)
{
if (m_boxstate != boxstate_t::box_out) {
return;
}
if (!EventPending(EV_DMBox_Goin)) {
PostEvent(EV_DMBox_Goin, 10.0);
} else {
PostponeEvent(EV_DMBox_Goin, 10.0);
}
}
void UIDMBox::PostDecayEvent(void)
{
if (!EventPending(EV_DMBox_Decay)) {
float fDelayTime;
int iNumLines;
int i;
const char *pszString = m_items[0].string.c_str();
//
// Calculate the number of lines
//
iNumLines = 1;
for (i = 0; pszString[i]; i++) {
if (pszString[i] == '\n') {
iNumLines++;
}
}
if (m_items[0].flags & DMBOX_ITEM_FLAG_BOLD) {
fDelayTime = iNumLines * 8.0;
}
//
// Bold as twice more decay
//
else if (m_items[0].flags & DMBOX_ITEM_FLAG_DEATH) {
fDelayTime = iNumLines * 6.0;
} else {
fDelayTime = iNumLines * 5.0;
}
m_iBeginDecay = cls.realtime;
m_iEndDecay = (int)(fDelayTime * 1000.0);
PostEvent(EV_DMBox_Decay, fDelayTime);
}
}
void UIDMBox::setShowState(void)
{
if (m_reallyshown) {
setShow(m_boxstate != box_in);
} else {
setShow(false);
}
}
void UIDMBox::RemoveTopItem(void)
{
int i;
if (m_numitems > 0) {
for (i = 0; i < m_numitems - 1; i++) {
m_items[i] = m_items[i + 1];
}
m_numitems--;
}
}
str UIDMBox::CalculateBreaks(UIFont *font, str text, float max_width)
{
str newText;
float fX;
float fwX;
const char *current;
int count;
current = text;
fX = 0.0;
for (count = font->DBCSGetWordBlockCount(current, -1); count;
current += count, count = font->DBCSGetWordBlockCount(current, -1)) {
fwX = font->getWidth(current, count);
if (fX + fwX > max_width) {
newText += "\n" + str(current, 0, count);
fX = 0;
} else {
newText += str(current, 0, count);
}
fX += fwX;
}
return newText;
}
float UIDMBox::PrintWrap(UIFont *font, float x, float y, str text)
{
const char *p1, *p2;
size_t n, l;
float fY;
fY = y;
p1 = text.c_str();
l = text.length();
for (;;) {
p2 = strchr(p1, '\n');
if (!p2) {
break;
}
n = p2 - p1;
if (n >= l) {
break;
}
font->Print(x, fY, p1, p2 - p1, getHighResScale());
p1 = p2 + 1;
l -= n;
fY += font->getHeight();
}
if (*p1) {
font->Print(x, fY, p1, l, getHighResScale());
fY += font->getHeight();
}
return fY - y;
}
float UIDMBox::DrawItem(item_t *in, float x, float y, float alpha)
{
if (m_drawoutline) {
//
// Draw an outline
//
in->font->setColor(UBlack);
in->font->setAlpha(alpha);
PrintWrap(in->font, x + 1, y + 2, in->string);
PrintWrap(in->font, x + 2, y + 1, in->string);
PrintWrap(in->font, x - 1, y + 2, in->string);
PrintWrap(in->font, x - 2, y + 1, in->string);
PrintWrap(in->font, x - 1, y - 2, in->string);
PrintWrap(in->font, x - 2, y - 1, in->string);
PrintWrap(in->font, x + 1, y - 2, in->string);
PrintWrap(in->font, x + 2, y - 1, in->string);
PrintWrap(in->font, x + 2, y, in->string);
PrintWrap(in->font, x - 2, y, in->string);
PrintWrap(in->font, x, y + 2, in->string);
PrintWrap(in->font, x, y - 2, in->string);
}
in->font->setColor(in->color);
in->font->setAlpha(alpha);
return PrintWrap(in->font, x, y, in->string);
}
void UIDMBox::Print(const char *text)
{
const char *text1 = text;
if (m_numitems > 5) {
//
// Overwrite an item
//
RemoveTopItem();
}
m_items[m_numitems].flags = 0;
if (*text == MESSAGE_CHAT_WHITE) {
m_items[m_numitems].color = UWhiteChatMessageColor;
m_items[m_numitems].font = m_fontbold;
m_items[m_numitems].flags |= DMBOX_ITEM_FLAG_BOLD;
text1 = text + 1;
} else if (*text == MESSAGE_CHAT_RED) {
m_items[m_numitems].color = URedChatMessageColor;
m_items[m_numitems].font = m_fontbold;
m_items[m_numitems].flags |= DMBOX_ITEM_FLAG_DEATH;
text1 = text + 1;
} else if (*text == MESSAGE_CHAT_GREEN) {
m_items[m_numitems].color = UGreenChatMessageColor;
m_items[m_numitems].font = m_fontbold;
m_items[m_numitems].flags |= DMBOX_ITEM_FLAG_DEATH;
text1 = text + 1;
} else {
m_items[m_numitems].color = m_foreground_color;
m_items[m_numitems].font = m_font;
}
m_items[m_numitems].string = CalculateBreaks(m_items[m_numitems].font, Sys_LV_CL_ConvertString(text1), s_dmboxWidth);
m_numitems++;
VerifyBoxOut();
PostDecayEvent();
}
void UIDMBox::OnSizeChanged(Event *ev)
{
s_dmboxWidth = m_frame.size.width;
}
void UIDMBox::Create(const UIRect2D& rect, const UColor& fore, const UColor& back, float alpha)
{
InitFrame(NULL, rect, 0, "facfont-20");
if (!m_fontbold) {
m_fontbold = new UIFont("facfont-20");
}
m_fontbold->setColor(URed);
setBackgroundColor(back, true);
setForegroundColor(fore);
setBackgroundAlpha(alpha);
Connect(this, W_SizeChanged, W_SizeChanged);
OnSizeChanged(NULL);
m_movespeed = rect.size.height * 3.0;
setShowState();
}
void UIDMBox::MoveInEvent(Event *ev) {}
void UIDMBox::DecayEvent(Event *ev)
{
RemoveTopItem();
if (m_numitems) {
PostDecayEvent();
}
}
void UIDMBox::Draw(void)
{
float fsY;
int i;
float alpha;
float alphaScale;
alphaScale = 0.8f;
HandleBoxMoving();
if (!m_numitems) {
//
// Nothing to show
//
return;
}
m_font->setColor(m_foreground_color);
alpha = (float)(cls.realtime - m_iBeginDecay) / (float)m_iEndDecay;
if (alpha > 1.0) {
alpha = 1.0;
}
alpha = (1.0 - alpha) * 4.0;
if (alpha > 1.0) {
alpha = 1.0;
}
if (cge) {
alphaScale = 1.0 - cge->CG_GetObjectiveAlpha();
}
fsY = DrawItem(m_items, s_dmboxOffsetX, s_dmboxOffsetY, alpha * alphaScale);
fsY = alpha <= 0.2 ? s_dmboxOffsetY : fsY + s_dmboxOffsetY;
for (i = 1; i < m_numitems; i++) {
fsY += DrawItem(&m_items[i], s_dmboxOffsetX, fsY, alphaScale);
if (fsY > m_frame.size.height) {
if (EventPending(EV_DMBox_Decay)) {
CancelEventsOfType(EV_DMBox_Decay);
}
PostEvent(EV_DMBox_Decay, 0.0);
break;
}
}
}
void UIDMBox::setRealShow(bool b)
{
this->m_reallyshown = b;
setShowState();
}
void UIDMBox::Clear(void)
{
m_numitems = 0;
}