lualove2d

Issue when executing a function from a class


I'm running into some confusion in trying to make a button that executes a function from another class I have.

Firstly, i have a "button" class that i made, in which one of the parameters is a function that can be done when the button is clicked.

The first button i made was simple, it would give you 1 coin when pressed by calling a function called addcoins:

function addcoins()
   coins = coins + 1
end

super simple, but the function only worked when i put it in the parameters without the parentheses (the only thing that matters here is where i have addcoins, all other things are working fine)

bigButton = Button.new("circle", x_mid, y_mid, 100, 100, addcoins, nil, "button :D")

otherwise it would refuse to function. however, now I'm making a new button that calls a function from another class. This function a) increases the "level" of a "soul" and increases the price for the next level

function Soul:buy()
    if self.lvl + 1 <= 100 then
        self.lvl = self.lvl + 1
        self.price = self.price * 1.01
    end
end

so, i try to call the function the same way that I did the previous one with

wheat.button = Button.new("rectangle", 0, 15, 100, 30, wheat:buy, self, 100)

however, this doesn't work! It gives me an error saying that function parameters are expected. When i try to add the parentheses like so

wheat.button = Button.new("rectangle", 0, 15, 100, 30, wheat:buy(), self, 100)

I avoid the error message, but the function seems to get called exactly once when i start the program and the button still doesn't work, neither increasing the level or price.

I'm still quite new to Lua and very inexperienced in coding in general and especially classes, so I worry I may be missing something thats making this not work. What can I do to fix it and make the button work?

Here's all the relevant code. I'm sorry there's a lot, but I'm not sure where it's going wrong.

local love = require "love"
local constants = require "constants"

local Button = {}
Button.__index = Button
function Button.new(shape, x, y, width, height, func, func_param, text)
    local self = setmetatable({}, Button)
    self.shape = shape 
    self.x = x
    self.y = y
    self.width = width
    self.height = height
    self.func = func or function () end
    self.func_param = func_param or nil
    self.text = text or ''
    if self.shape == "circle" then
        self.x_min = self.x - self.width
        self.x_max = self.x + self.width
        self.y_min = self.y - self.width
        self.y_max = self.y + self.width
    else
        self.x_min = self.x
        self.x_max = self.x + self.width
        self.y_min = self.y
        self.y_max = self.y + self.height
    end
    return self
end
function Button:clicked(mouse_x, mouse_y)
    if mouse_x > self.x_min and
    mouse_x < self.x_max and
    mouse_y > self.y_min and
    mouse_y < self.y_max then 
        if self.func_param then 
            self.func(func_param) 
        else 
            self.func()
        end
    end
end
function Button:draw()
    if self.func_con then 
        love.graphics.setColor(255,255,255)
    else
        love.graphics.setColor(200,200,200)
    end
    if self.shape == "circle" then
        love.graphics.circle ("fill", self.x, self.y, self.height)
    else
        love.graphics.rectangle ("fill", self.x, self.y, self.width, self.height)
    end
    love.graphics.setColor(0,0,0)
    if self.shape == "circle" then
        love.graphics.printf (self.text, self.x_min, self.y-7.5, self.width*2, "center")
    else
        love.graphics.printf (self.text, self.x_min, (self.y_max + self.y_min)/2-7.5, self.width, "center")
    end
end

local Soul = {}
Soul.__index = Soul
function Soul.new(Name, Price)
    local self = setmetatable({}, Soul)
    self.name = Name
    self.lvl = 1
    self.price = price
    return self
end
function Soul:buy()
    if self.lvl + 1 <= 100 then
        self.lvl = self.lvl + 1
        self.price = self.price * (1.01)
    end
end
  
local x_size, y_size = love.graphics.getDimensions()
local x_mid = x_size/2
local y_mid = y_size/2
local coins = 0
function addcoins()
    coins = coins + 1
end
 
function love.load()
    bigButton = Button.new("circle", x_mid, y_mid, 100, 100, addcoins, "button :D")
    wheat = Soul.new(wheat, 100, 100, 100)
    wheat.button = Button.new("rectangle", 0, 15, 100, 30, wheat:buy, 100)
end
 
function love.update()
end
 
function love.mousepressed(x, y)
    bigButton:clicked(x, y)
    wheat.button:clicked(x,y)
end
 
function love.draw()
    bigButton:draw()
    wheat.button:draw()
    love.graphics.setColor(255,255,255)
    love.graphics.print(coins,0,0)
    love.graphics.print(wheat.lvl,0,45)
end 

Full code here -> https://pastebin.com/0qqYyM5u


Solution

  • The trouble is that wheat:buy is actually a call due to the : syntax, shorthand for wheat.buy(wheat), unlike a plain top-level function like addcoins.

    You can wrap wheat:buy in an anonymous function to work around this, since the anonymous function won't be invoked instantly:

    wheat.button = Button.new(
        "rectangle", 0, 15, 100, 30, function() wheat:buy() end, 100
    )
    

    Also, function Soul.new(Name, Price) should be function Soul.new(Name, price). Convention is to lowercase variable names (snake_case or camelCase), but the change suggested here at least gets the program running.