diff --git a/src/client/attachableobject.cpp b/src/client/attachableobject.cpp index 8da185befe..c3968d6158 100644 --- a/src/client/attachableobject.cpp +++ b/src/client/attachableobject.cpp @@ -31,13 +31,19 @@ #include "client.h" #include "game.h" -#include "mapview.h" +#include "map.h" #include "tile.h" #include "uimap.h" -#include "mapview.h" extern ParticleManager g_particles; +AttachableObject::~AttachableObject() +{ + clearAttachedEffects(); + clearAttachedParticlesEffect(); + clearAttachedWidgets(); +} + void AttachableObject::attachEffect(const AttachedEffectPtr& obj) { if (!obj) @@ -95,25 +101,25 @@ void AttachableObject::clearAttachedEffects() void AttachableObject::clearTemporaryAttachedEffects() { m_attachedEffects.erase(std::remove_if(m_attachedEffects.begin(), m_attachedEffects.end(), - [&](const AttachedEffectPtr& obj) { - if (!obj->isPermanent()) { - onDetachEffect(obj); - return true; - } - return false; - }), m_attachedEffects.end()); + [&](const AttachedEffectPtr& obj) { + if (!obj->isPermanent()) { + onDetachEffect(obj); + return true; + } + return false; + }), m_attachedEffects.end()); } void AttachableObject::clearPermanentAttachedEffects() { m_attachedEffects.erase(std::remove_if(m_attachedEffects.begin(), m_attachedEffects.end(), - [&](const AttachedEffectPtr& obj) { - if (obj->isPermanent()) { - onDetachEffect(obj); - return true; - } - return false; - }), m_attachedEffects.end()); + [&](const AttachedEffectPtr& obj) { + if (obj->isPermanent()) { + onDetachEffect(obj); + return true; + } + return false; + }), m_attachedEffects.end()); } AttachedEffectPtr AttachableObject::getAttachedEffectById(uint16_t id) @@ -201,24 +207,28 @@ void AttachableObject::updateAndAttachParticlesEffects(std::vector& attachParticleEffect(name); } -void AttachableObject::attachWidget(const UIWidgetPtr& widget) -{ - if (!widget) - return; +bool AttachableObject::isWidgetAttached(const UIWidgetPtr& widget) { + return std::find_if(m_attachedWidgets.begin(), m_attachedWidgets.end(), + [widget](const UIWidgetPtr& obj) { return obj == widget; }) != m_attachedWidgets.end(); +} - const auto& mapWidget = g_client.getMapWidget(); - if (!mapWidget) +void AttachableObject::attachWidget(const UIWidgetPtr& widget) { + if (!widget || isWidgetAttached(widget)) return; - if (widget->isAttached()) { - g_logger.error(stdext::format("Failed to attach widget %s, this widget is already attached to other object.", widget->getId())); + if (g_map.isWidgetAttached(widget)) { + g_logger.error(stdext::format("Failed to attach widget %s, this widget is already attached to map.", widget->getId())); return; } widget->setDraggable(false); - widget->setAttached(true); + widget->setParent(g_client.getMapWidget()); m_attachedWidgets.emplace_back(widget); + g_map.addAttachedWidgetToObject(widget, std::static_pointer_cast(shared_from_this())); widget->callLuaField("onAttached", asLuaObject()); + widget->addOnDestroyCallback("attached-widget-destroy", [this, widget]() { + detachWidget(widget); + }); } bool AttachableObject::detachWidgetById(const std::string& id) @@ -231,9 +241,8 @@ bool AttachableObject::detachWidgetById(const std::string& id) const auto widget = (*it); m_attachedWidgets.erase(it); - - widget->setAttached(false); - widget->setVisible(false); + g_map.removeAttachedWidgetFromObject(widget); + widget->removeOnDestroyCallback("attached-widget-destroy"); widget->callLuaField("onDetached", asLuaObject()); return true; } @@ -245,9 +254,8 @@ bool AttachableObject::detachWidget(const UIWidgetPtr& widget) return false; m_attachedWidgets.erase(it); - - widget->setAttached(false); - widget->setVisible(false); + g_map.removeAttachedWidgetFromObject(widget); + widget->removeOnDestroyCallback("attached-widget-destroy"); widget->callLuaField("onDetached", asLuaObject()); return true; } @@ -259,8 +267,8 @@ void AttachableObject::clearAttachedWidgets() m_attachedWidgets.clear(); for (const auto& widget : oldList) { - widget->setAttached(false); - widget->setVisible(false); + g_map.removeAttachedWidgetFromObject(widget); + widget->removeOnDestroyCallback("attached-widget-destroy"); widget->callLuaField("onDetached", asLuaObject()); } } @@ -274,45 +282,4 @@ UIWidgetPtr AttachableObject::getAttachedWidgetById(const std::string& id) return nullptr; return *it; -} - -void AttachableObject::drawAttachedWidgets(const Point& dest, const MapPosInfo& mapRect) -{ - if (m_attachedWidgets.empty()) - return; - - g_drawPool.select(DrawPoolType::FOREGROUND_MAP_WIDGETS); - { - std::vector toRemove; - for (const auto& widget : m_attachedWidgets) { - if (widget->isDestroyed()) { - toRemove.emplace_back(widget); - continue; - } - - if (!widget->isVisible()) - continue; - - Point p = dest - mapRect.drawOffset; - p.x *= mapRect.horizontalStretchFactor; - p.y *= mapRect.verticalStretchFactor; - p += mapRect.rect.topLeft(); - - p.x += widget->getMarginLeft(); - p.x -= widget->getMarginRight(); - p.y += widget->getMarginTop(); - p.y -= widget->getMarginBottom(); - - const auto& widgetRect = widget->getRect(); - const auto& rect = Rect(p, widgetRect.width(), widgetRect.height()); - - widget->setRect(rect); - widget->draw(mapRect.rect, DrawPoolType::FOREGROUND); - } - - for (const auto& widget : toRemove) - detachWidget(widget); - } - // Go back to use map pool - g_drawPool.select(DrawPoolType::MAP); } \ No newline at end of file diff --git a/src/client/attachableobject.h b/src/client/attachableobject.h index fce8a02878..712a129dea 100644 --- a/src/client/attachableobject.h +++ b/src/client/attachableobject.h @@ -24,14 +24,16 @@ #include "attachedeffect.h" #include -struct MapPosInfo; - class AttachableObject : public LuaObject { public: AttachableObject() = default; - virtual ~AttachableObject() = default; - + virtual ~AttachableObject(); + + virtual LuaObjectPtr attachedObjectToLuaObject() = 0; + virtual bool isTile() { return false; } + virtual bool isThing() { return false; } + void attachEffect(const AttachedEffectPtr& obj); void clearAttachedEffects(); void clearTemporaryAttachedEffects(); @@ -39,7 +41,6 @@ class AttachableObject : public LuaObject bool detachEffectById(uint16_t id); AttachedEffectPtr getAttachedEffectById(uint16_t id); - virtual LuaObjectPtr attachedObjectToLuaObject() = 0; virtual void onStartAttachEffect(const AttachedEffectPtr& effect) { }; virtual void onDispatcherAttachEffect(const AttachedEffectPtr& effect) { }; virtual void onStartDetachEffect(const AttachedEffectPtr& effect) { }; @@ -55,6 +56,7 @@ class AttachableObject : public LuaObject const std::vector& getAttachedWidgets() { return m_attachedWidgets; }; bool hasAttachedWidgets() { return !m_attachedWidgets.empty(); }; + bool isWidgetAttached(const UIWidgetPtr& widget); void attachWidget(const UIWidgetPtr& widget); void clearAttachedWidgets(); bool detachWidgetById(const std::string& id); @@ -65,7 +67,6 @@ class AttachableObject : public LuaObject void drawAttachedEffect(const Point& dest, LightView* lightView, bool isOnTop); void onDetachEffect(const AttachedEffectPtr& effect); void drawAttachedParticlesEffect(const Point& dest); - void drawAttachedWidgets(const Point& dest, const MapPosInfo& mapRect); std::vector m_attachedEffects; std::vector m_attachedParticles; diff --git a/src/client/creature.cpp b/src/client/creature.cpp index 4074e96ec1..6dd28ae740 100644 --- a/src/client/creature.cpp +++ b/src/client/creature.cpp @@ -322,7 +322,6 @@ void Creature::internalDraw(Point dest, LightView* lightView, const Color& color if (replaceColorShader) g_drawPool.resetShaderProgram(); else { - m_lastDrawDest = dest; drawAttachedEffect(dest, lightView, true); // On Top drawAttachedParticlesEffect(dest); } diff --git a/src/client/declarations.h b/src/client/declarations.h index 522f069c74..ea8c41128e 100644 --- a/src/client/declarations.h +++ b/src/client/declarations.h @@ -49,6 +49,7 @@ class ThingType; class ItemType; class TileBlock; class AttachedEffect; +class AttachableObject; #ifdef FRAMEWORK_EDITOR class House; @@ -75,6 +76,7 @@ using StaticTextPtr = std::shared_ptr; using ThingTypePtr = std::shared_ptr; using ItemTypePtr = std::shared_ptr; using AttachedEffectPtr = std::shared_ptr; +using AttachableObjectPtr = std::shared_ptr; #ifdef FRAMEWORK_EDITOR using HousePtr = std::shared_ptr; diff --git a/src/client/item.cpp b/src/client/item.cpp index 14f351ae96..c927f40101 100644 --- a/src/client/item.cpp +++ b/src/client/item.cpp @@ -58,8 +58,6 @@ void Item::draw(const Point& dest, bool drawThings, LightView* lightView) internalDraw(animationPhase, dest, getMarkedColor(), drawThings, true); else if (isHighlighted()) internalDraw(animationPhase, dest, getHighlightColor(), drawThings, true); - - m_lastDrawDest = dest; } void Item::internalDraw(int animationPhase, const Point& dest, const Color& color, bool drawThings, bool replaceColorShader, LightView* lightView) diff --git a/src/client/map.cpp b/src/client/map.cpp index 05a60a7af8..8ede977a77 100644 --- a/src/client/map.cpp +++ b/src/client/map.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #ifdef FRAMEWORK_EDITOR @@ -1130,6 +1131,74 @@ bool Map::isSightClear(const Position& fromPos, const Position& toPos) return true; } +bool Map::isWidgetAttached(const UIWidgetPtr& widget) const { + return m_attachedObjectWidgetMap.find(widget) != m_attachedObjectWidgetMap.end(); +} + +void Map::addAttachedWidgetToObject(const UIWidgetPtr& widget, const AttachableObjectPtr& object) { + if (isWidgetAttached(widget)) + return; + + m_attachedObjectWidgetMap.emplace(widget, object); +} + +bool Map::removeAttachedWidgetFromObject(const UIWidgetPtr& widget) { + // remove elemnt form unordered map + const auto it = m_attachedObjectWidgetMap.find(widget); + if (it == m_attachedObjectWidgetMap.end()) + return false; + + m_attachedObjectWidgetMap.erase(it); + return true; +} + +void Map::updateAttachedWidgets(const MapViewPtr& mapView) +{ + for (const auto& [widget, object] : m_attachedObjectWidgetMap) { + if (widget->isDestroyed()) { + continue; + } + + if (!widget->isVisible()) + continue; + + Position pos; + if (object->isTile()) { + const auto& tile = object->static_self_cast(); + pos = tile->getPosition(); + } else if (object->isThing()) { + const auto& thing = object->static_self_cast(); + pos = thing->getPosition(); + } + + if (!pos.isValid()) + continue; + + Point p = mapView->transformPositionTo2D(pos) - mapView->m_posInfo.drawOffset; + + if (object->isThing() && object->static_self_cast()->isCreature()) { + const auto& creature = object->static_self_cast()->static_self_cast(); + const auto& jumpOffset = creature->getJumpOffset() * g_drawPool.getScaleFactor(); + const auto& creatureOffset = Point(16 - creature->getDisplacementX(), -creature->getDisplacementY() - 2) + creature->getWalkOffset(); + p += creatureOffset * g_drawPool.getScaleFactor() - Point(std::round(jumpOffset.x), std::round(jumpOffset.y)); + } + + p.x *= mapView->m_posInfo.horizontalStretchFactor; + p.y *= mapView->m_posInfo.verticalStretchFactor; + p += mapView->m_posInfo.rect.topLeft(); + + p.x += widget->getMarginLeft(); + p.x -= widget->getMarginRight(); + p.y += widget->getMarginTop(); + p.y -= widget->getMarginBottom(); + + const auto& widgetRect = widget->getRect(); + const auto& newWidgetRect = Rect(p, widgetRect.width(), widgetRect.height()); + + widget->setRect(newWidgetRect); + } +} + #ifndef BOT_PROTECTION std::map> Map::findEveryPath(const Position& start, int maxDistance, const std::map& params) { diff --git a/src/client/map.h b/src/client/map.h index 4131fe873f..0ffcf0d2e7 100644 --- a/src/client/map.h +++ b/src/client/map.h @@ -217,6 +217,11 @@ class Map void addAnimatedText(const AnimatedTextPtr& txt, const Position& pos); bool removeAnimatedText(const AnimatedTextPtr& txt); + bool isWidgetAttached(const UIWidgetPtr& widget) const; + void addAttachedWidgetToObject(const UIWidgetPtr& widget, const AttachableObjectPtr& object); + bool removeAttachedWidgetFromObject(const UIWidgetPtr& widget); + void updateAttachedWidgets(const MapViewPtr& mapView); + void colorizeThing(const ThingPtr& thing, const Color& color); void removeThingColor(const ThingPtr& thing); @@ -317,6 +322,8 @@ class Map std::unordered_map m_knownCreatures; + std::unordered_map m_attachedObjectWidgetMap; + #ifdef FRAMEWORK_EDITOR std::unordered_map m_waypoints; std::unordered_map m_zoneColors; diff --git a/src/client/mapview.cpp b/src/client/mapview.cpp index 3d6fdb4dfa..a41db4a635 100644 --- a/src/client/mapview.cpp +++ b/src/client/mapview.cpp @@ -112,6 +112,8 @@ void MapView::preLoad() { m_fadeFinish = true; } } + + g_map.updateAttachedWidgets(static_self_cast()); } void MapView::draw(const Rect& rect) diff --git a/src/client/thing.cpp b/src/client/thing.cpp index a84f8ab601..3fd681e76f 100644 --- a/src/client/thing.cpp +++ b/src/client/thing.cpp @@ -97,6 +97,3 @@ void Thing::setShader(const std::string_view name) { m_shader = g_shaders.getShader(name.data()); } -void Thing::drawWidgets(const MapPosInfo& mapRect) { - drawAttachedWidgets(m_lastDrawDest, mapRect); -} \ No newline at end of file diff --git a/src/client/thing.h b/src/client/thing.h index 576ab68c0d..a871456908 100644 --- a/src/client/thing.h +++ b/src/client/thing.h @@ -37,9 +37,9 @@ class Thing : public AttachableObject { public: virtual void draw(const Point& /*dest*/, bool drawThings = true, LightView* /*lightView*/ = nullptr) {} - void drawWidgets(const MapPosInfo& mapRect); LuaObjectPtr attachedObjectToLuaObject() override { return asLuaObject(); } + bool isThing() override { return true; } virtual void setId(uint32_t /*id*/) {} virtual void setPosition(const Position& position, uint8_t stackPos = 0, bool hasElevation = false); @@ -220,8 +220,6 @@ class Thing : public AttachableObject Color m_markedColor{ Color::white }; Color m_highlightColor{ Color::white }; - Point m_lastDrawDest; - // Shader PainterShaderProgramPtr m_shader; std::function m_shaderAction{ nullptr }; diff --git a/src/client/tile.cpp b/src/client/tile.cpp index f6db2575d9..32068bea65 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -83,12 +83,6 @@ void Tile::draw(const Point& dest, const MapPosInfo& mapRect, int flags, LightVi drawTop(dest, flags, false, lightView); drawAttachedEffect(dest, lightView, false); drawAttachedParticlesEffect(dest); - - for (const auto& thing : m_things) { - thing->drawWidgets(mapRect); - } - - drawWidgets(mapRect); } void Tile::drawCreature(const Point& dest, const MapPosInfo& mapRect, int flags, bool forceDraw, LightView* lightView) @@ -130,11 +124,6 @@ void Tile::drawTop(const Point& dest, int flags, bool forceDraw, LightView* ligh } } -void Tile::drawWidgets(const MapPosInfo& mapRect) -{ - drawAttachedWidgets(m_lastDrawDest, mapRect); -} - void Tile::clean() { if (g_client.getMapWidget() diff --git a/src/client/tile.h b/src/client/tile.h index 333090a15d..5ccc145394 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -95,10 +95,10 @@ class Tile : public AttachableObject Tile(const Position& position); LuaObjectPtr attachedObjectToLuaObject() override { return asLuaObject(); } + bool isTile() override { return true; } void onAddInMapView(); void draw(const Point& dest, const MapPosInfo& mapRect, int flags, LightView* lightView = nullptr); - void drawWidgets(const MapPosInfo& rect); void clean(); diff --git a/src/client/uimap.cpp b/src/client/uimap.cpp index 0e380fb7fe..77d4276067 100644 --- a/src/client/uimap.cpp +++ b/src/client/uimap.cpp @@ -53,6 +53,7 @@ void UIMap::drawSelf(DrawPoolType drawPane) { UIWidget::drawSelf(drawPane); + const auto& mapRect = g_app.isScaled() ? Rect(0, 0, g_graphics.getViewportSize()) : m_mapRect; if (drawPane == DrawPoolType::FOREGROUND) { g_drawPool.addBoundingRect(m_mapRect.expanded(1), Color::black); g_drawPool.addAction([] {glDisable(GL_BLEND); }); @@ -61,8 +62,6 @@ void UIMap::drawSelf(DrawPoolType drawPane) return; } - const auto& mapRect = g_app.isScaled() ? Rect(0, 0, g_graphics.getViewportSize()) : m_mapRect; - if (drawPane == DrawPoolType::MAP) { g_drawPool.preDraw(drawPane, [this, &mapRect] { m_mapView->registerEvents(); diff --git a/src/framework/core/graphicalapplication.cpp b/src/framework/core/graphicalapplication.cpp index 8eafdeb4f0..621ea0ec2e 100644 --- a/src/framework/core/graphicalapplication.cpp +++ b/src/framework/core/graphicalapplication.cpp @@ -300,7 +300,6 @@ void GraphicalApplication::resize(const Size& size) if (USE_FRAMEBUFFER) { g_drawPool.get(DrawPoolType::CREATURE_INFORMATION)->setFramebuffer(size); g_drawPool.get(DrawPoolType::FOREGROUND_MAP)->setFramebuffer(size); - g_drawPool.get(DrawPoolType::FOREGROUND_MAP_WIDGETS)->setFramebuffer(size); } }); } diff --git a/src/framework/graphics/drawpool.cpp b/src/framework/graphics/drawpool.cpp index cb9b6fc2ef..3eeb09ad09 100644 --- a/src/framework/graphics/drawpool.cpp +++ b/src/framework/graphics/drawpool.cpp @@ -38,7 +38,7 @@ DrawPool* DrawPool::create(const DrawPoolType type) pool->m_temporaryFramebuffers.emplace_back(std::make_shared()); } } else { - pool->m_alwaysGroupDrawings = type != DrawPoolType::FOREGROUND_MAP_WIDGETS; // CREATURE_INFORMATION & TEXT + pool->m_alwaysGroupDrawings = true; // CREATURE_INFORMATION & TEXT if (type == DrawPoolType::FOREGROUND_MAP) { pool->setFPS(FPS60); diff --git a/src/framework/graphics/drawpool.h b/src/framework/graphics/drawpool.h index dc30f3caf6..7bb72a5a8d 100644 --- a/src/framework/graphics/drawpool.h +++ b/src/framework/graphics/drawpool.h @@ -40,7 +40,6 @@ enum class DrawPoolType : uint8_t LIGHT, FOREGROUND_MAP, FOREGROUND, - FOREGROUND_MAP_WIDGETS, LAST }; diff --git a/src/framework/graphics/drawpoolmanager.cpp b/src/framework/graphics/drawpoolmanager.cpp index e02861d7da..7aa79cd28e 100644 --- a/src/framework/graphics/drawpoolmanager.cpp +++ b/src/framework/graphics/drawpoolmanager.cpp @@ -179,9 +179,6 @@ void DrawPoolManager::preDraw(const DrawPoolType type, const std::functionresetState(); - if (type == DrawPoolType::MAP) - get(DrawPoolType::FOREGROUND_MAP_WIDGETS)->resetState(); - if (f) f(); std::scoped_lock l(pool->m_mutexDraw); @@ -194,9 +191,6 @@ void DrawPoolManager::preDraw(const DrawPoolType type, const std::functionrelease(); - - if (type == DrawPoolType::MAP) - get(DrawPoolType::FOREGROUND_MAP_WIDGETS)->release(); } bool DrawPoolManager::drawPool(const DrawPoolType type) { diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index 7db1437ebf..fdb4d2c6c3 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -859,11 +859,16 @@ void UIWidget::internalDestroy() child->internalDestroy(); m_children.clear(); + for (const auto& [id, destroyCallback] : m_onDestroyCallbacks) + destroyCallback(); + m_onDestroyCallbacks.clear(); + callLuaField("onDestroy"); releaseLuaFieldsTable(); g_ui.onWidgetDestroy(static_self_cast()); + } void UIWidget::destroy() @@ -1992,4 +1997,16 @@ void UIWidget::disableUpdateTemporarily() { self->m_layout->update(); self->setProp(PropDisableUpdateTemporarily, false); }); -} \ No newline at end of file +} + +void UIWidget::addOnDestroyCallback(const std::string& id, const std::function&& callback) +{ + m_onDestroyCallbacks.emplace(id, callback); +} + +void UIWidget::removeOnDestroyCallback(const std::string& id) +{ + auto it = m_onDestroyCallbacks.find(id); + if (it != m_onDestroyCallbacks.end()) + m_onDestroyCallbacks.erase(it); +} diff --git a/src/framework/ui/uiwidget.h b/src/framework/ui/uiwidget.h index 2d12373031..9ecf444791 100644 --- a/src/framework/ui/uiwidget.h +++ b/src/framework/ui/uiwidget.h @@ -103,13 +103,12 @@ class UIWidget : public LuaObject OTMLNodePtr m_style; stdext::map m_childrenById; + std::unordered_map> m_onDestroyCallbacks; Timer m_clickTimer; Fw::FocusReason m_lastFocusReason{ Fw::ActiveFocusReason }; Fw::AutoFocusPolicy m_autoFocusPolicy{ Fw::AutoFocusLast }; - bool m_attached{ false }; - friend class UIGridLayout; friend class UIHorizontalLayout; friend class UIVerticalLayout; @@ -205,10 +204,9 @@ class UIWidget : public LuaObject void setProp(FlagProp prop, bool v); bool hasProp(FlagProp prop) { return (m_flagsProp & prop); } - bool isAttached() { return m_attached; } - void setAttached(bool attached) { m_attached = attached; } - void disableUpdateTemporarily(); + void addOnDestroyCallback(const std::string& id, const std::function&& callback); + void removeOnDestroyCallback(const std::string&); private: uint32_t m_flagsProp{ 0 };