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 )