• 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+ Cannot give Achievements to players

peteralto

Member
Joined
Nov 1, 2020
Messages
93
Solutions
1
Reaction score
17
I cannot insert Achievements into players in my scripts with this:
player:addAchievement("A Total Nightmare")
player:addAchievement("Razing!")

Here is the lib:
Lua:
--[[
Functions:
    getAchievementInfoById(achievement_id)
    getAchievementInfoByName(achievement_name)
    getSecretAchievements()
    getPublicAchievements()
    getAchievements()
    Player:addAchievement(achievement_id/name[, showMsg])
    Player:removeAchievement(achievement_id/name)
    Player:hasAchievement(achievement_id/name)
    Player:addAllAchievements([showMsg])
    Player:removeAllAchievements()
    Player:getSecretAchievements()
    Player:getPublicAchievements()
    Player:getAchievements()
    isAchievementSecret(achievement_id/name)
    Player:getAchievementPoints()

Note:     This lib was created following the data found in tibia.wikia.com.
        Achievements with no points (or points equal to 0) are achievements with no available info about points in tibia.wikia.com. These achievements should be updated
]]

ACHIEVEMENTS_BASE = 300000 -- base storage
ACHIEVEMENTS_ACTION_BASE = 20000     --this storage will be used to save the process to obtain the certain achievement
                                    --(Ex: this storage + the id of achievement 'Allowance Collector' to save...
                                    -- ...how many piggy banks has been broken

achievements =
{
    [1] = {name = "Afraid of no Ghost!", grade = 1, points = 2, description = "You passed their test and helped the Spirithunters testing equipment, researching the supernatural and catching ghosts - it's you they're gonna call."},
    [2] = {name = "Allow Cookies?", grade = 1, points = 2, description = "With a perfectly harmless smile you fooled all of those wisecrackers into eating your exploding cookies. Consider a boy or girl scout outfit next time to make the trick even better."},
    [3] = {name = "Allowance Collector", grade = 1, points = 2, secret = true, description = "You certainly have your ways when it comes to acquiring money. Many of them are pink and paved with broken fragments of porcelain."},
    [4] = {name = "Amateur Actor", grade = 1, points = 2, description = "You helped bringing Princess Buttercup, Doctor Dumbness and Lucky the Wonder Dog to life - and will probably dream of them tonight, since you memorised your lines perfectly. What a .. special piece of.. screenplay."},
    [5] = {name = "Animal Activist", grade = 1, points = 2, description = "You have a soft spot for little, weak animals, and you do everything in your power to protect them - even if you probably eat dragons for breakfast."},
    [6] = {name = "Arachnoise", grade = 1, points = 1, description = "You've shattered each of Bloodweb's eight frozen legs. As they say: break a leg, and then some more."},
    [7] = {name = "Archpostman", grade = 1, points = 3, description = "Delivering letters and parcels has always been a secret passion of yours, and now you can officially put on your blue hat, blow your Post Horn and do what you like to do most. Beware of dogs!"},
    [8] = {name = "Askarak Nemesis", grade = 1, points = 1, description = "You are now the royal archfiend of the Askarak, prince slayer."},
    [9] = {name = "Baby Sitter", grade = 1, points = 1, description = "You have cheered up a demon baby and returned it to its mother. A quick count of your fingers will reveal if you made it through unharmed."},
    [10] = {name = "Back from the Dead", grade = 1, points = 2, description = "You overcame the undead Zanakeph and sent him back into the darkness that spawned him."},
    [11] = {name = "Back into the Abyss", grade = 1, points = 1, description = "You've cut off a whole lot of tentacles today. Thul was driven back to where he belongs."},
    [12] = {name = "Backpack Tourist", grade = 1, points = 1, description = "If someone lost a random thing in a random place, you're probably a good person to ask and go find it, even if you don't know what and where."},
    [13] = {name = "Bad Timing", grade = 1, points = 2, description = "Argh! Not now! How is it that those multifunctional tools never fail when you're using them for something completely trivial like squeezing juice, but mess up when you desperately need to climb up a rope spot with a fire-breathing dragon chasing you?"},
    [14] = {name = "Bane of the Hive", grade = 1, points = 2, description = "Countless fights and never tiring effort in the war against the hive grant you the experience to finish your outfit with the last remaining part. Your chitin outfit is a testament of your skills and dedication for the cause."},
    [15] = {name = "Banebringers' Bane", grade = 1, points = 2, description = "You sacrificed a lot of ingredients to create the protective brew of the witches and played a significant part in the efforts to repel the dreaded banebringers. The drawback is that even the banebringers may take notice of you ..."},
    [16] = {name = "Beach Tamer", grade = 1, points = 2, description = "You re-enacted the Taming of the Shrew on a beach setting and proved that you can handle capricious girls quite well. With or without fish tails."},
    [17] = {name = "Bearhugger", grade = 1, points = 1, description = "Warm, furry and cuddly - though that same bear you just hugged would probably rip you into pieces if he had been conscious, he reminded you of that old teddy bear which always slept in your bed when you were still small."},
    [18] = {name = "Beautiful Agony", grade = 1, points = 2, description = "Ethershreck's cry of agony kept ringing in your ear for hours after he had dissolved into thin air. He probably moved to another plane of existence... for a while."},
    [19] = {name = "Becoming a Bigfoot", grade = 1, points = 1, description = "You did it! You convinced the reclusive gnomes to accept you as one of their Bigfoots. Now you are ready to help them. With big feet big missions seen to come."},
    [20] = {name = "Berserker", grade = 1, points = 3, description = "RAWR! Strength running through your body, your heart racing faster and adrenaline fueling your every weapon swing. All in a little bottle. No refund for destroyed furniture. For further questions consult your healer or potion dealer."},
    [21] = {name = "Bibby's Bloodbath", grade = 1, points = 1, secret = true, description = "You lend a helping hand in defeating invading Orcs by destroying their warcamp along with their leader. Bibby's personal bloodbath..."},
    [22] = {name = "Blessed!", grade = 1, points = 2, description = "You travelled the world for an almost meaningless prayer - but at least you don't have to do that again and can get a new blessed stake in the blink of an eye."},
    [23] = {name = "Blood-Red Snapper", grade = 1, points = 1, description = "You've tainted the jungle floor with the Snapper's crimson blood."},
    [24] = {name = "Bluebarian", grade = 1, points = 2, description = "You live the life of hunters and gatherers. Well, especially that of a gatherer, and especially of one who gathers lots of blueberries. Have you checked the colour of your tongue lately?"},
    [25] = {name = "Bone Brother", grade = 1, points = 1, description = "You've joined the undead bone brothers - making death your enemy and your weapon as well. Devouring what's weak and leaving space for what's strong is your primary goal."},
    [26] = {name = "Breaking the Ice", grade = 1, points = 1, description = "You almost made friends with Shardhead... before he died. Poor guy only seems to attract violence with his frosty attitude."},
    [27] = {name = "Bunny Slipped", grade = 1, points = 2, description = "Indeed, you have a soft spot for rabbits. Maybe the rabbits you saved today will be the rabbits that will save you tomorrow. When you are really hungry."},
    [28] = {name = "Cake Conqueror", grade = 1, points = 1, description = "You have bravely stepped onto the cake isle. Is there any more beautiful, tasty place to be in the whole world?"},
    [29] = {name = "Call Me Sparky", grade = 1, points = 1, description = "Admittedly you enjoyed the killing as usual. But the part with the sparks still gives you shivers ... or is it that there is some charge left on you?"},
    [30] = {name = "Chest Robber", grade = 1, points = 1, description = "You've discovered three nomad camps and stole their supplies. Well, you can probably use them better then they can."},
    [31] = {name = "Choking on Her Venom", grade = 1, points = 1, description = "The Old Widow fell prey to your supreme hunting skills."},
    [32] = {name = "Chorister", grade = 1, points = 1, description = "Lalalala... you now know the cult's hymn sung in Liberty Bay"},
    [33] = {name = "Clay Fighter", grade = 1, points = 3, secret = true, description = "You love getting your hands wet and dirty - and covered with clay. Your perfect sculpture of Brog, the raging Titan is your true masterpiece."},
    [34] = {name = "Cocoon of Doom", grade = 1, points = 3, secret = true, description = "You helped bringing Devovorga's dangerous tentacles and her humongous cocoon down - not stopping her transformation, but ultimately completing a crucial step to her death."},
    [35] = {name = "Commitment Phobic", grade = 1, points = 2, description = "Longterm relationships are just not for you. And each time you think you're in love, you're proven wrong shortly afterwards. Or maybe you just end up with the wrong lover each time - exploited and betrayed. Staying single might just be better."},
    [36] = {name = "Confusion", grade = 1, points = 3, description = "The destruction you have caused by now can be felt throughout the whole hive. The mayhem that follows your step caused significant confusion in the consciousness of the hive."},
    [37] = {name = "Cookie Monster", grade = 1, points = 1, description = "You can easily be found by anyone if they just follow the cookie crumb trail. And for you, true love means to give away your last cookie."},
    [38] = {name = "Crawling Death", grade = 1, points = 1, description = "You ripped the ancient scarab Fleshcrawler apart and made sure he didn't get under your skin."},
    [39] = {name = "Crystal Clear", grade = 1, points = 3, description = "If the gnomes had told you that crystal armor is see-through you had probably changed your underwear in time."},
    [40] = {name = "Crystal Keeper", grade = 1, points = 1, description = "So you repaired the light of some crystals for those gnomes. What's next? Sitting a week in a mushroom bed as a temporary mushroom?"},
    [41] = {name = "Crystals in Love", grade = 1, points = 1, description = "You brought two loving crystals together. Perhaps they might even name one of their children after you. To bad you forgot to leave your calling card."},
    [42] = {name = "Cursed!", grade = 1, points = 3, description = "The wrath of the Noxious Spawn - you accidentally managed to incur it. Your days are counted and your death inevitable. Sometime. Someplace."},
    [43] = {name = "Daring Trespasser", grade = 1, points = 3, description = "You've entered the lair of Devovorga and joined the crew trying to take her down - whether crowned with success or not doesn't matter, but they can't blame you for not trying!"},
    [44] = {name = "Dark Voodoo Priest", grade = 1, points = 2, secret = true, description = "Sinister curses, evil magic - you don't shy away from punishing others by questionable means. Someone just gave you a strange look - now where's that needle again?"},
    [45] = {name = "Dazzler", grade = 1, points = 3, description = "In the war against the hive, your efforts in blinding it begin to pay off. Your actions have blinded the hive severely and the entity seems to become aware that something dangerous is happening."},
    [46] = {name = "Death from Below", grade = 1, points = 2, secret = true, description = "The face of the enemy is unmasked. You have encountered one of 'those below' and survived. More than that, you managed to kill the beast and prove once and for all that the enemy can be beaten."},
    [47] = {name = "Death Song", grade = 1, points = 3, description = "You hushed the songs of war in the black depths by sliencing more than three hundred Deepling Spellsingers."},
    [48] = {name = "Deer Hunt", grade = 1, points = 1, secret = true, description = "You managed to kill more than four hundred white deer - it looks like you are one of the main reasons they will soon be considered extinct, way to go!"},
    [49] = {name = "Demonic Barkeeper", grade = 1, points = 3, description = "Thick, red - shaken, not stirred - and with a straw in it: that's the way you prefer your demon blood. Served with an onion ring, the subtle metallic aftertaste is almost not noticeable. Beneficial effects on health or mana are welcome."},
    [50] = {name = "Depth Dwellers", grade = 1, points = 3, description = "By eliminating at least three hundred Deepling Warriors you delivered quite a blow to the amassing armies of the deep."},
    [51] = {name = "Desert Fisher", grade = 1, points = 1, description = "You managed to catch a fish in a surrounding that usually doesn't even carry water. Everything is subject to change, probably..."},
    [52] = {name = "Do Not Disturb", grade = 1, points = 1, description = "Urgh! Close the windows! Shut out the sun rearing its ugly yellow head, shut out the earsplitting laughter of your neighbour's corpulent children. Ahhh. Embrace sweet darkness and silence."},
    [53] = {name = "Doctor! Doctor!", grade = 1, points = 2, description = "Did someone call a doctor? You delivered 100 medicine bags to Ottokar of the Venore poor house in times of dire need, well done!"},
    [54] = {name = "Dog Sitter", grade = 1, points = 1, description = "You showed Noodles the way home. How long will it take this time until he's on the loose again? That dog must be really bored in the throne room by now."},
    [55] = {name = "Down the Drain", grade = 1, points = 2, description = "You've found a secret dungeon in the flooded plains and killed several of its inhabitants. And now you have wet feet."},
    [56] = {name = "Dream's Over", grade = 1, points = 1, description = "No more fear and bad dreams. You stabbed Tormentor to death with its scythe leg."},
    [57] = {name = "Dungeon Cleaner", grade = 1, points = 3, description = "Seen it all. Done it all. Your unstoppable force swept through the dungeons and you vanquished their masters. Not to forget the precious loot you took! Now stop reading this and continue hunting! Time is money after all!"},
    [58] = {name = "Efreet Ally", grade = 1, points = 3, description = "Even though the welcomed you only reluctantly and viewed you as \"only a human\" for quite some time, you managed to impress Malor and gained his respect and trade options with the green djinns."},
    [59] = {name = "Enter zze Draken!", grade = 1, points = 2, description = "You gave zzze draken a tazte of your finizzzing move."},
    [60] = {name = "Exquisite Taste", grade = 1, points = 2, description = "You love fish - but preferably those caught in the cold north. Even though they're hard to come by you never get tired of picking holes in ice sheets and hanging your fishing rod in."},
    [61] = {name = "Extreme Degustation", grade = 1, points = 2, description = "Almost all the plants you tested for Chartan in Zao where inedible - you tasted them all, yet you're still standing! You should really get some fresh air now, though."},
    [62] = {name = "Eye of the Deep", grade = 1, points = 1, description = "You didn't look into it - at least not for too long... but Groam did. And you relieved him. Just don't tell his friend Dronk."}
}

ACHIEVEMENT_FIRST = 1
ACHIEVEMENT_LAST = #achievements

function getAchievementInfoById(id)
    for k, v in pairs(achievements) do
        if k == id then
            local t = {}
            t.id = k
            t.actionStorage = ACHIEVEMENTS_ACTION_BASE + k
            for inf, it in pairs(v) do
                t[inf] = it
            end
            return t
        end
    end
    return false
end

function getAchievementInfoByName(name)
    for k, v in pairs(achievements) do
        if v.name:lower() == name:lower() then
            local t = {}
            t.id = k
            t.actionStorage = ACHIEVEMENTS_ACTION_BASE + k
            for inf, it in pairs(v) do
                t[inf] = it
            end
            return t
        end
    end
    return false
end

function getSecretAchievements()
    local t = {}
    for k, v in pairs(achievements) do
        if v.secret then
            t[#t + 1] = k
        end
    end
    return t
end

function getPublicAchievements()
    local t = {}
    for k, v in pairs(achivements) do
        if not v.secret then
            t[#t + 1] = k
        end
    end
    return t
end

function getAchievements()
    return achievements
end

function isAchievementSecret(ach)
    local achievement
    if isNumber(ach) then
        achievement = getAchievementInfoById(ach)
    else
        achievement = getAchievementInfoByName(ach)
    end
    if not achievement then return print("[!] -> Invalid achievement \"" .. ach .. "\".") and false end

    return achievement.secret
end

function Player.hasAchievement(self, ach)
    local achievement
    if isNumber(ach) then
        achievement = getAchievementInfoById(ach)
    else
        achievement = getAchievementInfoByName(ach)
    end
    if not achievement then return print("[!] -> Invalid achievement \"" .. ach .. "\".") and false end

    return self:getStorageValue(ACHIEVEMENTS_BASE + achievement.id) > 0
end

function Player.getAchievements(self)
    local t = {}
    for k = 1, #achievements do
        if self:hasAchievement(k) then
            t[#t + 1] = k
        end
    end
    return t
end

function Player.addAchievement(self, ach, denyMsg)
    local achievement
    if isNumber(ach) then
        achievement = getAchievementInfoById(ach)
    else
        achievement = getAchievementInfoByName(ach)
    end
    if not achievement then return print("[!] -> Invalid achievement \"" .. ach .. "\".") and false end

    if not self:hasAchievement(achievement.id) then
        self:setStorageValue(ACHIEVEMENTS_BASE + achievement.id, 1)
        if not denyMsg then
            self:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Congratulations! You earned the achievement \"" .. achievement.name .. "\".")
        end
    end
    return true
end

function Player.removeAchievement(self, ach)
    local achievement
    if isNumber(ach) then
        achievement = getAchievementInfoById(ach)
    else
        achievement = getAchievementInfoByName(ach)
    end
    if not achievement then return print("[!] -> Invalid achievement \"" .. ach .. "\".") and false end

    if self:hasAchievement(achievement.id) then
        self:setStorageValue(ACHIEVEMENTS_BASE + achievement.id, -1)
    end
    return true
end

function Player.addAllAchievements(self, denyMsg)
    for i = ACHIEVEMENT_FIRST, ACHIEVEMENT_LAST do
        self:addAchievement(i, denyMsg)
    end
    return true
end

function Player.removeAllAchievements(self)
    for k = 1, #achievements do
        if self:hasAchievement(k) then
            self:removeAchievement(k)
        end
    end
    return true
end

function Player.getSecretAchievements(self)
    local t = {}
    for k, v in pairs(achievements) do
        if self:hasAchievement(k) and v.secret then
            t[#t + 1] = k
        end
    end
    return t
end

function Player.getPublicAchievements(self)
    local t = {}
    for k, v in pairs(achievements) do
        if self:hasAchievement(k) and not v.secret then
            t[#t + 1] = k
        end
    end
    return t
end

function Player.getAchievementPoints(self)
    local points = 0
    local list = self:getAchievements()
    if #list > 0 then --has achievements
        for i = 1, #list do
            local a = getAchievementInfoById(list)
            if a.points > 0 then --avoid achievements with unknow points
                points = points + a.points
            end
        end
    end
    return points
end

function Player.addAchievementProgress(self, ach, value)
    local achievement = isNumber(ach) and getAchievementInfoById(ach) or getAchievementInfoByName(ach)
    if not achievement then
        print('[!] -> Invalid achievement "' .. ach .. '".')
        return true
    end

    local storage = ACHIEVEMENTS_ACTION_BASE + achievement.id
    local progress = self:getStorageValue(storage)
    if progress < value then
        self:setStorageValue(storage, math.max(1, progress) + 1)
    elseif progress == value then
        self:setStorageValue(storage, value + 1)
        self:addAchievement(achievement.id)
    end
    return true
end

There is no error in the distro.
 
Solution
So either something is wrong with "player" you calling in that second script, or his storage 300069 and 300012 are already 1 or higher. Have you tried changing the name "Razing!" for 69 and see if it works with id instead of name?
If you are using this lib there is never an achievement with the name "Razing!", nor is there "A Total Nightmare". You need to add them to the list before you can give them to a player.
 
check if you are not using these storage numbers somewhere else
I have already done this search, unfortunately there is no conflict in numbering.
If you are using this lib there is never an achievement with the name "Razing!", nor is there "A Total Nightmare". You need to add them to the list before you can give them to a player.
To accept the limit of 25k letters I removed about 400 Achievements from this code to post.
 
Then post the 2 lines, that contain the achievements and maybe the script, that wants to add the achievement. Maybe there is something wrong with "player".
 
That way I realized that it worked for this movement: creature:addAchievementProgress("Safely Stored Away", 100)

Lua:
if Tile(position):hasFlag(TILESTATE_PROTECTIONZONE) then
        local lookPosition = creature:getPosition()
        lookPosition:getNextPosition(creature:getDirection())
        local depotItem = Tile(lookPosition):getItemByType(ITEM_TYPE_DEPOT)
        if depotItem then
            local depotItems = creature:getDepotChest(getDepotId(depotItem:getUniqueId()), true):getItemHoldingCount()
            creature:sendTextMessage(MESSAGE_STATUS_DEFAULT, "Your depot contains " .. depotItems .. " item" .. (depotItems > 1 and "s." or "."))
            creature:addAchievementProgress("Safely Stored Away", 100)
            return true
        end
    end
But in this other case I don't get it.

Lua:
if player:removeItem(9020, 50) then
                npcHandler:say("Ye' brought the fifty tokens needed to advance to the last vampire hunter rank. Now that's something. You're razing-amazing! Let me share some of my experience and a little something with ye'!", cid)
                player:setStorageValue(Storage.VampireHunter.Rank, 6)
                player:addItem(9019, 1)
                player:addExperience(100 * 1000, true)
                player:addAchievement("Castlemania")
                player:addAchievement("Razing!")                   
            else
                npcHandler:say("Ye' don't have enought tokens.", cid)
            end

Lib lines:
Lua:
[12] = {name = "Castlemania", grade = 2, points = 5, secret = true, description = "You have an eye for suspicious places and love to read other people's diaries, especially those with vampire stories in it. You're also a dedicated token collector and explorer. Respect!"},
[69] = {name = "Razing!", grade = 3, points = 7, secret = true, description = "People with sharp canine teeth better beware of you, especially at nighttime, or they might find a stake between their ribs. You're a merciless vampire hunter and have gathered numerous tokens as proof."},
[193] = {name = "Safely Stored Away", grade = 1, points = 2, secret = true, description = "Don't worry, no one will be able to take it from you. Probably."},
 
So either something is wrong with "player" you calling in that second script, or his storage 300069 and 300012 are already 1 or higher. Have you tried changing the name "Razing!" for 69 and see if it works with id instead of name?
 
Solution
So either something is wrong with "player" you calling in that second script, or his storage 300069 and 300012 are already 1 or higher. Have you tried changing the name "Razing!" for 69 and see if it works with id instead of name?
I checked it before, and the storages are set with -1.

Using the numeric value worked. Pretty weird. But anyway, the important thing is to work haha.

Thanks.
 
Yeah, better work strange than not at all. But you can check if it works, when you copy the text from the lib and paste it into the script. Also check that both files have the same coding on UTF-8 or ANSI or whateever.
 
Back
Top