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

Lua Xikini's Free Scripting Service TFS 1.3

Status
Not open for further replies.

Xikini

I whore myself out for likes
Senator
Premium User
Joined
Nov 17, 2010
Messages
6,756
Solutions
578
Reaction score
5,305
Please request
actions / creatureevents / globalevents / npcs / movements / talkactions

Do not request
spells / weapons / monsters / source editing / database queries

My goal of this thread is to learn TFS 1.3 scripting.

------------------------------------
Support

If you have an issue with one of my scripts, I will attempt to help you, but not in this thread.
Make a thread in the support board.
Ensure to follow all rules of the support board.
Without all neccesary information it's impossible to help you.

------------------------------------
I will only be scripting for TFS 1.3

Not TFS 1.1 / 1.2
Not OTServBR / OTX
and certainly not TFS 0.4

When requesting a script, don't ask for "this script I saw on super popular OT".

I don't care where the idea came from.
I don't want to see a video of the script in action.

I'm here to learn how to script better.
Just describe what the script is supposed to do, and I'll try to make it.

Any script that I make in response to a request from this thread will be shared publically, here, in this thread.

I'm not going to make anything in private, so post your request in this thread only.
Please, for the love of god, don't pm me asking to make a script.
I will actually add you to my ignore list if you do that.
--------------

Anyways!

Thanks for coming by and checking the thread out.
If you think there is a better way to script something, feel free to let me know.
I'm here to learn.

Cheers,

Xikini

----

Completed Scripts

DPS Dummy -> using RevScripts
!playerinfo character name -> using RevScripts
Positional Text talkaction -> using RevScripts
||--> Updated version that can use text & effects
Vocational Item Bonus's -> using RevScripts
||--> Updated version with easier config.
Simple damge buff potion -> using Revscripts
Kill monster in specific area, remove stone for x seconds -> using RevScripts
Training Room Teleporter -> using RevScripts
Lever that removes/adds/transforms objects or ground tiles -> using RevScripts
Loot from monster goes into single backpack, no matter how much loot there is.
Extra loot in monsters, based on chance and tier list -> using RevScripts
||--> Updated version that allows specific monsters to have their own loot table
Random Item chest that disappears after use, and respawns after x time -> using RevScripts
Multiple players required to open passageway.
Monster Arena Lever (x amount of waves, complete all waves, teleport appears) -> using RevScripts
Daily Boosted Creatures (experience & loot chance of items)
||--> Updated main file of boosted creatures, so that it triggers at midnight, instead of when the server starts up.
Reward Chest - Extremely simple. -> using Revscripts
Simple Npc that can sell an item, via text.

----

Extremely useful libs that I use quite often.

data/lib/core/player.lua
Give player items by table

data/lib/core/container.lua
getContainerFreeSlots

----

To-Do List

Shalaby - Player death counter. (Movement and talkaction)
Shalaby - Npc kill monster task, with item reward.
Shalaby - Boss respawn after x time.

-------

Last spot I've read messages
 
Last edited:
make a trainer DPS script using revscripts

every 5 sec if player is attacking trainer send their dps and make it only show text (creature.say + monster talktype) to that player instead of all players on screen
 
imagine your server is Normal PvP. (if player kill another player, get skull and frag).
But in some área (x, y, z) to (toX, toY, toZ), this area will be PvP Enforced (you can kill another player withou get skull and frag. And killer will gain exp for killing). Of course a player who dies loose items/skills normaly..
 
imagine your server is Normal PvP. (if player kill another player, get skull and frag).
But in some área (x, y, z) to (toX, toY, toZ), this area will be PvP Enforced (you can kill another player withou get skull and frag. And killer will gain exp for killing). Of course a player who dies loose items/skills normaly..
you need sour edit to aff pvp enfo tiles
 
Command: !info "player .
Make info in deafult channel like:
Profession, level, hp/mp, cap, first login, skills and magic info(with percent too), last death, pz time, frags, sex. Make some exhausted on script (maybe 1 minute?)
Thanks a lot. :)
 
make a trainer DPS script using revscripts

every 5 sec if player is attacking trainer send their dps and make it only show text (creature.say + monster talktype) to that player instead of all players on screen
Honestly, this took forever. xD

Got to learn so much stuff creating this. (mostly in terms of revscripts)

bandicam-2020-08-19-02-26-07-379.gif

\data\scripts\creaturescripts\ onHealthChange_training_dummy.lua
Lua:
local creatureevent = CreatureEvent("onHealthChange_Training_Dummy")

local dps_check_time = 5 -- how long in seconds until DPS is shown to player
local DPS_info = {}

local function send_dps_info(playerID, monsterID)
    local player = Player(playerID)
    if player then
        player:say("Your DPS is " .. (math.floor((DPS_info[monsterID][playerID] / dps_check_time) + 0.5)) .. ".", TALKTYPE_MONSTER_SAY, false, player)
    end
    DPS_info[monsterID][playerID] = nil
end

function creatureevent.onHealthChange(monster, creature, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if not Player(creature) then
        return false
    end
    
    local monsterID = monster:getId()
    local playerID = creature:getId()
    
    if not DPS_info[monsterID] then
        DPS_info[monsterID] = {}
    end
    
    if not DPS_info[monsterID][playerID] then
        addEvent(send_dps_info, dps_check_time * 1000, playerID, monsterID)
    end

    DPS_info[monsterID][playerID] = (DPS_info[monsterID][playerID] or 0) + primaryDamage + secondaryDamage
    monster:getPosition():sendMagicEffect(CONST_ME_GIFT_WRAPS, creature)
    return false
end

creatureevent:register()
\data\monster\lua\ training_dummy.lua
Lua:
local mType = Game.createMonsterType("training dummy")
local monster = {}

monster.description = "a training dummy"
monster.outfit = {
    lookTypeEx = 5787
}

monster.health = 1
monster.maxHealth = monster.health
monster.speed = 0

monster.flags = {
    pushable = false,
    summonable = false,
    attackable = true,
    hostile = false,
    convinceable = false
}

mType.onAppear = function(monster, creature)
    if monster:getId() == creature:getId() then
        monster:registerEvent("onHealthChange_Training_Dummy")
    end
end

mType:register(monster)
 
Last edited:
Honestly, this took forever. xD

Got to learn so much stuff creating this. (mostly in terms of revscripts)

View attachment 48887

\data\scripts\creaturescripts\ onHealthChange_training_dummy.lua
Lua:
local creatureevent = CreatureEvent("onHealthChange_Training_Dummy")

local dps_check_time = 5 -- how long in seconds until DPS is shown to player
local DPS_info = {}

local function send_dps_info(playerID, monsterID)
    local player = Player(playerID)
    if player then
        player:say("Your DPS is " .. (math.floor((DPS_info[monsterID][playerID] / dps_check_time) + 0.5)) .. ".", TALKTYPE_MONSTER_SAY, false, player)
        DPS_info[monsterID][playerID] = nil
    end
end

function creatureevent.onHealthChange(monster, creature, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if not Player(creature) then
        return false
    end

    local monsterID = monster:getId()
    local playerID = creature:getId()

    if not DPS_info[monsterID] then
        DPS_info[monsterID] = {}
    end

    if not DPS_info[monsterID][playerID] then
        DPS_info[monsterID][playerID] = 0
        addEvent(send_dps_info, dps_check_time * 1000, playerID, monsterID)
    end

    DPS_info[monsterID][playerID] = DPS_info[monsterID][playerID] + primaryDamage + secondaryDamage
    monster:getPosition():sendMagicEffect(CONST_ME_GIFT_WRAPS, creature)
    return false
end

creatureevent:register()
\data\monster\lua\ training_dummy.lua
Lua:
local mType = Game.createMonsterType("training dummy")
local monster = {}

monster.description = "a training dummy"
monster.outfit = {
    lookTypeEx = 5787
}

monster.health = 1
monster.maxHealth = monster.health
monster.speed = 0

monster.flags = {
    pushable = false,
    summonable = false,
    attackable = true,
    hostile = false,
    convinceable = false
}

mType.onAppear = function(monster, creature)
    if monster:getId() == creature:getId() then
        monster:registerEvent("onHealthChange_Training_Dummy")
    end
end

mType:register(monster)
Using a better naming convention for variables makes it all look cleaner. Also, you produced a potential memory leak in your send_dps_info function, by only setting the value back to nil if the player was found. If the player logs out (maybe a PZ is close enough) or otherwise disappears, the value will never get cleared. Unlikely in this case, but nonetheless, the leak exists.

Lua:
local creatureEvent = CreatureEvent("onHealthChange_Training_Dummy")

local checkTime = 5 -- how long in seconds until DPS is shown to player
local DPSTable = {}

local function sendInfo(playerId, monsterId)
    local monsterDPS = DPSTable[monsterId]
    local player = Player(playerId)
    if player then
        player:say(("Your DPS is %d."):format(math.floor((monsterDPS[playerId] / checkTime) + 0.5)), TALKTYPE_MONSTER_SAY, false, player)
    end

    monsterDPS[playerId] = nil
end

function creatureEvent.onHealthChange(monster, creature, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
    if not Player(creature) then
        return false
    end

    local monsterId = monster:getId()
    local playerId = creature:getId()

    if not DPSTable[monsterId] then
        DPSTable[monsterId] = {}
    end

    if not DPSTable[monsterId][playerId] then
        addEvent(sendInfo, checkTime * 1000, playerId, monsterId)
    end

    DPSTable[monsterId][playerId] = (DPSTable[monsterId][playerId] or 0) + primaryDamage + secondaryDamage
    monster:getPosition():sendMagicEffect(CONST_ME_GIFT_WRAPS, creature)
    return false
end

creatureEvent:register()
 
you produced a potential memory leak in your send_dps_info function
This was an oversight on my part.
Thanks for pointing it out. 😅
I'll be updating the script with some of the alterations. 🙏

Using a better naming convention for variables makes it all look cleaner.
I try, I swear. xD
 
now make a configurable script where a specific piece of armor grants separate bonuses based on the vocation of the wearer
example:
sorc -> gain magic level
druid -> gain mana
pally -> gain dist
knight -> gain melee
 
imagine your server is Normal PvP. (if player kill another player, get skull and frag).
But in some área (x, y, z) to (toX, toY, toZ), this area will be PvP Enforced (you can kill another player withou get skull and frag. And killer will gain exp for killing). Of course a player who dies loose items/skills normaly..
I looked and looked, and can't find a way to do this.

As of right now I'd say this is impossible without a database query at minimum, and that would be the 'hacky' approach, since we'd be removing the frag after it was added.

In order to prevent the frag from occurring, I'm 99% sure it would require a source edit.

Command: !info "player .
Make info in deafult channel like:
Profession, level, hp/mp, cap, first login, skills and magic info(with percent too), last death, pz time, frags, sex. Make some exhausted on script (maybe 1 minute?)
Thanks a lot. :)
I'll try to do as many of these as possible.
 
imagine your server is Normal PvP. (if player kill another player, get skull and frag).
But in some área (x, y, z) to (toX, toY, toZ), this area will be PvP Enforced (you can kill another player withou get skull and frag. And killer will gain exp for killing). Of course a player who dies loose items/skills normaly..
Use map editor to map pvp enforced area to the specified areas you need?
 
Command: !info "player .
Make info in deafult channel like:
Profession, level, hp/mp, cap, first login, skills and magic info(with percent too), last death, pz time, frags, sex. Make some exhausted on script (maybe 1 minute?)
Thanks a lot. :)
'first login', 'last death' -> I was unable to do.
I think I got everything else you wanted, plus a bit more.

usage: !playerinfo character name

Code:
Name: Test Character Two
Time Online: 00:03:49
Frags: 3
Skull: red
PZ Lock: 00:00:59
Level: 8
Sex: male
Vocation: Druid
Health: 185/185
Mana: 25/35
Soul: 100/100
Capacity: 342/420
Feed Time: 00:01:06
Magic: 0 -- (I could probably do a bunch of math to get this %, but I don't want to.... xD)
Fist: 10, with 94% to next level
Club: 11, with 45% to next level
Sword: 13, with 77% to next level
Axe: 10, with 100% to next level
Distance: 10, with 100% to next level
Shielding: 10, with 100% to next level
Fishing: 10, with 100% to next level

data\scripts\talkactions\ playerinfo.lua
Lua:
local talk = TalkAction("!playerinfo")

local exhaustTable, exhaustTimer = {}, 60 -- in milliseconds

function talk.onSay(player, words, param)
    local cur_player, cur_time = player:getId(), os.mtime()
    if exhaustTable[cur_player] and exhaustTable[cur_player] > cur_time then
        player:sendCancelMessage("This talkaction is on cooldown. Can use again in " .. ((exhaustTable[cur_player] - cur_time) / 1000) .. " seconds.")
        return false
    end
    
    local target = Player(param)
    if not target then
        player:sendCancelMessage("Player '" .. param:lower() .. "' either does not exist or is offline.")
        exhaustTable[cur_player] = cur_time + 5000
        return false
    end
    exhaustTable[cur_player] = cur_time + exhaustTimer
    
    local text = "\nName: " .. target:getName()
    text = text .. "\nTime Online: " .. os.date('!%T', os.time() - target:getLastLoginSaved())
    
    local killCount = target:getSkullTime()
    text = text .. "\nFrags: " .. (killCount > 0 and math.ceil(killCount / configManager.getNumber(configKeys.FRAG_TIME)) or 0)
    
    local skull = target:getSkull()
    text = text .. "\nSkull: " .. (skull == 3 and "white" or skull == 4 and "red" or skull == 5 and "black" or "none")
    
    local pzLockTime = (target:isPzLocked() and target:getCondition(CONDITION_INFIGHT, CONDITIONID_DEFAULT):getTicks() / 1000 or 0)
    text = text .. "\nPZ Lock: " .. (pzLockTime > 0 and os.date('!%T', pzLockTime) or "none")
    
    text = text .. "\nLevel: " .. target:getLevel()
    text = text .. "\nSex: " .. (target:getSex() == 0 and "female" or "male")
    text = text .. "\nVocation: " .. target:getVocation():getName()
    text = text .. "\nHealth: " .. target:getHealth() .. "/" .. target:getMaxHealth()
    text = text .. "\nMana: " .. target:getMana() .. "/" .. target:getMaxMana()
    text = text .. "\nSoul: " .. target:getSoul() .. "/" .. target:getMaxSoul()
    text = text .. "\nCapacity: " .. (target:getFreeCapacity() / 100) .. "/" .. (target:getCapacity() / 100)
    
    local feedTime = target:getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT)
    text = text .. "\nFeed Time: " .. os.date('!%T', (feedTime and feedTime:getTicks() / 1000 or 0))
    
    text = text .. "\nMagic: " .. target:getMagicLevel()
    text = text .. "\nFist: " .. target:getSkillLevel(SKILL_FIST) .. ", with " .. (100 - target:getSkillPercent(SKILL_FIST)) .. "% to next level"
    text = text .. "\nClub: " .. target:getSkillLevel(SKILL_CLUB) .. ", with " .. (100 - target:getSkillPercent(SKILL_CLUB)) .. "% to next level"
    text = text .. "\nSword: " .. target:getSkillLevel(SKILL_SWORD) .. ", with " .. (100 - target:getSkillPercent(SKILL_SWORD)) .. "% to next level"
    text = text .. "\nAxe: " .. target:getSkillLevel(SKILL_AXE) .. ", with " .. (100 - target:getSkillPercent(SKILL_AXE)) .. "% to next level"
    text = text .. "\nDistance: " .. target:getSkillLevel(SKILL_DISTANCE) .. ", with " .. (100 - target:getSkillPercent(SKILL_DISTANCE)) .. "% to next level"
    text = text .. "\nShielding: " .. target:getSkillLevel(SKILL_SHIELD) .. ", with " .. (100 - target:getSkillPercent(SKILL_SHIELD)) .. "% to next level"
    text = text .. "\nFishing: " .. target:getSkillLevel(SKILL_FISHING) .. ", with " .. (100 - target:getSkillPercent(SKILL_FISHING)) .. "% to next level"
    
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, text)
    return false
end

talk:separator(" ")
talk:register()
 
'first login', 'last death' -> I was unable to do.
I think I got everything else you wanted, plus a bit more.

usage: !playerinfo character name

Code:
Name: Test Character Two
Time Online: 00:03:49
Frags: 3
Skull: red
PZ Lock: 00:00:59
Level: 8
Sex: male
Vocation: Druid
Health: 185/185
Mana: 25/35
Soul: 100/100
Capacity: 342/420
Feed Time: 00:01:06
Magic: 0 -- (I could probably do a bunch of math to get this %, but I don't want to.... xD)
Fist: 10, with 94% to next level
Club: 11, with 45% to next level
Sword: 13, with 77% to next level
Axe: 10, with 100% to next level
Distance: 10, with 100% to next level
Shielding: 10, with 100% to next level
Fishing: 10, with 100% to next level

data\scripts\talkactions\ playerinfo.lua
Lua:
local talk = TalkAction("!playerinfo")

local exhaustTable, exhaustTimer = {}, 60 -- in milliseconds

function talk.onSay(player, words, param)
    local cur_player, cur_time = player:getId(), os.mtime()
    if exhaustTable[cur_player] and exhaustTable[cur_player] > cur_time then
        player:sendCancelMessage("This talkaction is on cooldown. Can use again in " .. ((exhaustTable[cur_player] - cur_time) / 1000) .. " seconds.")
        return false
    end
  
    local target = Player(param)
    if not target then
        player:sendCancelMessage("Player '" .. param:lower() .. "' either does not exist or is offline.")
        exhaustTable[cur_player] = cur_time + 5000
        return false
    end
    exhaustTable[cur_player] = cur_time + exhaustTimer
  
    local text = "\nName: " .. target:getName()
    text = text .. "\nTime Online: " .. os.date('!%T', os.time() - target:getLastLoginSaved())
  
    local killCount = target:getSkullTime()
    text = text .. "\nFrags: " .. (killCount > 0 and math.ceil(killCount / configManager.getNumber(configKeys.FRAG_TIME)) or 0)
  
    local skull = target:getSkull()
    text = text .. "\nSkull: " .. (skull == 3 and "white" or skull == 4 and "red" or skull == 5 and "black" or "none")
  
    local pzLockTime = (target:isPzLocked() and target:getCondition(CONDITION_INFIGHT, CONDITIONID_DEFAULT):getTicks() / 1000 or 0)
    text = text .. "\nPZ Lock: " .. (pzLockTime > 0 and os.date('!%T', pzLockTime) or "none")
  
    text = text .. "\nLevel: " .. target:getLevel()
    text = text .. "\nSex: " .. (target:getSex() == 0 and "female" or "male")
    text = text .. "\nVocation: " .. target:getVocation():getName()
    text = text .. "\nHealth: " .. target:getHealth() .. "/" .. target:getMaxHealth()
    text = text .. "\nMana: " .. target:getMana() .. "/" .. target:getMaxMana()
    text = text .. "\nSoul: " .. target:getSoul() .. "/" .. target:getMaxSoul()
    text = text .. "\nCapacity: " .. (target:getFreeCapacity() / 100) .. "/" .. (target:getCapacity() / 100)
  
    local feedTime = target:getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT)
    text = text .. "\nFeed Time: " .. os.date('!%T', (feedTime and feedTime:getTicks() / 1000 or 0))
  
    text = text .. "\nMagic: " .. target:getMagicLevel()
    text = text .. "\nFist: " .. target:getSkillLevel(SKILL_FIST) .. ", with " .. (100 - target:getSkillPercent(SKILL_FIST)) .. "% to next level"
    text = text .. "\nClub: " .. target:getSkillLevel(SKILL_CLUB) .. ", with " .. (100 - target:getSkillPercent(SKILL_CLUB)) .. "% to next level"
    text = text .. "\nSword: " .. target:getSkillLevel(SKILL_SWORD) .. ", with " .. (100 - target:getSkillPercent(SKILL_SWORD)) .. "% to next level"
    text = text .. "\nAxe: " .. target:getSkillLevel(SKILL_AXE) .. ", with " .. (100 - target:getSkillPercent(SKILL_AXE)) .. "% to next level"
    text = text .. "\nDistance: " .. target:getSkillLevel(SKILL_DISTANCE) .. ", with " .. (100 - target:getSkillPercent(SKILL_DISTANCE)) .. "% to next level"
    text = text .. "\nShielding: " .. target:getSkillLevel(SKILL_SHIELD) .. ", with " .. (100 - target:getSkillPercent(SKILL_SHIELD)) .. "% to next level"
    text = text .. "\nFishing: " .. target:getSkillLevel(SKILL_FISHING) .. ", with " .. (100 - target:getSkillPercent(SKILL_FISHING)) .. "% to next level"
  
    player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE, text)
    return false
end

talk:separator(" ")
talk:register()
You can extract first login from database using db.storeQuery, it should be located in players table. Last death would be most likely more complex, but again a query to player_deaths and conditional to look for most recent death. (time column)
 
You can extract first login from database using db.storeQuery, it should be located in players table. Last death would be most likely more complex, but again a query from player_deaths and conditional to look for most recent death. (time column)
yeah, but I don't do database queries in this thread. xD
 
very nice thread and initiative!

Magic: 0 -- (I could probably do a bunch of math to get this %, but I don't want to.... xD)
well you could try using vocation:getRequiredManaSpent(magicLevel) to achieve this in a simple way.


I have a request tho, if you don't mind :p it will be very useful to the community (and players of course), to have a working Imbuing, system, but I thought about a really simple system (idea from this thread), no modal window involved so it can be used on lower tibia versions:

"Imbuing system" Adding attributes to items by slot

talkactions, for example

!imbue head, life leech, basic

and it will give player SPECIALSKILL_LIFELEECHCHANCE, 100 and SPECIALSKILL_LIFELEECHAMOUNT, 5 for 20 hours, at cost of 5k and 25 Vampire Teeth


okay, this seems a little more complicated than I thought as of writing it now 😁 If anyone feels like giving ideas on how to make it easier/more conceivable, I'm all ears
 
Damage type buffs (onuse)
For example drinking certain potion will increase the player holy damage by 5pts.
Frost,Fire damage, physical,desth etc.

So for example everytime he does damage with a holy spell it is gonna be +3.
 
Was wondering if is it possible to turn this Talking Signs into an actions script , I mean maybe changing positions with actionId like "Text1" if actionid xx "Text 2" if action id XX
in order to if i intented to set an immediat event i'll use /attr aid XX with the desired TEXT
 
Status
Not open for further replies.
Back
Top