Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ set(mmapper_SRCS
group/ColorGenerator.h
group/GroupManagerApi.cpp
group/GroupManagerApi.h
group/tokenmanager.cpp
group/tokenmanager.h
group/tokenpickerdialog.cpp
group/tokenpickerdialog.h
group/GroupModel.cpp
group/GroupModel.h
group/groupwidget.cpp
Expand Down
36 changes: 36 additions & 0 deletions src/configuration/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,24 @@ void Configuration::GroupManagerSettings::read(const QSettings &conf)
npcColorOverride = conf.value(KEY_GROUP_NPC_COLOR_OVERRIDE, false).toBool();
npcHide = conf.value(KEY_GROUP_NPC_HIDE, false).toBool();
npcSortBottom = conf.value(KEY_GROUP_NPC_SORT_BOTTOM, false).toBool();

// TODO: Replace string keys with KEY_GROUP_* constants for consistency
showTokens = conf.value("group/showTokens", false).toBool();
showMapTokens = conf.value("group/showMapTokens", false).toBool();
tokenIconSize = conf.value("group/tokenIconSize", 32).toInt();

tokenOverrides.clear();
const QVariantMap overridesMap = conf.value("group/tokenOverrides").toMap();
for (auto it = overridesMap.constBegin(); it != overridesMap.constEnd(); ++it) {
tokenOverrides.insert(it.key(), it.value().toString());
}

bearTokenOverrides.clear();
const QVariantMap bearOverridesMap = conf.value("group/bearTokenOverrides").toMap();
for (auto it = bearOverridesMap.constBegin(); it != bearOverridesMap.constEnd(); ++it) {
bearTokenOverrides.insert(it.key(), it.value().toString());
}
defaultBearToken = conf.value("group/defaultBearToken").toString();
}

void Configuration::MumeClockSettings::read(const QSettings &conf)
Expand Down Expand Up @@ -995,6 +1013,24 @@ void Configuration::GroupManagerSettings::write(QSettings &conf) const
conf.setValue(KEY_GROUP_NPC_COLOR_OVERRIDE, npcColorOverride);
conf.setValue(KEY_GROUP_NPC_HIDE, npcHide);
conf.setValue(KEY_GROUP_NPC_SORT_BOTTOM, npcSortBottom);

// TODO: Replace string keys with KEY_GROUP_* constants for consistency
conf.setValue("group/showTokens", showTokens);
conf.setValue("group/showMapTokens", showMapTokens);
conf.setValue("group/tokenIconSize", tokenIconSize);

QVariantMap overridesMap;
for (auto it = tokenOverrides.constBegin(); it != tokenOverrides.constEnd(); ++it) {
overridesMap.insert(it.key(), it.value());
}
conf.setValue("group/tokenOverrides", overridesMap);

QVariantMap bearOverridesMap;
for (auto it = bearTokenOverrides.constBegin(); it != bearTokenOverrides.constEnd(); ++it) {
bearOverridesMap.insert(it.key(), it.value());
}
conf.setValue("group/bearTokenOverrides", bearOverridesMap);
conf.setValue("group/defaultBearToken", defaultBearToken);
}

void Configuration::MumeClockSettings::write(QSettings &conf) const
Expand Down
6 changes: 6 additions & 0 deletions src/configuration/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,12 @@ class NODISCARD Configuration final
bool npcColorOverride = false;
bool npcSortBottom = false;
bool npcHide = false;
bool showTokens = false;
bool showMapTokens = false;
int tokenIconSize = 32;
QMap<QString, QString> tokenOverrides;
QMap<QString, QString> bearTokenOverrides;
QString defaultBearToken;

private:
SUBGROUP();
Expand Down
126 changes: 119 additions & 7 deletions src/display/Characters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "../configuration/configuration.h"
#include "../group/CGroupChar.h"
#include "../group/mmapper2group.h"
#include "../group/tokenmanager.h"
#include "../map/roomid.h"
#include "../mapdata/mapdata.h"
#include "../mapdata/roomselection.h"
Expand All @@ -25,6 +26,7 @@
#include <glm/glm.hpp>
#include <glm/gtx/norm.hpp>

#include <QOpenGLContext>
#include <QtCore>

static constexpr float CHAR_ARROW_LINE_WIDTH = 2.f;
Expand All @@ -49,7 +51,8 @@ bool CharacterBatch::isVisible(const Coordinate c, float margin) const
return m_mapScreen.isRoomVisible(c, margin);
}

void CharacterBatch::drawCharacter(const Coordinate c, const Color color, bool fill)
void CharacterBatch::drawCharacter(
const Coordinate c, const Color color, bool fill, const QString &dispName, const bool bearForm)
{
const Configuration::CanvasSettings &settings = getConfig().canvas;

Expand Down Expand Up @@ -100,7 +103,7 @@ void CharacterBatch::drawCharacter(const Coordinate c, const Color color, bool f
}

const bool beacon = visible && !differentLayer && wantBeacons;
gl.drawBox(c, fill, beacon, isFar);
gl.drawBox(c, fill, beacon, isFar, dispName, bearForm);
}

void CharacterBatch::CharFakeGL::drawPathSegment(const glm::vec3 p1,
Expand Down Expand Up @@ -235,7 +238,9 @@ void CharacterBatch::CharFakeGL::drawQuadCommon(const glm::vec2 in_a,
void CharacterBatch::CharFakeGL::drawBox(const Coordinate coord,
bool fill,
bool beacon,
const bool isFar)
const bool isFar,
const QString &dispName,
const bool bearForm)
{
const bool dontFillRotatedQuads = true;
const bool shrinkRotatedQuads = false; // REVISIT: make this a user option?
Expand Down Expand Up @@ -300,6 +305,38 @@ void CharacterBatch::CharFakeGL::drawBox(const Coordinate coord,
}
}

if (!dispName.isEmpty() && getConfig().groupManager.showMapTokens && numAlreadyInRoom == 0) {
const Color tokenColor{1.f, 1.f, 1.f, 1.f};
const auto &mtx = m_stack.top().modelView;

auto pushVert = [this, &tokenColor, &mtx](const glm::vec2 &roomPos, const glm::vec2 &uv) {
const auto tmp = mtx * glm::vec4(roomPos, 0.f, 1.f);
m_charTokenQuads.emplace_back(tokenColor, glm::vec3{uv, 0.f}, glm::vec3{tmp / tmp.w});
};

// Scale the room quad around its center to 80% so the character square remains visible.
static constexpr float kTokenScale = 0.80f;
const glm::vec2 center = 0.5f * (a + c);
const auto scaleAround = [&](const glm::vec2 &p) {
return center + (p - center) * kTokenScale;
};

const glm::vec2 sa = scaleAround(a);
const glm::vec2 sb = scaleAround(b);
const glm::vec2 sc = scaleAround(c);
const glm::vec2 sd = scaleAround(d);

// Keep full UVs so the whole texture shows on the smaller quad.
pushVert(sa, {0.f, 0.f});
pushVert(sb, {1.f, 0.f});
pushVert(sc, {1.f, 1.f});
pushVert(sd, {0.f, 1.f});

// Resolve the override once in TokenManager, using the display name as the key.
m_charTokenKeys.emplace_back(dispName);
m_charTokenBearForms.emplace_back(bearForm);
}

glPopMatrix();
}

Expand All @@ -324,6 +361,7 @@ void CharacterBatch::CharFakeGL::drawArrow(const bool fill, const bool beacon)

void CharacterBatch::CharFakeGL::reallyDrawCharacters(OpenGL &gl, const MapCanvasTextures &textures)
{
tokenManager().processPendingTextureChanges(gl);
const auto blended_noDepth
= GLRenderState().withDepthFunction(std::nullopt).withBlend(BlendModeEnum::TRANSPARENCY);

Expand All @@ -343,6 +381,50 @@ void CharacterBatch::CharFakeGL::reallyDrawCharacters(OpenGL &gl, const MapCanva
gl.renderColoredTris(m_charTris, blended_noDepth);
}

if (!m_charTokenQuads.empty() && !m_charTokenKeys.empty()) {
size_t base = 0;

for (size_t tokenIndex = 0; tokenIndex < m_charTokenKeys.size(); ++tokenIndex) {
if (base + 3 >= m_charTokenQuads.size())
break;

const QString &key = m_charTokenKeys[tokenIndex];
const bool bearForm = m_charTokenBearForms[tokenIndex];
MMTextureId id = tokenManager().textureIdFor(key, bearForm);

if (id == INVALID_MM_TEXTURE_ID) {
if (!QOpenGLContext::currentContext()) {
base += 4;
continue;
}

const QPixmap px = tokenManager().getToken(key, bearForm);

if (!px.isNull()) {
id = tokenManager().uploadNow(key, bearForm, px);
}
}

if (id == INVALID_MM_TEXTURE_ID) {
base += 4;
continue;
}

SharedMMTexture tex = tokenManager().textureById(id);
if (!tex) {
base += 4;
continue;
}

gl.setTextureLookup(id, tex);

gl.renderColoredTexturedQuads(View<ColoredTexVert>{m_charTokenQuads.data() + base, 4},
blended_noDepth.withTexture0(id));

base += 4;
}
}

if (!m_charLines.empty()) {
gl.renderColoredLines(m_charLines,
blended_noDepth.withLineParams(LineParams{CHAR_ARROW_LINE_WIDTH}));
Expand All @@ -357,6 +439,10 @@ void CharacterBatch::CharFakeGL::reallyDrawCharacters(OpenGL &gl, const MapCanva
gl.renderFont3d(textures.char_arrows, m_screenSpaceArrows);
m_screenSpaceArrows.clear();
}

m_charTokenQuads.clear();
m_charTokenKeys.clear();
m_charTokenBearForms.clear();
}

void CharacterBatch::CharFakeGL::reallyDrawPaths(OpenGL &gl)
Expand Down Expand Up @@ -471,8 +557,16 @@ void MapCanvas::paintCharacters()

CharacterBatch characterBatch{m_mapScreen, getCurrentLayer(), getTotalScaleFactor()};

const CGroupChar *playerChar = nullptr;
for (const auto &pCharacter : m_groupManager.selectAll()) {
if (pCharacter->isYou()) {
playerChar = pCharacter.get();
break;
}
}

// IIFE to abuse return to avoid duplicate else branches
std::invoke([this, &characterBatch]() -> void {
std::invoke([this, &characterBatch, playerChar]() -> void {
if (const std::optional<RoomId> opt_pos = m_data.getCurrentRoomId()) {
const auto &id = opt_pos.value();
if (const auto room = m_data.findRoomHandle(id)) {
Expand All @@ -484,8 +578,11 @@ void MapCanvas::paintCharacters()

// paint char current position
const Color color{getConfig().groupManager.color};
characterBatch.drawCharacter(pos, color);

characterBatch.drawCharacter(pos,
color,
true,
playerChar ? playerChar->getDisplayName() : QString(),
playerChar && playerChar->isBearForm());
// paint prespam
const auto prespam = m_data.getPath(id, m_prespammedPath.getQueue());
characterBatch.drawPreSpammedPath(pos, prespam, color);
Expand All @@ -510,6 +607,17 @@ void MapCanvas::drawGroupCharacters(CharacterBatch &batch, ServerRoomId yourServ

RoomIdSet drawnRoomIds;
const Map &map = m_data.getCurrentMap();

RoomId playerRoomId = INVALID_ROOMID;
for (const auto &p : m_groupManager.selectAll()) {
if (p->isYou()) {
if (const auto r = map.findRoomHandle(p->getServerId())) {
playerRoomId = r.getId();
}
break;
}
}

for (const auto &pCharacter : m_groupManager.selectAll()) {
// Omit player so that they know group members are below them
if (pCharacter->isYou()) {
Expand Down Expand Up @@ -538,7 +646,11 @@ void MapCanvas::drawGroupCharacters(CharacterBatch &batch, ServerRoomId yourServ
const auto color = Color{character.getColor()};
const bool fill = !drawnRoomIds.contains(id);

batch.drawCharacter(pos, color, fill);
const bool showToken = (id != playerRoomId);
// Use the player's token for their room; don't stack group-member tokens there.
const QString tokenKey = showToken ? character.getDisplayName() : QString();

batch.drawCharacter(pos, color, fill, tokenKey);

if (srvId != INVALID_SERVER_ROOMID && srvId != yourServerId) {
QString name = character.getLabel().isEmpty() ? character.getName().toQString()
Expand Down
17 changes: 15 additions & 2 deletions src/display/Characters.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include <glm/gtc/matrix_transform.hpp>

#include <QColor>
#include <QString>

class MapScreen;
class OpenGL;
Expand Down Expand Up @@ -102,6 +103,9 @@ class NODISCARD CharacterBatch final
MatrixStack m_stack;
std::vector<ColorVert> m_charTris;
std::vector<ColorVert> m_charBeaconQuads;
std::vector<ColoredTexVert> m_charTokenQuads;
std::vector<QString> m_charTokenKeys;
std::vector<bool> m_charTokenBearForms;
std::vector<ColorVert> m_charLines;
std::vector<ColoredTexVert> m_charRoomQuads;
std::vector<ColorVert> m_pathPoints;
Expand Down Expand Up @@ -152,7 +156,12 @@ class NODISCARD CharacterBatch final
m = glm::translate(m, v);
}
void drawArrow(bool fill, bool beacon);
void drawBox(Coordinate coord, bool fill, bool beacon, bool isFar);
void drawBox(Coordinate coord,
bool fill,
bool beacon,
bool isFar,
const QString &dispName,
bool bearForm);
void addScreenSpaceArrow(glm::vec3 pos, float degrees, Color color, bool fill);
void addName(Coordinate c, const std::string &name, Color color, const MapScreen &mapScreen);
void drawPathSegment(glm::vec3 p1, glm::vec3 p2, Color color);
Expand Down Expand Up @@ -194,7 +203,11 @@ class NODISCARD CharacterBatch final
NODISCARD bool isVisible(Coordinate c, float margin) const;

public:
void drawCharacter(Coordinate coordinate, Color color, bool fill = true);
void drawCharacter(Coordinate coordinate,
Color color,
bool fill = true,
const QString &dispName = QString(),
bool bearForm = false);

void drawName(const Coordinate c, const std::string &name, const Color color)
{
Expand Down
2 changes: 2 additions & 0 deletions src/display/mapcanvas_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "../global/logging.h"
#include "../global/progresscounter.h"
#include "../global/utils.h"
#include "../group/tokenmanager.h"
#include "../map/coordinate.h"
#include "../mapdata/mapdata.h"
#include "../opengl/Font.h"
Expand Down Expand Up @@ -131,6 +132,7 @@ void MapCanvas::hostCleanupGL()
// and it also owns the lifetime of some OpenGL objects (e.g. VBOs).
m_batches.resetExistingMeshesAndIgnorePendingRemesh();
m_weather.cleanup();
TokenManager::cleanupOpenGLTexturesIfCreated(getOpenGL());
m_textures.destroyAll();
getGLFont().cleanup();
getOpenGL().cleanup();
Expand Down
5 changes: 5 additions & 0 deletions src/group/CGroupChar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,8 @@ bool CGroupChar::setScore(const QString &textHP, const QString &textMana, const
#undef X_SCORE
return updated;
}

QString CGroupChar::getDisplayName() const
{
return getName().toQString();
}
12 changes: 12 additions & 0 deletions src/group/CGroupChar.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ class NODISCARD CGroupChar final : public std::enable_shared_from_this<CGroupCha
CharacterPositionEnum position = CharacterPositionEnum::UNDEFINED;
CharacterTypeEnum type = CharacterTypeEnum::UNDEFINED;
CharacterAffectFlags affects;
bool bearForm = false;

void reset() { *this = Server{}; }
};
Expand Down Expand Up @@ -134,6 +135,15 @@ class NODISCARD CGroupChar final : public std::enable_shared_from_this<CGroupCha
NODISCARD CharacterTypeEnum getType() const { return m_server.type; }
NODISCARD CharacterPositionEnum getPosition() const { return m_server.position; }
NODISCARD const CharacterAffectFlags &getAffects() const { return m_server.affects; }
NODISCARD bool isBearForm() const { return m_server.bearForm; }
bool setBearForm(const bool bearForm)
{
if (m_server.bearForm == bearForm) {
return false;
}
m_server.bearForm = bearForm;
return true;
}
NODISCARD int getHits() const { return m_server.hp; }
NODISCARD int getMaxHits() const { return m_server.maxhp; }
NODISCARD int getMana() const { return m_server.mana; }
Expand All @@ -154,4 +164,6 @@ class NODISCARD CGroupChar final : public std::enable_shared_from_this<CGroupCha
m_server.mp = _moves;
m_server.maxmp = _maxmoves;
}

QString getDisplayName() const;
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
};
Loading
Loading