Progress forwards and backwards through table - lua

first time here on Stack Overflow but definitely have found a lot of useful information here!
Currently I'm trying to figure out how to select the next item, or previous item in a table based on the current selection.
My current table is as follows:
maleSkins = { 7,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,32,33,34,35,
36,37,43,44,45,46,47,48,49,51,52,57,58,59,60,61,66,67,72,73,80,82,83,
84,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
112,113,114,115,116,117,118,120,121,122,123,124,125,126,127,128,132,
133,134,135,136,137,142,143,144,146,147,153,154,156,159,160,161,162,
168,170,173,174,175,176,177,179,180,181,182,183,184,185,186,187,188,
189,200,202,203,204,206,210,212,213,217,220,221,222,223,227,228,229,
230,234,235,236,239,240,241,242,247,248,249,250,252,254,258,259,260,261,262 }
femaleSkins = { 9,10,11,12,13,31,38,39,40,41,53,54,55,56,69,76,77,88,89,90,91,92,
93,129,130,131,138,139,140,141,145,148,151,152,157,190,191,192,193,195,
196,197,198,199,201,207,211,214,215,216,218,219,224,225,226,232,233,237,238,243,244,245,246,251,256,257 }
The default selection is "7" in the maleSkins table, and I will use the femaleSkins table when they choose "female" for their gender.
Currently my function call looks like this
function selSkin(button,state)
if button ~= "left" and state ~= "up" then
return
end
if source == createChar.maleButt then
femaleSkin = false
maleSkin = true
elseif source == createChar.femaleButt then
maleSkin = false
femaleSkin = true
end
if source == createChar.nextSkin then
if maleSkin == true then
newModel = table.concat(maleSkins,)
elseif femaleSkin == true then
end
elseif source == createChar.prevSkin then
if maleSkin == true then
elseif femaleSkin == true then
end
end
end
So, inside the "createChar.nextSkin" and "createChar.prevSkin" is where I'm trying to sort through the table based on the current skin, but I'm unsure of how to proceed.
I would love if someone could give me the building blocks to do this, and I will build the rest myself!
(Side note: Predefined variables I will be using to make this happen)
maleSkin = true
femaleSkin = true
curSkin = 7
newModel = nil

First of all welcome to stack overflow :)
A small thing I noticed: you have two variables to store if the character is male or female. This allows for 4 combinations, 2 of which make no sense. You could just as well only use one variable, say, maleSkin and when it's false, then you use the female skin instead. Alternatively, you could just do skinType = 'male' and skinType = 'female' (Lua interns strings, so this is just as fast as comparing integers)
now, unless you plan to have a few millions of skins or more, you can just iterate through the table to find the current skin and then use the previous one.
function skinOffset(skin, skinList, offset)
for i,current_skin in ipairs(skinList) do
if current_skin == skin then
return skinList[i + offset]
else
end
end
Now, to get the next skin you can do skinOffset(curSkin, maleSkins, 1) and for the previous one skinOffset(curSkin, femaleSkins, -1).

Additionally save the table index in a global var.
There are several ways, for instance skinIndex = 1, your code could look like this:
if source == createChar.nextSkin then
newModel = maleSkin and maleSkins[skinIndex + 1] or femaleSkins[skinIndex + 1]
elseif source == createChar.prevSkin then
newModel = maleSkin and maleSkins[skinIndex - 1] or femaleSkins[skinIndex - 1]
end
But you have to beware of skinIndex < 1 or > #maleSkins/#femaleSkins

Related

How to check bool nested in table in lua

I'm new to lua, and I'm having trouble with a basic sort-by-bool-condition thing for entries in a table.
`local tblFormReturn = {
{
['Name'] = 'Spike',
['Year'] = '10',
['House'] = 'Holmes',
['Form Returned'] = true
},
{
['Name'] = 'Elvis',
['Year'] = '11',
['House'] = 'Shaw',
['Form Returned'] = true
},
{
['Name'] = 'Michael',
['Year'] = '10',
['House'] = 'Langley',
['Form Returned'] = false
},
{
['Name'] = 'Chang',
['Year'] = '11',
['House'] = 'Holmes',
['Form Returned'] = false
}
}`
Basically, I want to be able to take this table, and for each chunk, check whether the kid is in Holmes house (1) and if they have returned their form (2). My feeling is I need to run a for-loop in pairs based off the lua manual, but I'm confused as to how I can access these values, given each chunk is sort of a sub-table. My attempts have all been based around something like this.
for i,'Form Returned' in tblFormReturned('Form Returned') do
if 'Form Returned' == true then
if 'House' == 'Holmes' then
print ('Number of Holmes forms returned' +1)
end
end
end
I'm not sure how to make this work. Any help greatly appreciated.
A few things of note here.
When you quote something (indicated by using the single quotes), you effectively make it a string.
A for loop loops through a table using ipairs (indexed pairs, such as yours is) or pairs (used on dictionary tables). Dictionary tables are considered those that are have a defined key rather than an index key (e.g. tblPets = {dog = "Fido", cat = "Sassy", duck = "Quackers} - this would allow you to return tblPets.dog (or tblPets["dog"]) to get the value).
Your print statement to add a number does not work. You cannot add a number to a string. Instead, you will need to set a count as a variable and add to it, provided it is a number.
Lastly, you can also combine the if statements into one to make it easier.
formCount = 0 -- This initializes the variable formCount as an interger, starting with 0.
for i,v in ipairs(tblFormReturned) do -- This iterates through the table
if v["Form Returned"] and v.House == "Holmes" then -- Looks to see if the form returned is true and house is Holmes. Note that with boolean values, you do not have to see if it equals true or false. if v["Form Returned"] == true and this format returns the same answer.
formCount = formCount + 1 -- Adds 1 to the formCount
end -- end if statement
end -- end for loop
Hopefully this helps a little with understanding. If you have any questions, don't hesitate to ask for clarification.

How can I dynamically retrieve the variable used to initialize an instance?

I'm trying to implement a speed-based turn system for a roguelike. I've set up a Mob class using metamethods, so that assigning the following to a variable will spawn a mob into the map at certain grid coordinates:
function Mob:spawn(x,y,m)
local mob = {}
setmetatable(mob, Mob)
mob.x = x
mob.y = y
mob.is_monster = m
return mob
end
Once that's done, I call the following:
function Mob:roll_call()
who_is_here[self.y][self.x] = self.is_monster
self.turn_counter = self.turn_counter * math.random(0.9, 1.1)
table.insert(allTurnCounters, self.turn_counter)
end
This puts the mob's self.turn_counter into a table. Meanwhile, in another module, I've defined these two functions, the heart of the problem:
function turn.decrement_counters(dt) -- runs in Dungeon.update(dt) and subtracts from allTurnCounters
for i = 1,#allMobsSpawned do
if allTurnCounters[i] <= 0 then
allTurnCounters[i] = 0
turn_active = true
whose_turn = i
return
elseif allTurnCounters[i] > 0 then
allTurnCounters[i] = allTurnCounters[i] - (10 * dt)
end
end
end
function turn.whose_is_it() -- called when an entry in allTurnCounters goes zero
if whose_turn == 1 then -- spots 1 and 2 in the spawn list are only ever for players
player1.my_turn = true -- turns on player 1's keys
elseif whose_turn == 2 then
player2.my_turn = true -- turns on player 2's keys
elseif whose_turn >= 3 then -- above 3 we're in monster territory
end
end
I've decided that the first two instances of Mob to be initialized will always be players 1 and 2, assigned to the variables player1 and player2, respectively. And, as it is, it works fine for passing control back and forth between players! But obviously, that's not enough for a fully-featured game. I need monsters, too.
The allTurnCounters table gets new entries, in order, from every mob that spawns (a class which includes both the players and the monsters, so they can share stats). Here's my question: How can I get Lua to dynamically retrieve the name of the table associated with a given turn_counter/value within that table, and use it to take turn priority, even in the event that I don't know what's been procedurally spawned ahead of time or what place it will occupy in the spawn order?
I have 3 ideas, none of which I'm solid on how to implement. One method would be something like sending the entire instance table to another table, rather than just their turn_counters, and then somehow grabbing a pair of values (the table itself and my_turn within the table), updating my_turn's value directly.
Another method might be to use the environment _G... somehow. I'm still poring over Chapter 14 of PiL trying to adapt it to my purposes, but value = _G[varname] seems to be a powerful bit of code I might be able to use for this. Not sure how, just yet.
My last idea was to maybe write some kind of string-sensing find-replace that can grab some other value in each mob's table and then pop it on the front of my_turn. Like, assigning some value with a known pattern for each mob type, that I can use in a string.find and then a string.gsub, to like... manually make the code line read as intended. Seems inelegant, though.
I had good luck with asking my previous Lua/Love2D question here, so I figured let's toss it out there while I'm thinking!
Here is my suggestion for how you should implement this:
Instead of allTurnCounters[i], give mobs a turn_counter property and use allMobsSpawned[i].turn_counter. Then, delete allTurnCounters.
Instead of storing the mob number in whose_turn, store the mob itself. (Note: when I say "the mob itself", it's short for "a reference to the mob itself")
So instead of
whose_turn = i
you would have:
whose_turn = allMobsSpawned[i]
Now whose_turn holds the mob whose turn it is. You can easily check whose_turn == player1, whose_turn == player2, etc. As a bonus, it doesn't rely on the players being the first mobs any more.
You can access the mob's properties through whose_turn - if whose_turn == player1 is true for example then whose_turn.x accesses the same field as player1.x
Here's a somewhat janky solution that can be made more elegant by incorporating the method from the other answer. This is what I came up with on my own while waiting for an answer.
-- in "Mob.lua" module
function Mob:roll_call()
who_is_here[self.y][self.x] = self.is_monster
self.turn_counter = self.turn_counter * math.random(0.9, 1.1)
table.insert(allMobs, {self.name, self.turn_counter})
if self.is_monster == true then
table.insert(allMonsters, {self.name, self.turn_counter})
end
end
function Mob:move(dx, dy)
who_is_here[self.y][self.x] = nil
self.x, self.y = self.x + dx, self.y + dy
who_is_here[self.y][self.x] = self.is_monster
self.turn_counter = 1 * self.speed
for k,v in ipairs(allMobs) do
if v[1] == self.name then
v[2] = self.turn_counter
end
end
self.my_turn = false -- flags turn is over
turn_active = false -- flags no active turn
end
-- in "turn.lua" module
function turn.decrement_counters(dt)
if turn_active == false then
for k,v in ipairs(allMobs) do
v[2] = v[2] - (10 * dt)
if v[2] < 0 then
v[2] = 0
turn_active = true
whose_turn = v[1]
return
end
end
else turn.whose_is_it()
end
end
function turn.whose_is_it()
if whose_turn == player1.name then
player1.my_turn = true
elseif whose_turn == player2.name then
player2.my_turn = true
elseif whose_turn == ant1.name then
ant1.my_turn = true
end
end
turn.whose_is_it() is the part that will need refining. If I use immibis' method of assigning allMobs[i].turn_counter, that will simplify things considerably and allow for future expansion. This answer only works for player1, player2, and an ant called ant1 in particular.

Cloning+Dragging model when button is click ROBLOX [LUA]?

So I thought of having this for so long, I just don't know where to start. I am new to this language and I keep learning, but kind of hard for me. But I have built my very own custom character which took 2 weeks for me. Anyway, For my question. An example is if I have a button and I click it, a model will be clone and I can drag that model and put it anywhere nearby. What possible method I can use to achieve this?
First things first, I suggest for any future questions, you head over to https://scriptinghelpers.org/
now, on to your question, for cloning the model, you should use mouse.Target.Parent:Clone() or the GetTopParent(mouse.Target) function in my function library (which you can get here; http://www.roblox.com/item.aspx?id=244244638)
then deposit the model into workspace and MakeJoints()
the next step is to move the model, this can be tricky, but the simplest method is model:MoveTo(mouse.Hit.p) on mouse.Moved (but that's a little buggy)
Another method for movement would be to use the Handles class, but I'm not really familiar with it, so you'd have to figure that one out on your own.
To make the first method less buggy, I'd suggest something along the lines of
model:MoveTo(mouse.Hit.p.X, mouse.Target.Position.Y + (model:GetExtentsSize().Y / 2), mouse.Hit.p.Z)
but you'd have to set up the mouse to ignore the model, which I can't really help with.
A really good place to start is to search the free models in Studio Toolbox for a 'Dragger Tool' or 'Model Dragger Tool' and then use the script inside to get started creating your own. I have learned to create my own custom draggers by doing this and it is way easier than you may think at first. Once you find a good dragger tool to borrow code from, if you need to enhance it, you can find the dragger api in the Roblox Wiki to help you further customize it to your specific needs.
http://wiki.roblox.com/index.php?title=API:Class/Dragger
EDIT: So here's the first dragger script that showed when I searched. It will drag models and parts but you will have to edit it to meet your requirements using the dragger api. Create a Tool in player.BackPack then create a LocalScript inside the Tool then copy and paste the code below into the LocalScript, and that will get you started.
local Tool = script.Parent
enabled = true
local origTexture = Tool.TextureId
game:GetService("ContentProvider"):Preload("rbxasset://icons/freemove_sel.png")
local selectionBox
local currentSelection
local currentSelectionColors = {}
local selectionLasso
local inGui = false
local inPalette = false
local lockTime = 0
function canSelectObject(part)
return part and not (part.Locked) and (part.Position - script.Parent.Parent.Head.Position).Magnitude < 60
end
function findModel(part)
while part ~= nil do
if part.className == "Model" then
return part
end
part = part.Parent
end
return nil
end
function startDrag(mousePart, hitPoint, collection)
dragger = Instance.new("Dragger")
pcall(function() dragger:MouseDown(mousePart, hitPoint, collection) end)
end
function collectBaseParts(object, collection)
if object:IsA("BasePart") then
collection[#collection+1] = object
end
for index,child in pairs(object:GetChildren()) do
collectBaseParts(child, collection)
end
end
function onMouseDown(mouse)
mouse.Icon ="rbxasset://textures\\GrabRotateCursor.png"
local part = mouse.Target
if canSelectObject(part) then
local hitPoint = mouse.Hit:toObjectSpace(part.CFrame).p
if trySelection(part) then
local instances = {}
collectBaseParts(currentSelection, instances)
startDrag(part, hitPoint, instances)
return
end
end
--Clear the selection if we weren't able to lock succesfullu
onMouseUp(mouse)
end
function onMouseUp(mouse)
mouse.Icon ="rbxasset://textures\\GrabCursor.png"
if dragger ~= nil then
pcall(function() dragger:MouseUp() end)
dragger = nil
end
end
function trySelection(part)
if canSelectObject(part) then
selectionLasso.Part = part
local model = findModel(part)
if model then
return setSelection(model)
else
return setSelection(part)
end
else
clearSelection()
return false
end
end
function onKeyDown(key)
if dragger ~= nil then
if key == 'R' or key == 'r' then
dragger:AxisRotate(Enum.Axis.Y)
elseif key == 'T' or key == 't' then
dragger:AxisRotate(Enum.Axis.Z)
end
end
end
local alreadyMoving
function onMouseMove(mouse)
if alreadyMoving then
return
end
alreadyMoving = true
if dragger ~= nil then
--Maintain the lock
if time() - lockTime > 3 then
Instance.Lock(currentSelection)
lockTime = time()
end
--Then drag
pcall(function() dragger:MouseMove(mouse.UnitRay) end)
else
trySelection(mouse.Target)
end
alreadyMoving = false
end
function saveSelectionColor(instance)
if instance:IsA("BasePart") then
currentSelectionColors[instance] = instance.BrickColor
if instance.BrickColor == BrickColor.Blue() then
instance.BrickColor = BrickColor.new("Deep blue")
else
instance.BrickColor = BrickColor.Blue()
end
end
local children = instance:GetChildren()
if children then
for pos, child in pairs(children) do
saveSelectionColor(child)
end
end
end
function setSelection(partOrModel)
if partOrModel ~= currentSelection then
clearSelection()
if Instance.Lock(partOrModel) then
lockTime = time()
currentSelection = partOrModel
saveSelectionColor(currentSelection)
selectionBox.Adornee = currentSelection
return true
end
else
if currentSelection ~= nil then
if time() - lockTime > 2 then
--Maintain the lock
if not(Instance.Lock(currentSelection)) then
--we lost the lock
clearSelection()
return false
else
lockTime = time()
return true
end
else
return true
end
end
end
return false
end
function clearSelection()
if currentSelection ~= nil then
for part, color in pairs(currentSelectionColors) do
part.BrickColor = color
end
selectionBox.Adornee = nil
Instance.Unlock(currentSelection)
end
currentSelectionColors = {}
currentSelection = nil
selectionLasso.Part = nil
selectionBox.Adornee = nil
end
function onEquippedLocal(mouse)
Tool.TextureId = "rbxasset://icons/freemove_sel.png"
local character = script.Parent.Parent
local player = game.Players:GetPlayerFromCharacter(character)
inGui = false
inPalette = false
mouse.Icon ="rbxasset://textures\\GrabCursor.png"
mouse.Button1Down:connect(function() onMouseDown(mouse) end)
mouse.Button1Up:connect(function() onMouseUp(mouse) end)
mouse.Move:connect(function() onMouseMove(mouse) end)
mouse.KeyDown:connect(function(string) onKeyDown(string) end)
selectionBox = Instance.new("SelectionBox")
selectionBox.Name = "Model Delete Selection"
selectionBox.Color = BrickColor.Blue()
selectionBox.Adornee = nil
selectionBox.Parent = player.PlayerGui
selectionLasso = Instance.new("SelectionPartLasso")
selectionLasso.Name = "Model Drag Lasso"
selectionLasso.Humanoid = character.Humanoid
selectionLasso.archivable = false
selectionLasso.Visible = true
selectionLasso.Parent = game.workspace
selectionLasso.Color = BrickColor.Blue()
alreadyMoving = false
end
function onUnequippedLocal()
Tool.TextureId = origTexture
clearSelection()
selectionBox:Remove()
selectionLasso:Remove()
end
Tool.Equipped:connect(onEquippedLocal)
Tool.Unequipped:connect(onUnequippedLocal)

Lua - Table.hasValue returning nil

I have a table something like this:
table = {milk, butter, cheese} -- without "Quotation marks"
I was searching for a way to check if a given value is in the table or not, and found this:
if table.hasValue(table, milk) == true then ...
but it returns nil, any reason why? (it says .hasValue is invalid) or can I get an alternative to check if value exists in that table? I tried several ways like:
if table.milk == true then ...
if table[milk] == true then ...
All of these returns nil or false.
you can try this
items = {milk=true, butter=true, cheese=true}
if items.milk then
...
end
OR
if items.butter == true then
...
end
A Lua table can act as an array or as an associative array (map).
There is no hasValue, but by using a table as an associative array you can easily implement it efficiently:
local table = {
milk = true,
butter = true,
cheese = true,
}
-- has milk?
if table.milk then
print("Has milk!")
end
if table.rocks then
print("Has rocks!")
end
You have a few options here.
One, is to create a set:
local set = {
foo = true,
bar = true,
baz = true
}
Then you check if either of these are in the table:
if set.bar then
The drawback to this approach is that you won't iterate over it in any specific order (pairs returns items in an arbitrary order).
Another option would be to use a function to check each value in a table. This'll be very slow in large tables, which brings us to back to a modification of the first option: A reverse lookup generator: (This is what I'd recommend doing -- unless your set is static)
local data = {"milk", "butter", "cheese"}
local function reverse(tbl, target)
local target = target or {}
for k, v in pairs(tbl) do
target[v] = k
end
return target
end
local revdata = reverse(data)
print(revdata.cheese, revdata.butter, revdata.milk)
-- Output: 3 2 1
This'll generate a set (with the added bonus of giving you the index where the value was in your original table). You can also put the reverse into the same table as the data was in, but this won't go well with numbers (and it'll be messy if you need to generate the reverse again).
If you write table = {milk=true, butter=true, cheese=true}, then you can use if table.milk == true then ....

Comparing two index tables by index-value in lua

I'm attempting to compare two tables of equal length with a function, since I don't know of any other way to do so. However, with the following function, it fails to register, and I've no clue why. I'm hoping someone can provide insight to this problem or has a better way of comparing the two tables.
The tables are being populated with the following code:
str = "parameters determined by program (all digits)"
tableone = {}
for word in str:gmatch("%d") do table.insert(tableone,word) end
It's identical for both tables, except, of course, the individual table names. The tables are being populated properly, and display properly when I print them. Here are two tables for the sake of this question:
tableone = {}
tabletwo = {}
for i=1,4 do table.insert(tableone, i) end
for i=1,4 do table.insert(tabletwo, i) end
Obviously, these two tables are going to be equal to each other. The function I wrote to compare the index tables is as follows:
function comparetables(t1, t2)
matchct = 0
for i=1,#t1 do
if t1[i] == t2[i] then
matchct = matchct + 1
end
if matchct == #t1 then
return true
end
end
I tried doing
print(comparetables(tableone,tabletwo))
to see if it'll print "true" but no luck. To me, it seems like it should work without a problem. Yet it doesn't. What am I missing? I've tried searching for something like a table.compare function that someone may have already written, but no such luck in finding one. Thanks for any suggestions!
Additional information:
The reason I'm comparing tables is for a mastermaind-type game. That means the following three rules must apply when comparing tables. The function I created was to just get me started, thinking I could work from there.
When comparing the tables, if the numbers match, Ccount increases by 1.
When comparing tables, if the value exists in a different index position, increment Pcount by 1
For example, with a table of values {1, 3, 3, 4} and a guess of {4, 4, 3, 1}, it would return Pcount of 2 (the one 4 and the 1) and a Ccount of 1 (the three in the third position). I think one of the hardest parts is going to be getting the comparison to recognize that the second 4 in the guess should not increment the Pcount at all.
A slight variant on your code that should work is:
function comparetables(t1, t2)
if #t1 ~= #t2 then return false end
for i=1,#t1 do
if t1[i] ~= t2[i] then return false end
end
return true
end
However I use something more like this: It checks the types of the arguments, their metatables, and a few other cases.
-- This is not clever enough to find matching table keys
-- i.e. this will return false
-- recursive_compare( { [{}]:1 }, { [{}]:1 } )
-- but this is unusual enough for me not to care ;)
-- It can also get stuck in infinite loops if you use it on
-- an evil table like this:
-- t = {}
-- t[1] = t
function recursive_compare(t1,t2)
-- Use usual comparison first.
if t1==t2 then return true end
-- We only support non-default behavior for tables
if (type(t1)~="table") then return false end
-- They better have the same metatables
local mt1 = getmetatable(t1)
local mt2 = getmetatable(t2)
if( not recursive_compare(mt1,mt2) ) then return false end
-- Check each key-value pair
-- We have to do this both ways in case we miss some.
-- TODO: Could probably be smarter and not check those we've
-- already checked though!
for k1,v1 in pairs(t1) do
local v2 = t2[k1]
if( not recursive_compare(v1,v2) ) then return false end
end
for k2,v2 in pairs(t2) do
local v1 = t1[k2]
if( not recursive_compare(v1,v2) ) then return false end
end
return true
end
Here's an example of it in use:
print( recursive_compare( {1,2,3,{1,2,1}}, {1,2,3,{1,2,1}} ) ) -- prints true
print( recursive_compare( {1,2,3,{1,2,1}}, {2,2,3,{1,2,3}} ) ) -- prints false
If you're comparing objects that are more objecty than tabley in an Object oriented sense, then I'd look at implementing the functions in the lua OO way.
Something like this should do the trick:
GameState = {}
GameState.mt = {}
GameState.mt.fns = {}
GameState.mt.__index = GameState.mt.fns
function GameState.new(a,b,c,d)
-- TODO: put argument checks here...
local retval = {}
retval[1] = a
retval[2] = b
retval[3] = c
retval[4] = d
setmetatable(retval, GameState.mt)
return retval
end
function GameState.mt.fns.print( self )
print(" GameState: ", self[1], self[2], self[3], self[4] )
end
function GameState.mt.__tostring( self )
return "GameState: "..self[1].." "..self[2].." "..self[3].." "..self[4]
end
function GameState.mt.__eq(self, other)
-- Check it's actually a GameState, and all its bits match
return getmetatable(other)==GameState.mt and
(self[1] == other[1]) and
(self[2] == other[2]) and
(self[3] == other[3]) and
(self[4] == other[4])
end
Then you'd use it like this:
state1 = GameState.new(1,2,3,4)
state2 = GameState.new(1,2,3,4)
print("State 1 is:")
state1:print()
print("State 2 is:")
print(state2)
print( "state1 == state2 : ", state1 == state2 )
print( "Changing state 2")
state2[1]=2
print( "state1 == state2 : ", state1 == state2 )

Resources