Feature [TFS & OTClient] Progressbar for creatures

oen432

Active Member
Joined
Oct 3, 2014
Messages
278
Reaction score
246
Location
Poland
This month I wanted to start sharing source changes for TFS and OTClient. There are plenty of stuff coming so I'll start with smaller ones.

Progressbar

Simple and straight forward. Progressbar can be used to show something about to happen (spell is about to be cast) or about to end (like a buff).
Progress is shown under creature HP/MP bar. Works for players and monsters so it can be used in many ways. New Lua methods and events included.


Installation is for advanced users and I don't take responsibility for damage caused by wrongly added code.

Installation
First we start with TFS.
  1. Open const.h and add
    C++:
    #define PROGRESSBAR
  2. Open game.h and add
    C++:
    #ifdef PROGRESSBAR
            void startProgressbar(Creature* creature, uint32_t duration, bool ltr = true);
    #endif
  3. Open game.cpp and add
    C++:
    #ifdef PROGRESSBAR
    void Game::startProgressbar(Creature* creature, uint32_t duration, bool ltr)
    {
        SpectatorHashSet spectators;
        map.getSpectators(spectators, creature->getPosition(), false, true);
        for (Creature* spectator : spectators) {
            if (Player* tmpPlayer = spectator->getPlayer()) {
                tmpPlayer->sendProgressbar(creature->getID(), duration, ltr);
            }
        }
    }
    #endif
  4. Open luascript.h and add
    C++:
    #ifdef PROGRESSBAR
            static int luaCreatureSetProgressbar(lua_State* L);
    #endif
  5. Open luascript.cpp and add inside registerFunctions
    C++:
    #ifdef PROGRESSBAR
        registerMethod("Creature", "sendProgressbar", LuaScriptInterface::luaCreatureSetProgressbar);
    #endif
  6. At the bottom add
    C++:
    #ifdef PROGRESSBAR
    int LuaScriptInterface::luaCreatureSetProgressbar(lua_State* L)
    {
        // creature:sendProgressbar(duration, leftToRight)
        Creature* creature = getUserdata<Creature>(L, 1);
        uint32_t duration = getNumber<uint32_t>(L, 2);
        bool ltr = getBoolean(L, 3);
        if (creature) {
            g_game.startProgressbar(creature, duration, ltr);
            pushBoolean(L, true);
        }
        else {
            lua_pushnil(L);
        }
    
        return 1;
    }
    #endif
  7. Open player.h and add under sendNetworkMessage
    C++:
    #ifdef PROGRESSBAR
            void sendProgressbar(uint32_t id, uint32_t duration, bool ltr = true) {
                if (client) {
                    client->sendProgressbar(id, duration, ltr);
                }
            }
    #endif
  8. Open protocolgame.h and add
    C++:
    #ifdef PROGRESSBAR
            void sendProgressbar(uint32_t id, uint32_t duration, bool ltr = true);
    #endif
  9. Open protocolgame.cpp and add
    C++:
    #ifdef PROGRESSBAR
    void ProtocolGame::sendProgressbar(uint32_t id, uint32_t duration, bool ltr)
    {
        NetworkMessage msg;
        msg.addByte(0x3b);
        msg.add<uint32_t>(id);
        msg.add<uint32_t>(duration);
        msg.addByte(ltr);
        writeToOutputBuffer(msg);
    }
    #endif
Now OTClient.
  1. Open const.h and add
    C++:
    #define PROGRESSBAR
  2. Open protocolcodes.h and under GameServerChangeMapAwareRange = 51, add
    C++:
    #ifdef PROGRESSBAR
            ServerRunProgressbar = 59,
    #endif
  3. Open protocolgame.h and add
    C++:
    #ifdef PROGRESSBAR
        void parseProgressbar(const InputMessagePtr& msg);
    #endif
  4. Open protocolgameparse.cpp and under Proto::GameServerChangeMapAwareRange: after break; add
    C++:
    #ifdef PROGRESSBAR
                case Proto::ServerRunProgressbar:
                    parseProgressbar(msg);
                    break;
    #endif
  5. At the bottom add
    C++:
    #ifdef PROGRESSBAR
    void ProtocolGame::parseProgressbar(const InputMessagePtr& msg)
    {
        uint32 id = msg->getU32();
        uint32 duration = msg->getU32();
        bool ltr = msg->getU8();
        CreaturePtr creature = g_map.getCreatureById(id);
        if (creature)
            creature->setProgressbar(duration, ltr);
        else
            g_logger.traceError(stdext::format("could not get creature with id %d", id));
    }
    #endif
  6. Open creature.h and add
    C++:
    #ifdef PROGRESSBAR
        uint8 getProgressbarPercent() { return m_progressbarPercent; }
        void setProgressbar(uint32 duration, bool ltr);
        void updateProgressbar(uint32 duration, bool ltr);
    #endif
  7. Under uint8 m_healthPercent; add
    C++:
    #ifdef PROGRESSBAR
        uint8 m_progressbarPercent;
        ScheduledEventPtr m_progressbarUpdateEvent;
        Timer m_progressbarTimer;
    #endif
  8. Open creature.cpp and inside Creature::Creature() : Thing() add
    C++:
    #ifdef PROGRESSBAR
        m_progressbarPercent = 0;
        m_progressbarUpdateEvent = nullptr;
    #endif
  9. Under if(drawFlags & Otc::DrawManaBar && isLocalPlayer()) { (under last closing bracket of that if) add
    C++:
    #ifdef PROGRESSBAR
            if (getProgressbarPercent()) {
                backgroundRect.moveTop(backgroundRect.bottom());
    
                g_painter->setColor(Color::black);
                g_painter->drawFilledRect(backgroundRect);
    
                Rect progressbarRect = backgroundRect.expanded(-1);
                double maxBar = 100;
                progressbarRect.setWidth(getProgressbarPercent() / (maxBar * 1.0) * 25);
    
                g_painter->setColor(Color::white);
                g_painter->drawFilledRect(progressbarRect);
            }
    #endif
  10. Somewhere at the bottom add
    C++:
    #ifdef PROGRESSBAR
    void Creature::setProgressbar(uint32 duration, bool ltr)
    {
        if (m_progressbarUpdateEvent) {
            m_progressbarUpdateEvent->cancel();
            m_progressbarUpdateEvent = nullptr;
        }
    
        if (duration > 0) {
            m_progressbarTimer.restart();
            updateProgressbar(duration, ltr);
        }
        else
            m_progressbarPercent = 0;
    
        callLuaField("onProgressbarStart", duration, ltr);
    }
    
    void Creature::updateProgressbar(uint32 duration, bool ltr)
    {
        if (m_progressbarTimer.ticksElapsed() < duration) {
            if(ltr)
                m_progressbarPercent = abs(m_progressbarTimer.ticksElapsed() / static_cast<double>(duration) * 100);
            else
                m_progressbarPercent = abs((m_progressbarTimer.ticksElapsed() / static_cast<double>(duration) * 100) - 100);
    
            auto self = static_self_cast<Creature>();
            m_progressbarUpdateEvent = g_dispatcher.scheduleEvent([=] {
                self->updateProgressbar(duration, ltr);
            }, 50);
        }
        else {
            m_progressbarPercent = 0;
        }
        callLuaField("onProgressbarUpdate", m_progressbarPercent, duration, ltr);
    }
    #endif
Usage

TFS-side you can use it inside Lua scripts.
Lua:
creature:sendProgressbar(duration, ltr)
duration - in ms, how long should it take to fill or empty the bar
ltr - true if bar should fill from left to right, false if should empty from right to left
Creature can be Player or Monster.

OTClient-side you can check if progressbar started or is being updated. This can be used for all sort of different things (like in video above)
Lua:
connect(
  Creature,
  {
    onProgressbarStart = onProgressbarStart,
    onProgressbarUpdate = onProgressbarUpdate
  }
)
function onProgressbarStart(creature, duration, ltr)
   -- Code
end

function onProgressbarUpdate(creature, percent, duration, ltr)
   -- Code
end
Feedback is very welcome!
 
Last edited:

Felipe93

Ghost Member
Joined
Mar 21, 2015
Messages
1,355
Reaction score
188
Location
Chile
can't this be added only in client side? or it wont work
 
OP
oen432

oen432

Active Member
Joined
Oct 3, 2014
Messages
278
Reaction score
246
Location
Poland
can't this be added only in client side? or it wont work
It can, you can then use module to start the progressbar. It's just easier if server does it. You don't have to worry about when the bar should appear.
 
Top