• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Feature [TFS & OTClient] Progressbar for creatures

oen432

Legendary OT User
Joined
Oct 3, 2014
Messages
1,900
Solutions
55
Reaction score
2,121
Location
Poland
GitHub
Oen44
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:
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.
 
I see that everything you add to the sources has this

#ifdef PROGRESSBAR

do I have to add it together or just the code inside?
 
I see that everything you add to the sources has this

#ifdef PROGRESSBAR

do I have to add it together or just the code inside?
You can add just the code. I'm using defines to mark custom stuff and possibility to disable them with single //.
 
You can add just the code. I'm using defines to mark custom stuff and possibility to disable them with single //.
oh yes thanks, now I understand why I was giving error when compiling xD
 
Hi @oen432
I am not very good with codes... but it's possible create that system with "custom points" instead "time duration"? for create another "health bar" like a magic shield or another custom system, for example limited oxygen when players are swimming, then we set a new stamina for swimming and display with these new bar.
 
Hi @oen432
I am not very good with codes... but it's possible create that system with "custom points" instead "time duration"? for create another "health bar" like a magic shield or another custom system, for example limited oxygen when players are swimming, then we set a new stamina for swimming and display with these new bar.
So another health/mana bar. Then all you need is to copy code related to one and adjust for your needs.
 
So another health/mana bar. Then all you need is to copy code related to one and adjust for your needs.
True! I will try, thank u very much for answer
 
Back
Top