• 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 1.X+ Pocket dummy training

Ciosny

Member
Joined
Aug 16, 2024
Messages
106
Reaction score
12
Hi! I would like to create a "Training dummy" that can be summoned from an item and placed on the ground to skill without special weapons. Additionally, it can be done outside the house.
I would also like its duration to be specified (e.g. 10 hours of being summoned), which does not count if it is "in your pocket" :)

Anyone have an idea how to do this?:)

TFS 1.4.2 10.98 OTCv8
 
You'd want to make something similar to a pokeball script.

Make the creature however you like.
example? (non-pushable, can't walk, give it melee attacks or not, high health, self healing, no loot, make it undead if you don't want blood splats)

When you summon the creature using the pocket dummy item, give the creature an addEvent to start checking and counting stuff.
If the summoner goes offline, destroy the creature.
If the summoner no longer has the pocket item, destroy the creature.
Upon each check, check if the pocket dummy item still has charges/timer left, and reduce it by 1 second or something.

If no duration remaining, destroy creature.
Optional -> destroy pocket item? or let summoner know that it needs to be recharged.

--
As the duration/timer only goes down while the creature is spawned, the pocket item will never count down on it's own.

Some further considerations when spawning the creature.. should probably check that it's within town limits?
Or some sort of check to ensure that it's not spawned in a place where it would block access to things.. like in a 1 tile tunnel.
And should also check whether or not the pocket item has already summoned a creature. (store the creatureId on the pocket item?)

--
That's about all I can think of.
Should be a good starting point at least.
 
You'd want to make something similar to a pokeball script.

Make the creature however you like.
example? (non-pushable, can't walk, give it melee attacks or not, high health, self healing, no loot, make it undead if you don't want blood splats)

When you summon the creature using the pocket dummy item, give the creature an addEvent to start checking and counting stuff.
If the summoner goes offline, destroy the creature.
If the summoner no longer has the pocket item, destroy the creature.
Upon each check, check if the pocket dummy item still has charges/timer left, and reduce it by 1 second or something.

If no duration remaining, destroy creature.
Optional -> destroy pocket item? or let summoner know that it needs to be recharged.

--
As the duration/timer only goes down while the creature is spawned, the pocket item will never count down on it's own.

Some further considerations when spawning the creature.. should probably check that it's within town limits?
Or some sort of check to ensure that it's not spawned in a place where it would block access to things.. like in a 1 tile tunnel.
And should also check whether or not the pocket item has already summoned a creature. (store the creatureId on the pocket item?)

--
That's about all I can think of.
Should be a good starting point at least.
Sounds like a difficult job :d I saw a dummy on tibiascape that could be bought in the item shop and placed e.g. under the depot and then several people trained with it
 
I made this system for a friend about a year ago. I think he saw that this system was based on Zezenia, so I made it very similar. For example, you summon the dummy in a place, train, and click an item to return the dummy to your backpack. Then, in another place, you summon it again, and there is a countdown until it expires. If the time expires, the item is removed from your backpack as well. Feel free to test it out! :)

Hey Xiniki, feel free to take the script and improve it if you want... I made it about a year ago. Thanks!



dummy.gif


data/scripts/dummy.lua - Revscripts
LUA:
local config = {
    summonItemId = 8300, -- ID of the item that summons the monster
    monsterName = "Training Dummy", -- Name of the monster to be summoned
    summonStorage = 7550, -- Storage used to control if the monster has been summoned
    summonDuration = 36000, -- Summon duration in seconds (10 hours)
    cooldownTime = 10, -- Cooldown time in seconds
    allowSummonInPZ = false -- Set to true to allow summon in PZ, false to restrict it
}

local function createSummon(player)
    local creature = Game.createMonster(config.monsterName, player:getPosition())
    if creature then
        player:setStorageValue(config.summonStorage, creature:getId())
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have summoned a Training Dummy.")
        player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
        local playerGuid = player:getGuid()

        addEvent(function()
            local summonCreatureId = player:getStorageValue(config.summonStorage)
            local summonCreature = Creature(summonCreatureId)
            local playerObject = Player(playerGuid)
            if summonCreature and summonCreature:isCreature() then
                summonCreature:remove()
                if playerObject then
                    playerObject:setStorageValue(config.summonStorage, nil)
                    playerObject:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The Training Dummy has disappeared.")
                    playerObject:removeItem(config.summonItemId, 1)
                end
            end
        end, config.summonDuration * 1000)

        player:setStorageValue("cooldownSummon", os.time() + config.cooldownTime)
    end
end

local function formatMinutes(seconds)
    local minutes = math.floor(seconds / 60)
    local remainingSeconds = seconds % 60
    return string.format("%d:%02d", minutes, remainingSeconds)
end

local Dummy = Action()

function Dummy.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local tile = Tile(player:getPosition())
   
    if not config.allowSummonInPZ and tile and tile:hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendCancelMessage("You cannot use this item in a protection zone.")
        return true
    end

    if player:isPzLocked() then
        player:sendCancelMessage("You cannot use this item while PZ locked.")
        return true
    end

    local summonCreatureId = player:getStorageValue(config.summonStorage)
    local cooldownTime = player:getStorageValue("cooldownSummon")
    local currentTime = os.time()

    if cooldownTime > currentTime then
        local remainingTime = cooldownTime - currentTime
        local remainingTimeFormatted = formatMinutes(remainingTime)
        player:sendTextMessage(MESSAGE_INFO_DESCR, "You need to wait " .. remainingTimeFormatted .. " before summoning the Training Dummy again.")
        return true
    end

    local summonCreature = Creature(summonCreatureId)
    if summonCreature and summonCreature:isCreature() then
        summonCreature:remove()
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The Training Dummy has disappeared.")
        player:setStorageValue(config.summonStorage, nil)
        player:setStorageValue("cooldownSummon", currentTime + config.cooldownTime)
        return true
    end

    if item:getId() == config.summonItemId then
        createSummon(player)
        return true
    end

    return true
end

Dummy:id(config.summonItemId)
Dummy:register()




add your monster.xml
this <look typeex="5787" /> — you should change it to the proper dummy ID, right
dummy
XML:
<?xml version="1.0" encoding="UTF-8"?>
    <monster name="Training Dummy" nameDescription="a training dummy" race="undead" experience="0" speed="0">
    <health now="100000000" max="100000000" />
    <look typeex="5787" />
    <flags>
        <flag summonable="0" />
        <flag attackable="1" />
        <flag hostile="1" />
        <flag illusionable="0" />
        <flag convinceable="0" />
        <flag pushable="0" />
        <flag canpushitems="0" />
        <flag canpushcreatures="0" />
        <flag targetdistance="1" />
        <flag staticattack="100" />
        <flag runonhealth="0" />
        <flag canwalkonenergy="0" />
        <flag canwalkonfire="0" />
        <flag canwalkonpoison="0" />
    </flags>
    <attacks>
        <attack name="melee" interval="2000" min="0" max="-1" />
    </attacks>
    <defenses armor="0" defense="0">
        <defense name="healing" interval="5000" chance="100" min="100000000" max="100000000">
            <attribute key="areaEffect" value="blueshimmer" />
        </defense>
    </defenses>
    <immunities>
        <immunity invisible="1" />
    </immunities>
</monster>
 
I made this system for a friend about a year ago. I think he saw that this system was based on Zezenia, so I made it very similar. For example, you summon the dummy in a place, train, and click an item to return the dummy to your backpack. Then, in another place, you summon it again, and there is a countdown until it expires. If the time expires, the item is removed from your backpack as well. Feel free to test it out! :)

Hey Xiniki, feel free to take the script and improve it if you want... I made it about a year ago. Thanks!



View attachment 87385


data/scripts/dummy.lua - Revscripts
LUA:
local config = {
    summonItemId = 8300, -- ID of the item that summons the monster
    monsterName = "Training Dummy", -- Name of the monster to be summoned
    summonStorage = 7550, -- Storage used to control if the monster has been summoned
    summonDuration = 36000, -- Summon duration in seconds (10 hours)
    cooldownTime = 10, -- Cooldown time in seconds
    allowSummonInPZ = false -- Set to true to allow summon in PZ, false to restrict it
}

local function createSummon(player)
    local creature = Game.createMonster(config.monsterName, player:getPosition())
    if creature then
        player:setStorageValue(config.summonStorage, creature:getId())
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have summoned a Training Dummy.")
        player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
        local playerGuid = player:getGuid()

        addEvent(function()
            local summonCreatureId = player:getStorageValue(config.summonStorage)
            local summonCreature = Creature(summonCreatureId)
            local playerObject = Player(playerGuid)
            if summonCreature and summonCreature:isCreature() then
                summonCreature:remove()
                if playerObject then
                    playerObject:setStorageValue(config.summonStorage, nil)
                    playerObject:sendTextMessage(MESSAGE_EVENT_ADVANCE, "The Training Dummy has disappeared.")
                    playerObject:removeItem(config.summonItemId, 1)
                end
            end
        end, config.summonDuration * 1000)

        player:setStorageValue("cooldownSummon", os.time() + config.cooldownTime)
    end
end

local function formatMinutes(seconds)
    local minutes = math.floor(seconds / 60)
    local remainingSeconds = seconds % 60
    return string.format("%d:%02d", minutes, remainingSeconds)
end

local Dummy = Action()

function Dummy.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local tile = Tile(player:getPosition())
 
    if not config.allowSummonInPZ and tile and tile:hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendCancelMessage("You cannot use this item in a protection zone.")
        return true
    end

    if player:isPzLocked() then
        player:sendCancelMessage("You cannot use this item while PZ locked.")
        return true
    end

    local summonCreatureId = player:getStorageValue(config.summonStorage)
    lokalny cooldownTime = player:getStorageValue("cooldownSummon")
    lokalny przebiegCzas = os.time()

    jeśli cooldownTime > currentTime następnie
        lokalny miejsceCzas = cooldownTime - uruchomienieCzas
        lokalny miejsceCzasFormatowany = formatMinutes(pozostałyCzas)
        player:sendTextMessage(MESSAGE_INFO_DESCR, "Musisz odczekać " .. pozostałyTimeFormatted .. " przed koniecznością przywołania Manekina treningowego.")
        zwróć wartość prawdziwą
    koniec

    lokalne przywołanie stworzenia = Stworzenie(przywołanieIdentyfikatorStworzenie)
    jeśli summonCreature i summonCreature:isCreature() wtedy
        przywołajStworzenie:usuń()
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Manekin treningowy zniknął.")
        gra: setStorageValue(config.summonStorage, nil)
        player:setStorageValue("cooldownSummon", bieżąceCzas + config.cooldownTime)
        zwróć wartość prawdziwą
    koniec

    jeśli item:getId() == config.summonItemId następnie
        stwórzWezwanie(gracz)
        zwróć wartość prawdziwą
    koniec

    zwróć wartość prawdziwą
koniec

Przypomnienie:id(config.summonItemId)
Manekin:zarejestruj()
[/KOD]




dodaj swój monster.xml
[B]to <look typeex="5787" /> — odrzuca na inny identyfikator fikcyjny, prawda[/B]
przyłapać
[CODE=xml]<?xml version="1.0" encoding="UTF-8"?>
    <monster name="Training Dummy" nameDescription="a training dummy" race="undead" experience="0" speed="0">
    <zdrowie teraz="100000000" max="100000000" />
    <spójrz typeex="5787" />
    <flagi>
        <flaga summonable="0" />
        <flaga atakowalna="1" />
        <flaga wroga="1" />
        <flaga iluzjonistyczna="0" />
        <flaga przemawiająca="0" />
        <flaga pushable="0" />
        <flaga canpushitems="0" />
        <flaga canpushcreatures="0" />
        <flaga targetdistance="1" />
        <flaga staticattack="100" />
        <flaga runonhealth="0" />
        <flaga canwalkonenergy="0" />
        <flaga canwalkonfire="0" />
        <flaga canwalkonpoison="0" />
    </flagi>
    <ataki>
        <nazwa elektryczna="walka elektryczna" interwał="2000" min="0" maks="-1" />
    </ataki>
    <obrona pancerza="0" obrona="0">
        <nazwa obrony="leczenie" interwał="5000" szansa="100" min="100000000" maks="100000000">
            <attribute key="areaEffect" value="blueshimmer" />
        </obrona>
    </obrona>
    <odporność>
        <immunity niewidoczna="1" />
    </odporność>
</potwór>
Wow super!
I think it will be useful for many people :D My only question is whether there is any limitation that it can only be used in cities etc.? Can people also build it on respawns?

It would be better if people didn't use it to block other people in pvp or to block bosses
 
Great job! I will definitely test it, thank you! :D
Post automatically merged:

Wow super!
I think it will be useful for many people :D My only question is whether there is any limitation that it can only be used in cities etc.? Can people also build it on respawns?

It would be better if people didn't use it to block other people in pvp or to block bosses
Or make it so that if the player has PZ, the dummy simply disappears. Then you can create it anywhere, but if a mob comes or PK starts, it immediately disappears. This makes it unusable for PVP and PVE :)

I think it would be less work then and the effect would be the same :)
 
I tested it but I don't know why I have a strange error... No matter where I click it uses - out-of-range


Lua Script Error: [Scripts Interface]
C:\OTS\forgottenserver-1.4.2\data\scripts\dummy.lua:callback
LuaScriptInterface::getNumber(). Argument 2 has out-of-range value for unsigned int: -1
stack traceback:
[C]: in function 'Creature'
C:\OTS\forgottenserver-1.4.2\data\scripts\dummy.lua:142: in function <C:\OTS Pendr\forgottenserver-1.4.2\data\scripts\dummy.lua:113>
 
Last edited:
I added a spectator function to check if there are any mobs near the dummy. If there are, it automatically returns the dummy to the player's backpack. Additionally, if the player has a skull (white, red, etc.) and attacks the dummy or another player, the dummy is automatically returned to the player's backpack (innovative dummy item), and the time is saved normally. When you summon it again, the remaining time is preserved until it expires, and the item is removed directly from the player's backpack. I also added an onLook function to display how much time is left. The script is very simple. Now, I’m going to try creating another script for a dedicated exercise dummy without summoning a monster. You can summon the dummy and train with specific weapons anywhere, even in temples or depots.


dummy.gif








LUA:
local config = {
    summonItemId = 8300, -- ID of the item that summons the monster
    monsterName = "Training Dummy", -- Name of the monster to be summoned
    summonStorage = 7550, -- Storage used to control if the monster has been summoned
    summonDuration = 36000, -- Summon duration in seconds (10 hours)
    cooldownTime = 10, -- Cooldown time in seconds
    allowSummonInPZ = false -- Set to true to allow summon in PZ, false to restrict it
}

local function removeDummyIfCreaturesNearby(summonCreatureId, playerGuid, item)
    local summonCreature = Creature(summonCreatureId)
    if not summonCreature then return end

    local spectators = Game.getSpectators(summonCreature:getPosition(), false, false, 5, 5, 5, 5)
    for _, spectator in ipairs(spectators) do
        if spectator:isMonster() and spectator:getName() ~= config.monsterName then
            summonCreature:remove()
            local player = Player(playerGuid)
            if player then
                local summonExpiry = item:getCustomAttribute("summonExpiry")
                local remainingTime = summonExpiry - os.time()
                item:setCustomAttribute("remainingSummonTime", remainingTime)

                player:setStorageValue(config.summonStorage, nil)
                player:sendTextMessage(MESSAGE_INFO_DESCR, "Your Training Dummy was removed because a monster approached.")
            end
            return
        end

        if spectator:isPlayer() then
            local skull = spectator:getSkull()
            if skull == SKULL_WHITE or skull == SKULL_RED or skull == SKULL_BLACK then
                summonCreature:remove()
                local player = Player(playerGuid)
                if player then
                    local summonExpiry = item:getCustomAttribute("summonExpiry")
                    local remainingTime = summonExpiry - os.time()
                    item:setCustomAttribute("remainingSummonTime", remainingTime)

                    player:setStorageValue(config.summonStorage, nil)
                    player:sendTextMessage(MESSAGE_INFO_DESCR, "Your Training Dummy was removed because a player with a skull was nearby.")
                end
                return
            end
        end
    end

    addEvent(function() removeDummyIfCreaturesNearby(summonCreatureId, playerGuid, item) end, 1000)
end

local function removeDummy(player, item)
    local summonCreatureId = player:getStorageValue(config.summonStorage)
    local summonCreature = Creature(summonCreatureId)

    if summonCreature then
        local summonExpiry = item:getCustomAttribute("summonExpiry")
        local remainingTime = summonExpiry - os.time()

        if remainingTime > 0 then
            item:setCustomAttribute("remainingSummonTime", remainingTime)
        end

        summonCreature:remove()
        player:sendTextMessage(MESSAGE_INFO_DESCR, "The Training Dummy has been removed.")
    end
end

local function createSummon(player, item, remainingTime)
    local creature = Game.createMonster(config.monsterName, player:getPosition())
    if creature then
        local summonCreatureId = creature:getId()
        player:setStorageValue(config.summonStorage, summonCreatureId)
        player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
        local playerGuid = player:getGuid()

        local summonExpirationTime
        if remainingTime then
            summonExpirationTime = os.time() + remainingTime
        else
            summonExpirationTime = os.time() + config.summonDuration
        end

        item:setCustomAttribute("summonExpiry", summonExpirationTime)

        addEvent(function()
            local summonCreature = Creature(player:getStorageValue(config.summonStorage))
            if summonCreature then
                summonCreature:remove()
                local player = Player(playerGuid)
                if player then
                    player:setStorageValue(config.summonStorage, nil)
                    player:removeItem(config.summonItemId, 1)
                end
                item:removeCustomAttribute("summonExpiry")
            end
        end, (summonExpirationTime - os.time()) * 1000)

        addEvent(function() removeDummyIfCreaturesNearby(summonCreatureId, playerGuid, item) end, 1000)

        player:setStorageValue("cooldownSummon", os.time() + config.cooldownTime)
    end
end

local function formatTime(seconds)
    local hours = math.floor(seconds / 3600)
    local minutes = math.floor((seconds % 3600) / 60)
    local remainingSeconds = seconds % 60
    return string.format("%02d:%02d:%02d", hours, minutes, remainingSeconds)
end

local Dummy = Action()

function Dummy.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local tile = Tile(player:getPosition())

    local skull = player:getSkull()
    if skull == SKULL_WHITE or skull == SKULL_RED or skull == SKULL_BLACK then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You cannot summon the Training Dummy while having a skull.")
        return true
    end

    if not config.allowSummonInPZ and tile and tile:hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendCancelMessage("You cannot use this item in a protection zone.")
        return true
    end

    if player:isPzLocked() then
        player:sendCancelMessage("You cannot use this item while PZ locked.")
        return true
    end

    local summonCreatureId = player:getStorageValue(config.summonStorage)
    local cooldownTime = player:getStorageValue("cooldownSummon")
    local currentTime = os.time()

    if cooldownTime > currentTime then
        local remainingTime = cooldownTime - currentTime
        player:sendTextMessage(MESSAGE_INFO_DESCR, "You need to wait " .. formatTime(remainingTime) .. " before summoning the Training Dummy again.")
        return true
    end

    local summonCreature = Creature(summonCreatureId)
    if summonCreature then
        removeDummy(player, item)
        return true
    end

    local remainingTime = item:getCustomAttribute("remainingSummonTime")
    if remainingTime and remainingTime > 0 then
        createSummon(player, item, remainingTime)
    else
        createSummon(player, item)
    end

    item:removeCustomAttribute("remainingSummonTime")

    return true
end

Dummy:id(config.summonItemId)
Dummy:register()

local DummyLook = EventCallback

function DummyLook.onLook(player, thing, position, distance, description)
    if thing:isItem() and thing:getId() == config.summonItemId then
        local expiryTime = thing:getCustomAttribute("summonExpiry")
        local remainingTime = thing:getCustomAttribute("remainingSummonTime")
     
        if remainingTime and remainingTime > 0 then
            description = description .. "\nTraining Dummy remaining time: " .. formatTime(remainingTime)
        elseif expiryTime and expiryTime > os.time() then
            local totalTime = expiryTime - os.time()
            description = description .. "\nTraining Dummy remaining time: " .. formatTime(totalTime)
        else
            thing:removeCustomAttribute("summonExpiry")
        end
    end
    return description
end

DummyLook:register(2)

local creatureevent = CreatureEvent("onLogoutDummy")

function creatureevent.onLogout(player)
    local summonCreatureId = player:getStorageValue(config.summonStorage)
    if summonCreatureId > 0 then
        local summonCreature = Creature(summonCreatureId)
        if summonCreature then
            local item = player:getItemById(config.summonItemId)
            if item then
                local summonExpiry = item:getCustomAttribute("summonExpiry")
                if summonExpiry then
                    local remainingTime = summonExpiry - os.time()
                    if remainingTime > 0 then
                        item:setCustomAttribute("remainingSummonTime", remainingTime)
                    end
                end
            end
            summonCreature:remove()
            player:setStorageValue(config.summonStorage, nil)
        end
    end
    return true
end

creatureevent:register()
 
Last edited:
I added a spectator function to check if there are any mobs near the dummy. If there are, it automatically returns the dummy to the player's backpack. Additionally, if the player has a skull (white, red, etc.) and attacks the dummy or another player, the dummy is automatically returned to the player's backpack (innovative dummy item), and the time is saved normally. When you summon it again, the remaining time is preserved until it expires, and the item is removed directly from the player's backpack. I also added an onLook function to display how much time is left. The script is very simple. Now, I’m going to try creating another script for a dedicated exercise dummy without summoning a monster. You can summon the dummy and train with specific weapons anywhere, even in temples or depots.


View attachment 87423








LUA:
local config = {
    summonItemId = 8300, -- ID of the item that summons the monster
    monsterName = "Training Dummy", -- Name of the monster to be summoned
    summonStorage = 7550, -- Storage used to control if the monster has been summoned
    summonDuration = 36000, -- Summon duration in seconds (10 hours)
    cooldownTime = 10, -- Cooldown time in seconds
    allowSummonInPZ = false -- Set to true to allow summon in PZ, false to restrict it
}

local function removeDummyIfCreaturesNearby(summonCreatureId, playerGuid, item)
    local summonCreature = Creature(summonCreatureId)
    if not summonCreature then return end

    local spectators = Game.getSpectators(summonCreature:getPosition(), false, false, 5, 5, 5, 5)
    for _, spectator in ipairs(spectators) do
        if spectator:isMonster() and spectator:getName() ~= config.monsterName then
            summonCreature:remove()
            local player = Player(playerGuid)
            if player then
                local summonExpiry = item:getCustomAttribute("summonExpiry")
                local remainingTime = summonExpiry - os.time()
                item:setCustomAttribute("remainingSummonTime", remainingTime)

                player:setStorageValue(config.summonStorage, nil)
                player:sendTextMessage(MESSAGE_INFO_DESCR, "Your Training Dummy was removed because a monster approached.")
            end
            return
        end

        if spectator:isPlayer() then
            local skull = spectator:getSkull()
            if skull == SKULL_WHITE or skull == SKULL_RED or skull == SKULL_BLACK then
                summonCreature:remove()
                local player = Player(playerGuid)
                if player then
                    local summonExpiry = item:getCustomAttribute("summonExpiry")
                    local remainingTime = summonExpiry - os.time()
                    item:setCustomAttribute("remainingSummonTime", remainingTime)

                    player:setStorageValue(config.summonStorage, nil)
                    player:sendTextMessage(MESSAGE_INFO_DESCR, "Your Training Dummy was removed because a player with a skull was nearby.")
                end
                return
            end
        end
    end

    addEvent(function() removeDummyIfCreaturesNearby(summonCreatureId, playerGuid, item) end, 1000)
end

local function removeDummy(player, item)
    local summonCreatureId = player:getStorageValue(config.summonStorage)
    local summonCreature = Creature(summonCreatureId)

    if summonCreature then
        local summonExpiry = item:getCustomAttribute("summonExpiry")
        local remainingTime = summonExpiry - os.time()

        if remainingTime > 0 then
            item:setCustomAttribute("remainingSummonTime", remainingTime)
        end

        summonCreature:remove()
        player:sendTextMessage(MESSAGE_INFO_DESCR, "The Training Dummy has been removed.")
    end
end

local function createSummon(player, item, remainingTime)
    local creature = Game.createMonster(config.monsterName, player:getPosition())
    if creature then
        local summonCreatureId = creature:getId()
        player:setStorageValue(config.summonStorage, summonCreatureId)
        player:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
        local playerGuid = player:getGuid()

        local summonExpirationTime
        if remainingTime then
            summonExpirationTime = os.time() + remainingTime
        else
            summonExpirationTime = os.time() + config.summonDuration
        end

        item:setCustomAttribute("summonExpiry", summonExpirationTime)

        addEvent(function()
            local summonCreature = Creature(player:getStorageValue(config.summonStorage))
            if summonCreature then
                summonCreature:remove()
                local player = Player(playerGuid)
                if player then
                    player:setStorageValue(config.summonStorage, nil)
                    player:removeItem(config.summonItemId, 1)
                end
                item:removeCustomAttribute("summonExpiry")
            end
        end, (summonExpirationTime - os.time()) * 1000)

        addEvent(function() removeDummyIfCreaturesNearby(summonCreatureId, playerGuid, item) end, 1000)

        player:setStorageValue("cooldownSummon", os.time() + config.cooldownTime)
    end
end

local function formatTime(seconds)
    local hours = math.floor(seconds / 3600)
    local minutes = math.floor((seconds % 3600) / 60)
    local remainingSeconds = seconds % 60
    return string.format("%02d:%02d:%02d", hours, minutes, remainingSeconds)
end

local Dummy = Action()

function Dummy.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    local tile = Tile(player:getPosition())

    local skull = player:getSkull()
    if skull == SKULL_WHITE or skull == SKULL_RED or skull == SKULL_BLACK then
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You cannot summon the Training Dummy while having a skull.")
        return true
    end

    if not config.allowSummonInPZ and tile and tile:hasFlag(TILESTATE_PROTECTIONZONE) then
        player:sendCancelMessage("You cannot use this item in a protection zone.")
        return true
    end

    if player:isPzLocked() then
        player:sendCancelMessage("You cannot use this item while PZ locked.")
        return true
    end

    local summonCreatureId = player:getStorageValue(config.summonStorage)
    local cooldownTime = player:getStorageValue("cooldownSummon")
    local currentTime = os.time()

    if cooldownTime > currentTime then
        local remainingTime = cooldownTime - currentTime
        player:sendTextMessage(MESSAGE_INFO_DESCR, "You need to wait " .. formatTime(remainingTime) .. " before summoning the Training Dummy again.")
        return true
    end

    local summonCreature = Creature(summonCreatureId)
    if summonCreature then
        removeDummy(player, item)
        return true
    end

    local remainingTime = item:getCustomAttribute("remainingSummonTime")
    if remainingTime and remainingTime > 0 then
        createSummon(player, item, remainingTime)
    else
        createSummon(player, item)
    end

    item:removeCustomAttribute("remainingSummonTime")

    return true
end

Dummy:id(config.summonItemId)
Dummy:register()

local DummyLook = EventCallback

function DummyLook.onLook(player, thing, position, distance, description)
    if thing:isItem() and thing:getId() == config.summonItemId then
        local expiryTime = thing:getCustomAttribute("summonExpiry")
        local remainingTime = thing:getCustomAttribute("remainingSummonTime")
    
        if remainingTime and remainingTime > 0 then
            description = description .. "\nTraining Dummy remaining time: " .. formatTime(remainingTime)
        elseif expiryTime and expiryTime > os.time() then
            local totalTime = expiryTime - os.time()
            description = description .. "\nTraining Dummy remaining time: " .. formatTime(totalTime)
        else
            thing:removeCustomAttribute("summonExpiry")
        end
    end
    return description
end

DummyLook:register(2)

local creatureevent = CreatureEvent("onLogoutDummy")

function creatureevent.onLogout(player)
    local summonCreatureId = player:getStorageValue(config.summonStorage)
    if summonCreatureId > 0 then
        local summonCreature = Creature(summonCreatureId)
        if summonCreature then
            local item = player:getItemById(config.summonItemId)
            if item then
                local summonExpiry = item:getCustomAttribute("summonExpiry")
                if summonExpiry then
                    local remainingTime = summonExpiry - os.time()
                    if remainingTime > 0 then
                        item:setCustomAttribute("remainingSummonTime", remainingTime)
                    end
                end
            end
            summonCreature:remove()
            player:setStorageValue(config.summonStorage, nil)
        end
    end
    return true
end

creatureevent:register()

Good job :D!
Do you know why I'm getting this error above? :)
 
Good job :D!
Do you know why I'm getting this error above? :)
Probably, you didn’t fix some parts of the data/lib or some source files. I don’t remember if I had already fixed my TFS, but I tested it with 1.3 8.6, and it worked fine. The 1.5 version from NeKiRo also didn’t show any errors. As for 1.4.2, nothing happens because I’ve updated all my TFS, and everything is working fine. I suggest you look for errors, as they might be in the data/lib or source, and fix your TFS accordingly.

 
Probably, you didn’t fix some parts of the data/lib or some source files. I don’t remember if I had already fixed my TFS, but I tested it with 1.3 8.6, and it worked fine. The 1.5 version from NeKiRo also didn’t show any errors. As for 1.4.2, nothing happens because I’ve updated all my TFS, and everything is working fine. I suggest you look for errors, as they might be in the data/lib or source, and fix your TFS accordingly.

Hmm... I made these changes from the post you sent:

and i have in luascript.cpp:


// Creature
int LuaScriptInterface::luaCreatureCreate(lua_State* L)
{
// Creature(id or name or userdata)
Creature* creature;
if (isNumber(L, 2)) {
creature = g_game.getCreatureByID(getNumber<uint32_t>(L, 2));
} else if (isString(L, 2)) {
creature = g_game.getCreatureByName(getString(L, 2));
} else if (isUserdata(L, 2)) {
LuaDataType type = getUserdataType(L, 2);
if (type != LuaData_Player && type != LuaData_Monster && type != LuaData_Npc) {
lua_pushnil(L);
return 1;
}
creature = getUserdata<Creature>(L, 2);
} else {
creature = nullptr;
}

if (creature) {
pushUserdata<Creature>(L, creature);
setCreatureMetatable(L, -1, creature);
} else {
lua_pushnil(L);
}
return 1;
}


I had a few things different in src, but despite the changes and compilation, the error still persists.

I'll look for information on the forum to solve it, and if I don't find it, I'll just create a post. Maybe someone will know how to solve this for TFS 1.4.2 :)


Sooner or later it will be done. But anyway, thanks a lot for the script. As soon as I solve the:

"Lua Script Error: [Scripts Interface]" issue
C:\OTS\forgottenserver-1.4.2\data\scripts\dummy.lua:callback
LuaScriptInterface::getNumber(). Argument 2 has out-of-range value for unsigned int: -1
stack traceback:
[C]: in function 'Creature'
C:\OTS\forgottenserver-1.4.2\data\scripts\dummy.lua:142: in function <C:\OTS Pendr\forgottenserver-1.4.2\data\scripts\dummy.lua:113>"


I'll definitely check it out! :)
 
Idk why I can't spawn the Dummy in PZ , i have "allowSummonInPZ = true". Outside of pz scripts work good. I tried spawning in Temple and in House. Doesn't work in both. I use tfs 1.5 by nekiro. I added debugging it says

allowSummonInPZ: true
Tile Protection Zone Flag: true


EDIT1: fixed it with local creature = Game.createMonster(config.monsterName, position, false, true) -- Set force to true

EDIT2 :I still can't attack the dumy tho and the dummy doesnt attack me in pz.

EDIT3: I fixed attacking dummy with this in combat.cpp
//pz-zone
if (attacker->getZone() == ZONE_PROTECTION) {
// Check for a specific property or name
if (target->getName() == "Training Dummy") { // Replace with the actual name
return RETURNVALUE_NOERROR; // Allow attack
}

return RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE;
}

EDIT4: Now i can't deal spell damage cause im in Pz -.- i have no idea how to fix it since spells.cpp dont have target function defined
EIDT5: SOLVED
 
Last edited:
fix:
LUA:
    if summonCreatureId >= 0 then
        local summonCreature = Creature(summonCreatureId)
        if summonCreature then
            removeDummy(player, item)
            return true
        end
    end

Yeah install more commits to check if the storage isn't -1 😵‍💫
LOVE! Its work! :D <3

Is there any way to make dummy skill the shield? Considering that he is a "single attacker", because he has protection against moback and players to disappear (which is good in itself).
 
Back
Top