• 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.3] [Revscriptsys] Free Lua scripting service - Post your requests! Let's learn it together!

Znote

<?php echo $title; ?>
Staff member
Global Moderator
Premium User
Joined
Feb 14, 2008
Messages
7,030
Solutions
256
Reaction score
2,115
Location
Norway
GitHub
Znote
What is revscriptsys?
Revscriptsys is a new way to register your scripts in an easy drag and drop way without the need to register it with xml.
Basically you just place your .lua file into data/scripts/ or any subfolder according to your taste, except for monster files, they go into data/monster/.

The files in data/scripts, are loaded automatically on startup (Unless the filename starts with a # symbol, which means the file is disabled).

Revscriptsys supports multiple different interface methods to be used in one file (Actions, Moveevents, GlobalEvents, ...) which comes in hand if you do prolonged quests and such which usually would need to be done in different files.

You might have heard of the TFS 0.4 mods system? This is similar to that, but in pure Lua, no XML!

With Revscriptsys you should design your script in a header and footer way.
Action onUse example:
Lua:
local testAction = Action() -- this would be the "header" first thing we have to write (unless configuration tables and such)

function testAction.onUse(player, item, fromPosition, itemEx, toPosition, isHotkey) -- now we can design the action itself
    return print("We used this item: .." item.itemid)
end

testAction.id(2550) -- the item is a scythe
testAction.register() -- this is our "footer" it has to be the last function executed.

@Evil Hero has offered to take on requests, I will also chip in if I can. If you find any script that is not for revscriptsys, even 0.3/4 scripts, we will try to convert them to Revscriptsys for TFS 1.3. We also take on new requests, as long as they arent too complicated, and the request is well written and easy to understand.

We would also appreciate any contributors to the thread, who wants to convert and script Revscriptsys stuff for us, or with us. And it would be awesome if you could try it out! We want to give Revscriptsys a good test in-game to make sure it works great before TFS 1.3 is officially released. :)

It invokes metatables for:
  • Action()
  • CreatureEvent()
  • GlobalEvent()
  • MoveEvent()
  • TalkAction()
  • Weapon()
  • Spell()
with their corresponding functions to be able to set them up properly.

Supports:
  • Actions
  • Creaturescripts
  • Globalevents
  • Movements
  • Talkactions
  • Weapons
  • Spells
  • Monsters
For instance you could write the functionality to a Shovel item as an action:
Lua:
local shovel = Action()

local holes = {468, 481, 483}
function shovel.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    if toPosition.x == CONTAINER_POSITION then
        return false
    end

    local tile = Tile(toPosition)
    if not tile then
        return false
    end

    local ground = tile:getGround()
    if not ground then
        return false
    end

    local groundId = ground:getId()
    if isInArray(holes, groundId) then
        ground:transform(groundId + 1)
        ground:decay()

        toPosition.z = toPosition.z + 1
        tile:relocateTo(toPosition)
    elseif groundId == 231 then
        local randomValue = math.random(1, 100)
        if randomValue == 1 then
            Game.createItem(2159, 1, toPosition)
        elseif randomValue > 95 then
            Game.createMonster("Scarab", toPosition)
        end
        toPosition:sendMagicEffect(CONST_ME_POFF)
    else
        return false
    end

    return true
end

shovel:id(2554)
shovel:register()

More samples are available here:
WIKI (Work in progress): otland/forgottenserver (https://github.com/otland/forgottenserver/wiki/Revscriptsys)
And if you download the latest version from github, you should get some sample scripts bundled in the default data pack already inside data/scripts.

First submission by @Evil Hero to get the ball rolling:
TFS 1.2 Portal created on monster death? by @mdwilliams

After you kill a boss monster (configure monster name), spawn a teleport at configured x,y,z location:
Lua:
-- Boss teleport spawn script by mdwilliams.
-- https://otland.net/threads/tfs-1-2-portal-created-on-monster-death.265567/#post-2567024
-- Converted to TFS 1.3 Revscriptsys by Evil Hero.

local teleportToPosition = Position(1000, 1000, 7)
local teleportCreatePosition = Position(2000, 2000, 7)
local bossName = "boss monster"
local killMessage = "You have killed Boss Monster! A teleport has been created but it will disappear in 5 minutes!"

-- Function that will remove the teleport after a given time
local function removeTeleport(position)
    local teleportItem = Tile(position):getItemById(1387)
    if teleportItem then
        teleportItem:remove()
        position:sendMagicEffect(CONST_ME_POFF)
    end
end

local event = CreatureEvent("BossKill")

function event.onKill(creature, target)
    if target:isPlayer() or target:getMaster()  or target:getName():lower() ~= bossName then
        return true
    end

    local position = target:getPosition()
    position:sendMagicEffect(CONST_ME_TELEPORT)
    local item = Game.createItem(1387, 1, teleportCreatePosition)

    if item:isTeleport() then
        item:setDestination(teleportToPosition)
    end

    target:say(killMessage, TALKTYPE_MONSTER_SAY, 0, 0, position)

    -- Remove portal after 5 minutes
    addEvent(removeTeleport, 5 * 60 * 1000, position)

    return true
end

event:register()

local login = CreatureEvent("RegisterBossKill")

function login.onLogin(player)
    player:registerEvent("BossKill")
    return true
end

login:register()

Thread Accomplishments: (Released revscriptsys files):
 
Last edited:
does monsters support multiple folders structure or do we have to have all in a single folder?
 
[TFS 1.2] Teleport Scroll by @Stigma

Converted to Revscriptsys, 3 XML file changes and 3 file additions into 1 file:

Teleports player to default town position, creates a teleport near the player (that only the player can see).
The player has 30 seconds to enter the teleport to get back to their previous position.
Also added a 30 seconds cooldown, and tons of comments to explain the code.

Lua:
-- TP Scroll by Stigma: https://otland.net/threads/tfs-1-2-teleport-scroll.245184/
-- Converted to TFS 1.3 Revscriptsys by Znote.

-- Part 1 = Action (scroll item)
-- Part 2 = Movement (step in teleport positions)
-- Part 3 = Creaturescript (disable/cleanup if the player decides to log out while system is active)


------------------------------------
-- Begin code of Part 1 = Action: --
tpScroll = Action() -- Create a new action that we call "tpScroll"
tpScroll:id(1953) -- Item id of the scroll we want to attach this action to.

-- Create an internal storage object to keep track of positions
local savePos = {}

-- Supplementary function to this particular action.
-- (apply and re-apply effect as a pulse while the effect position is stored in internal object)
local function sendEffects(position, effect, pid)
    if savePos[pid] then
        if savePos[pid].Enabled then
            local player = Player(pid)
            if isPlayer(player) then
                position:sendMagicEffect(effect, player)
                addEvent(sendEffects, 400, position, effect, pid, CS)
            end
        end
    end
end

-- Attach an "onUse" functionality to this tpScroll action.
-- When you use the id associated to the action, this function will trigger.
function tpScroll.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    -- Store player id, we will use this as a logical reference in our internal storage
    -- and to re-create the player object in our SendEffects function
    local pid = player:getId()

    -- Don't allow players to use this scroll if they have engaged aggressively in PvP
    if not player:isPzLocked() then

        -- If player (pid) is not added to internal storage yet
        -- Add it to internal storage and execute this action
        if not savePos[pid] then
            -- Save the players position in his own internal storage (pid)
            savePos[pid] = {
                ScrollActivatedPosition = player:getPosition(),
                RandomTemplePosition = nil, -- We will save this a bit further down
                Enabled = true,
                Cooldown = os.time() + 31
            }

            -- Teleport player to his temple
            player:teleportTo(player:getTown():getTemplePosition())
          
            -- Get and store the position of the temple
            local pos = player:getPosition()

            -- Calculate a close nearby random position to this temple and save it
            savePos[pid].RandomTemplePosition = Position(pos.x+math.random(3), pos.y+math.random(2), pos.z)

            -- Execute the function that creates a pulse effect while this action is running
            -- On both positions, but with different effect type. (12 and 35)
            sendEffects(savePos[pid].RandomTemplePosition, 12, pid)
            sendEffects(savePos[pid].ScrollActivatedPosition, 35, pid)

            -- Find the ground item at the random temple position, and give it an action Id
            -- (For the movement script/PART 2)
            local item = Item(Tile(savePos[pid].RandomTemplePosition):getGround().uid)
            item:setActionId(3006)

            -- Also, execute a function in 30 seconds that will remove this added action id.
            addEvent(
                function()
                    if item:getActionId(3006) then
                        item:removeAttribute('aid')
                        savePos[pid] = nil
                    end
                end
            ,30 * 1000) -- 30 * 1000 ms = 30 seconds until this internal function executes.

        else -- This action is already executed, the teleport is already spawned.
            player:getPosition():sendMagicEffect(CONST_ME_POFF)
            -- If the system is still enabled, they havent entered the teleport yet
            if savePos[pid].Enabled then
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, 'You must enter your teleport first before you use this again. The teleport will exist for ' .. savePos[pid].Cooldown - os.time() .. ' more seconds.')
            else -- They have entered the teleport, but there is a lingering addevent we have to wait for to complete before they can use it again
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, 'Remaining cooldown: ' .. savePos[pid].Cooldown - os.time() .. ' seconds.')
            end
        end
    else -- If the player IS pz locked. (has attacked another player)
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, 'You may not use this while you are in battle.')
    end
    return true
end

-- Register/enable this action!
tpScroll:register()


--------------------------------------
-- Begin code of Part 2 = Movement: --
randomTemplePosition = MoveEvent()

-- In step 1 we added action id 3006 on this tile. Now lets register the "functionality" of it.
randomTemplePosition:aid(3006)

-- When you step on this actionid, the MoveEvent executes.
function randomTemplePosition.onStepIn(creature, item, position, fromPosition)
    -- Load the player id that moved on this randomTemplePosition, using that, find the relevant internal storage for him
    local pid = creature:getId()
    local pos = savePos[pid]

    -- If we found this players positions in the internal storage, proceed
    if pos and pos.RandomTemplePosition then

        -- Teleport him back to the position where he activated the tp scroll
        creature:teleportTo(pos.ScrollActivatedPosition)

        -- Send the stun effect on both positions (random temple + scroll)
        pos.ScrollActivatedPosition:sendMagicEffect(CONST_ME_STUN)
        pos.RandomTemplePosition:sendMagicEffect(CONST_ME_STUN)

        -- Remove the added action id. (Which will stop this event from happening until next time)
        item:removeAttribute('aid')

        -- Remove the player from the internal storage, so the pulse effects in step 1 stop pulsing.
        savePos[pid].Enabled = false
    end
    return true
end

-- Register/activate this onstepin effect.
randomTemplePosition:register()

----------------------------------------------------------------------------------------------
---------------------------- Begin code of Part 3 = Movement: --------------------------------
-- Creaturescript (disable/cleanup if the player decides to log out while system is active) --
local tpscroll_player = CreatureEvent("tpscroll_player")

-- onLogin and onLogout CreatureEvent function declarations should automatically be registered to every player
function tpscroll_player.onLogout(player)
    local pid = player:getId()
    -- If we have any internal storage on this player, remove it. Also remove the actionid on the floor
    if savePos[pid] then
        Item(Tile(savePos[pid].RandomTemplePosition):getGround().uid):removeAttribute('aid')
        savePos[pid].Enabled = false
    end
    return true
end

-- Enable/activate this Creature Event
tpscroll_player:register()
 
Last edited:
[TFS 1.2] Teleport Scroll by @Stigma

Converted to Revscriptsys, 3 XML file changes and 3 file additions into 1 file:

Teleports player to default town position, creates a teleport near the player (that only the player can see).
The player has 30 seconds to enter the teleport to get back to their previous position.
Also added a 30 seconds cooldown, and tons of comments to explain the code.

Lua:
-- TP Scroll by Stigma: https://otland.net/threads/tfs-1-2-teleport-scroll.245184/
-- Converted to TFS 1.3 Revscriptsys by Znote.

-- Part 1 = Action (scroll item)
-- Part 2 = Movement (step in teleport positions)
-- Part 3 = Creaturescript (disable/cleanup if the player decides to log out while system is active)


------------------------------------
-- Begin code of Part 1 = Action: --
tpScroll = Action() -- Create a new action that we call "tpScroll"
tpScroll:id(1953) -- Item id of the scroll we want to attach this action to.

-- Create an internal storage object to keep track of positions
local savePos = {}

-- Supplementary function to this particular action.
-- (apply and re-apply effect as a pulse while the effect position is stored in internal object)
local function sendEffects(position, effect, pid)
    print("SendEffects({x="..position.x..", y="..position.y..",z="..position.z.."}, "..effect..", "..pid..")")
    if savePos[pid] then
        if savePos[pid].Enabled then
            local player = Player(pid)
            if isPlayer(player) then
                position:sendMagicEffect(effect, player)
                addEvent(sendEffects, 400, position, effect, pid, CS)
            end
        end
    end
end

-- Attach an "onUse" functionality to this tpScroll action.
-- When you use the id associated to the action, this function will trigger.
function tpScroll.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    -- Store player id, we will use this as a logical reference in our internal storage
    -- and to re-create the player object in our SendEffects function
    local pid = player:getId()

    -- Don't allow players to use this scroll if they have engaged aggressively in PvP
    if not player:isPzLocked() then

        -- If player (pid) is not added to internal storage yet
        -- Add it to internal storage and execute this action
        if not savePos[pid] then
            -- Save the players position in his own internal storage (pid)
            savePos[pid] = {
                ScrollActivatedPosition = player:getPosition(),
                RandomTemplePosition = nil, -- We will save this a bit further down
                Enabled = true,
                Cooldown = os.time() + 31
            }

            -- Teleport player to his temple
            player:teleportTo(player:getTown():getTemplePosition())
          
            -- Get and store the position of the temple
            local pos = player:getPosition()

            -- Calculate a close nearby random position to this temple and save it
            savePos[pid].RandomTemplePosition = Position(pos.x+math.random(3), pos.y+math.random(2), pos.z)

            -- Execute the function that creates a pulse effect while this action is running
            -- On both positions, but with different effect type. (12 and 35)
            sendEffects(savePos[pid].RandomTemplePosition, 12, pid)
            sendEffects(savePos[pid].ScrollActivatedPosition, 35, pid)

            -- Find the ground item at the random temple position, and give it an action Id
            -- (For the movement script/PART 2)
            local item = Item(Tile(savePos[pid].RandomTemplePosition):getGround().uid)
            item:setActionId(3006)

            -- Also, execute a function in 30 seconds that will remove this added action id.
            addEvent(
                function()
                    if item:getActionId(3006) then
                        item:removeAttribute('aid')
                        savePos[pid] = nil
                    end
                end
            ,30 * 1000) -- 30 * 1000 ms = 30 seconds until this internal function executes.

        else -- This action is already executed, the teleport is already spawned.
            player:getPosition():sendMagicEffect(CONST_ME_POFF)
            -- If the system is still enabled, they havent entered the teleport yet
            if savePos[pid].Enabled then
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, 'You must enter your teleport first before you use this again. The teleport will exist for ' .. savePos[pid].Cooldown - os.time() .. ' more seconds.')
            else -- They have entered the teleport, but there is a lingering addevent we have to wait for to complete before they can use it again
                player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, 'Remaining cooldown: ' .. savePos[pid].Cooldown - os.time() .. ' seconds.')
            end
        end
    else -- If the player IS pz locked. (has attacked another player)
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, 'You may not use this while you are in battle.')
    end
    return true
end

-- Register/enable this action!
tpScroll:register()


--------------------------------------
-- Begin code of Part 2 = Movement: --
randomTemplePosition = MoveEvent()

-- In step 1 we added action id 3006 on this tile. Now lets register the "functionality" of it.
randomTemplePosition:aid(3006)

-- When you step on this actionid, the MoveEvent executes.
function randomTemplePosition.onStepIn(creature, item, position, fromPosition)
    -- Load the player id that moved on this randomTemplePosition, using that, find the relevant internal storage for him
    local pid = creature:getId()
    local pos = savePos[pid]

    -- If we found this players positions in the internal storage, proceed
    if pos and pos.RandomTemplePosition then

        -- Teleport him back to the position where he activated the tp scroll
        creature:teleportTo(pos.ScrollActivatedPosition)

        -- Send the stun effect on both positions (random temple + scroll)
        pos.ScrollActivatedPosition:sendMagicEffect(CONST_ME_STUN)
        pos.RandomTemplePosition:sendMagicEffect(CONST_ME_STUN)

        -- Remove the added action id. (Which will stop this event from happening until next time)
        item:removeAttribute('aid')

        -- Remove the player from the internal storage, so the pulse effects in step 1 stop pulsing.
        savePos[pid].Enabled = false
    end
    return true
end

-- Register/activate this onstepin effect.
randomTemplePosition:register()

----------------------------------------------------------------------------------------------
---------------------------- Begin code of Part 3 = Movement: --------------------------------
-- Creaturescript (disable/cleanup if the player decides to log out while system is active) --
local tpscroll_player = CreatureEvent("tpscroll_player")

-- onLogin and onLogout CreatureEvent function declarations should automatically be registered to every player
function tpscroll_player.onLogout(player)
    local pid = player:getId()
    -- If we have any internal storage on this player, remove it. Also remove the actionid on the floor
    if savePos[pid] then
        Item(Tile(savePos[pid].RandomTemplePosition):getGround().uid):removeAttribute('aid')
        savePos[pid].Enabled = false
    end
    return true
end

-- Enable/activate this Creature Event
tpscroll_player:register()
You still have a debug print on line 20 ;)
 
Converted by me.

Common tier upgrader you see in most servers. Use an item to upgrade it with a chance to succeed, and sends message and effect based on whether it succeeds or fails. Important sections/code are commented with explanations.

Lua:
-- Tier Upgrader by Stigma: https://otland.net/threads/tfs-0-4-1-2-tier-upgrading-system.245047/
-- Converted to TFS 1.3 Revscriptsys by Stigma

local action = Action()

local config = {
    messages = {
        success = {
            text  = 'Upgrade!',
            talkType = TALKTYPE_MONSTER_SAY,
            effect   = CONST_ME_FIREWORK_RED
        },

        fail = {
            text  = 'Upgrade Failed.',
            talkType = TALKTYPE_MONSTER_SAY,
            effect   = CONST_ME_POFF
        },
    },

    gear = {
        -- [key id] = {tier = item tier, upgraderType = 'key, soil, crystal, etc (whatever you want it to say, there's no limit)', chance = upgrade chance}
        [2087] = {tier = 1, upgraderType = 'key', chance = 30,
            items = {
                -- [from id] = [to id]
                [2505] = 2492,
                [2160] = 2148
            }
        },
        [2088] = {tier = 2, upgraderType = 'key', chance = 80,
            items = {
                [2148] = 2160
            }
        }
    }
}

function action.onUse(player, item, fromPosition, target, toPosition, isHotkey)
    -- Using player's position if the target item is located in a container, otherwise use the item's position located on the map
    local pos = target:getPosition().x == 65535 and player:getPosition() or target:getPosition()
    -- Make sure the player is not attempting to target a creature
    if not target:isItem() then
        player:sendCancelMessage('You must select an item.')
        pos:sendMagicEffect(CONST_ME_POFF)
        return true
    end
    -- Attempt to get the config based on which key id the player is using
    local keyConfig = config.gear[item:getId()]
    -- Adding article to full item name if possible, ex: "a sword"
    local name  = (target:getArticle() ~= '') and string.format('%s %s', target:getArticle(), target:getName()) or target:getName()
    if keyConfig then
        -- Directly attempt to access the item id to upgrade to by indexing the item list with the target item's id
        local upgradeId = keyConfig.items[target:getId()]
        -- Prevent attempting to upgrade an item that isn't in config
        if not upgradeId then
            player:sendCancelMessage(string.format('You are unable to upgrade %s with a tier %d %s.', (name == '') and 'this' or name, keyConfig.tier, keyConfig.upgraderType))
            pos:sendMagicEffect(CONST_ME_POFF)
            return true
        end
        -- Prevent attempting to upgrade a stackable item that has more than 1 in it's stack
        if target:getCount() > 1 then
            player:sendCancelMessage('You may only upgrade this item one at a time.')
            pos:sendMagicEffect(CONST_ME_POFF)
            return true
        end
        -- Use the "success" table in config if the random value is less than or equal to the chance, otherwise use the "fail" table
        local confKey = (math.random(100) <= keyConfig.chance and 'success' or 'fail')
        local resultConfig = config.messages[confKey]
        pos:sendMagicEffect(resultConfig.effect)
        player:say(resultConfig.text, resultConfig.talkType)
        if confKey == 'success' then
            target:transform(upgradeId)
        end
        -- Make sure to remove only 1 item in the case the upgrader is a stackable item
        item:remove(1)
    end
    return true
end

-- Automatically register the key ids in config
for keyId, _ in pairs(config.gear) do
    action:id(keyId)
end
action:register() -- Enable the action for use
 
Decoy spell, say once to summon, say it again to remove it (if it's not dead). Copies the owner's stats such as current health, max health, speed, outfit.

Lua:
-- Decoy spell made for TFS 1.3 Revscriptsys by Stigma

local spell = Spell(SPELL_INSTANT)

function spell.onCastSpell(creature, variant)
    local summons = creature:getSummons()
    -- Loop through all existing summons, if we find a decoy in the summon list, remove it.
    for _, summon in pairs(summons) do
        if summon:getName() == creature:getName() then
            creature:say("Goodbye, me!", TALKTYPE_MONSTER_SAY)
            summon:getPosition():sendMagicEffect(CONST_ME_POFF)
            summon:remove()
            return true
        end
    end
    -- Create a new monsterType with copied stats from the owner.
    local mType = Game.createMonsterType(creature:getName())
    mType:name(creature:getName())
    mType:health(creature:getHealth())
    mType:maxHealth(creature:getMaxHealth())
    mType:outfit(creature:getOutfit())
    mType:baseSpeed(creature:getSpeed())

    -- Use the new monsterType blueprint to summon a physical copy
    local decoy = Game.createMonster(mType:getName(), creature:getPosition())
    if decoy then
        creature:addSummon(decoy)
        creature:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
    else
        creature:sendCancelMessage("Decoy could not be created, move to a more open spot.")
        creature:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end
    return true
end

spell:name("Decoy")
spell:words("Decoy")
spell:group("support")
spell:cooldown(10000) -- milliseconds, 10 seconds
spell:level(20)
spell:manaPercent(25)
spell:isAggressive(false)

spell:register()
 
Decoy spell, say once to summon, say it again to remove it (if it's not dead). Copies the owner's stats such as current health, max health, speed, outfit.

Lua:
-- Decoy spell made for TFS 1.3 Revscriptsys by Stigma

local spell = Spell(SPELL_INSTANT)

function spell.onCastSpell(creature, variant)
    local summons = creature:getSummons()
    -- Loop through all existing summons, if we find a decoy in the summon list, remove it.
    for _, summon in pairs(summons) do
        if summon:getName() == creature:getName() then
            creature:say("Goodbye, me!", TALKTYPE_MONSTER_SAY)
            summon:getPosition():sendMagicEffect(CONST_ME_POFF)
            summon:remove()
            return true
        end
    end
    -- Create a new monsterType with copied stats from the owner.
    local mType = Game.createMonsterType(creature:getName())
    mType:name(creature:getName())
    mType:health(creature:getHealth())
    mType:maxHealth(creature:getMaxHealth())
    mType:outfit(creature:getOutfit())
    mType:baseSpeed(creature:getSpeed())

    -- Use the new monsterType blueprint to summon a physical copy
    local decoy = Game.createMonster(mType:getName(), creature:getPosition())
    if decoy then
        creature:addSummon(decoy)
        creature:getPosition():sendMagicEffect(CONST_ME_MAGIC_BLUE)
    else
        creature:sendCancelMessage("Decoy could not be created, move to a more open spot.")
        creature:getPosition():sendMagicEffect(CONST_ME_POFF)
        return false
    end
    return true
end

spell:name("Decoy")
spell:words("Decoy")
spell:group("support")
spell:cooldown(10000) -- milliseconds, 10 seconds
spell:level(20)
spell:manaPercent(25)
spell:isAggressive(false)

spell:register()
Well done, this just shows how lightweight and powerfull Revscriptsys can be ;)
 
TalkAction to edit items, creatures and players.
examples:
  • /attr attack,9999
  • /attr maxHealth,10000
  • /attr vocation,1
  • /attr vocation,Sorcerer

Lua:
local talk = TalkAction("/attr")

local itemFunctions = {
    ['actionid'] = { isActive = true, targetFunction = function (item, target) return item:setActionId(target) end },
    ['description'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, target) end },
    ['desc'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, target) end },
    ['remove'] = { isActive = true, targetFunction = function (item, target) return item:remove() end },
    ['decay'] = { isActive = true, targetFunction = function (item, target) return item:decay() end },
    ['transform'] = { isActive = true, targetFunction = function (item, target) return item:transform(target) end },
    ['clone'] = { isActive = true, targetFunction = function (item, target) return item:clone() end },
    ['attack'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_ATTACK, target) end },
    ['defense'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_DEFENSE, target) end },
    ['armor'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_ARMOR, target) end },
    ['name'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_NAME, target) end },
    ['extradefense'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_EXTRADEFENSE, target) end },
    ['range'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_SHOOTRANGE, target) end },
    ['charges'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_CHARGES, target) end }
}

local creatureFunctions = {
    ['health'] = { isActive = true, targetFunction = function (creature, target) return creature:addHealth(target) end },
    ['setHealth'] = { isActive = true, targetFunction = function (creature, target) return creature:setHealth(target) end },
    ['mana'] = { isActive = true, targetFunction = function (creature, target) return creature:addMana(target) end },
    ['speed'] = { isActive = true, targetFunction = function (creature, target) return creature:changeSpeed(target) end },
    ['droploot'] = { isActive = true, targetFunction = function (creature, target) return creature:setDropLoot(target) end },
    ['skull'] = { isActive = true, targetFunction = function (creature, target) return creature:setSkull(target) end },
    ['direction'] = { isActive = true, targetFunction = function (creature, target) return creature:setDirection(target) end },
    ['maxHealth'] = { isActive = true, targetFunction = function (creature, target) return creature:setMaxHealth(target) and creature:addHealth(creature:getMaxHealth() - creature:getHealth()) end },
    ['say'] = { isActive = true, targetFunction = function (creature, target) creature:say(target, TALKTYPE_SAY) end },
    ['startPos'] = { isActive = true, targetFunction = function (creature, target) if creature:isNpc() then return false end return creature:teleportTo(creature:getSpawnPosition(), true) end }
}

local playerFunctions = {
    ['fyi'] = { isActive = true, targetFunction = function (player, target) return player:popupFYI(target) end },
    ['tutorial'] = { isActive = true, targetFunction = function (player, target) return player:sendTutorial(target) end },
    ['guildnick'] = { isActive = true, targetFunction = function (player, target) return player:setGuildNick(target) end },
    ['group'] = { isActive = true, targetFunction = function (player, target) player:setGroup(Group(target)) return player:remove() end },
    ['vocation'] = { isActive = true, targetFunction = function (player, target) return player:setVocation(tonumber(target) or 0) end },
    ['stamina'] = { isActive = true, targetFunction = function (player, target) return player:setStamina(target) end },
    ['town'] = { isActive = true, targetFunction = function (player, target) return player:setTown(Town(target)) end },
    ['balance'] = { isActive = true, targetFunction = function (player, target) return player:setBankBalance(target + player:getBankBalance()) end },
    ['save'] = { isActive = true, targetFunction = function (player, target) return target:save() end },
    ['type'] = { isActive = true, targetFunction = function (player, target) return player:setAccountType(target) end },
    ['skullTime'] = { isActive = true, targetFunction = function (player, target) return player:setSkullTime(target) end },
    ['maxMana'] = { isActive = true, targetFunction = function (player, target) return player:setMaxMana(target) end },
    ['addItem'] = { isActive = true, targetFunction = function (player, target, count) return player:addItem(target, tonumber(count) or 1) end },
    ['removeItem'] = { isActive = true, targetFunction = function (player, target, count) return player:removeItem(target, tonumber(count) or 1) end },
    ['premium'] = { isActive = true, targetFunction = function (player, target) return player:addPremiumDays(target) end }
}

function talk.onSay(player, words, param)
    if player:getGroup():getAccess() and param ~= "" then
        local position = player:getPosition()
        position:getNextPosition(player:getDirection(), 1)
        local split = param:split(",")
        local itemFunction = itemFunctions[split[1]]
        local creatureFunction = creatureFunctions[split[1]]
        local playerFunction = playerFunctions[split[1]]
        if itemFunction and itemFunction.isActive then
            local item = Tile(position):getTopVisibleThing(player)
            if not item or not item:isItem() then
                return not player:sendCancelMessage("Object not found, you may not be in front of an object.")
            end
            if itemFunction.targetFunction(item, split[2]) then
                position:sendMagicEffect(CONST_ME_MAGIC_GREEN)
            else
                player:sendCancelMessage("The attribute you want to add is not supported or does not exist.")
            end
        elseif creatureFunction and creatureFunction.isActive then
            local creature = Tile(position):getTopCreature()
            if not creature or not creature:isCreature() then
                return not player:sendCancelMessage("Creature not found or maybe not in front of a creature.")
            end
            if creatureFunction.targetFunction(creature, split[2]) then
                position:sendMagicEffect(CONST_ME_MAGIC_GREEN)
            else
                player:sendCancelMessage("The attribute you want to add is not supported or does not exist.")
            end
        elseif playerFunction and playerFunction.isActive then
            local targetPlayer = Tile(position):getTopCreature()
            if not targetPlayer or not targetPlayer:getPlayer() then
                return not player:sendCancelMessage("Player not found or maybe not in front of a player.")
            end
            if playerFunction.targetFunction(targetPlayer, split[2]) then
                position:sendMagicEffect(CONST_ME_MAGIC_GREEN)
            else
                player:sendCancelMessage("The attribute you want to add is not supported or does not exist.")
            end
        else
            player:sendCancelMessage("The attribute you want to add is not supported or does not exist.")
        end
           return false
    end
    return true
end

talk:separator(" ")
talk:register()
 
TalkAction to edit items, creatures and players.
examples:
  • /attr attack,9999
  • /attr maxHealth,10000
  • /attr vocation,1
  • /attr vocation,Sorcerer

Lua:
local talk = TalkAction("/attr")

local itemFunctions = {
    ['actionid'] = { isActive = true, targetFunction = function (item, target) return item:setActionId(target) end },
    ['description'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, target) end },
    ['desc'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_DESCRIPTION, target) end },
    ['remove'] = { isActive = true, targetFunction = function (item, target) return item:remove() end },
    ['decay'] = { isActive = true, targetFunction = function (item, target) return item:decay() end },
    ['transform'] = { isActive = true, targetFunction = function (item, target) return item:transform(target) end },
    ['clone'] = { isActive = true, targetFunction = function (item, target) return item:clone() end },
    ['attack'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_ATTACK, target) end },
    ['defense'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_DEFENSE, target) end },
    ['armor'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_ARMOR, target) end },
    ['name'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_NAME, target) end },
    ['extradefense'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_EXTRADEFENSE, target) end },
    ['range'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_SHOOTRANGE, target) end },
    ['charges'] = { isActive = true, targetFunction = function (item, target) return item:setAttribute(ITEM_ATTRIBUTE_CHARGES, target) end }
}

local creatureFunctions = {
    ['health'] = { isActive = true, targetFunction = function (creature, target) return creature:addHealth(target) end },
    ['setHealth'] = { isActive = true, targetFunction = function (creature, target) return creature:setHealth(target) end },
    ['mana'] = { isActive = true, targetFunction = function (creature, target) return creature:addMana(target) end },
    ['speed'] = { isActive = true, targetFunction = function (creature, target) return creature:changeSpeed(target) end },
    ['droploot'] = { isActive = true, targetFunction = function (creature, target) return creature:setDropLoot(target) end },
    ['skull'] = { isActive = true, targetFunction = function (creature, target) return creature:setSkull(target) end },
    ['direction'] = { isActive = true, targetFunction = function (creature, target) return creature:setDirection(target) end },
    ['maxHealth'] = { isActive = true, targetFunction = function (creature, target) return creature:setMaxHealth(target) and creature:addHealth(creature:getMaxHealth() - creature:getHealth()) end },
    ['say'] = { isActive = true, targetFunction = function (creature, target) creature:say(target, TALKTYPE_SAY) end },
    ['startPos'] = { isActive = true, targetFunction = function (creature, target) if creature:isNpc() then return false end return creature:teleportTo(creature:getSpawnPosition(), true) end }
}

local playerFunctions = {
    ['fyi'] = { isActive = true, targetFunction = function (player, target) return player:popupFYI(target) end },
    ['tutorial'] = { isActive = true, targetFunction = function (player, target) return player:sendTutorial(target) end },
    ['guildnick'] = { isActive = true, targetFunction = function (player, target) return player:setGuildNick(target) end },
    ['group'] = { isActive = true, targetFunction = function (player, target) player:setGroup(Group(target)) return player:remove() end },
    ['vocation'] = { isActive = true, targetFunction = function (player, target) return player:setVocation(tonumber(target) or 0) end },
    ['stamina'] = { isActive = true, targetFunction = function (player, target) return player:setStamina(target) end },
    ['town'] = { isActive = true, targetFunction = function (player, target) return player:setTown(Town(target)) end },
    ['balance'] = { isActive = true, targetFunction = function (player, target) return player:setBankBalance(target + player:getBankBalance()) end },
    ['save'] = { isActive = true, targetFunction = function (player, target) return target:save() end },
    ['type'] = { isActive = true, targetFunction = function (player, target) return player:setAccountType(target) end },
    ['skullTime'] = { isActive = true, targetFunction = function (player, target) return player:setSkullTime(target) end },
    ['maxMana'] = { isActive = true, targetFunction = function (player, target) return player:setMaxMana(target) end },
    ['addItem'] = { isActive = true, targetFunction = function (player, target, count) return player:addItem(target, tonumber(count) or 1) end },
    ['removeItem'] = { isActive = true, targetFunction = function (player, target, count) return player:removeItem(target, tonumber(count) or 1) end },
    ['premium'] = { isActive = true, targetFunction = function (player, target) return player:addPremiumDays(target) end }
}

function talk.onSay(player, words, param)
    if player:getGroup():getAccess() and param ~= "" then
        local position = player:getPosition()
        position:getNextPosition(player:getDirection(), 1)
        local split = param:split(",")
        local itemFunction = itemFunctions[split[1]]
        local creatureFunction = creatureFunctions[split[1]]
        local playerFunction = playerFunctions[split[1]]
        if itemFunction and itemFunction.isActive then
            local item = Tile(position):getTopVisibleThing(player)
            if not item or not item:isItem() then
                return not player:sendCancelMessage("Object not found, you may not be in front of an object.")
            end
            if itemFunction.targetFunction(item, split[2]) then
                position:sendMagicEffect(CONST_ME_MAGIC_GREEN)
            else
                player:sendCancelMessage("The attribute you want to add is not supported or does not exist.")
            end
        elseif creatureFunction and creatureFunction.isActive then
            local creature = Tile(position):getTopCreature()
            if not creature or not creature:isCreature() then
                return not player:sendCancelMessage("Creature not found or maybe not in front of a creature.")
            end
            if creatureFunction.targetFunction(creature, split[2]) then
                position:sendMagicEffect(CONST_ME_MAGIC_GREEN)
            else
                player:sendCancelMessage("The attribute you want to add is not supported or does not exist.")
            end
        elseif playerFunction and playerFunction.isActive then
            local targetPlayer = Tile(position):getTopCreature()
            if not targetPlayer or not targetPlayer:getPlayer() then
                return not player:sendCancelMessage("Player not found or maybe not in front of a player.")
            end
            if playerFunction.targetFunction(targetPlayer, split[2]) then
                position:sendMagicEffect(CONST_ME_MAGIC_GREEN)
            else
                player:sendCancelMessage("The attribute you want to add is not supported or does not exist.")
            end
        else
            player:sendCancelMessage("The attribute you want to add is not supported or does not exist.")
        end
           return false
    end
    return true
end

talk:separator(" ")
talk:register()
Post where the original script came from as well so people can learn how to convert!!
 
If somebody have miss this funny event by @Perun
(Revscriptsys)
 
my request is this one, post #4, an useful yet simple talk action to get outfit/creature type on the fly to make npcs/mobs easier
 
my request is this one, post #4, an useful yet simple talk action to get outfit/creature type on the fly to make npcs/mobs easier
Lua:
local talkAction = TalkAction("/outfit")

function talkAction.onSay(player, words, param)
    local target = Creature(param)
    -- Attempt to create userdata with the argument passed to a command
    if not target then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Creature not found.")
        return false
    end
    local outfit = target:getOutfit()
    -- Convert outfit table to XML format
    local msg = string.format('<look type="%s" head="%s" body="%s" legs="%s" feet="%s" addons="%s" />', outfit.lookType, outfit.lookHead, outfit.lookBody, outfit.lookLegs, outfit.lookFeet, outfit.lookAddons)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, msg)
    return false
end

talkAction:separator(" ") -- Separate arguments passed to the command by a space
talkAction:register()
 
Last edited:
Lua:
local talkAction = TalkAction("/outfit")

function talkAction.onSay(player, words, param)
    local target = Creature(param)
    -- Attempt to create userdata with the argument passed to a command
    if not target then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Creature not found.")
    end
    local outfit = target:getOutfit()
    -- Convert outfit table to XML format
    local msg = string.format('<look type="%s" head="%s" body="%s" legs="%s" feet="%s" addons="%s" />', outfit.lookType, outfit.lookHead, outfit.lookBody, outfit.lookLegs, outfit.lookFeet, outfit.lookAddons)
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, msg)
    return false
end

talkAction:separator(" ") -- Separate arguments passed to the command by a space
talkAction:register()

missing the return false on
Code:
if not target then
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, "Creature not found.")
        return false
    end
 
Maybe a Zombie event if that is not to hard to do
 
Back
Top