Browse Source

Chat Interface Revision (#3840)

Co-authored-by: Anders Jenbo <anders@jenbo.dk>
pull/3841/head
KPhoenix 4 years ago committed by GitHub
parent
commit
a08026097a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 30
      Source/debug.cpp
  2. 13
      Source/diablo.cpp
  3. 22
      Source/engine/render/text_render.cpp
  4. 2
      Source/engine/render/text_render.hpp
  5. 8
      Source/msg.cpp
  6. 6
      Source/multi.cpp
  7. 126
      Source/plrmsg.cpp
  8. 16
      Source/plrmsg.h

30
Source/debug.cpp

@ -22,6 +22,7 @@
#include "lighting.h" #include "lighting.h"
#include "monstdat.h" #include "monstdat.h"
#include "monster.h" #include "monster.h"
#include "plrmsg.h"
#include "quests.h" #include "quests.h"
#include "setmaps.h" #include "setmaps.h"
#include "spells.h" #include "spells.h"
@ -94,18 +95,19 @@ void SetSpellLevelCheat(spell_id spl, int spllvl)
void PrintDebugMonster(int m) void PrintDebugMonster(int m)
{ {
char dstr[MAX_SEND_STR_LEN];
auto &monster = Monsters[m]; auto &monster = Monsters[m];
sprintf(dstr, "Monster %i = %s", m, monster.mName); EventPlrMsg(fmt::format(
NetSendCmdString(1 << MyPlayerId, dstr); "Monster {:i} = {:s}\nX = {:i}, Y = {:i}\nEnemy = {:i}, HP = {:i}\nMode = {:i}, Var1 = {:i}",
sprintf(dstr, "X = %i, Y = %i", monster.position.tile.x, monster.position.tile.y); m,
NetSendCmdString(1 << MyPlayerId, dstr); monster.mName,
sprintf(dstr, "Enemy = %i, HP = %i", monster._menemy, monster._mhitpoints); monster.position.tile.x,
NetSendCmdString(1 << MyPlayerId, dstr); monster.position.tile.y,
sprintf(dstr, "Mode = %i, Var1 = %i", static_cast<int>(monster._mmode), monster._mVar1); monster._menemy,
NetSendCmdString(1 << MyPlayerId, dstr); monster._mhitpoints,
static_cast<int>(monster._mmode),
monster._mVar1),
UiFlags::ColorWhite);
bool bActive = false; bool bActive = false;
@ -114,8 +116,7 @@ void PrintDebugMonster(int m)
bActive = true; bActive = true;
} }
sprintf(dstr, "Active List = %i, Squelch = %i", bActive ? 1 : 0, monster._msquelch); EventPlrMsg(fmt::format("Active List = {:i}, Squelch = {:i}", bActive ? 1 : 0, monster._msquelch), UiFlags::ColorWhite);
NetSendCmdString(1 << MyPlayerId, dstr);
} }
void ProcessMessages() void ProcessMessages()
@ -795,14 +796,11 @@ void GetDebugMonster()
void NextDebugMonster() void NextDebugMonster()
{ {
char dstr[MAX_SEND_STR_LEN];
DebugMonsterId++; DebugMonsterId++;
if (DebugMonsterId == MAXMONSTERS) if (DebugMonsterId == MAXMONSTERS)
DebugMonsterId = 0; DebugMonsterId = 0;
sprintf(dstr, "Current debug monster = %i", DebugMonsterId); EventPlrMsg(fmt::format("Current debug monster = {:i}", DebugMonsterId), UiFlags::ColorWhite);
NetSendCmdString(1 << MyPlayerId, dstr);
} }
void SetDebugLevelSeedInfos(uint32_t mid1Seed, uint32_t mid2Seed, uint32_t mid3Seed, uint32_t endSeed) void SetDebugLevelSeedInfos(uint32_t mid1Seed, uint32_t mid2Seed, uint32_t mid3Seed, uint32_t endSeed)

13
Source/diablo.cpp

@ -1225,7 +1225,6 @@ void GameLogic()
#endif #endif
sound_update(); sound_update();
ClearPlrMsg();
CheckTriggers(); CheckTriggers();
CheckQuests(); CheckQuests();
force_redraw |= 1; force_redraw |= 1;
@ -1486,17 +1485,17 @@ void InitKeymapActions()
N_("Displays game infos."), N_("Displays game infos."),
'V', 'V',
[] { [] {
char pszStr[MAX_SEND_STR_LEN];
const char *difficulties[3] = { const char *difficulties[3] = {
_("Normal"), _("Normal"),
_("Nightmare"), _("Nightmare"),
_("Hell"), _("Hell"),
}; };
CopyUtf8(pszStr, fmt::format(_(/* TRANSLATORS: {:s} means: Character Name, Game Version, Game Difficulty. */ EventPlrMsg(fmt::format(
"{:s}, version = {:s}, mode = {:s}"), _(/* TRANSLATORS: {:s} means: Character Name, Game Version, Game Difficulty. */ "{:s} {:s}, Difficulty: {:s}"),
PROJECT_NAME, PROJECT_VERSION, difficulties[sgGameInitInfo.nDifficulty]), PROJECT_NAME,
sizeof(pszStr)); PROJECT_VERSION,
NetSendCmdString(1 << MyPlayerId, pszStr); difficulties[sgGameInitInfo.nDifficulty]),
UiFlags::ColorWhite);
}, },
[&]() { return !IsPlayerDead(); }); [&]() { return !IsPlayerDead(); });
for (int i = 0; i < 8; ++i) { for (int i = 0; i < 8; ++i) {

22
Source/engine/render/text_render.cpp

@ -283,18 +283,10 @@ bool ContainsSmallFontTallCodepoints(string_view text)
return false; return false;
} }
int GetLineHeight(string_view text, unsigned fontIndex) int GetLineHeight(string_view fmt, DrawStringFormatArg *args, std::size_t argsLen, GameFontTables fontIndex)
{
if (fontIndex == 0 && IsSmallFontTall() && ContainsSmallFontTallCodepoints(text)) {
return SmallFontTallLineHeight;
}
return LineHeights[fontIndex];
}
int GetLineHeight(string_view fmt, DrawStringFormatArg *args, std::size_t argsLen, unsigned fontIndex)
{ {
constexpr std::array<int, 6> LineHeights = { 12, 26, 38, 42, 50, 22 }; constexpr std::array<int, 6> LineHeights = { 12, 26, 38, 42, 50, 22 };
if (fontIndex == 0 && IsSmallFontTall()) { if (fontIndex == GameFont12 && IsSmallFontTall()) {
char32_t prev = U'\0'; char32_t prev = U'\0';
char32_t next; char32_t next;
FmtArgParser fmtArgParser { fmt, args, argsLen }; FmtArgParser fmtArgParser { fmt, args, argsLen };
@ -307,7 +299,7 @@ int GetLineHeight(string_view fmt, DrawStringFormatArg *args, std::size_t argsLe
const std::optional<std::size_t> fmtArgPos = fmtArgParser(rest); const std::optional<std::size_t> fmtArgPos = fmtArgParser(rest);
if (fmtArgPos) { if (fmtArgPos) {
if (ContainsSmallFontTallCodepoints(args[*fmtArgPos].GetFormatted())) if (ContainsSmallFontTallCodepoints(args[*fmtArgPos].GetFormatted()))
return true; return SmallFontTallLineHeight;
prev = U'\0'; prev = U'\0';
continue; continue;
} }
@ -484,6 +476,14 @@ int GetLineWidth(string_view fmt, DrawStringFormatArg *args, std::size_t argsLen
return lineWidth != 0 ? (lineWidth - spacing) : 0; return lineWidth != 0 ? (lineWidth - spacing) : 0;
} }
int GetLineHeight(string_view text, GameFontTables fontIndex)
{
if (fontIndex == GameFont12 && IsSmallFontTall() && ContainsSmallFontTallCodepoints(text)) {
return SmallFontTallLineHeight;
}
return LineHeights[fontIndex];
}
int AdjustSpacingToFitHorizontally(int &lineWidth, int maxSpacing, int charactersInLine, int availableWidth) int AdjustSpacingToFitHorizontally(int &lineWidth, int maxSpacing, int charactersInLine, int availableWidth)
{ {
if (lineWidth <= availableWidth || charactersInLine < 2) if (lineWidth <= availableWidth || charactersInLine < 2)

2
Source/engine/render/text_render.hpp

@ -143,6 +143,8 @@ int GetLineWidth(string_view text, GameFontTables size = GameFont12, int spacing
*/ */
int GetLineWidth(string_view fmt, DrawStringFormatArg *args, std::size_t argsLen, GameFontTables size, int spacing, int *charactersInLine = nullptr); int GetLineWidth(string_view fmt, DrawStringFormatArg *args, std::size_t argsLen, GameFontTables size, int spacing, int *charactersInLine = nullptr);
int GetLineHeight(string_view text, GameFontTables fontIndex);
[[nodiscard]] std::string WordWrapString(string_view text, size_t width, GameFontTables size = GameFont12, int spacing = 1); [[nodiscard]] std::string WordWrapString(string_view text, size_t width, GameFontTables size = GameFont12, int spacing = 1);
/** /**

8
Source/msg.cpp

@ -1669,7 +1669,7 @@ DWORD OnPlayerJoinLevel(const TCmd *pCmd, int pnum)
ResetPlayerGFX(player); ResetPlayerGFX(player);
player.plractive = true; player.plractive = true;
gbActivePlayers++; gbActivePlayers++;
EventPlrMsg(fmt::format(_("Player '{:s}' (level {:d}) just joined the game"), player._pName, player._pLevel).c_str()); EventPlrMsg(fmt::format(_("Player '{:s}' (level {:d}) just joined the game"), player._pName, player._pLevel));
} }
if (player.plractive && MyPlayerId != pnum) { if (player.plractive && MyPlayerId != pnum) {
@ -1811,13 +1811,13 @@ DWORD OnSetVitality(const TCmd *pCmd, int pnum)
return sizeof(message); return sizeof(message);
} }
DWORD OnString(const TCmd *pCmd, int pnum) DWORD OnString(const TCmd *pCmd, Player &player)
{ {
auto *p = (TCmdString *)pCmd; auto *p = (TCmdString *)pCmd;
int len = strlen(p->str); int len = strlen(p->str);
if (gbBufferMsgs == 0) if (gbBufferMsgs == 0)
SendPlrMsg(pnum, p->str); SendPlrMsg(player, p->str);
return len + 2; // length of string + nul terminator + sizeof(p->bCmd) return len + 2; // length of string + nul terminator + sizeof(p->bCmd)
} }
@ -2826,7 +2826,7 @@ uint32_t ParseCmd(int pnum, const TCmd *pCmd)
case CMD_SETVIT: case CMD_SETVIT:
return OnSetVitality(pCmd, pnum); return OnSetVitality(pCmd, pnum);
case CMD_STRING: case CMD_STRING:
return OnString(pCmd, pnum); return OnString(pCmd, player);
case CMD_SYNCQUEST: case CMD_SYNCQUEST:
return OnSyncQuest(pCmd, pnum); return OnSyncQuest(pCmd, pnum);
case CMD_CHEAT_EXPERIENCE: case CMD_CHEAT_EXPERIENCE:

6
Source/multi.cpp

@ -202,7 +202,7 @@ void PlayerLeftMsg(int pnum, bool left)
pszFmt = _("Player '{:s}' dropped due to timeout"); pszFmt = _("Player '{:s}' dropped due to timeout");
break; break;
} }
EventPlrMsg(fmt::format(pszFmt, player._pName).c_str()); EventPlrMsg(fmt::format(pszFmt, player._pName));
} }
player.plractive = false; player.plractive = false;
player._pName[0] = '\0'; player._pName[0] = '\0';
@ -398,7 +398,7 @@ void HandleEvents(_SNETEVENT *pEvt)
gbDeltaSender = MAX_PLRS; gbDeltaSender = MAX_PLRS;
break; break;
case EVENT_TYPE_PLAYER_MESSAGE: case EVENT_TYPE_PLAYER_MESSAGE:
ErrorPlrMsg((char *)pEvt->data); EventPlrMsg((char *)pEvt->data);
break; break;
} }
} }
@ -805,7 +805,7 @@ void recv_plrinfo(int pnum, const TCmdPlrInfoHdr &header, bool recv)
} else { } else {
szEvent = _("Player '{:s}' (level {:d}) is already in the game"); szEvent = _("Player '{:s}' (level {:d}) is already in the game");
} }
EventPlrMsg(fmt::format(szEvent, player._pName, player._pLevel).c_str()); EventPlrMsg(fmt::format(szEvent, player._pName, player._pLevel));
SyncInitPlr(pnum); SyncInitPlr(pnum);

126
Source/plrmsg.cpp

@ -9,32 +9,41 @@
#include <fmt/format.h> #include <fmt/format.h>
#include "DiabloUI/ui_flags.hpp"
#include "control.h" #include "control.h"
#include "engine/render/text_render.hpp" #include "engine/render/text_render.hpp"
#include "inv.h" #include "inv.h"
#include "utils/language.h" #include "utils/language.h"
#include "utils/stdcompat/string_view.hpp"
#include "utils/utf8.hpp" #include "utils/utf8.hpp"
namespace devilution { namespace devilution {
namespace { namespace {
#define PMSG_COUNT 8 struct PlayerMessage {
/** Time message was recived */
uint8_t plr_msg_slot; Uint32 time;
_plrmsg plr_msgs[PMSG_COUNT]; /** The default text color */
UiFlags style;
/** Maps from player_num to text color, as used in chat messages. */ /** The text message to display on screen */
const UiFlags TextColorFromPlayerId[MAX_PLRS + 1] = { UiFlags::ColorWhite, UiFlags::ColorWhite, UiFlags::ColorWhite, UiFlags::ColorWhite, UiFlags::ColorWhitegold }; std::string text;
/** First portion of text that should be rendered in gold */
string_view from;
/** The line height of the text */
int lineHeight;
};
std::array<PlayerMessage, 8> Messages;
int CountLinesOfText(string_view text)
{
return 1 + std::count(text.begin(), text.end(), '\n');
}
void PrintChatMessage(const Surface &out, int x, int y, int width, char *textPtr, UiFlags style) PlayerMessage &GetNextMessage()
{ {
const size_t length = strlen(textPtr); std::move_backward(Messages.begin(), Messages.end() - 1, Messages.end()); // Push back older messages
std::replace(textPtr, textPtr + length, '\n', ' ');
const string_view text { textPtr, length }; return Messages.front();
DrawString(out, WordWrapString(text, width), { { x, y }, { width, 0 } }, style, 1, 18);
} }
} // namespace } // namespace
@ -49,87 +58,70 @@ void plrmsg_delay(bool delay)
} }
plrmsgTicks += SDL_GetTicks(); plrmsgTicks += SDL_GetTicks();
_plrmsg *pMsg = plr_msgs; for (PlayerMessage &message : Messages)
for (int i = 0; i < PMSG_COUNT; i++, pMsg++) message.time += plrmsgTicks;
pMsg->time += plrmsgTicks;
} }
void ErrorPlrMsg(const char *pszMsg) void EventPlrMsg(string_view text, UiFlags style)
{ {
_plrmsg *pMsg = &plr_msgs[plr_msg_slot]; PlayerMessage &message = GetNextMessage();
plr_msg_slot = (plr_msg_slot + 1) & (PMSG_COUNT - 1);
pMsg->player = MAX_PLRS;
pMsg->time = SDL_GetTicks();
CopyUtf8(pMsg->str, pszMsg, sizeof(pMsg->str));
}
size_t EventPlrMsg(const char *pszFmt, ...) message.style = style;
{ message.time = SDL_GetTicks();
_plrmsg *pMsg; message.text = std::string(text);
va_list va; message.from = string_view(message.text.data(), 0);
message.lineHeight = GetLineHeight(message.text, GameFont12) + 3;
va_start(va, pszFmt);
pMsg = &plr_msgs[plr_msg_slot];
plr_msg_slot = (plr_msg_slot + 1) & (PMSG_COUNT - 1);
pMsg->player = MAX_PLRS;
pMsg->time = SDL_GetTicks();
vsprintf(pMsg->str, pszFmt, va);
va_end(va);
return strlen(pMsg->str);
} }
void SendPlrMsg(int pnum, const char *pszStr) void SendPlrMsg(Player &player, string_view text)
{ {
_plrmsg *pMsg = &plr_msgs[plr_msg_slot]; PlayerMessage &message = GetNextMessage();
plr_msg_slot = (plr_msg_slot + 1) & (PMSG_COUNT - 1);
pMsg->player = pnum;
pMsg->time = SDL_GetTicks();
auto &player = Players[pnum];
assert(strlen(player._pName) < PLR_NAME_LEN);
assert(strlen(pszStr) < MAX_SEND_STR_LEN);
CopyUtf8(pMsg->str, fmt::format(_("{:s} (lvl {:d}): {:s}"), player._pName, player._pLevel, pszStr), sizeof(pMsg->str));
}
void ClearPlrMsg() std::string from = fmt::format(_("{:s} (lvl {:d}): "), player._pName, player._pLevel);
{
_plrmsg *pMsg = plr_msgs;
uint32_t tick = SDL_GetTicks();
for (int i = 0; i < PMSG_COUNT; i++, pMsg++) { message.style = UiFlags::ColorWhite;
if ((int)(tick - pMsg->time) > 10000) message.time = SDL_GetTicks();
pMsg->str[0] = '\0'; message.text = from + std::string(text);
} message.from = string_view(message.text.data(), from.size());
message.lineHeight = GetLineHeight(message.text, GameFont12) + 3;
} }
void InitPlrMsg() void InitPlrMsg()
{ {
memset(plr_msgs, 0, sizeof(plr_msgs)); Messages = {};
plr_msg_slot = 0;
} }
void DrawPlrMsg(const Surface &out) void DrawPlrMsg(const Surface &out)
{ {
int x = 10; int x = 10;
int y = 58; int y = PANEL_TOP - 13;
int width = gnScreenWidth - 20; int width = gnScreenWidth - 20;
_plrmsg *pMsg;
if (chrflag || QuestLogIsOpen) { if (!talkflag && (chrflag || QuestLogIsOpen)) {
x += GetLeftPanel().position.x + GetLeftPanel().size.width; x += GetLeftPanel().position.x + GetLeftPanel().size.width;
width -= GetLeftPanel().size.width; width -= GetLeftPanel().size.width;
} }
if (invflag || sbookflag) if (!talkflag && (invflag || sbookflag))
width -= gnScreenWidth - GetRightPanel().position.x; width -= gnScreenWidth - GetRightPanel().position.x;
if (width < 300) if (width < 300)
return; return;
pMsg = plr_msgs; width = std::min(540, width);
for (int i = 0; i < PMSG_COUNT; i++) {
if (pMsg->str[0] != '\0') for (PlayerMessage &message : Messages) {
PrintChatMessage(out, x, y, width, pMsg->str, TextColorFromPlayerId[pMsg->player]); if (message.text.empty())
pMsg++; break;
y += 35; if (!talkflag && SDL_GetTicks() - message.time >= 10000)
break;
std::string text = WordWrapString(message.text, width);
int chatlines = CountLinesOfText(text);
y -= message.lineHeight * chatlines;
DrawHalfTransparentRectTo(out, x - 3, y, width + 6, message.lineHeight * chatlines);
DrawString(out, text, { { x, y }, { width, 0 } }, UiFlags::ColorWhite, 1, message.lineHeight);
DrawString(out, message.from, { { x, y }, { width, 0 } }, UiFlags::ColorWhitegold, 1, message.lineHeight);
} }
} }

16
Source/plrmsg.h

@ -7,22 +7,18 @@
#include "SDL.h" #include "SDL.h"
#include <cstdint> #include <cstdint>
#include <string>
#include "DiabloUI/ui_flags.hpp"
#include "engine.h" #include "engine.h"
#include "player.h"
#include "utils/stdcompat/string_view.hpp"
namespace devilution { namespace devilution {
struct _plrmsg {
Uint32 time;
uint8_t player;
char str[144];
};
void plrmsg_delay(bool delay); void plrmsg_delay(bool delay);
void ErrorPlrMsg(const char *pszMsg); void EventPlrMsg(string_view text, UiFlags style = UiFlags::ColorWhitegold);
size_t EventPlrMsg(const char *pszFmt, ...); void SendPlrMsg(Player &player, string_view text);
void SendPlrMsg(int pnum, const char *pszStr);
void ClearPlrMsg();
void InitPlrMsg(); void InitPlrMsg();
void DrawPlrMsg(const Surface &out); void DrawPlrMsg(const Surface &out);

Loading…
Cancel
Save