You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
5.4 KiB

4 years ago
/**
* @file chatlog.cpp
*
* Implementation of the in-game chat log.
*/
#include <fmt/format.h>
#include <string>
#include <vector>
#include "DiabloUI/ui_flags.hpp"
#include "automap.h"
#include "chatlog.h"
#include "control.h"
#include "doom.h"
#include "engine/render/text_render.hpp"
#include "error.h"
#include "gamemenu.h"
#include "help.h"
#include "init.h"
#include "inv.h"
#include "minitext.h"
#include "stores.h"
#include "utils/language.h"
#include "utils/stdcompat/string_view.hpp"
namespace devilution {
namespace {
struct ColoredText {
std::string text;
UiFlags color;
};
struct MultiColoredText {
std::string text;
std::vector<ColoredText> colors;
int offset = 0;
};
bool UnreadFlag = false;
unsigned int SkipLines;
unsigned int MessageCounter = 0;
std::vector<MultiColoredText> ChatLogLines;
constexpr int PaddingTop = 32;
constexpr int PaddingLeft = 32;
constexpr int PanelHeight = 297;
constexpr int ContentTextWidth = 577;
int LineHeight()
{
return IsSmallFontTall() ? 18 : 14;
}
int BlankLineHeight()
{
return 12;
}
int DividerLineMarginY()
{
return BlankLineHeight() / 2;
}
int HeaderHeight()
{
return PaddingTop + LineHeight() + 2 * BlankLineHeight() + DividerLineMarginY();
}
int ContentPaddingY()
{
return BlankLineHeight();
}
int ContentsTextHeight()
{
return PanelHeight - HeaderHeight() - DividerLineMarginY() - 2 * ContentPaddingY() - BlankLineHeight();
}
int NumVisibleLines()
{
return (ContentsTextHeight() - 1) / LineHeight() + 1; // Ceil
}
} // namespace
bool ChatLogFlag = false;
void ToggleChatLog()
{
if (ChatLogFlag) {
ChatLogFlag = false;
} else {
stextflag = STORE_NONE;
4 years ago
CloseInventory();
4 years ago
chrflag = false;
sbookflag = false;
spselflag = false;
if (qtextflag && leveltype == DTYPE_TOWN) {
qtextflag = false;
stream_stop();
}
QuestLogIsOpen = false;
HelpFlag = false;
AutomapActive = false;
CancelCurrentDiabloMsg();
gamemenu_off();
SkipLines = 0;
ChatLogFlag = true;
doom_close();
}
}
void AddMessageToChatLog(const std::string &message, Player *player, UiFlags flags)
{
MessageCounter++;
time_t tm = time(nullptr);
std::string timestamp = fmt::format("[#{:d}] {:02}:{:02}:{:02}", MessageCounter, localtime(&tm)->tm_hour, localtime(&tm)->tm_min, localtime(&tm)->tm_sec);
int oldSize = ChatLogLines.size();
ChatLogLines.emplace_back(MultiColoredText { "", { {} } });
if (player == nullptr) {
ChatLogLines.emplace_back(MultiColoredText { "{0} {1}", { { timestamp, UiFlags::ColorRed }, { message, flags } } });
} else {
std::string playerInfo = fmt::format(_("{:s} (lvl {:d}): "), player->_pName, player->_pLevel);
4 years ago
ChatLogLines.emplace_back(MultiColoredText { message, { {} }, 20 });
UiFlags nameColor = player == &Players[MyPlayerId] ? UiFlags::ColorWhitegold : UiFlags::ColorBlue;
ChatLogLines.emplace_back(MultiColoredText { "{0} - {1}", { { timestamp, UiFlags::ColorRed }, { playerInfo, nameColor } } });
}
unsigned int diff = ChatLogLines.size() - oldSize;
// only autoscroll when on top of the log
if (SkipLines != 0) {
SkipLines += diff;
UnreadFlag = true;
}
}
void DrawChatLog(const Surface &out)
{
4 years ago
if (!ChatLogFlag) {
return;
}
4 years ago
DrawSTextHelp();
DrawQTextBack(out);
if (SkipLines == 0) {
UnreadFlag = false;
}
const int lineHeight = LineHeight();
const int blankLineHeight = BlankLineHeight();
const int sx = PANEL_X + PaddingLeft;
const int sy = UI_OFFSET_Y;
DrawString(out, fmt::format(_("Chat History (Messages: {:d})"), MessageCounter),
{ { sx, sy + PaddingTop + blankLineHeight }, { ContentTextWidth, lineHeight } },
(UnreadFlag ? UiFlags::ColorRed : UiFlags::ColorWhitegold) | UiFlags::AlignCenter);
time_t tm = time(nullptr);
std::string timestamp = fmt::format("{:02}:{:02}:{:02}", localtime(&tm)->tm_hour, localtime(&tm)->tm_min, localtime(&tm)->tm_sec);
DrawString(out, timestamp, { { sx, sy + PaddingTop + blankLineHeight }, { ContentTextWidth, lineHeight } }, UiFlags::ColorWhitegold);
const int titleBottom = sy + HeaderHeight();
DrawSLine(out, titleBottom);
const int numLines = NumVisibleLines();
const int contentY = titleBottom + DividerLineMarginY() + ContentPaddingY();
for (int i = 0; i < numLines; i++) {
if (i + SkipLines >= ChatLogLines.size())
break;
MultiColoredText &text = ChatLogLines[ChatLogLines.size() - (i + SkipLines + 1)];
const string_view line = text.text;
std::vector<DrawStringFormatArg> args;
for (auto &x : text.colors) {
args.emplace_back(DrawStringFormatArg { x.text, x.color });
}
DrawStringWithColors(out, line, args, { { (sx + text.offset), contentY + i * lineHeight }, { ContentTextWidth - text.offset * 2, lineHeight } }, UiFlags::ColorWhite, /*spacing=*/1, lineHeight);
}
DrawString(out, _("Press ESC to end or the arrow keys to scroll."),
{ { sx, contentY + ContentsTextHeight() + ContentPaddingY() + blankLineHeight }, { ContentTextWidth, lineHeight } },
UiFlags::ColorWhitegold | UiFlags::AlignCenter);
}
void ChatLogScrollUp()
{
if (SkipLines > 0)
SkipLines--;
}
void ChatLogScrollDown()
{
if (SkipLines + NumVisibleLines() < ChatLogLines.size())
SkipLines++;
}
void ChatLogScrollTop()
{
SkipLines = 0;
}
void ChatLogScrollBottom()
{
SkipLines = ChatLogLines.size() - NumVisibleLines();
}
} // namespace devilution