lua open a file with a table and change certain values - lua

I have a file with a large table. Need to open it, get certain values and replace other values with the new ones. With this example, Females and Males Classroom a and b must equal Teachers Classroom a and b.
Read as much as I can on IO, but I cannot seem to get this to work correctly. Any help will be greatly appreciated.
--file with sample data to test, original file has 7000+lines
theData =
{
["theSchool"] =
{
["theTeachers"] =
{
["theClassroom"] =
{
["a"] = 001,
["b"] = 002,
},
},
["theFemales"] =
{
["theClassroom"] =
{
["a"] = 005,
["b"] = 010,
},
},
["theMales"] =
{
["theClassroom"] =
{
["a"] = 012,
["b"] = 014,
},
},
},
}
Then the function file looks like this
local file = io.open(filepath,'r')
local newCa = '["a"] = '..theData.theSchool.theTeachers.theClassroom.a
local newCb = '["b"] = '..theData.theSchool.theTeachers.theClassroom.b
local replaceFa = '["a"] = '..tostring(theData.theSchool.theFemales.theClassroom.a)
local replaceFb = '["b"] = '..tostring(theData.theSchool.theFemales.theClassroom.b)
local replaceMa = '["a"] = '..tostring(theData.theSchool.theMales.theClassroom.a)
local replaceMb = '["b"] = '..tostring(theData.theSchool.theMales.theClassroom.b)
file:close()
--Will it work?
print("Original Values:")
print(newCa)
print(newCb)
print(replaceFa)
print(replaceFb)
print(replaceMa)
print(replaceMb)
file = io.open(filepath,'r+')
function lines_from(filepath)
lines = {}
for line in io.lines(filepath) do
lines[#lines + 1] = line
end
return lines
end
local lines = lines_from(filepath)
for k,v in ipairs(lines) do
if v == replaceFa then
file:write(newCa..'\n')
elseif v == replaceFb then
file:write(newCb..'\n')
elseif v == replaceMa then
file:write(newCa..'\n')
elseif v == replaceMb then
file:write(newCb..'\n')
else
file:write(v..'\n')
end
--('[' .. k .. ']', v)
end
--replaceF = {[a] = newCa, [b] = newCb}
--replaceM = {[a] = newCa, [b] = newCb}
--file:write(replaceF)
--file:write(replaceM)
--print(tostring(theData.theSchool.theFemales.theClassroom.a))
file:close()
file = io.open(filepath,'r')
--Did it work?
print("New Values:")
print(theData.theSchool.theTeachers.theClassroom.a)
print(theData.theSchool.theTeachers.theClassroom.b)
print(theData.theSchool.theFemales.theClassroom.a)
print(theData.theSchool.theFemales.theClassroom.b)
print(theData.theSchool.theMales.theClassroom.a)
print(theData.theSchool.theMales.theClassroom.b)```
> I'll remove all the print things after it works.

try this solution, loading the table in one read of the file will be faster, and writing is also better with one command write
--- read
local handle = io.open("data.lua",'rb')
local data = handle:read("*a")
handle:close()
data = data .. "\n return theData"
local t = loadstring(data)()
-- edit
theData.theSchool.theTeachers.theClassroom.a = theData.theSchool.theTeachers.theClassroom.a + 1
-- write
local function concat(t, r)
r = r or {}
for k,v in pairs(t) do
if type(v)=="table" then
r[#r+1] = string.format('\t["%s"]={\n',k )
concat(v, r)
r[#r+1] = "\t},\n"
else
r[#r+1] = string.format('\t\t["%s"]=%03s,\n',k ,v )
end
end
return r
end
local r = concat(t)
local text = "theData = { \n " .. table.concat(r) .. "}"
print(text) -- just control
local handle = io.open("data.lua",'wb')
local data = handle:write(text)
handle:close()

Related

How would I compare results in lua

DISCLAIMER: The title is bad but I dont know how else to word it
Issue: I am trying to compare entities in a FiveM script I am working on and tell if a player is close to a valid animal model and I cant get it to work and the only model being detected is the player its self
Current code:
local QBCore = exports['qb-core']:GetCoreObject()
local animals = GetGamePool('CPed')
local hasTool = false
local hasKnife =
QBCore.Functions.HasItem('Knife', function(result)
if result then
hasTool = true
hasKnife = true
end
end)
for k, v in pairs(animals) do
local animal = animals[k]
local animalModel = GetEntityModel(animal)
local animalHash = GetHashKey(animal)
local boar = GetHashKey('a_c_boar')
local deer = GetHashKey('a_c_deer')
local coyote = GetHashKey('a_c_coyote')
print("Deer: " .. deer .. "boar: " .. boar .. "coyote: " .. coyote)
if animalHash == boar or deer or coyote then
local entPos = GetEntityCoords(v)
local playerPos = GetEntityCoords(PlayerPedId())
if #(entPos - playerPos) < 1.0 then
foundAnimal = v
print("Animal close by" .. animalHash)
break
end
else
print("No animals found")
end
end
Other things I have tried:
local QBCore = exports['qb-core']:GetCoreObject()
local animals = GetGamePool('CPed')
local validAnimals = {
["a_c_boar"] = true,
["a_c_coyote"] = true,
["a_c_crow"] = true,
["a_c_deer"] = true,
["a_c_mtlion"] = true,
["a_c_pig"] = true,
["a_c_rabbit_01"] = true,
}
local hasTool = false
local hasKnife =
QBCore.Functions.HasItem('Knife', function(result)
if result then
hasTool = true
hasKnife = true
end
end)
for k, v in pairs(animals) do
local animal = animals[k]
local animalModel = GetEntityModel(animal)
local animalModelName = GetEntityModel(animalModel)
if validAnimals[animalModelName] then
local entPos = GetEntityCoords(v)
local playerPos = GetEntityCoords(PlayerPedId())
if #(entPos - playerPos) < 2.0 then
foundAnimal = v
print("Found animal" .. foundAnimal .. "!" .. "Model: " .. entModel)
break
end
end
local QBCore = exports['qb-core']:GetCoreObject()
Citizen.CreateThread(function ()
local animals = GetGamePool('CPed')
local validAnimals = {
["a_c_boar"] = true,
["a_c_coyote"] = true,
["a_c_crow"] = true,
["a_c_deer"] = true,
["a_c_mtlion"] = true,
["a_c_pig"] = true,
["a_c_rabbit_01"] = true,
}
local hasTool = false
local hasKnife =
QBCore.Functions.HasItem('Knife', function(result)
if result then
hasTool = true
hasKnife = true
end
end)
for k, v in pairs(animals) do
local entModel = GetEntityModel(v)
if validAnimals[entModel] then
local entPos = GetEntityCoords(v)
local playerPos = GetEntityCoords(PlayerPedId())
if #(entPos - playerPos) < 2.0 then
foundAnimal = v
print("Found animal" .. foundAnimal .. "!" .. " " .. "Model: " .. entModel)
break
end
end)
Edit: I have tried a few other things aswell but i dont have those versions saved
Edit2: The current code the if statement seems like it doesnt even check anything its like all the code inside of it will run even if and the hashes dont match
Edit3: This also could be a distance calculation issue with the vector3 im not sure tho

How can I merge two tables like this? - Lua

I have these tables set like this
local tableone = {["Gold"] = 10, ["Gem"] = 5}
local tabletwo = {["Level"] = 1}
This is the code for merging
local test = {tableone, tabletwo}
print(test)
But if I try to merge the tables then the output is like this
[1] = {
["Gold"] = 10,
["Gem"] = 5
},
[2] = {
["Level"] = 1
}
And I would like to have the output like this
[1] = {
["Gold"] = 10,
["Gem"] = 5,
["Level"] = 1
}
Is this possible?
Sorry if I'm not that good at explaining.
You can do this with a simple nested loop.
local function merge(...)
local result <const> = {}
-- For each source table
for _, t in ipairs{...} do
-- For each pair in t
for k, v in pairs(t) do
result[k] = v
end
end
return result
end
local t <const> = {merge(tableone, tabletwo)}
I put the result in a table constructor due to the [1] in the question.

How to implement the exercise 15.5 in pil4?

I am working on this exercise in pil4.
Exercise 15.5:
The approach of avoiding constructors when saving tables with cycles is too radical. It is
possible to save the table in a more pleasant format using constructors for the simple case, and to use
assignments later only to fix sharing and loops. Reimplement the function save (Figure 15.3, “Saving
tables with cycles”) using this approach. Add to it all the goodies that you have implemented in the previous
exercises (indentation, record syntax, and list syntax).
I have tried this with the code below, but it seems not to work on the nested table with a string key.
local function basicSerialize(o)
-- number or string
return string.format("%q",o)
end
local function save(name,value,saved,indentation,isArray)
indentation = indentation or 0
saved = saved or {}
local t = type(value)
local space = string.rep(" ",indentation + 2)
local space2 = string.rep(" ",indentation + 4)
if not isArray then io.write(name," = ") end
if t == "number" or t == "string" or t == "boolean" or t == "nil" then
io.write(basicSerialize(value),"\n")
elseif t == "table" then
if saved[value] then
io.write(saved[value],"\n")
else
if #value > 0 then
if indentation > 0 then io.write(space) end
io.write("{\n")
end
local indexes = {}
for i = 1,#value do
if type(value[i]) ~= "table" then
io.write(space2)
io.write(basicSerialize(value[i]))
else
local fname = string.format("%s[%s]",name,i)
save(fname,value[i],saved,indentation + 2,true)
end
io.write(",\n")
indexes[i] = true
end
if #value > 0 then
if indentation > 0 then io.write(space) end
io.write("}\n")
else
io.write("{}\n")
end
saved[value] = name
for k,v in pairs(value) do
if not indexes[k] then
k = basicSerialize(k)
local fname = string.format("%s[%s]",name,k)
save(fname,v,saved,indentation + 2)
io.write("\n")
end
end
end
else
error("cannot save a " .. t)
end
end
local a = { 1,2,3, {"one","Two"} ,5, {4,b = 4,5,6} ,a = "ddd"}
local b = { k = a[4]}
local t = {}
save("a",a,t)
save("b",b,t)
print()
And I got the wrong ouput.
a = {
1,
2,
3,
{
"one",
"Two",
}
,
5,
{
4,
5,
6,
}
a[6]["b"] = 4
,
}
a["a"] = "ddd"
b = {}
b["k"] = a[4]
How could I make the text ' a[6]["b"] = 4 ' jump out of the table constructor?

what`s wrong with 'iup.config' in iup package on lua

I started to learn lua (version 5.1) plus iup (version 3.5) library, but this issue shut me down :(
So, the clearest example from iup tutorial http://webserver2.tecgraf.puc-rio.br/iup/en/tutorial/tutorial3.html#Recent_Config
require("iuplua")
require("iupluaimglib")
--********************************** Utilities *****************************************
function str_find(str, str_to_find, casesensitive, start)
if (not casesensitive) then
return str_find(string.lower(str), string.lower(str_to_find), true, start)
end
return string.find(str, str_to_find, start, true)
end
function read_file(filename)
local ifile = io.open(filename, "r")
if (not ifile) then
iup.Message("Error", "Can't open file: " .. filename)
return nil
end
local str = ifile:read("*a")
if (not str) then
iup.Message("Error", "Fail when reading from file: " .. filename)
return nil
end
ifile:close()
return str
end
function write_file(filename, str)
local ifile = io.open(filename, "w")
if (not ifile) then
iup.Message("Error", "Can't open file: " .. filename)
return false
end
if (not ifile:write(str)) then
iup.Message("Error", "Fail when writing to file: " .. filename)
end
ifile:close()
return true
end
--********************************** Main (Part 1/2) *****************************************
-- it`s there:
config = iup.config{} ------ here it is, first error
config.app_name = "simple_notepad"
config:Load()
lbl_statusbar = iup.label{title = "Lin 1, Col 1", expand = "HORIZONTAL", padding = "10x5"}
multitext = iup.text{
multiline = "YES",
expand = "YES"
}
font = config:GetVariable("MainWindow", "Font")
if (font) then
multitext.font = font
end
item_open = iup.item{title = "&Open...\tCtrl+O"}
item_saveas = iup.item{title="Save &As...\tCtrl+S"}
item_font = iup.item{title="&Font..."}
item_about = iup.item{title="&About..."}
item_find = iup.item{title="&Find...\tCtrl+F"}
item_goto = iup.item{title="&Go To..."}
item_exit = iup.item{title="E&xit"}
--********************************** Callbacks *****************************************
function config:recent_cb()
local filename = self.title
local str = read_file(filename)
if (str) then
multitext.value = str
end
end
function multitext:caret_cb(lin, col)
lbl_statusbar.title = "Lin "..lin..", Col "..col
end
function item_open:action()
local filedlg = iup.filedlg{
dialogtype = "OPEN",
filter = "*.txt",
filterinfo = "Text Files",
parentdialog=iup.GetDialog(self)
}
filedlg:popup(iup.CENTERPARENT, iup.CENTERPARENT)
if (tonumber(filedlg.status) ~= -1) then
local filename = filedlg.value
local str = read_file(filename)
if (str) then
config:RecentUpdate(filename)
multitext.value = str
end
end
filedlg:destroy()
end
function item_saveas:action()
local filedlg = iup.filedlg{
dialogtype = "SAVE",
filter = "*.txt",
filterinfo = "Text Files",
parentdialog=iup.GetDialog(self)
}
filedlg:popup(iup.CENTERPARENT, iup.CENTERPARENT)
if (tonumber(filedlg.status) ~= -1) then
local filename = filedlg.value
if (write_file(filename, multitext.value)) then
config:RecentUpdate(filename)
end
end
filedlg:destroy()
end
function item_exit:action()
config:DialogClosed(iup.GetDialog(self), "MainWindow")
config:Save()
config:destroy()
return iup.CLOSE
end
function item_goto:action()
local line_count = multitext.linecount
local lbl_goto = iup.label{title = "Line Number [1-"..line_count.."]:"}
local txt_goto = iup.text{mask = iup.MASK_UINT, visiblecolumns = 20} --unsigned integer numbers only
local bt_goto_ok = iup.button{title = "OK", text_linecount = 0, padding = "10x2"}
bt_goto_ok.text_linecount = line_count
function bt_goto_ok:action()
local line_count = tonumber(self.text_linecount)
local line = tonumber(txt_goto.value)
if (line < 1 or line >= line_count) then
iup.Message("Error", "Invalid line number.")
return
end
goto_dlg.status = 1
return iup.CLOSE
end
local bt_goto_cancel = iup.button{title = "Cancel", padding = "10x2"}
function bt_goto_cancel:action()
goto_dlg.status = 0
return iup.CLOSE
end
local box = iup.vbox{
lbl_goto,
txt_goto,
iup.hbox{
iup.fill{},
bt_goto_ok,
bt_goto_cancel,
normalizesize="HORIZONTAL",
},
margin = "10x10",
gap = "5",
}
goto_dlg = iup.dialog{
box,
title = "Go To Line",
dialogframe = "Yes",
defaultenter = bt_goto_ok,
defaultesc = bt_goto_cancel,
parentdialog = iup.GetDialog(self)
}
goto_dlg:popup(iup.CENTERPARENT, iup.CENTERPARENT)
if (tonumber(goto_dlg.status) == 1) then
local line = txt_goto.value
local pos = iup.TextConvertLinColToPos(multitext, line, 0)
multitext.caretpos = pos
multitext.scrolltopos = pos
end
goto_dlg:destroy()
end
function item_find:action()
local find_dlg = self.find_dialog
if (not find_dlg) then
local find_txt = iup.text{visiblecolumns = "20"}
local find_case = iup.toggle{title = "Case Sensitive"}
local bt_find_next = iup.button{title = "Find Next", padding = "10x2"}
local bt_find_close = iup.button{title = "Close", padding = "10x2"}
function bt_find_next:action()
local find_pos = tonumber(find_dlg.find_pos)
local str_to_find = find_txt.value
local casesensitive = (find_case.value == "ON")
-- test again, because it can be called from the hot key
if (not str_to_find or str_to_find:len()==0) then
return
end
if (not find_pos) then
find_pos = 1
end
local str = multitext.value
local pos, end_pos = str_find(str, str_to_find, casesensitive, find_pos)
if (not pos) then
pos, end_pos = str_find(str, str_to_find, casesensitive, 1) -- try again from the start
end
if (pos) and (pos > 0) then
pos = pos - 1
find_dlg.find_pos = end_pos
iup.SetFocus(multitext)
multitext.selectionpos = pos..":"..end_pos
local lin, col = iup.TextConvertPosToLinCol(multitext, pos)
local pos = iup.TextConvertLinColToPos(multitext, lin, 0) -- position at col=0, just scroll lines
multitext.scrolltopos = pos
else
find_dlg.find_pos = nil
iup.Message("Warning", "Text not found.")
end
end
function bt_find_close:action()
iup.Hide(iup.GetDialog(self)) -- do not destroy, just hide
end
box = iup.vbox{
iup.label{title = "Find What:"},
find_txt,
find_case,
iup.hbox{
iup.fill{},
bt_find_next,
bt_find_close,
normalizesize="HORIZONTAL",
},
margin = "10x10",
gap = "5",
}
find_dlg = iup.dialog{
box,
title = "Find",
dialogframe = "Yes",
defaultenter = bt_next,
defaultesc = bt_close,
parentdialog = iup.GetDialog(self)
}
-- Save the dialog to reuse it
self.find_dialog = find_dlg -- from the main dialog */
end
-- centerparent first time, next time reuse the last position
find_dlg:showxy(iup.CURRENT, iup.CURRENT)
end
function item_font:action()
local font = multitext.font
local fontdlg = iup.fontdlg{value = font, parentdialog=iup.GetDialog(self)}
fontdlg:popup(iup.CENTERPARENT, iup.CENTERPARENT)
if (tonumber(fontdlg.status) == 1) then
multitext.font = fontdlg.value
config:SetVariable("MainWindow", "Font", fontdlg.value)
end
fontdlg:destroy()
end
function item_about:action()
iup.Message("About", " Simple Notepad\n\nAutors:\n Gustavo Lyrio\n Antonio Scuri")
end
--********************************** Main (Part 2/2) *****************************************
recent_menu = iup.menu{}
file_menu = iup.menu{
item_open,
item_saveas,
iup.separator{},
iup.submenu{title="Recent &Files", recent_menu},
item_exit
}
edit_menu = iup.menu{item_find, item_goto}
format_menu = iup.menu{item_font}
help_menu = iup.menu{item_about}
sub_menu_file = iup.submenu{file_menu, title = "&File"}
sub_menu_edit = iup.submenu{edit_menu, title = "&Edit"}
sub_menu_format = iup.submenu{format_menu, title = "F&ormat"}
sub_menu_help = iup.submenu{help_menu, title = "&Help"}
menu = iup.menu{
sub_menu_file,
sub_menu_edit,
sub_menu_format,
sub_menu_help,
}
btn_open = iup.button{image = "IUP_FileOpen", flat = "Yes", action = item_open.action, canfocus="No", tip = "Open (Ctrl+O)"}
btn_save = iup.button{image = "IUP_FileSave", flat = "Yes", action = item_saveas.action, canfocus="No", tip = "Save (Ctrl+S)"}
btn_find = iup.button{image = "IUP_EditFind", flat = "Yes", action = item_find.action, canfocus="No", tip = "Find (Ctrl+F)"}
toolbar_hb = iup.hbox{
btn_open,
btn_save,
iup.label{separator="VERTICAL"},
btn_find,
margin = "5x5",
gap = 2,
}
vbox = iup.vbox{
toolbar_hb,
multitext,
lbl_statusbar,
}
dlg = iup.dialog{
vbox,
title = "Simple Notepad",
size = "HALFxHALF",
menu = menu,
close_cb = item_exit.action,
}
function dlg:k_any(c)
if (c == iup.K_cO) then
item_open:action()
elseif (c == iup.K_cS) then
item_saveas:action()
elseif (c == iup.K_cF) then
item_find:action()
elseif (c == iup.K_cG) then
item_goto:action()
end
end
config:RecentInit(recent_menu, 10)
-- parent for pre-defined dialogs in closed functions (IupMessage)
iup.SetGlobal("PARENTDIALOG", iup.SetHandleName(dlg))
config:DialogShow(dlg, "MainWindow")
-- to be able to run this script inside another context
if (iup.MainLoopLevel()==0) then
iup.MainLoop()
iup.Close()
end
don`t working:
error output:
wlua: source.wlua:52: attempt to call field 'config' (a nil value)
So, why it isn`t working?

Lua, is this possible?

I am working on a game using love2d and i haven't quite programmed in lua. Not sure on technical wording so i'll post my code and explain what i'm trying to do:
item = {}
item.stat = {}
player.x = 100
player.y = 100
--
item[0].stat.damage = 10
What i'm trying to do is make an inventory system and an item database. I want to be able to make the item database with the code above so i could add an item like so:
item[1].stat.damage = 10
item[1].stat.speed = 10
item[2].stat.damage = 20
item[2].stat.speed = 5
--
player.inventory[0] = item[1]
player.inventory[1] = item[2]
can someone tell me what coding principle this may so i can research it? I basically want to make a matrix that i can access like above while having the convenience of named arrays instead of saying item[1,"damage"] = 10
Edit:
I realise now i can do item.stat.damage[1] = 10 but i have to setup an array for each one, is there an easier way?
Simply use tables:
player = {}
player.x = 100
print(player.x) -- prints 100
Note that player.x is simply syntactic sugar for player["x"], so the following lines are equivalent:
print(player.x) -- prints 100
print(player["x"]) -- also prints 100
With that in mind, you could construct your game data like this for example:
item = {}
item[1] = {}
item[1].stat = {}
item[1].stat.damage = 10
item[1].stat.speed = 10
item[2] = {}
item[2].stat = {}
item[2].stat.damage = 20
item[2].stat.speed = 5
player = {}
player.x = 100
player.y = 100
player.inventory = {}
player.inventory[1] = item[1]
player.inventory[2] = item[2]
print(player.inventory[2].stat.damage) -- prints 20
print(player["inventory"][2]["stat"]["damage"]) -- equivalent, also prints 20
It is probably a good idea to define functions that create items or players and automatically set all the required fields.
Eventually, you may want to use actual classes and objects (for example, if you want to define methods on your objects).
EDIT:
Here is the example from above with functions create_item, create_player to create items or players. I've used named parameters for these functions so one doesn't have to remember the order of the function parameters (note the curly braces when calling the functions).
function create_item(arg)
local item = {}
item.stat = {}
item.stat.damage = arg.damage
item.stat.speed = arg.speed
return item
end
function create_player(arg)
local player = {}
player.x = arg.x
player.y = arg.y
player.inventory = {}
return player
end
item = {}
item[1] = create_item{damage=10, speed=10}
item[2] = create_item{damage=20, speed=5}
player = create_player{x=100, y=100}
player.inventory[1] = item[1]
player.inventory[2] = item[2]
print(player.inventory[2].stat.damage) -- prints 20
print(player["inventory"][2]["stat"]["damage"]) -- equivalent, also prints 20
You can always shorten your code:
item = {
stat = {},
[0] = { stat = { damage = 10 } },
[1] = { stat = { damage = 10, speed = 10 } },
[2] = { stat = { damage = 20, speed = 5 } },
}
player = { x = 100, y = 100, inventory = { [0] = item[1], [1] = item[2] } }
You can access that code in matrix way like
function item:getstat(index, param)
return self[index] and self[index].stat and self[index].stat[param];
end
function item:setstat(index, param, value)
local t1 = self[index]
if (t1 == nil) then
t1 = {}
self[index] = t1
end
local t2 = t1.stat
if (t2 == nil) then
t2 = {}
t1.stat = t2
end
t2[param] = value
end
print(item:getstat(0, "damage"))
item:setstat(1, "speed", 20)

Resources