• 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+ Some scripting questions (mainly creaturescripts)

Nostalgian

Member
Joined
Mar 17, 2018
Messages
66
Reaction score
15
Hey, its me again.... hung up with yet another problem I can't seem to figure out. :D

I just discovered an issue with my smelting system. I am using opcodes to communicate back and forth between Tfs 1.2 > Otclient

This code works exactly as intended...... except for the fact that it only works for one character a time, and I have no idea why.

So here is the code:

The client sends opcode 52 to my server, and then this receives it:
Code:
function onExtendedOpcode(player, opcode, buffer)
local player = Player(cid)
    if opcode == 52 then
--        print(buffer)
    
local choice = tonumber(buffer)
    
    if choice == 1 then
        Player:smeltingBronze()
    end
end
All well and good here.

and then once it receives it, here is the function that is being performed:
Code:
function Player.smeltingBronze(self)
 local exhaust = Condition(CONDITION_EXHAUST_WEAPON)
 local STORAGE_SKILL_LEVEL = 20038
 local STORAGE_SKILL_TRY = 20039
 local STORAGE_smithingMASTERY = 20040
 
 for _, player in ipairs(Game.getPlayers()) do
     if player:getCondition(CONDITION_EXHAUST_WEAPON) then
       return player:sendTextMessage(MESSAGE_STATUS_SMALL, Game.getReturnMessage(RETURNVALUE_YOUAREEXHAUSTED))
    end
    
    local skillLevel = player:getStorageValue(STORAGE_SKILL_LEVEL)
    local skillTry = player:getStorageValue(STORAGE_SKILL_TRY)
    local smithingMastery = player:getStorageValue(STORAGE_smithingMASTERY)
    
    
    local quant = configs.experience[skillLevel][1]
 
    if not quant then return false end
    player:addCondition(exhaust)

      
    if player:getItemCount(3266) < 1 then
        player:sendCancelMessage("You need a copper ore.")
    return true
    end   
    
    if player:getItemCount(3269) < 1 then
        player:sendCancelMessage("You need a tin ore.")
    return true
    end   
    

    if (skillLevel) >= 1 then
        player:removeItem(3266, 1)
        player:removeItem(3269, 1)
        doPlayerAddItem(player,3249,1)
        player:sendTextMessage(MESSAGE_STATUS_CONSOLE_BLUE,"You smelted a bronze bar.")
        player:say("+6 Smithing Exp", TALKTYPE_MONSTER_SAY)
        player:sendTextMessage(MESSAGE_EXPERIENCE,"You received 6 smithing exp by smelting a bronze bar.")   
        if skillLevel >= 1 and skillLevel <= 99 and skillTry >= (quant-6) then   
            setPlayerStorageValue(player,STORAGE_SKILL_TRY,skillTry + 6)   
            setPlayerStorageValue(player,STORAGE_SKILL_LEVEL,skillLevel + 1)
            player:sendSmithingLevel()
            player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "You have advanced from Smithing Level ".. skillLevel .." to Smithing Level ".. skillLevel + 1 .."")
            player:getPosition():sendMagicEffect(CONST_ME_FIREWORK_YELLOW)
        else
            setPlayerStorageValue(player,STORAGE_SKILL_TRY,skillTry + 6)
            player:sendSmithingTries()
        return true   
        end
        end
        
        if skillTry >= (expmax-6) and smithingMastery == 0 then
        broadcastMessage("[SYSTEM] "..player:getName().." has just advanced to Smithing Level 100.", MESSAGE_STATUS_WARNING)
        player:sendWoodcuttingLevel()
        player:sendTextMessage(MESSAGE_EVENT_ADVANCE, "Congratulations! You earned the achievement 'Smithing Mastery'.")
        player:setStorageValue(STORAGE_smithingMASTERY, 1)
    return true
    end
end
end
I placed the above function into a seperate lua file inside my lib folder. I am suspecting that this is the reason why I am having the problem that I am having, but I am not sure the solution.

This function executes exactly as intended. The problem is that it will only work for one character at a time. What happens is, I log into one character, use a "forge" and can create a bronze bar. Once I do that, i can't do it with another character until the first one logs off.

If I use it on one character, and keep him logged in, then there is no functionality with additional characters until the first one is logged off.

What I have tried so far:

  • Moving the function inside my opcode file instead of calling the function from there. (It works exactly the same, same problem)
  • Removing
    Code:
     for _, player in ipairs(Game.getPlayers()) do
    and just using
    Code:
    local player = Player(cid)
    This just returns an error of player being a nil value(But its not a nil value, right? it = Player(cid)?!). I have used this method for all of my action scripts, and they work flawlessly.... but it doesn't work with creaturescripts for some reason?


Gahhhh. I'm confused and I've ran out of google.

Thank you in advance for your help :D
 
Solution
Ok, I've tried every possible combination I could think of.

In my mind, it seems like if I declare a variable:
Code:
player = Player(cid)
Why does the rest of my code not recognize that? Why does it give me that player is nil?

How do I declare that the variable "player" is not in fact nil, but references the character that is logged in?
Because cid is not declared within the scope of that function, so you're calling Player(nil), which returns nil. Then that returned nil value gets reassigned to the existing player object (which is already valid and you have access to it, no reassignment is needed) so it's making player a nil value for the rest of the script.
local player = Player(cid)
unnecessary line

Player:smeltingBronze()

player:smeltingBronze() -- you want to call specific player, not the class

function Player.smeltingBronze(self)

function Player:smeltingBronze() -- correct way to build that function
^ when function looks like that, the player variable is in "self" so you should be calling functions like that:
self:addItem(...), self:teleportTo(...), etc.



now when we write Player, player, cid and self:
  • Player is a class (like wardrobe), player and self are objects (like clothes). You can access the objects through constructors Player("name") and Player(cid) or function parameters (see below)
  • player is an object of Player class. If your function has "player" as a parameter, you can do player:something(), however if your function looks like this:
function Player:doSomething()
-- code
end
  • the player object is "self" here, similar to "this" in C++ or Java so you call self:something() instead of player:something()
  • cid was a short of "creature id" in older distributions. Currently it doesn't occur naturally, but if you have reasons to obtain it (like addEvent because it's unstable when it gets objects as parameters), you can call player:getId() to obtain the "cid"
 
Ok, so sticking the function in my lib folder to access it is ok then?

upon some research, i believe that
Code:
for _, player in ipairs(Game.getPlayers()) do
is the culprit of having only one character being able to use the script at a time?

The problem I'm having is that using that is the only way i can figure out how to make my script work. If I make it like my action scripts I just get the nil value errors.

If I use player:whatever, I get
Code:
attempt to index local 'player' (a nil value)
If I use self:whatever, I get
Code:
attempt to index local 'self' (a nil value)

How can I get rid of the ipairs method and make it so that "player" only applies to that person, like in an action script? Everything I try throws those nil value errors.

Maybe I have to restructure completely how I do this in creaturescripts? The function I made is how I made it in my actions with other scripts, and they all work great xD. I feel like I'm not understanding something lol
 
Ok, I've tried every possible combination I could think of.

In my mind, it seems like if I declare a variable:
Code:
player = Player(cid)
Why does the rest of my code not recognize that? Why does it give me that player is nil?

How do I declare that the variable "player" is not in fact nil, but references the character that is logged in?
 
Ok, I've tried every possible combination I could think of.

In my mind, it seems like if I declare a variable:
Code:
player = Player(cid)
Why does the rest of my code not recognize that? Why does it give me that player is nil?

How do I declare that the variable "player" is not in fact nil, but references the character that is logged in?
Because cid is not declared within the scope of that function, so you're calling Player(nil), which returns nil. Then that returned nil value gets reassigned to the existing player object (which is already valid and you have access to it, no reassignment is needed) so it's making player a nil value for the rest of the script.
 
Solution
Thank you. I'm not sure why I was making this so complicated. Basically, instead of just putting all of my functionality into my receive opcode function, i was trying to create my own function in a different file and call it.

Just using the code in one file has(I think), solved this problem.
 
Back
Top