diff --git a/data/styles/10-effect.otui b/data/styles/10-effect.otui new file mode 100644 index 0000000000..e3718ad6c4 --- /dev/null +++ b/data/styles/10-effect.otui @@ -0,0 +1 @@ +Effect < UIEffect \ No newline at end of file diff --git a/data/styles/10-missile.otui b/data/styles/10-missile.otui new file mode 100644 index 0000000000..62b5034449 --- /dev/null +++ b/data/styles/10-missile.otui @@ -0,0 +1 @@ +Missile < UIMissile \ No newline at end of file diff --git a/src/client/declarations.h b/src/client/declarations.h index a1cb4acdca..755a029fae 100644 --- a/src/client/declarations.h +++ b/src/client/declarations.h @@ -108,6 +108,8 @@ using ProtocolLoginPtr = std::shared_ptr; // ui class UIItem; +class UIEffect; +class UIMissile; class UICreature; class UIGraph; class UIMap; @@ -118,6 +120,8 @@ class UIPositionAnchor; class UISprite; using UIItemPtr = std::shared_ptr; +using UIEffectPtr = std::shared_ptr; +using UIMissilePtr = std::shared_ptr; using UICreaturePtr = std::shared_ptr; using UIGraphPtr = std::shared_ptr; using UISpritePtr = std::shared_ptr; diff --git a/src/client/luafunctions.cpp b/src/client/luafunctions.cpp index 2307979300..28fbccba04 100644 --- a/src/client/luafunctions.cpp +++ b/src/client/luafunctions.cpp @@ -46,6 +46,9 @@ #include "towns.h" #include "uicreature.h" #include "uiitem.h" +#include "uieffect.h" +#include "uimissile.h" + #include "uimap.h" #include "uimapanchorlayout.h" #include "uiminimap.h" @@ -926,6 +929,32 @@ void Client::registerLuaFunctions() g_lua.bindClassMemberFunction("isVirtual", &UIItem::isVirtual); g_lua.bindClassMemberFunction("isItemVisible", &UIItem::isItemVisible); + g_lua.registerClass(); + g_lua.bindClassStaticFunction("create", [] { return std::make_shared(); }); + g_lua.bindClassMemberFunction("setEffectId", &UIEffect::setEffectId); + g_lua.bindClassMemberFunction("setEffectVisible", &UIEffect::setEffectVisible); + g_lua.bindClassMemberFunction("setEffect", &UIEffect::setEffect); + g_lua.bindClassMemberFunction("setVirtual", &UIEffect::setVirtual); + g_lua.bindClassMemberFunction("clearEffect", &UIEffect::clearEffect); + g_lua.bindClassMemberFunction("getEffectId", &UIEffect::getEffectId); + g_lua.bindClassMemberFunction("getEffect", &UIEffect::getEffect); + g_lua.bindClassMemberFunction("isVirtual", &UIEffect::isVirtual); + g_lua.bindClassMemberFunction("isEffectVisible", &UIEffect::isEffectVisible); + + g_lua.registerClass(); + g_lua.bindClassStaticFunction("create", [] { return std::make_shared(); }); + g_lua.bindClassMemberFunction("setMissileId", &UIMissile::setMissileId); + g_lua.bindClassMemberFunction("setMissileVisible", &UIMissile::setMissileVisible); + g_lua.bindClassMemberFunction("setMissile", &UIMissile::setMissile); + g_lua.bindClassMemberFunction("setVirtual", &UIMissile::setVirtual); + g_lua.bindClassMemberFunction("clearMissile", &UIMissile::clearMissile); + g_lua.bindClassMemberFunction("getMissileId", &UIMissile::getMissileId); + g_lua.bindClassMemberFunction("getMissile", &UIMissile::getMissile); + g_lua.bindClassMemberFunction("isVirtual", &UIMissile::isVirtual); + g_lua.bindClassMemberFunction("isMissileVisible", &UIMissile::isMissileVisible); + g_lua.bindClassMemberFunction("setDirection", &UIMissile::setDirection); + g_lua.bindClassMemberFunction("getDirection", &UIMissile::getDirection); + g_lua.registerClass(); g_lua.bindClassStaticFunction("create", [] { return std::make_shared(); }); g_lua.bindClassMemberFunction("setSpriteId", &UISprite::setSpriteId); diff --git a/src/client/missile.cpp b/src/client/missile.cpp index be3da0896f..388c7e96a9 100644 --- a/src/client/missile.cpp +++ b/src/client/missile.cpp @@ -32,7 +32,7 @@ void Missile::draw(const Point& dest, bool drawThings, const LightViewPtr& light if (!canDraw() || isHided()) return; - const float fraction = m_animationTimer.ticksElapsed() / m_duration; + const float fraction = m_duration > 0 ? m_animationTimer.ticksElapsed() / m_duration : 1; if (g_drawPool.getCurrentType() == DrawPoolType::MAP) { if (drawThings && g_client.getMissileAlpha() < 1.f) @@ -55,7 +55,7 @@ void Missile::setPath(const Position& fromPosition, const Position& toPosition) return; } - m_direction = fromPosition.getDirectionFromPosition(toPosition); + setDirection(fromPosition.getDirectionFromPosition(toPosition)); m_duration = (g_gameConfig.getMissileTicksPerFrame() * 2) * std::sqrt(deltaLength); m_delta *= g_gameConfig.getSpriteSize(); @@ -63,40 +63,45 @@ void Missile::setPath(const Position& fromPosition, const Position& toPosition) m_distance = fromPosition.distance(toPosition); { // Update Pattern - if (m_direction == Otc::NorthWest) { - m_numPatternX = 0; - m_numPatternY = 0; - } else if (m_direction == Otc::North) { - m_numPatternX = 1; - m_numPatternY = 0; - } else if (m_direction == Otc::NorthEast) { - m_numPatternX = 2; - m_numPatternY = 0; - } else if (m_direction == Otc::East) { - m_numPatternX = 2; - m_numPatternY = 1; - } else if (m_direction == Otc::SouthEast) { - m_numPatternX = 2; - m_numPatternY = 2; - } else if (m_direction == Otc::South) { - m_numPatternX = 1; - m_numPatternY = 2; - } else if (m_direction == Otc::SouthWest) { - m_numPatternX = 0; - m_numPatternY = 2; - } else if (m_direction == Otc::West) { - m_numPatternX = 0; - m_numPatternY = 1; - } else { - m_numPatternX = 1; - m_numPatternY = 1; - } } // schedule removal g_dispatcher.scheduleEvent([self = asMissile()] { g_map.removeThing(self); }, m_duration); } +void Missile::setDirection(Otc::Direction dir) { + m_direction = dir; + + if (m_direction == Otc::NorthWest) { + m_numPatternX = 0; + m_numPatternY = 0; + } else if (m_direction == Otc::North) { + m_numPatternX = 1; + m_numPatternY = 0; + } else if (m_direction == Otc::NorthEast) { + m_numPatternX = 2; + m_numPatternY = 0; + } else if (m_direction == Otc::East) { + m_numPatternX = 2; + m_numPatternY = 1; + } else if (m_direction == Otc::SouthEast) { + m_numPatternX = 2; + m_numPatternY = 2; + } else if (m_direction == Otc::South) { + m_numPatternX = 1; + m_numPatternY = 2; + } else if (m_direction == Otc::SouthWest) { + m_numPatternX = 0; + m_numPatternY = 2; + } else if (m_direction == Otc::West) { + m_numPatternX = 0; + m_numPatternY = 1; + } else { + m_numPatternX = 1; + m_numPatternY = 1; + } +} + void Missile::setId(uint32_t id) { if (!g_things.isValidDatId(id, ThingCategoryMissile)) diff --git a/src/client/missile.h b/src/client/missile.h index 3d7a39323c..68ff3e482e 100644 --- a/src/client/missile.h +++ b/src/client/missile.h @@ -40,6 +40,9 @@ class Missile : public Thing MissilePtr asMissile() { return static_self_cast(); } + void setDirection(Otc::Direction dir); + auto getDirection() { return m_direction; } + protected: ThingType* getThingType() const override; diff --git a/src/client/uieffect.cpp b/src/client/uieffect.cpp new file mode 100644 index 0000000000..ac463d08ff --- /dev/null +++ b/src/client/uieffect.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2010-2022 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "uieffect.h" + +UIEffect::UIEffect() { setProp(PropDraggable, true, false); } + +void UIEffect::drawSelf(DrawPoolType drawPane) +{ + if (drawPane != DrawPoolType::FOREGROUND) + return; + + // draw style components in order + if (m_backgroundColor.aF() > Fw::MIN_ALPHA) { + Rect backgroundDestRect = m_rect; + backgroundDestRect.expand(-m_borderWidth.top, -m_borderWidth.right, -m_borderWidth.bottom, -m_borderWidth.left); + drawBackground(m_rect); + } + + drawImage(m_rect); + + if (m_effectVisible && m_effect) { + const int exactSize = std::max(g_gameConfig.getSpriteSize(), m_effect->getExactSize()); + + g_drawPool.bindFrameBuffer(exactSize); + m_effect->draw(Point(exactSize - g_gameConfig.getSpriteSize()) + m_effect->getDisplacement()); + g_drawPool.releaseFrameBuffer(getPaddingRect()); + } + + drawBorder(m_rect); + drawIcon(m_rect); + drawText(m_rect); +} + +void UIEffect::setEffectId(int id) +{ + if (id == 0) + m_effect = nullptr; + else { + if (!m_effect) + m_effect = std::make_shared(); + m_effect->setId(id); + } +} + +void UIEffect::setEffect(const EffectPtr& e) +{ + m_effect = e; +} + +void UIEffect::onStyleApply(const std::string_view styleName, const OTMLNodePtr& styleNode) +{ + UIWidget::onStyleApply(styleName, styleNode); + + for (const auto& node : styleNode->children()) { + if (node->tag() == "effect-id") + setEffectId(node->value()); + else if (node->tag() == "effect-visible") + setEffectVisible(node->value()); + else if (node->tag() == "virtual") + setVirtual(node->value()); + else if (node->tag() == "show-id") + m_showId = node->value(); + } +} \ No newline at end of file diff --git a/src/client/uieffect.h b/src/client/uieffect.h new file mode 100644 index 0000000000..7d6f4c40d7 --- /dev/null +++ b/src/client/uieffect.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010-2022 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include +#include "declarations.h" +#include "effect.h" + +class UIEffect : public UIWidget +{ +public: + UIEffect(); + void drawSelf(DrawPoolType drawPane) override; + + void setEffectId(int id); + void setEffectVisible(bool visible) { m_effectVisible = visible; } + void setEffect(const EffectPtr& effect); + void setVirtual(bool virt) { m_virtual = virt; } + void clearEffect() { setEffectId(0); } + + int getEffectId() { return m_effect ? m_effect->getId() : 0; } + auto getEffect() { return m_effect; } + bool isVirtual() { return m_virtual; } + bool isEffectVisible() { return m_effectVisible; } + +protected: + void onStyleApply(const std::string_view styleName, const OTMLNodePtr& styleNode) override; + + EffectPtr m_effect; + bool m_virtual{ false }; + bool m_showId{ false }; + bool m_effectVisible{ true }; +}; diff --git a/src/client/uimissile.cpp b/src/client/uimissile.cpp new file mode 100644 index 0000000000..b5a6d5c832 --- /dev/null +++ b/src/client/uimissile.cpp @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2010-2022 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "UIMissile.h" + +UIMissile::UIMissile() { setProp(PropDraggable, true, false); } + +void UIMissile::drawSelf(DrawPoolType drawPane) +{ + if (drawPane != DrawPoolType::FOREGROUND) + return; + + // draw style components in order + if (m_backgroundColor.aF() > Fw::MIN_ALPHA) { + Rect backgroundDestRect = m_rect; + backgroundDestRect.expand(-m_borderWidth.top, -m_borderWidth.right, -m_borderWidth.bottom, -m_borderWidth.left); + drawBackground(m_rect); + } + + drawImage(m_rect); + + if (m_missileVisible && m_missile) { + const int exactSize = std::max(g_gameConfig.getSpriteSize(), m_missile->getExactSize()); + + g_drawPool.bindFrameBuffer(exactSize); + m_missile->draw(Point(exactSize - g_gameConfig.getSpriteSize()) + m_missile->getDisplacement()); + g_drawPool.releaseFrameBuffer(getPaddingRect()); + } + + drawBorder(m_rect); + drawIcon(m_rect); + drawText(m_rect); +} + +void UIMissile::setMissileId(int id) +{ + if (id == 0) + m_missile = nullptr; + else { + if (!m_missile) + m_missile = std::make_shared(); + m_missile->setId(id); + m_missile->setDirection(Otc::South); + } +} + +void UIMissile::setMissile(const MissilePtr& e) +{ + m_missile = e; +} + +void UIMissile::onStyleApply(const std::string_view styleName, const OTMLNodePtr& styleNode) +{ + UIWidget::onStyleApply(styleName, styleNode); + + for (const auto& node : styleNode->children()) { + if (node->tag() == "missile-id") + setMissileId(node->value()); + else if (node->tag() == "missile-visible") + setMissileVisible(node->value()); + else if (node->tag() == "virtual") + setVirtual(node->value()); + else if (node->tag() == "show-id") + m_showId = node->value(); + else if (node->tag() == "direction") + setDirection(static_cast(node->value())); + } +} \ No newline at end of file diff --git a/src/client/uimissile.h b/src/client/uimissile.h new file mode 100644 index 0000000000..a07d3e587e --- /dev/null +++ b/src/client/uimissile.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2010-2022 OTClient + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include +#include "declarations.h" +#include "missile.h" + +class UIMissile : public UIWidget +{ +public: + UIMissile(); + void drawSelf(DrawPoolType drawPane) override; + + void setMissileId(int id); + void setMissileVisible(bool visible) { m_missileVisible = visible; } + void setMissile(const MissilePtr& missile); + void setVirtual(bool virt) { m_virtual = virt; } + void setDirection(Otc::Direction dir) { if (m_missile) m_missile->setDirection(dir); } + void clearMissile() { setMissileId(0); } + + int getMissileId() { return m_missile ? m_missile->getId() : 0; } + auto getDirection() { return m_missile ? m_missile->getDirection() : Otc::Direction::InvalidDirection; } + auto getMissile() { return m_missile; } + bool isVirtual() { return m_virtual; } + bool isMissileVisible() { return m_missileVisible; } + +protected: + void onStyleApply(const std::string_view styleName, const OTMLNodePtr& styleNode) override; + + MissilePtr m_missile; + bool m_virtual{ false }; + bool m_showId{ false }; + bool m_missileVisible{ true }; +}; diff --git a/vc17/otclient.vcxproj b/vc17/otclient.vcxproj index 286370769d..499342b9f3 100644 --- a/vc17/otclient.vcxproj +++ b/vc17/otclient.vcxproj @@ -358,11 +358,13 @@ cmd /c "start ../vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\tools\protobuf\ + + @@ -512,11 +514,13 @@ cmd /c "start ../vcpkg_installed\$(VcpkgTriplet)\$(VcpkgTriplet)\tools\protobuf\ + + diff --git a/vc17/otclient.vcxproj.filters b/vc17/otclient.vcxproj.filters index a2cd4250b5..22c3296ac5 100644 --- a/vc17/otclient.vcxproj.filters +++ b/vc17/otclient.vcxproj.filters @@ -564,6 +564,12 @@ Source Files\framework\core + + Source Files\client + + + Source Files\client + @@ -1097,6 +1103,12 @@ Header Files\framework\stdext + + Header Files\client + + + Header Files\client +