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.
212 lines
5.4 KiB
212 lines
5.4 KiB
/** |
|
* @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; |
|
CloseInventory(); |
|
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); |
|
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) |
|
{ |
|
if (!ChatLogFlag) { |
|
return; |
|
} |
|
|
|
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
|
|
|