• 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!

[TFS 0.3.6pl1] "Catacombs" Teleporter like Diablo 3 Teleport - Great for RPG servers (Remake)

Oskar1121

Excellent OT User
Joined
Jul 15, 2009
Messages
634
Reaction score
537
Location
Poland

In data/lib create 010-teleport.lua file and paste:
PHP:
places = {
        {placeName = 'Cirith Deposit', placeStorage = 6661, placepos = {x = 1304, y = 891, z = 8}},
        {placeName = 'Azshara West Catacomb', placeStorage = 6662, placepos = {x = 246, y = 485, z = 8}},
        {placeName = 'Azshara North Catacomb', placeStorage = 6663, placepos = {x = 259, y = 401, z = 7}}
        }

modalId = 1900
       
function getCatacombByName(name)
for k, v in pairs(places) do
    if v.placeName:lower() == name:lower() then
        return k
    end
end
return false
end

function sendCatacombWindow(cid)
modalWindowCreate(modalId, 'Teleporter', 'Select place:')

modalWindowAddButton(modalId, 1, 'Teleport')
setDefaultEnterButton(modalId, 1)
modalWindowAddButton(modalId, 2, 'Cancel')
setDefaultEscapeButton(modalId, 2)

for i = 1, #places do
    if getCreatureStorage(cid, places[i].placeStorage) == 1 then
        addChoice(modalId, i, places[i].placeName)
    end
end  
sendToPlayer(cid, modalId)
return true
end


In data/movements/movements.xml paste:
PHP:
<movevent type="StepIn" actionid="9024" event="script" value="catacombs/teleporter.lua"/>
<movevent type="StepIn" uniqueid="6661-6663" event="script" value="catacombs/catacombsTiles.lua"/>

And create folder with name catacombs into data/movements/scripts and create two folders:
teleporter.lua
PHP:
function onStepIn(cid, item, position, fromPosition)
    return sendCatacombWindow(cid)
end


And catacombsTiles.lua
PHP:
local config = {
                [6661] = {{x = 1305, y = 891, z = 8}, CONST_ME_METEORITE, 'Cirith Deposit'},
               
               
                }

function onStepIn(cid, item, position, fromPosition)
local catacombsConfig = config[item.uid]
if not catacombsConfig then
    return true
end
if getCreatureStorage(cid, item.uid) < 1 then
    doCreatureSetStorage(cid, item.uid, 1)
    doCreatureSay(cid, 'It looks like a teleporter.', TALKTYPE_MONSTER)
    doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE, 'You have unlocked Catacomb Teleporter: ' .. catacombsConfig[3] .. '.')
    doSendMagicEffect(catacombsConfig[1], catacombsConfig[2])
end
return true
end


Next step is data/creaturescripts. Into .xml file paste:
PHP:
<event type="modalwindow" name="catacombw" event="script" value="catacomb_window.lua"/>

And data/creaturescripts/scripts create new file catacomb_window.lua and paste:
PHP:
function onModalWindow(cid, modalWindowId, buttonId, choiceId)
    if modalWindowId ~= 1900 then
        return false
    end
    if buttonId == 1 then -- Teleport
        if getCreatureStorage(cid, places[choiceId].placeStorage) == 1 then
            doSendMagicEffect(getThingPos(cid), CONST_ME_TELEPORT)
            doTeleportThing(cid, places[choiceId].placepos, true, true)
            doPlayerSendTextMessage(cid, MESSAGE_EVENT_ADVANCE, 'You have succesfully teleported to ' .. places[choiceId].placeName .. '.')
            doSendMagicEffect(places[choiceId].placepos, CONST_ME_TELEPORT)
        else
            sendCatacombWindow(cid)
        end
    elseif buttonId == 255 then
        doPlayerPopupFYI(cid,"Please use a button.")
        sendCatacombWindow(cid)
    end
return true
end
 
That's all with LUA part. Now open your source and open enums.h file and paste:
PHP:
typedef std::map<std::string, uint8_t> ModalWindowMap;
struct ModalWindow
{
    ModalWindowMap buttons, choices;
    std::string title, message;
    uint32_t id;
    uint8_t defaultEnterButton, defaultEscapeButton;
    bool priority;

    ModalWindow()
    {
        id = 0;
        title = message = "";
        defaultEnterButton = defaultEscapeButton = 0xFF;
        priority = false;
    }
};


Next step is creatureevent.cpp and creatureevent.h.
Find:
PHP:
else if(tmpStr == "death")
    m_type = CREATURE_EVENT_DEATH;
And under this, paste:
PHP:
else if(tmpStr == "modalwindow")
    m_type = CREATURE_EVENT_MODALWINDOW;

Find:
PHP:
case CREATURE_EVENT_DEATH:
    return "onDeath";
And under this, paste:
PHP:
case CREATURE_EVENT_MODALWINDOW:
    return "onModalWindow";

Find:
PHP:
case CREATURE_EVENT_DEATH:
    return "cid, corpse, deathList";
And under this, paste:
PHP:
case CREATURE_EVENT_MODALWINDOW:
    return "player, modalWindowId, buttonId, choiceId";

And at the bottom paste:
PHP:
void CreatureEvent::executeModalWindow(Player* player, uint32_t modalWindowId, uint8_t buttonId, uint8_t choiceId)
{
    //onModalWindow(player, modalWindowId, buttonId, choiceId)
    if(!m_interface->reserveEnv())
    {
        std::cout << "[Error - CreatureEvent::executeModalWindow] Call stack overflow." << std::endl;
        return;
    }

    ScriptEnviroment* env = m_interface->getEnv();
    env->setScriptId(m_scriptId, m_interface);

    lua_State* L = m_interface->getState();
  
    m_interface->pushFunction(m_scriptId);
    lua_pushnumber(L, player ? player->getID() : 0);
    lua_pushnumber(L, modalWindowId);
    lua_pushnumber(L, buttonId);
    lua_pushnumber(L, choiceId);

    m_interface->callFunction2(4);
    m_interface->releaseEnv();
}


Now open creatureevent.h and find:
PHP:
CREATURE_EVENT_DEATH,
And under this, paste:
PHP:
CREATURE_EVENT_MODALWINDOW,

Now find:
PHP:
bool executeDeath(Creature* creature, Item* corpse, DeathList deathList);
And under this, paste:
PHP:
void executeModalWindow(Player* player, uint32_t modalWindowId, uint8_t buttonId, uint8_t choiceId);


Now open player.cpp and at the bottom paste:
PHP:
void Player::sendModalWindow(const ModalWindow modalWindow)
{
    if(!client)
        return;

    client->sendModalWindow(modalWindow);
}

Open player.h and under:
PHP:
uint32_t getGUID() const {return guid;}
Paste:
PHP:
void sendModalWindow(const ModalWindow modalWindow);


Now open game.cpp and at the bottom paste:
PHP:
void Game::createModalWindow(const uint32_t id, const std::string title, const std::string message)
{
    ModalWindow modalWindowStruct;
    modalWindowStruct.id = id;
    modalWindowStruct.title = title;
    modalWindowStruct.message = message;
    modalWindowStruct.defaultEnterButton = 0xFF;
    modalWindowStruct.defaultEscapeButton = 0xFF;
    modalWindowStruct.priority = false;
  
    modalList[id] = modalWindowStruct;
}

ModalWindow Game::getModalWindowById(const uint32_t id)
{
    ModalList::iterator it = modalList.find(id);
    if(it != modalList.end())
        return it->second;
  
    ModalWindow _modalWindow;
    return _modalWindow;
}

void Game::playerAnswerModalWindow(uint32_t playerId, uint32_t modalWindowId, uint8_t button, uint8_t choice)
{
    Player* player = getPlayerByID(playerId);
    if(!player)
        return;

    CreatureEventList modalWindowEvents = player->getCreatureEvents(CREATURE_EVENT_MODALWINDOW);
    for(CreatureEventList::iterator it = modalWindowEvents.begin(); it != modalWindowEvents.end(); ++it)
        (*it)->executeModalWindow(player, modalWindowId, button, choice);
}

void Game::saveModalWindow(const uint32_t id, ModalWindow modalWindow)
{
    modalList[id] = modalWindow;
}

And into game.h under:
PHP:
void globalSave();
Paste:
PHP:
void playerAnswerModalWindow(uint32_t playerId, uint32_t modalWindowId, uint8_t button, uint8_t choice);
void createModalWindow(const uint32_t id, const std::string title, const std::string message);
void saveModalWindow(const uint32_t id, ModalWindow modalWindow);
ModalWindow getModalWindowById(const uint32_t id);

Find:
PHP:
void internalDecayItem(Item* item);
And under this, paste:
PHP:
typedef std::map<uint32_t, ModalWindow> ModalList;
ModalList modalList;
 
Now open protocolgame.cpp and at the bottom paste:
PHP:
void ProtocolGame::sendModalWindow(const ModalWindow& modalWindow)
{
    NetworkMessage_ptr msg = getOutputBuffer();
    if(!msg)
        return;

    TRACK_MESSAGE(msg);

    msg->AddByte(0xFA);

    msg->AddU32(modalWindow.id);
    msg->AddString(modalWindow.title);
    msg->AddString(modalWindow.message);

    msg->AddByte(modalWindow.buttons.size());
    for(ModalWindowMap::const_iterator it = modalWindow.buttons.begin(); it != modalWindow.buttons.end(); it++)
    {
        msg->AddString(it->first);
        msg->AddByte(it->second);
    }

    msg->AddByte(modalWindow.choices.size());
    for(ModalWindowMap::const_iterator it = modalWindow.choices.begin(); it != modalWindow.choices.end(); it++)
    {
        msg->AddString(it->first);
        msg->AddByte(it->second);
    }

    msg->AddByte(modalWindow.defaultEnterButton);
    msg->AddByte(modalWindow.defaultEscapeButton);
    msg->AddByte(modalWindow.priority ? 0x01 : 0x00);
}

void ProtocolGame::parseModalWindowAnswer(NetworkMessage& msg)
{
    uint32_t id = msg.GetU32();
    uint8_t button = msg.GetByte();
    uint8_t choice = msg.GetByte();
    addGameTask(&Game::playerAnswerModalWindow, player->getID(), id, button, choice);
}

Find:
PHP:
case 0xE6:
    parseBugReport(msg);
    break;
And under this, paste:
PHP:
case 0xF9:
    parseModalWindowAnswer(msg);
    break;


Into protocolgame.h find:
PHP:
void parseCancelRuleViolation(NetworkMessage& msg);
And under this, paste:
PHP:
void parseModalWindowAnswer(NetworkMessage& msg);
void sendModalWindow(const ModalWindow& modalWindow);


And, finally, luascript.cpp. At the bottom paste:
PHP:
int32_t LuaScriptInterface::luaModalWindowCreate(lua_State* L)
{
    // modalWindow(id, title, message)
    const std::string message = popString(L);
    const std::string title = popString(L);
    const uint32_t id = popNumber(L);

    g_game.createModalWindow(id, title, message);
    return 1;
}

int32_t LuaScriptInterface::luaModalWindowAddButton(lua_State* L)
{
    // modalWindowAddButton(id, buttonId, text)
    const std::string text = popString(L);
    const uint8_t buttonId = popNumber(L);
    const uint32_t id = popNumber(L);
   
    ModalWindow modalWindow = g_game.getModalWindowById(id);
    if(modalWindow.id != 0)
    {
        modalWindow.buttons[text] = buttonId;
        g_game.saveModalWindow(id, modalWindow);
        lua_pushboolean(L, true);
    }
    else
        lua_pushboolean(L, false);
   
    return 1;
}

int32_t LuaScriptInterface::luaModalWindowSetDefaultEnterButton(lua_State* L)
{
    // setDefaultEnterButton(id, buttonId)
    const uint8_t buttonId = popNumber(L);
    const uint32_t id = popNumber(L);
   
    ModalWindow modalWindow = g_game.getModalWindowById(id);
    if(modalWindow.id != 0)
    {
        modalWindow.defaultEnterButton = buttonId;
        g_game.saveModalWindow(id, modalWindow);
        lua_pushboolean(L, true);
    }
    else
        lua_pushboolean(L, false);
   
    return 1;
}

int32_t LuaScriptInterface::luaModalWindowSetDefaultEscapeButton(lua_State* L)
{
    // modalWindow:setDefaultEscapeButton(id, buttonId)
    const uint8_t buttonId = popNumber(L);
    const uint32_t id = popNumber(L);
   
    ModalWindow modalWindow = g_game.getModalWindowById(id);
    if(modalWindow.id != 0)
    {
        modalWindow.defaultEscapeButton = buttonId;
        g_game.saveModalWindow(id, modalWindow);
        lua_pushboolean(L, true);
    }
    else
        lua_pushboolean(L, false);
   
    return 1;
}

int32_t LuaScriptInterface::luaModalWindowAddChoice(lua_State* L)
{
    // addChoice(id, buttonId, text)
    const std::string text = popString(L);
    const uint8_t buttonId = popNumber(L);
    const uint32_t id = popNumber(L);
   
    ModalWindow modalWindow = g_game.getModalWindowById(id);
    if(modalWindow.id != 0)
    {
        modalWindow.choices[text] = buttonId;
        g_game.saveModalWindow(id, modalWindow);
        lua_pushboolean(L, true);
    }
    else
        lua_pushboolean(L, false);
   
    return 1;
}

int32_t LuaScriptInterface::luaModalWindowSendToPlayer(lua_State* L)
{
    // sendToPlayer(cid, id)
    const uint32_t id = popNumber(L);
   
    ScriptEnviroment* env = getEnv();
    Player* player = env->getPlayerByUID(popNumber(L));
    if(!player)
    {
        errorEx(getError(LUA_ERROR_PLAYER_NOT_FOUND));
        lua_pushboolean(L, false);
        return 1;
    }
   
    ModalWindow modalWindow = g_game.getModalWindowById(id);
    if(modalWindow.id != 0)
    {
        player->sendModalWindow(modalWindow);
        lua_pushboolean(L, true);
    }
    else
        lua_pushboolean(L, false);
   
    return 1;
}


Find:
PHP:
lua_register(m_luaState, "getCreatureHealth", LuaScriptInterface::luaGetCreatureHealth);
And under this, paste:
PHP:
    //modalWindowCreate(cid, type)
    lua_register(m_luaState, "modalWindowCreate", LuaScriptInterface::luaModalWindowCreate);
   
    //addChoice(id, buttonId, text)
    lua_register(m_luaState, "addChoice", LuaScriptInterface::luaModalWindowAddChoice);
   
    //setDefaultEnterButton(id, buttonId)
    lua_register(m_luaState, "setDefaultEnterButton", LuaScriptInterface::luaModalWindowSetDefaultEnterButton);
   
    //setDefaultEscapeButton(id, buttonId)
    lua_register(m_luaState, "setDefaultEscapeButton", LuaScriptInterface::luaModalWindowSetDefaultEscapeButton);
   
    //modalWindowAddButton(id, butonId, text)
    lua_register(m_luaState, "modalWindowAddButton", LuaScriptInterface::luaModalWindowAddButton);
   
    //sendToPlayer(uid, id)
    lua_register(m_luaState, "sendToPlayer", LuaScriptInterface::luaModalWindowSendToPlayer);

luascript.h, find:
PHP:
virtual void registerFunctions();
And under this, paste:
PHP:
        static int32_t luaModalWindowCreate(lua_State* L);
        static int32_t luaModalWindowAddChoice(lua_State* L);
        static int32_t luaModalWindowSetDefaultEnterButton(lua_State* L);
        static int32_t luaModalWindowSetDefaultEscapeButton(lua_State* L);
        static int32_t luaModalWindowAddButton(lua_State* L);
        static int32_t luaModalWindowSendToPlayer(lua_State* L);

That's all. Thanks.
 
I really love you man. I have two questions. Is it tested? and... How can I rep you?
 
Have you tested it with a 9.7+ tibia client? Modal windows came in 9.7, but there are no servers 9.7+ 0.3.6pl1 at the moment
 
Last edited:
I tested it on 8.54 version, but I used OTClient with modal window.
 
Hey, tried to compile, but I got an error. I got tfs 0.3.6pl1 sources for 8.6v2 client.

PHP:
1>..\creatureevent.cpp(1934): error C2039: 'callFunction2' : is not a member of 'LuaScriptInterface'
1>  c:\users\...\sources\luascript.h(211) : see declaration of 'LuaScriptInterface'

How can I solve this? Maybe just replacing "CallFunction2" for "CallFunction"?

Edit: Yes, I was right, fixed it replacing "CallFunction2" for "CallFunction".

Btw, now I have another issue. If the player had discovered a catacomb, the buttons of modal window works perfectly, but if he didn't, he can't exit the window when he clicks on "Cancel". I had a look on the lua code, but haven't seen any errors. Otherwise, it's my first time with modalwindows.

Edit2: I realised that buttons only work when there are choices avalaible. They won't work if there aren't.

Thanks very much again to Oskar1121 for the ModalWindows for 0.3.6pl1. It will be even more awesome if you release the code for all other ModalWindows related functions (e.g. getMessage or setMessage).

Thx for all!
 
Last edited:
memerto, I have a 10.10 version of 0.3.7
Could you compile one with this function of modal windows for me?
 
I have a problem.
The compilation is fine but when I try to open the serv crash (image)
I use cryingdamson 0.3.6 (8.60) V8.2
 

Attachments

What's the difference between this and the original one? (aside of the GIF sample :[ )
 
should work with 0.4 just change the beginnings from luaScriptInterace to luaInterface.

And does the part of the code from enums.h is correct?

cause I have the error "stray \160 in program" hehe what does it mean?
 
Back
Top