• 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+ Check what the player wrote [NPC]

Svira

Banned User
Joined
Jan 27, 2008
Messages
361
Solutions
13
Reaction score
105
Hello, I'm trying to make an NPC Coder, an additional attraction and a chance for cash, which is not enough in my case.

THE NPC SHOULD ACT LIKE THIS:

Code:
Starting the Challenge: Players can start the challenge by typing "start" to the NPC. The NPC will then generate a secret word consisting of 4 letters (randomly selected from a prepared list of words). The player gets 10 attempts to guess the word.

Guess the Word: The player submits their guess, which is a string of 4 letters. The NPC checks:

Correctly guessed letters in the correct position will be displayed as "Letter X is in the correct position."
Correctly guessed letters but in the wrong position will be displayed as "Letter X is in the word but in the wrong position."
Incorrectly guessed letters will be displayed as "Letter X is not in the word."
The player has 10 attempts to guess the entire word. After each attempt, the NPC will inform the player of the number of remaining attempts.

Rewards and Chances to Win:

For 1-4 attempts (Fast Win): The player will receive between 10 and 150 Crystal Coins.
For 5-10 attempts (Average Win): The player will receive between 1 and 5 Crystal Coins.
Limits:

The player can only take on the challenge once every 24 hours. After this time, they can come back and try again.
If the player guesses the word within 10 attempts, the challenge ends, and the player will not receive any additional attempts.
Challenge Reset: After guessing the word, the player is rewarded, and the NPC sets a 24-hour cooldown period before the challenge becomes available again.
However, I encountered problems:
The NPC is not detectable/compares what the player wrote regarding the word.

e.g. the word COLD was selected.
Dialog:

Code:
13:58 God [7]: hi
13:58 Coder: Hello, adventurer! I have a challenge for you. Type 'start' to begin and I will explain the rules.
13:58 God [7]: start
13:58 Coder: Challenge started! You have 10 attempts to decipher my 4-letter word. Say your guess!
13:58 God [7]: COOL
13:58 Coder: Your guess: COOL -> Letter C is not in the word. Letter O is not in the word. Letter O is not in the word. Letter L is not in the word. (Attempt 1/10)
13:58 God [7]: COLD
13:58 Coder: Your guess: COLD -> Letter C is not in the word. Letter O is not in the word. Letter L is not in the word. Letter D is not in the word. (Attempt 2/10)
13:58 God [7]: cold
13:58 Coder: Your guess: COLD -> Letter C is not in the word. Letter O is not in the word. Letter L is not in the word. Letter D is not in the word. (Attempt 3/10)

How can I build these mechanics correctly in Lua because the console doesn't give me any errors?

NPC CODE:

LUA:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

function onCreatureAppear(cid)            npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)         npcHandler:onCreatureDisappear(cid)         end
function onCreatureSay(cid, type, msg)    npcHandler:onCreatureSay(cid, type, msg)    end
function onThink()                        npcHandler:onThink()                        end

local storageBase = 689633114 -- Unique storage ID for NPC
local words = {
    "FIRE", "CAVE", "KEYS", "RUNE", "TIME", "MOVE", "STAR", "BIRD", "WIND", "RAIN",
    "GAME", "ROSE", "HERO", "BLUE", "GOLD", "SOUL", "IRON", "BONE", "MIND", "DARK",
    "LIGHT", "COLD", "HEAL", "ROAD", "TREE", "FALL", "JUMP", "FORK", "HAND", "SING",
    "WAVE", "BASS", "CLUB", "LION", "MOON", "PEAR", "BOLT", "SLOW", "BARK", "VINE",
    "SHIP", "TIDE", "MELT", "GLOW", "BURN", "HARM", "MARK", "SOFT", "BUZZ", "TRAP"
}
local maxAttempts = 10
local rewardTier1 = {id = 2160, min = 10, max = 150} -- Reward for ≤4 attempts
local rewardTier2 = {id = 2160, min = 1, max = 5} -- Reward for ≤10 attempts
local cooldownTime = 86400 -- 24h in seconds

-- Set greeting and farewell messages
npcHandler:setMessage(MESSAGE_GREET, "Hello, adventurer! I have a challenge for you. Type 'start' to begin and I will explain the rules.")
npcHandler:setMessage(MESSAGE_FAREWELL, "Goodbye! Come back tomorrow for a new challenge.")

local function generateSecretWord(player)
    local secretWord = words[math.random(#words)]
    player:setStorageValue(storageBase + 1, secretWord)
    player:setStorageValue(storageBase + 2, 0) -- Reset attempt count
    return secretWord
end

local function checkGuess(player, guess)
    local secretWord = player:getStorageValue(storageBase + 1)
    if not secretWord or secretWord == -1 then
        return "You don't have an active challenge. Say 'start' to begin!"
    end

    secretWord = tostring(secretWord) -- Ensure secretWord is a string

    local attempts = player:getStorageValue(storageBase + 2)
    if attempts >= maxAttempts then
        player:setStorageValue(storageBase + 1, -1) -- Reset word
        return "You have used all 10 attempts! The word was '" .. secretWord .. "'. Come back tomorrow for a new challenge."
    end

    local response = {}
    local correctCount = 0

    -- Convert secret word to a table of letters
    local secretLetters = {}
    for i = 1, #secretWord do
        secretLetters[i] = secretWord:sub(i, i)
    end

    -- Check each letter in the guess
    for i = 1, #guess do
        local guessLetter = guess:sub(i, i)
        local secretLetter = secretLetters[i]

        if guessLetter == secretLetter then
            response[i] = "Letter " .. guessLetter .. " is in the correct position."
            correctCount = correctCount + 1
            secretLetters[i] = nil -- Remove the matched letter
        elseif table.concat(secretLetters):find(guessLetter) then
            response[i] = "Letter " .. guessLetter .. " is in the word but in the wrong position."
        else
            response[i] = "Letter " .. guessLetter .. " is not in the word."
        end
    end

    -- Update attempt count
    attempts = attempts + 1
    player:setStorageValue(storageBase + 2, attempts)

    -- If guessed correctly
    if correctCount == #secretWord then
        local reward
        if attempts < 5 then
            reward = rewardTier1
        else
            reward = rewardTier2
        end

        local amount = math.random(reward.min, reward.max)
        player:addItem(reward.id, amount)

        player:setStorageValue(storageBase, os.time() + cooldownTime) -- 24h cooldown
        player:setStorageValue(storageBase + 1, -1) -- Reset word

        return "Congratulations! You deciphered the word '" .. secretWord .. "' in " .. attempts .. " attempts and receive " .. amount .. " Crystal Coins!"
    end

    return "Your guess: " .. guess .. " -> " .. table.concat(response, " ") .. " (Attempt " .. attempts .. "/10)"
end

local function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    end

    local player = Player(cid)
    if not player then
        return false
    end

    local playerStorage = player:getStorageValue(storageBase)

    if msgcontains(msg, "start") then
        if playerStorage > os.time() then
            npcHandler:say("You have already tried today! Come back in " .. math.ceil((playerStorage - os.time()) / 3600) .. " hours.", cid)
            return true
        end

        local secretWord = generateSecretWord(player)
        npcHandler:say("Challenge started! You have 10 attempts to decipher my 4-letter word. Say your guess!", cid)
        print("The secret word is: " .. secretWord) -- Print the secret word to the console
        return true
    end

    if player:getStorageValue(storageBase + 1) == -1 then
        npcHandler:say("You don't have an active challenge. Say 'start' to begin!", cid)
        return true
    end

    if #msg ~= 4 then
        npcHandler:say("Your guess must be exactly 4 letters!", cid)
        return true
    end

    local result = checkGuess(player, msg:upper())
    npcHandler:say(result, cid)
    return true
end

npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:addModule(FocusModule:new())
 
Solution
I took the default TFS script and organized all its functions properly, including the dialogue. I tested it, and it works perfectly.

LUA:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

function onCreatureAppear(cid)            npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)         npcHandler:onCreatureDisappear(cid)         end
function onCreatureSay(cid, type, msg)    npcHandler:onCreatureSay(cid, type, msg)    end
function onThink()                        npcHandler:onThink()                        end

-- Storage configuration
local storageBase = 689633114 -- Unique storage ID for NPC
local STORAGE_WORD =...
I took the default TFS script and organized all its functions properly, including the dialogue. I tested it, and it works perfectly.

LUA:
local keywordHandler = KeywordHandler:new()
local npcHandler = NpcHandler:new(keywordHandler)
NpcSystem.parseParameters(npcHandler)

function onCreatureAppear(cid)            npcHandler:onCreatureAppear(cid)            end
function onCreatureDisappear(cid)         npcHandler:onCreatureDisappear(cid)         end
function onCreatureSay(cid, type, msg)    npcHandler:onCreatureSay(cid, type, msg)    end
function onThink()                        npcHandler:onThink()                        end

-- Storage configuration
local storageBase = 689633114 -- Unique storage ID for NPC
local STORAGE_WORD = storageBase + 1
local STORAGE_ATTEMPTS = storageBase + 2
local STORAGE_COOLDOWN = storageBase + 3

local words = {
    "FIRE", "CAVE", "KEYS", "RUNE", "TIME", "MOVE", "STAR", "BIRD", "WIND", "RAIN",
    "GAME", "ROSE", "HERO", "BLUE", "GOLD", "SOUL", "IRON", "BONE", "MIND", "DARK",
    "LIGHT", "COLD", "HEAL", "ROAD", "TREE", "FALL", "JUMP", "FORK", "HAND", "SING",
    "WAVE", "BASS", "CLUB", "LION", "MOON", "PEAR", "BOLT", "SLOW", "BARK", "VINE",
    "SHIP", "TIDE", "MELT", "GLOW", "BURN", "HARM", "MARK", "SOFT", "BUZZ", "TRAP"
}

local maxAttempts = 10
local rewardTier1 = {id = 2160, min = 10, max = 150} -- Reward for ≤4 attempts
local rewardTier2 = {id = 2160, min = 1, max = 5} -- Reward for ≤10 attempts
local cooldownTime = 86400 -- 24h in seconds

-- Convert word to a numeric string for storage
local function encodeWord(word)
    local encoded = 0
    for i = 1, #word do
        encoded = encoded * 256 + string.byte(word:sub(i,i))
    end
    return encoded
end

-- Convert numeric string back to word
local function decodeWord(encoded)
    if not encoded or encoded == -1 then return nil end
    
    local word = ""
    encoded = tonumber(encoded)
    while encoded > 0 do
        local char = string.char(encoded % 256)
        word = char .. word
        encoded = math.floor(encoded / 256)
    end
    return word
end

local function generateSecretWord(player)
    local secretWord = words[math.random(#words)]
    local encodedWord = encodeWord(secretWord)
    
    player:setStorageValue(STORAGE_WORD, encodedWord)
    player:setStorageValue(STORAGE_ATTEMPTS, 0) -- Reset attempt count
    
    return secretWord
end

local function checkGuess(player, guess)
    local encodedWord = player:getStorageValue(STORAGE_WORD)
    local secretWord = decodeWord(encodedWord)
    
    if not secretWord then
        return "You don't have an active challenge. Say 'start' to begin!"
    end

    guess = guess:upper()
    secretWord = secretWord:upper()
    
    local attempts = player:getStorageValue(STORAGE_ATTEMPTS)
    if attempts >= maxAttempts then
        player:setStorageValue(STORAGE_WORD, -1)
        return "You have used all 10 attempts! The word was '" .. secretWord .. "'. Come back tomorrow for a new challenge."
    end

    local secretLetters = {}
    local usedPositions = {}
    local response = {}
    local correctCount = 0

    for i = 1, 4 do
        secretLetters[i] = secretWord:sub(i,i)
    end

    for i = 1, 4 do
        local guessLetter = guess:sub(i,i)
        if guessLetter == secretLetters[i] then
            response[i] = "Letter " .. guessLetter .. " is in the correct position."
            usedPositions[i] = true
            secretLetters[i] = nil
            correctCount = correctCount + 1
        end
    end

    -- Second pass: Check for letters in wrong positions
    for i = 1, 4 do
        if not usedPositions[i] then
            local guessLetter = guess:sub(i,i)
            local found = false
            
            for j = 1, 4 do
                if secretLetters[j] and guessLetter == secretLetters[j] then
                    response[i] = "Letter " .. guessLetter .. " is in the word but in the wrong position."
                    secretLetters[j] = nil
                    found = true
                    break
                end
            end
            
            if not found then
                response[i] = "Letter " .. guessLetter .. " is not in the word."
            end
        end
    end

   -- Update attempt count
    attempts = attempts + 1
    player:setStorageValue(STORAGE_ATTEMPTS, attempts)

    -- If guessed correctly
    if correctCount == 4 then
        local reward = (attempts <= 4) and rewardTier1 or rewardTier2
        local amount = math.random(reward.min, reward.max)
        player:addItem(reward.id, amount)
        
        player:setStorageValue(STORAGE_COOLDOWN, os.time() + cooldownTime)
        player:setStorageValue(STORAGE_WORD, -1)
        
        return "Congratulations! You deciphered the word '" .. secretWord .. "' in " .. attempts .. " attempts and receive " .. amount .. " Crystal Coins!"
    end

    return "Your guess: " .. guess .. " -> " .. table.concat(response, " ") .. " (Attempt " .. attempts .. "/10)"
end

local function greetCallback(cid)
    local player = Player(cid)
    if player then
        npcHandler:setMessage(MESSAGE_GREET, "Hello, adventurer! I have a challenge for you. Type 'start' to begin and I will explain the rules.")
    end
    return true
end

local function creatureSayCallback(cid, type, msg)
    if not npcHandler:isFocused(cid) then
        return false
    end

    local player = Player(cid)
    if not player then
        return false
    end

    local cooldown = player:getStorageValue(STORAGE_COOLDOWN)

    if msgcontains(msg, "start") then
        if cooldown > os.time() then
            npcHandler:say("You have already tried today! Come back in " .. math.ceil((cooldown - os.time()) / 3600) .. " hours.", cid)
            return true
        end

        local secretWord = generateSecretWord(player)
        npcHandler:say("Challenge started! You have 10 attempts to decipher my 4-letter word. Say your guess!", cid)
        print("The secret word is: " .. secretWord) -- Print the secret word to the console
        return true
    end

    if #msg == 4 then
        local result = checkGuess(player, msg:upper())
        npcHandler:say(result, cid)
        return true
    elseif msg:lower() == "bye" or msg:lower() == "goodbye" then
        npcHandler:say("Goodbye! Come back tomorrow for a new challenge.", cid)
        npcHandler:releaseFocus(cid)
        return true
    else
        if player:getStorageValue(STORAGE_WORD) ~= -1 then
            npcHandler:say("Your guess must be exactly 4 letters!", cid)
        end
        return true
    end
end

npcHandler:setCallback(CALLBACK_GREET, greetCallback)
npcHandler:setCallback(CALLBACK_MESSAGE_DEFAULT, creatureSayCallback)
npcHandler:setMessage(MESSAGE_FAREWELL, "Goodbye! Come back tomorrow for a new challenge.")
npcHandler:addModule(FocusModule:new())
 
Solution

Similar threads

Replies
0
Views
147
Back
Top