Repeat function within itself - lua

Here is a sample code:
local function name()
startingAction()
if trigger() then
endingAction()
return true
else
resetAction()
startingAction()
if trigger() then
endingAction()
return true
else
resetAction()
startingAction()
if trigger() then
endingAction()
return true
else
return false
end
end
end
Basically its a function that does something, but if in between it fails to do it, I would like to repeat this function by prefixing it with another action. The sample is 3 levels deep, but I would like to be able to control number of repeats.
Any suggestions?

You could use a basic while loop for this:
local MAX_RETRIES = 3
local
function name ()
local tries_remaining = MAX_RETRIES
while tries_remaining > 0 do
startingAction()
if trigger() then
endingAction()
return true
end
-- Implies else due to early return above.
resetAction()
tries_remaining = tries_remaining - 1
end
return false
end
If getting rid of the last resetAction call is necessary then you can modify the whole loop a little bit, although it is less readable and not-so-standardized:
local
function name2 ()
local tries_remaining = MAX_RETRIES
while true do
startingAction()
if trigger() then
endingAction()
return true
end
if tries_remaining > 0 then
resetAction()
tries_remaining = tries_remaining - 1
else
return false
end
end
end
Of course you can also do it with e.g. numeric for, labels or nice recursive calls. I'll leave these cases to you.
In general I encourage you to read Lua Reference Manual - 3.3.4 - Control Structures ff.

Related

I'm getting the error "attempt to index local self (a number value)

require 'class'
Paddle=class{}
function Paddle:init(x,y,width,height)
self.x=x
self.y=y
self.width=width
self.height=height
self.dy=0
end function Paddle:update(dt)
if self.dy < 0 then
self.y = math.max(`enter code here`0, self.y + self.dy * dt)
else
self.y=math.min(VIRTUAL_HEIGHT,-self.height,self.y+self.dy*dt)
end
end
function Paddle:render()
love.graphics.rectangle('fill',self.x,self.y,self.width,self.height)
end
I am following the course CS50 lecture 0 pong update 5, and the same code is working for the teacher. I don't know why this is happening neither understand the problem because it makes no sense. If you want, here's 'class'. This problem isn't happening in the other class I made called 'ball' which does exactly the same thing. I also defined self.dy, and it does have a value "0" so I don't know why it does that error and what that error means.
local function include_helper(to, from, seen)
if from == nil then
return to
elseif type(from) ~= 'table' then
return from
elseif seen[from] then
return seen[from]
end
seen[from] = to
for k,v in pairs(from) do
k = include_helper({}, k, seen) -- keys might also be tables
if to[k] == nil then
to[k] = include_helper({}, v, seen)
end
end
return to
end
-- deeply copies `other' into `class'. keys in `other' that are already
-- defined in `class' are omitted
local function include(class, other)
return include_helper(class, other, {})
end
-- returns a deep copy of `other'
local function clone(other)
return setmetatable(include({}, other), getmetatable(other))
end
local function new(class)
-- mixins
class = class or {} -- class can be nil
local inc = class.__includes or {}
if getmetatable(inc) then inc = {inc} end
for _, other in ipairs(inc) do
if type(other) == "string" then
other = _G[other]
end
include(class, other)
end
-- class implementation
class.__index = class
class.init = class.init or class[1] or function() end
class.include = class.include or include
class.clone = class.clone or clone
-- constructor call
return setmetatable(class, {__call = function(c, ...)
local o = setmetatable({}, c)
o:init(...)
return o
end})
end
-- interface for cross class-system compatibility (see https://github.com/bartbes/Class-Commons).
if class_commons ~= false and not common then
common = {}
function common.class(name, prototype, parent)
return new{__includes = {prototype, parent}}
end
function common.instance(class, ...)
return class(...)
end
end
-- the module
return setmetatable({new = new, include = include, clone = clone},
{__call = function(_,...) return new(...) end})
So this is the part where I call the update function, which is what someone said might be the error
function love.update(dt)
if love.keyboard.isDown('w') then
player1.dy=-PADDLE_SPEED
elseif love.keyboard.isDown('s') then
player1.dy=PADDLE_SPEED
else
player1.dy=0
end
if love.keyboard.isDown('up') then
player2.dy=-PADDLE_SPEED
elseif love.keyboard.isDown('down') then
player2.dy=PADDLE_SPEED
else
player2.dy=0
end
if gameState=='play' then
ball.update(dt)
end
player1.update(dt)
player2.update(dt)
This error is pretty clear on what you're doing wrong.
You're indexing local self, a number value.
That means that somewhere you're doing something like self.dy where self is not a table but a number and using the index operator . on numbers is not allowed as it does not make any sense.
The question is why self is not a table.
function myTable:myFunction() end
is short (syntactic sugar) for
function myTable.myFunction(self) end
and the function call
myTable:myFunction() is short for myTable.myFunction(myTable)
Please refer to the Lua manual.
Function Calls
Function Definitions
Find a function in your code that is defined with : and called with . and gets a number as first argument during that call.
That way a number ends up where you expect self.
I guess the error is in the main.lua which you did not provide.
There you have several calls to Paddle:update(dt). Writing myPaddle.update(dt) would cause that error for example. But I can't tell for sure as you did not provide your code.
But that it works for the teacher, but not for you is usually because you do something different/wrong.
Edit:
As you've provided more code I can tell for sure that the observed error is caused by
ball.update(dt)
player1.update(dt)
player2.update(dt)
This will put dt a number value, where the function expects self, the table ball.
replace it by
ball.update(ball, dt) or ball:update(dt)
player1.update(player1, dt) or player1:update(dt)
player2.update(player2, dt) or player2:update(dt)

Why is the userdata object added to tables in this Lua 5.1 __gc workaround?

I'm looking at solutions to add garbage collection to my tables (objects) in Lua 5.1. I have found that this can be worked around using newproxy() and __gc:
Lua 5.1 workaround for __gc metamethod for tables
https://github.com/katlogic/__gc
What I don't understand is the author's use of inserting the userdata as a field in the table.
All objects you set a metatable on through this wrapper get "polluted" with special key __gc_proxy (can be any string, user definable through __GC_PROXY global). You'll have to special-case it if you iterate over the fields of tables (next(), pairs() ...).
and
There is one thing to concern while using suggested solution - if you will traverse the table by pairs() you will get one addition key. It is possibly to avoid it by using proxy object with proper metamethods in place of original table.
Here is a copy/paste example from the Stack Overflow thread:
function setmt__gc(t, mt)
local prox = newproxy(true)
getmetatable(prox).__gc = function() mt.__gc(t) end
t[prox] = true
return setmetatable(t, mt)
end
iscollected = false
function gctest(self)
iscollected = true
print("cleaning up:", self)
end
test = setmt__gc({}, {__gc = gctest})
collectgarbage()
assert(not iscollected)
for k, v in pairs(test) do
print(tostring(k) .. " " .. tostring(v))
end
The output is:
userdata: 0003BEB0 true
cleaning up: table: 00039D58
But this cleanup is from the script ending and not at the call of collectgarbage().
This can be demonstrated by a slightly modified version that ends in a loop. The output should be "cleaning up":
function setmt__gc(t, mt)
local prox = newproxy(true)
getmetatable(prox).__gc = function() mt.__gc(t) end
t[prox] = true
return setmetatable(t, mt)
end
function gctest(self)
print("cleaning up:", self)
io.flush()
end
test = setmt__gc({}, {__gc = gctest})
collectgarbage()
while (true) do
end
Instead, by removing the offending t[prox] = true, the collection works as expected:
function setmt__gc(t, mt)
local prox = newproxy(true)
getmetatable(prox).__gc = function() mt.__gc(t) end
t[prox] = true
return setmetatable(t, mt)
end
function gctest(self)
print("cleaning up")
io.flush()
end
test = setmt__gc({}, {__gc = gctest})
collectgarbage()
while (true) do
end
Output:
cleaning up

Boolean function arguments and returning

so i know you can do stuff like this in lua to kind of shorten your code so you don't have to make unnecessary if statements
function checkMath(equation)
if equation == 4 then
return true
end
return false
end
workspace.Part.BrickColor = BrickColor.Green() or BrickColor.Red()
but is there a way to do that for a return statement inside a function?
basically, what I'm asking is: is it possible to return amount and items if returnItems is true or only amount if returnItems is false without a if statement?
what I've thought of doing (haven't tested):
countDictItems = function(tab,returnItems)
local amount = 0
local items = {}
for _, ind in pairs(tab) do
amount = amount + 1
end
return amount, items or amount
end
Answered in a separate thread I posted on another website.
function blah(returnitems)
amount = 15
items = {"blah1", "blah2"}
return amount, returnitems and items or nil
end
print(blah(true))
print(blah(false))
output:
>15 table: 0x9e26e0
>15 nil

Lua Set Functions in ComputerCraft

I have a ComputerCraft program set to turn on a siren when any non-whitelisted players are near:
sensor = peripheral.wrap("top")
function arraysubset(a, b)
local s = set(b)
for _, el in pairs(a)
if not s[el] then
return false
end
end
return true
end
function sirenOn() rs.setBundledOutput("back",colors.blue) end
function sirenOff() rs.setBundledOutput("back",0) end
while 1 do
playersNear = sensor.getPlayerNames()
allowedPlayers = {"VirtualDXS","jettrom","Shad0wlurker16","Demonicmobster","FireFang0113","riggs135","DaisySnow123","MasterAlex930"}
if playersNear[1] ~= nil then
if arraysubset(playersNear,allowedPlayers) then sirenOff() else sirenOn() end
else sirenOff() end
end
However, on line 3 I get an attempt to call nil. This makes me think that the set() function is not present on computercraft. I'm wondering:
Is there another (maybe better) way to find if array a is a subset of array b and
If not where can I get an API with the set() function?
Rereading the source for the subset() code, I am seeing that the code I used required more code from earlier in the answer:
function set(list)
local t = {}
for _, item in pairs(list) do
t[item] = true
end
return t
end

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)

Resources