Multiple Messages & Issues Searching Lua Tables - lua

I am attempting to make a Lua script for an online community I am a part of, I am having a problem when I attempt to search through a table array I believe. It doesn't detect the results I want.
The way it is supposed to work is that when someone types /gps [streetname] it will search the table at the top, detect the matching streetname & the coordinates and then set a waypoint to that relevant position.
At the moment it works when there is just one entry in the table, but when I put more, it will provide the error message for any non-matching streets & then the waypoint set message for the matching streets. I've Googled and don't appear to be able to find anything to help.
Any help would be appreciated.
waypoint = {
{404.08, -920.23, 'sinnerstreet', 'Sinner Street'},
{360.85, -956.46, 'atleestreet', 'Atlee Street'},
{500.48, -956.80, 'littlebighornavenue', 'Little Bighorn Avenue'},
}
RegisterCommand('gps', function(source, args, rawCommand)
for k,v in pairs(waypoint) do
x, y, streetname, displayname = table.unpack(v)
results = ""
if args[1] == nil then
if IsWaypointActive() then
SetWaypointOff()
TriggerEvent('chatMessage', '^1^*GPS Navigation: ^r^7Your GPS system has been reset.')
return end
elseif args[2] == nil and args[3] == nil then
results = args[1]
elseif args[2] ~= nil and args[3] == nil then
results = args[1] .. args[2]
else
results = args[1] .. args[2] .. args[3]
end
results = string.lower(results) -- This convertes the args into lower case
end
-- This locates the streetname and sets a waypoint to it for the player
if string.find(streetname, results) then
SetNewWaypoint(x, y)
TriggerEvent('chatMessage', '^1^*GPS Navigation: ^r^7Your waypoint to ^1' .. displayname .. '^r^7 has been set.')
else
TriggerEvent('chatMessage', '^1^*GPS Navigation: ^r^7There has been an error with your street name, please try again.')
end
end)
TriggerEvent('chat:addSuggestion', '/gps', 'This creates a waypoint to your designated street. ^*USE: /gps [streetname]')

To be honest, your code makes little to no sense, and it's probably because you're not using all the nice stuff Lua has to offer.
{404.08, -920.23, 'sinnerstreet', 'Sinner Street'},
You're storing redundant data there. The third value is really just the fourth one with spaces removed and all lowercase.
'sinnerstreet' == ('Sinner Street'):gsub("[^%l]", ""):lower()
In english: take "Sinner Street", globally (meaning in the entire string) substitute everything that is not a lowercase (%l) letter with nothing (""), then make the result of that lowercase. What you get is "sinnerstreet".
x, y, streetname, displayname = table.unpack(v)
Using globals there, that's not good. Globals are the devil. Don't use them.
Then, a few lines further down:
SetNewWaypoint(x, y)
Think about it for a moment. You set x and y in each iteration of your for loop. After the loop is done, they always contain the coordinates of the last waypoint you iterated over. I doubt that's what you want. Use local; it forces you to think what you want the scope of your variables to be, which will help you spot this kind of problem.
elseif args[2] ~= nil and args[3] == nil then
results = args[1] .. args[2]
Unless you specifically want to limit it to 3 arguments, which I doubt, you can also use table.concat to concatenate all the values in a sequence (read: array)
results = string.lower( table.concat(args) )
The thing that puzzles me is why you do this in a loop. For every waypoint, you set result to the same value, which is all the arguments concatenated and converted to lower case.
now what though? You check if result (what the user searched for) contains streetname, which, as we have previously found out, contains the name of the last waypoint in the list.
Using tables for searching
Lua has tables, one of if not the most powerful general-purpose data structure in programming.
local map = {}
for _,waypoint in ipairs(waypoints) do
map[waypoint[3]:lower()] = waypoint
end
This will get you something that looks about like this:
local map = {
sinnerstreet = {404.08, -920.23, 'sinnerstreet', 'Sinner Street'},
atleestreet = {360.85, -956.46, 'atleestreet', 'Atlee Street'},
littlebighornavenue ={500.48, -956.80, 'littlebighornavenue', 'Little Bighorn Avenue'},
}
and if you want to know if a street exists, you can just do this:
if map['atleestreet'] then
print(map.atleestreet[4])
end
if treats everything that isn't false or nil as truthy, so you can just write `map['atleestreet'] in the condition
my_table['text'] can be written as my_table.text
Looking up string indices in a table is pretty fast because of how it's implemented.
Conclusion
Try thinking your code through. If necessary, go through it line by line, writing down what values the variables hold in each moment. If you've been at it for a while, get some rest first or do something else for a while.
Then set your variables to local wherever possible (read: everywhere), figure out what needs to be inside and outside the loop and try again.
Remarks
Instead of if something == nil you can just write if not something, and if something ~= nil just if something
Apologies
Sorry for the long wall of text and using spaces inside brackets, but I wanted things to be specially easy to understand.

Related

Lua How to create a table inside a function?

I'm a beginner at Lua and programming in general (I've had some experience in other languages but nothing huge) and I've been following a tutorial where there's this one exercise on tables:
"Make a function with a table in it, where each key in the table is an animal name. Give each key a value equal to the sound the animal makes and return the animal sound. Try invoking the function and see if you get back the correct sound."
Here's my current solution:
make_sound = function(input)
animal_sounds = {
["cat"] = "meow",
["dog"] = "woof"
}
return animal_sounds.input
end
print(make_sound("cat"))
This just prints 'nil'. I've tried so many variations of this but they all either print 'nil' as well or give me an error saying something about nil (sorry I can't remember the original message or erroneous code).
I know this is a really dumb question and probably has an extremely basic answer so I'm sorry for my stupidity. All the other exercises have been a breeze and then I suddenly get hit with this thing for an hour. I searched everywhere but could only find results about functions inside arrays or something else completely. I didn't want to just give up on a seemingly easy task so here I am...
If your function returns the whole animal_sounds table, though it is not what is asked of you, you get the animal sound by print(make_sound().cat):
make_sound is a function,
make_sound() returns a table,
make_sound()['cat'] is a field of that table,
and make_sound().cat is syntactic sugar for it, as is said in the answer above.
Also, better declare everything local, including function make_sound and animal_sounds table.
And you can skip [""]/[''] in table keys, if they are strings of basic Latin, numbers and underscores: cat = 'mew' not ['cat'] = 'mew'.
Unless you are going to use make_sound as a variable, it is better to declare it with local function syntax, rather than an assignment.
You can skip parentheses around the only string or table parametre in the function call: f'str' rather than f( 'str' ).
Most imporantly, your function never uses input, which is supposed to be the animal. Therefore, it has to return not the table, but the sound. So, move the [] part inside the function.
So:
local function make_sound( input )
local animal_sounds = {
cat = 'meow',
dog = 'woof',
cow = 'moo'
}
return animal_sounds[input]
end
print( make_sound 'cat' )
P.S. You can even make the table anonymous, though it will need to be surrounded with parentheses, otherwise Lua will think that return is not the last operator before end as it should be:
local function make_sound( input )
return ({
cat = 'meow',
dog = 'woof',
cow = 'moo'
})[input]
end
print( make_sound 'cat' )
Try this:
return animal_sounds[input]
The animal_sounds.input is equivalent to animal_sounds["input"] and your table does not have the "input" key, hence it returns nil.

[lua]: Key with value 1 doesnt do the same as regular 1

Ive been coding for a mod Im making for a game but I ran into an issue with tables not returning values when the key is entered:
for k, v in pairs(self.math) do
print(self.exce[1])
print(self.exce[k])
print(k)
if self.exce[k] ~= nil then
self.math[k] = nil
end
end
This is the specific part of the script that is breaking. When I run these in the game it returns:
[lua]: true
[lua]: nil
[lua]: 1
Which means is basically saying that 1 is not equal to 1.
The function I used to store my data is
function filterExceptions.server_onException( self, id )
if self.exce[id] == nil then
self.exce[id] = true
self.network:sendToClients( "client_onList", id )
else
self.exce[id] = true
self.network:sendToClients( "client_offList", id )
end
end
In this code the self is a table made by the game you can acces and get game data from or store it in and the id comes from a function I made to get the players id. This id in this case is a 1 (I printed it multiple times).I know that every part of this code is working except for the code in the first block, and escpecialy the part where it tries to do self.exce[k]. Ive tried a lot like going trough every variable in self.exce to see if it was in there and then do stuff, but it still wouldn't work. Its very annoying how lua thinks that k ~= 1 while it definitely is, ive even used similar code in a part that is working.
So what is wrong about this code that its not printing the self.exce[k] while self.exce[1] does work? Dont worry about the creation of the table and stuff, cuz that is already happening whenever it is needed, else it would have given errors about that too.
Putting together a couple different comments and your code here, it looks like the index value of the array in some particular iteration of the "for in pairs" loop (or perhaps all of them, but I'll touch on that in a minute) is a string instead of an integer.
To summarize if you don't want to read the entire thing, "for k, v in pairs" loops will iterate through an entire array, setting k to the index of the value v. It appears your "for in pairs" loop is attempting to iterate through a value of nil where k is a string instead of an integer. You may also want to look into using ipairs instead of pairs in your for loop.
The value of someArray[1]is different than the value of someArray["1"].
The index [1] is a completely different index than the index ["1"] for any given array.
A simple fix would be to use
ind = tonumber(k)
print(self.exce[ind])
This converts the string k to a number type. Be aware this may throw an error if k is a non-numerical string. If the array has any values where the index is a non-numerical string, you may get an error. As the other answer suggests, converting the index k to a string instead of an integer would work as well, and would not throw errors if you used a non-numerical value for your indices.
My guess as to why this is happening would be that the function that you're using to store your data to an array, filterExceptions.server_onException( self, id ), is being passed a string instead of an integer, which would result in the k value being set to a string in that particular iteration of the "for in pairs" loop.
To help better understand this, here's a bit of example code:
a = {true, false, false}
a[1] = true
a["1"] = true
print("Raw for in pairs loop")
for k, v in pairs (a) do
print(type(k)..k)
end
print("For in pairs converting k to a number")
for k, v in pairs (a) do
ind = tonumber(k)
print(type(ind)..ind)
end
print("For in ipairs")
--which I'm not sure I completely understand but
--it seems to skip over any iteration where k is not a number
for k, v in ipairs(a) do
print(type(k)..k)
end
This code produces the following output:
Raw for in pairs loop
number1
number2
number3
string1
For in pairs converting k to a number
number1
number2
number3
number1
For in ipairs
number1
number2
number3
EDIT: Not sure what's going on in the self.math table so I can't comment on that.
EDIT2: I'd also refer you to the following link: lua: iterate through all pairs in table
The top answer there should help understand the difference between pairs and ipairs, if you don't already. You may want to use ipairs to prevent values of k where v == nil from being iterated through with pairs. pairs will iterate through every key/value pair, whereas ipairs will iterate through integer keys starting at 1 and going until it hits a nil value.
EDIT3: I'm sorry this is such a long answer...I just wanted to be thorough.
It apears converting the id to a string fixes this, tough im still confused as to why this same code worked on another block and not this one.
function filterExceptions.server_onException( self, id )
local id2 = tostring(id)
if self.exce[id2] == nil then
self.exce[id2] = true
self.network:sendToClients( "client_onList", id )
else
self.exce[id2] = true
self.network:sendToClients( "client_offList", id )
end
end

Find all upper/lower/mixed combinations of a string

I need this for a game server using Lua..
I would like to be able to save all combinations of a name
into a string that can then be used with:
if exists (string)
example:
ABC_-123
aBC_-123
AbC_-123
ABc_-123
abC_-123
etc
in the game only numbers, letters and _ - . can be used as names.
(A_B-C, A-B.C, AB_8 ... etc)
I understand the logic I just don't know how to code it:D
0-Lower
1-Upper
then
000
001
etc
You can use recursive generator. The first parameter contains left part of the string generated so far, and the second parameter is the remaining right part of the original string.
function combinations(s1, s2)
if s2:len() > 0 then
local c = s2:sub(1, 1)
local l = c:lower()
local u = c:upper()
if l == u then
combinations(s1 .. c, s2:sub(2))
else
combinations(s1 .. l, s2:sub(2))
combinations(s1 .. u, s2:sub(2))
end
else
print(s1)
end
end
So the function is called in this way.
combinations("", "ABC_-123")
You only have to store intermediate results instead of printing them.
If you are interested only in the exists function then you don't need all combinations.
local stored_string = "ABC_-123"
function exists(tested_string)
return stored_string:lower() == tested_string:lower()
end
You simply compare the stored string and the tested string in case-insensitive way.
It can be easily tested:
assert(exists("abC_-123"))
assert(not exists("abd_-123"))
How to do this?
There's native function in Lua to generate all permutations of a string, but here are a few things that may prove useful.
Substrings
Probably the simplest solution, but also the least flexible. Rather than combinations, you can check if a substring exists within a given string.
if str:find(substr) then
--code
end
If this solves your problem, I highly reccomend it.
Get all permutations
A more expensive, but still a working solution. This accomplishes nearly exactly what you asked.
function GetScrambles(str, tab2)
local tab = {}
for i = 1,#str do
table.insert(tab, str:sub(i, i))
end
local tab2 = tab2 or {}
local scrambles = {}
for i = 0, Count(tab)-1 do
local permutation = ""
local a = Count(tab)
for j = 1, #tab do
tab2[j] = tab[j]
end
for j = #tab, 1, -1 do
a = a / j
b = math.floor((i/a)%j) + 1
permutation = permutation .. tab2[b]
tab2[b] = tab2[j]
end
table.insert(scrambles, permutation)
end
return scrambles
end
What you asked
Basically this would be exactly what you originally asked for. It's the same as the above code, except with every substring of the string.
function GetAllSubstrings(str)
local substrings = {}
for i = 1,#str do
for ii = i,#str do
substrings[#substrings+1]=str:sub(ii)
end
end
return substrings
end
Capitals
You'd basically have to, with every permutation, make every possible combination of capitals with it.
This shouldn't be too difficult, I'm sure you can code it :)
Are you joking?
After this you should probably be wondering. Is all of this really necessary? It seems like a bit much!
The answer to this lies in what you are doing. Do you really need all the combinations of the given characters? I don't think so. You say you need it for case insensitivity in the comments... But did you know you could simply convert it into lower/upper case? It's very simple
local str = "hELlO"
print(str:lower())
print(str:upper())
This is HOW you should store names, otherwise you should leave it case sensitive.
You decide
Now YOU pick what you're going to do. Whichever direction you pick, I wish you the best of luck!

Lua: Why does my table revert to nil after one remove?

The following is part of a script I use to indicate which items (in this case, MOBs in a MUD) are not in my tabled database.
if mqtable[1] == nil then
Note("No mobs missing from database!")
else
if "%1" ~= "" then
mindex = "%1"
mdesc = mqtable[tonumber(mindex)]
end
if "%2" ~= "" then mlvl = "%2" end
if "%3" ~= "" then mkeyw = "%3" end
if not mindex and mqtable[1] then
tprint(mqtable)
elseif mindex and not mlvl then
Note(mdesc)
elseif mindex and mlvl and not mkeyw then
Note("Syntax is: mqmob [index] [level] [keywords]")
else
mobtable[mdesc]={level = mlvl, keywords = mkeyw}
table.save(mobtable,savepath.."/Mobs/"..areazone..".tbl")
Note(mqtable[tonumber(mindex)] .. " saved. Level: ".. mlvl .. " -- Keywords: " .. mkeyw)
table.remove(mqtable, tonumber(mindex))
mobtable = table.load(savepath .. "/Mobs/" .. areazone .. ".tbl")
mlvl, mkeyw, mdesc = nil, nil, nil
end
Assume all undefined functions are working as intended and do not appear to be the root cause. "%1", "%2", and "%3" are passed parameters through a trigger. Essentially, if this were a function, those three parameters would be called as foo(blah, bleh, blargh).
Problem
If I type mqmob (which fires the above script), it displays within the room which MOBs have not been added to my table. I then use mqmob <index> <level> <keyword> to add the mob to that table. Once that has been done, however, the script refuses to work, and regardless of the number of items that were originally on the table, mqmob returns nil. Why is the table being completely wiped after storing one bit of information?
Example
I look in a room and see the following:
A rat dwells here.
A mouse chases after some cheese.
A spider makes its home in the corner.
A flea leaps about playfully.
Say that I have only A spider makes its home in the corner. in the database. The remaining three descriptions are placed into mqtable, and when I call mqmob, it displays:
1="A rat dwells here."
2="A mouse chases after some cheese."
3="A flea leaps about playfully."
Now, I type mqmob 1 20 rat to create mobtable["A rat dwells here."] with {level = 20, keywords = rat}. But once I do that, if I type mqmob again, it returns nil.
Explanation of variables
mobtable is merely a table that contains the mob's description as the key, and its corresponding level and keywords as values. For example:
mobtable = {"A rat nibbles on some cheese." = {keyword = "rat", level = 20}}
mqtable is populated only when there is no corresponding match in mobtable. Essentially, the trigger fires, compares to keys in mobtable, and if not found, populates mqtable. This is to help prevent me from unnecessary work of adding mobs already in mobtable.
The problem I keep running into is that I will add one mob, but when I run the print command a second time, it returns nil. However, I can still add and view the individual indexes in mqtable. It just doesn't seem able to print it up anymore.

Elder Scroll Online Addon

This is my first time working with Lua, but not with programming. I have experience in Java, Action Script, and HTML. I am trying to create an addon for Elder Scroll Online. I managed to find the ESO API at the following link:
http://wiki.esoui.com/API#Player_Escorting
I am trying to make a function that returns a count of how many items each guild member has deposited in the bank. The code I have so far is as follows
function members()
for i=0, GetNumGuildEvents(3, GUILD_EVENT_BANKITEM_ADDED)
do
GetGuildEventInfo(3, GUILD_EVENT_BANKITEM_ADDED, i)
end
I am having trouble referencing the character making the specific deposit. Once I am able to do that I foresee making a linked list storing character names and an integer/double counter for the number of items deposited. If anyone has an idea of how to reference the character for a given deposit it would be much appreciated.
I don't have the game to test and the API documentation is sparse, so what follows are educated guesses/tips/hints (I know Lua well and programmed WoW for years).
Lua supports multiple assignment and functions can return multiple values:
function foo()
return 1, "two", print
end
local a, b, c = foo()
c(a,b) -- output: 1, "two"
GetGuildEventInfo says it returns the following:
eventType, secsSinceEvent, param1, param2, param3, param4, param5
Given that this function applies to multiple guild event types, I would expect param1 through param5 are specific to the particular event you're querying. Just print them out and see what you get. If you have a print function available that works something like Lua's standard print function (i.e. accepts multiple arguments and prints them all), you can simple write:
print(GetGuildEventInfo(3,GUILD_EVENT_BANKITEM_ADDED,i))
To print all its return values.
If you don't have a print, you should write one. I see the function LogChatText which looks suspiciously like something that would write text to your chat window. If so, you can write a Lua-esque print function like this:
function print(...)
LogChatText(table.concat({...}, ' '))
end
If you find from your experimentation that, say, param1 is the name of the player making the deposit, you can write:
local eventType, secsSinceEvent, playerName = GetGuildEventInfo(3,GUILD_EVENT_BANKITEM_ADDED, i)
I foresee making a linked list storing character names and an integer/double counter for the number of items deposited.
You wouldn't want to do that with a linked list (not in Lua, Java nor ActionScript). Lua is practically built on hashtables (aka 'tables'), which in Lua are very powerful and generalized, capable of using any type as either key or value.
local playerEvents = {} -- this creates a table
playerEvents["The Dude"] = 0 -- this associates the string "The Dude" with the value 0
print(playerEvents["The Dude"]) -- retrieve the value associated with the string "The Dude"
playerEvents["The Dude"] = playerEvents["The Dude"] + 1 -- this adds 1 to whatever was previous associated with The Dude
If you index a table with a key which hasn't been written to, you'll get back nil. You can use this to determine if you've created an entry for a player yet.
We're going to pretend that param1 contains the player name. Fix this when you find out where it's actually located:
local itemsAdded = {}
function members()
for i=0, GetNumGuildEvents(3, GUILD_EVENT_BANKITEM_ADDED ) do
local eventType, secsSinceEvent, playerName = GetGuildEventInfo(3, GUILD_EVENT_BANKITEM_ADDED, i)
itemsAdded[playerName] = (itemsAdded[playerName] or 0) + 1
end
end
itemsAdded now contains the number of items added by each player. To print them out:
for name, count in pairs(itemsAdded) do
print(string.format("Player %s has added %d items to the bank.", name, count))
end

Resources