This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Gathoc's profile picture

[Lua][Error] Attempt to index ? (a nil value)

Started by Gathoc, 19 January 2013 - 05:18 AM
Gathoc #1
Posted 19 January 2013 - 06:18 AM
Im getting an error at line 52 and i suppose it goes for most of the variables i create in the table.


–[[ Local Variables ]]–

local termWidth, termHeight = term.getSize()
local inMainMenu = true
local inLightsMenu = false

–[ Menu Methods ]]–

function Choice1()
term.clear()
term.setCursorPos(1,1)
print("hello!")
sleep(2)
end

function Choice2()
inLightsMenu = true
selectedItem = 1

while inLightsMenu do
term.clear()
term.setCursorPos(1,1)
printMenu(lightsMenu)

event, key = os.pullEvent("key")
mouseClick( key, lightsMenu )
end
end

function LightsOn()
print("lights online")
sleep(2)
inLightsMenu = false
end
function LightsOff()
print("lights offline")
sleep(2)
inLightsMenu = false

end

function Exit()
inMainMenu = false
end

–[[ Menu Definitions ]]–

mainMenu = {
[1] = {
text = "Choice 1",
buttonType = 1,
optionStartX = termWidth-math.floor(string.len(mainMenu[1].text))/2,
optionEndX = termWidth/2+math.floor(string.len(mainMenu[1].text/2)),
optionStartY = math.floor(termHeight/2)-1,
optionEndY = math.floor(termHeight/2)-1,
handler = Choice1
},
[2] = {
text = "Choice 2",
buttonType = 1,
optionStartX = termWidth/2-math.floor(string.len(mainMenu[2].text/2)),
optionEndX = termWidth/2+math.floor(string.len(mainMenu[2].text/2)),
optionStartY = math.floor(termHeight/2),
optionEndY = math.floor(termHeight/2),
handler = Choice2
},
[3] = {
text = "Exit",
buttonType = 1,
optionStartX = termWidth/2-math.floor(string.len(mainMenu[3].text/2)),
optionEndX = termWidth/2+math.floor(string.len(mainMenu[3].text/2)),
optionStartY = math.floor(termHeight/2+1),
optionEndY = math.floor(termHeight/2+1),
handler = Exit
}
}

lightsMenu = {
[1] = {text = "Lights On", handler = LightsOn },
[2] = {text = "Lights Off", handler = LightsOff }
}

–[[ Printing Methods ]]–

function printMenu( mainMenu )
term.clear()
term.setCursorPos(1,1)
for k,v in ipairs(mainMenu) do
term.setCursorPos(mainMenu[k].optionStartX, mainMenu[k].optionStartY)
print(mainMenu[k].text)
end
end

–[[ Handler Method ]]–

function mouseClick(mainMenu)
event, buttonType, x, y = os.pullEvent("mouse_click")
for k,v in ipairs(mainMenu) do
if buttonType == mainMenu[k].buttonType then
if x >= mainMenu[k].optionStartX and x <= mainMenu[k].optionEndX and y >= mainMenu[k].optionStartY and y <= mainMenu[k].optionEndY then
mainMenu[k].handler()
end
end
end
term.clear()
term.setCursorPos(1,1)
end

–[[function mouseClick()
curX, curY = term.getCursorPos()

event, button, X, Y = os.pullEvent("mouse_click")
XY = X..","..Y
if button == 1 then
if (X => and X <= 6 and Y =>1 and Y =< 6) then
onItemSelected(menu)
end
end
end]]–

function onItemSelected( mainMenu )
mainMenu[selectedItem].handler()
end

–[[ Main Method ]]–

function main()
while inMainMenu do
term.clear()
term.setCursorPos(1,1)
printMenu(mainMenu)

event, key = os.pullEvent("key")
mouseClick(key,mainMenu)
end
end

main()
remiX #2
Posted 19 January 2013 - 06:32 AM
Look's like it finds it nil because you're trying to access a variable within the table that hasn't been defined yet. What you can do is this:


--[[ Menu Definitions ]]--
mainMenu = {}

mainMenu = {
	[1] = {
		text = "Choice 1",
		buttonType = 1,
		optionStartY = math.floor(termHeight/2)-1,
		optionEndY = math.floor(termHeight/2)-1,
		handler = Choice1
	},
	[2] = {
		text = "Choice 2",
		buttonType = 1,
		optionStartY = math.floor(termHeight/2),
		optionEndY = math.floor(termHeight/2),
		handler = Choice2
	},
	[3] = {
		text = "Exit",
		buttonType = 1,
		optionStartY = math.floor(termHeight/2+1),
		optionEndY = math.floor(termHeight/2+1),
		handler = Exit
	}
}

mainMenu[1].optionStartX = termWidth/2-math.floor(string.len(mainMenu[1].text)/2)
mainMenu[1].optionEndX = termWidth/2+math.floor(string.len(mainMenu[1].text)/2)
mainMenu[2].optionStartX = termWidth/2-math.floor(string.len(mainMenu[2].text)/2)
mainMenu[2].optionEndX = termWidth/2+math.floor(string.len(mainMenu[2].text)/2)
mainMenu[3].optionStartX = termWidth/2-math.floor(string.len(mainMenu[3].text)/2)
mainMenu[3].optionEndX = termWidth/2+math.floor(string.len(mainMenu[3].text)/2)

Also, when you're using for k, v in pairs(table) do, you're accessing the table wrong, the way you're doing it as if you're looping through it with:

for k = 1, #mainMenu do
--code etc
end

with pairs it's like this:


for k,v in ipairs(mainMenu) do
	term.setCursorPos(v.optionStartX, v.optionStartY)
	print(v.text)
end

EDIT: Instead of using handlers, which isn't bad, another way is to return the option that was selected from the menu.

Try this for instance.

Spoiler

--[[ Local Variables ]]--

local termWidth, termHeight = term.getSize()
local inMainMenu = true
local inLightsMenu = false
local bLightsStatus = false -- off, true is on

--[[ Menu Definitions ]]--

-- Menu Tables

mainMenu = {}

mainMenu = {
	[1] = {
		text = "Choice 1",
		buttonType = 1,
		xPos = math.floor((termWidth - string.len("Choice 1"))/2),
		yPos = math.floor(termHeight/2)-1,
	},
	[2] = {
		text = "Lights",
		buttonType = 1,
		xPos = math.floor((termWidth - string.len("Lights"))/2),
		yPos = math.floor(termHeight/2),
	},
	[3] = {
		text = "Exit",
		buttonType = 1,
		xPos = math.floor((termWidth - string.len("Exit"))/2),
		yPos = math.floor(termHeight/2)+1,
	}
}

lightsMenu = {
	[1] = {
		text = "Lights On",
		buttonType = 1,
		xPos = math.floor((termWidth - string.len("Lights On"))/2),
		yPos = math.floor(termHeight/2)-1,
	},
	[2] = {
		text = "Lights Off",
		buttonType = 1,
		xPos = math.ceil((termWidth - string.len("Lights Off"))/2),
		yPos = math.floor(termHeight/2),
	},
	[3] = {
		text = "Back",
		buttonType = 1,
		xPos = math.floor((termWidth - string.len("Back"))/2),
		yPos = math.floor(termHeight/2)+1,
	},
}

--[[ Printing Methods ]]--

function printMenu( menu )
	term.clear()
	term.setCursorPos(1,1)
	for k = 1, #menu do
		term.setCursorPos(menu[k].xPos, menu[k].yPos)
		write(menu[k].text)
	end
end

--[[ Handler Method ]]--

function isValidClick(menu)
	_, button, x, y = os.pullEvent("mouse_click")
	for k = 1, #menu do
		if button == menu[k].buttonType
		and x >= menu[k].xPos and x <= (menu[k].xPos + string.len(menu[k].text) - 1) -- the -1 is needed because of how the co-ords work.
		and y == menu[k].yPos then
			return true, menu[k].text -- if it finds it, it returns true and the option which was clicked
		end
	end
	return false, nil
end

--[[ Main Method ]]--

function main()
	while inMainMenu do
		printMenu(mainMenu)
		bValid, option = isValidClick(mainMenu) -- check for a valid click
		if bValid then
			if option == "Choice 1" then
				term.clear()
				term.setCursorPos(1,1)
				print("hello!")
				sleep(2)
			elseif option == "Lights" then
				inLightsMenu = true
				while inLightsMenu do
					printMenu(lightsMenu)
					term.setCursorPos(1,1)
					print(bLightsStatus and "Lights online" or "Lights offline")
					bValid, option = isValidClick( lightsMenu )
					if bValid then
						if (option == "Lights On" and not bLightsStatus) or (option == "Lights Off" and bLightsStatus) then
							bLightsStatus = not bLightsStatus
						elseif option == "Back" then
							inLightsMenu = false
						end
					end
				end
			elseif option == "Exit" then
				inMainMenu = false
			end
		end
	end
end

main()

-- clean up

term.setCursorPos(1, 1)
term.clear()