Chat Interface Revision (#3840)
Co-authored-by: Anders Jenbo <anders@jenbo.dk>
This commit is contained in:
parent
6de6825ce0
commit
a08026097a
8 changed files with 105 additions and 118 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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;
|
||||
};
|
||||
|
||||
uint8_t plr_msg_slot;
|
||||
_plrmsg plr_msgs[PMSG_COUNT];
|
||||
std::array<PlayerMessage, 8> Messages;
|
||||
|
||||
/** 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 };
|
||||
|
||||
void PrintChatMessage(const Surface &out, int x, int y, int width, char *textPtr, UiFlags style)
|
||||
int CountLinesOfText(string_view text)
|
||||
{
|
||||
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);
|
||||
return 1 + std::count(text.begin(), text.end(), '\n');
|
||||
}
|
||||
|
||||
PlayerMessage &GetNextMessage()
|
||||
{
|
||||
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();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
size_t EventPlrMsg(const char *pszFmt, ...)
|
||||
void SendPlrMsg(Player &player, string_view text)
|
||||
{
|
||||
_plrmsg *pMsg;
|
||||
va_list va;
|
||||
PlayerMessage &message = GetNextMessage();
|
||||
|
||||
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);
|
||||
}
|
||||
std::string from = fmt::format(_("{:s} (lvl {:d}): "), player._pName, player._pLevel);
|
||||
|
||||
void SendPlrMsg(int pnum, const char *pszStr)
|
||||
{
|
||||
_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));
|
||||
}
|
||||
|
||||
void ClearPlrMsg()
|
||||
{
|
||||
_plrmsg *pMsg = plr_msgs;
|
||||
uint32_t tick = SDL_GetTicks();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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…
Add table
Add a link
Reference in a new issue