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 "monstdat.h"
#include "monster.h"
#include "plrmsg.h"
#include "quests.h"
#include "setmaps.h"
#include "spells.h"
@ -94,18 +95,19 @@ void SetSpellLevelCheat(spell_id spl, int spllvl)
void PrintDebugMonster(int m)
{
char dstr[MAX_SEND_STR_LEN];
auto &monster = Monsters[m];
sprintf(dstr, "Monster %i = %s", m, monster.mName);
NetSendCmdString(1 << MyPlayerId, dstr);
sprintf(dstr, "X = %i, Y = %i", monster.position.tile.x, monster.position.tile.y);
NetSendCmdString(1 << MyPlayerId, dstr);
sprintf(dstr, "Enemy = %i, HP = %i", monster._menemy, monster._mhitpoints);
NetSendCmdString(1 << MyPlayerId, dstr);
sprintf(dstr, "Mode = %i, Var1 = %i", static_cast<int>(monster._mmode), monster._mVar1);
NetSendCmdString(1 << MyPlayerId, dstr);
EventPlrMsg(fmt::format(
"Monster {:i} = {:s}\nX = {:i}, Y = {:i}\nEnemy = {:i}, HP = {:i}\nMode = {:i}, Var1 = {:i}",
m,
monster.mName,
monster.position.tile.x,
monster.position.tile.y,
monster._menemy,
monster._mhitpoints,
static_cast<int>(monster._mmode),
monster._mVar1),
UiFlags::ColorWhite);
bool bActive = false;
@ -114,8 +116,7 @@ void PrintDebugMonster(int m)
bActive = true;
}
sprintf(dstr, "Active List = %i, Squelch = %i", bActive ? 1 : 0, monster._msquelch);
NetSendCmdString(1 << MyPlayerId, dstr);
EventPlrMsg(fmt::format("Active List = {:i}, Squelch = {:i}", bActive ? 1 : 0, monster._msquelch), UiFlags::ColorWhite);
}
void ProcessMessages()
@ -795,14 +796,11 @@ void GetDebugMonster()
void NextDebugMonster()
{
char dstr[MAX_SEND_STR_LEN];
DebugMonsterId++;
if (DebugMonsterId == MAXMONSTERS)
DebugMonsterId = 0;
sprintf(dstr, "Current debug monster = %i", DebugMonsterId);
NetSendCmdString(1 << MyPlayerId, dstr);
EventPlrMsg(fmt::format("Current debug monster = {:i}", DebugMonsterId), UiFlags::ColorWhite);
}
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
sound_update();
ClearPlrMsg();
CheckTriggers();
CheckQuests();
force_redraw |= 1;
@ -1486,17 +1485,17 @@ void InitKeymapActions()
N_("Displays game infos."),
'V',
[] {
char pszStr[MAX_SEND_STR_LEN];
const char *difficulties[3] = {
_("Normal"),
_("Nightmare"),
_("Hell"),
};
CopyUtf8(pszStr, fmt::format(_(/* TRANSLATORS: {:s} means: Character Name, Game Version, Game Difficulty. */
"{:s}, version = {:s}, mode = {:s}"),
PROJECT_NAME, PROJECT_VERSION, difficulties[sgGameInitInfo.nDifficulty]),
sizeof(pszStr));
NetSendCmdString(1 << MyPlayerId, pszStr);
EventPlrMsg(fmt::format(
_(/* TRANSLATORS: {:s} means: Character Name, Game Version, Game Difficulty. */ "{:s} {:s}, Difficulty: {:s}"),
PROJECT_NAME,
PROJECT_VERSION,
difficulties[sgGameInitInfo.nDifficulty]),
UiFlags::ColorWhite);
},
[&]() { return !IsPlayerDead(); });
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;
}
int GetLineHeight(string_view text, unsigned 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)
int GetLineHeight(string_view fmt, DrawStringFormatArg *args, std::size_t argsLen, GameFontTables fontIndex)
{
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 next;
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);
if (fmtArgPos) {
if (ContainsSmallFontTallCodepoints(args[*fmtArgPos].GetFormatted()))
return true;
return SmallFontTallLineHeight;
prev = U'\0';
continue;
}
@ -484,6 +476,14 @@ int GetLineWidth(string_view fmt, DrawStringFormatArg *args, std::size_t argsLen
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)
{
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 GetLineHeight(string_view text, GameFontTables fontIndex);
[[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);
player.plractive = true;
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) {
@ -1811,13 +1811,13 @@ DWORD OnSetVitality(const TCmd *pCmd, int pnum)
return sizeof(message);
}
DWORD OnString(const TCmd *pCmd, int pnum)
DWORD OnString(const TCmd *pCmd, Player &player)
{
auto *p = (TCmdString *)pCmd;
int len = strlen(p->str);
if (gbBufferMsgs == 0)
SendPlrMsg(pnum, p->str);
SendPlrMsg(player, p->str);
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:
return OnSetVitality(pCmd, pnum);
case CMD_STRING:
return OnString(pCmd, pnum);
return OnString(pCmd, player);
case CMD_SYNCQUEST:
return OnSyncQuest(pCmd, pnum);
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");
break;
}
EventPlrMsg(fmt::format(pszFmt, player._pName).c_str());
EventPlrMsg(fmt::format(pszFmt, player._pName));
}
player.plractive = false;
player._pName[0] = '\0';
@ -398,7 +398,7 @@ void HandleEvents(_SNETEVENT *pEvt)
gbDeltaSender = MAX_PLRS;
break;
case EVENT_TYPE_PLAYER_MESSAGE:
ErrorPlrMsg((char *)pEvt->data);
EventPlrMsg((char *)pEvt->data);
break;
}
}
@ -805,7 +805,7 @@ void recv_plrinfo(int pnum, const TCmdPlrInfoHdr &header, bool recv)
} else {
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);

126
Source/plrmsg.cpp

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

Loading…
Cancel
Save