• Useful Code Snippets
    232 replies, posted
A function binding function. [lua]function BindFunction(func, object, ...) local args = {...} return function(...) return func(object, unpack(table.Add(table.Copy(args), {...}))) end end[/lua]
[QUOTE=sannys;51898628]A function binding function. [lua]function BindFunction(func, object, ...) local args = {...} return function(...) table.Add(args, {...}) return func(object, unpack(args)) end end[/lua][/QUOTE] What kind of use is there for this?
[QUOTE=txike;51898762]What kind of use is there for this?[/QUOTE] Imagine you have an object with a function defined with the [B]:[/B] character [lua]local Person = {Name = "Garry"} function Person:Greet() print("My name is", self.Name) end Person:Greet()[/lua] That's completely fine. But what if you wanted to save that Greet function into a variable, and then call that variable? [lua]local Person = {Name = "Garry"} function Person:Greet() print("My name is", self.Name) end local greet = Person.Greet greet()[/lua] This code would generate an error because you cannot implicitly include a self reference like that. [quote]attempt to index local 'self' (a nil value)[/quote] In order to make that code work, you'd have to pass the self reference manually (or manually wrap it in a function). [lua]greet(Person)[/lua] However, if we make a bind for it, we don't need to do that anymore. [lua]local greet = BindFunction(Person.Greet, Person) greet()[/lua] Usually the first argument would be a metatable's method, and the second argument would be an instance of that class. Basically it just lets you not have to worry about self references when saving object methods to a variable.
[QUOTE=sannys;51898840]:snip:[/QUOTE] if you're using BindFunction just to add in the `self` arg to a member function, why not go for a simple solution like [CODE] function bind(f,self) return function(...) return f(self,...) end end bind(Person.Greet,Person) [/CODE] no idea why you're using table.Add in your original code: that would eventually result in `args` getting very large and all past args being passed into the bound function each time you call it
[QUOTE=swadicalrag;51898883]if you're using BindFunction just to add in the `self` arg to a member function, why not go for a simple solution like [CODE] function bind(f,self) return function(...) return f(self,...) end end bind(Person.Greet,Person) [/CODE] no idea why you're using table.Add in your original code: that would eventually result in `args` getting very large and all past args being passed into the bound function each time you call it[/QUOTE] It's not only for self references. It's used to prepend extra arguments. [url]https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind[/url] I see what you mean with the args becoming increasingly large, though. This should do it. [lua]function BindFunction(func, object, ...) local args = {...} return function(...) return func(object, unpack(table.Add(table.Copy(args), {...}))) end end[/lua]
[QUOTE=sannys;51898923]It's not only for self references. It's used to prepend extra arguments. [url]https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind[/url] I see what you mean with the args becoming increasingly large, though. This should do it. [lua]function BindFunction(func, object, ...) local args = {...} return function(...) return func(object, unpack(table.Add(table.Copy(args), {...}))) end end[/lua][/QUOTE] FWIW, object is now just one of the args unless it's nil, and in correct usage it would never be nil. Speaking of nil, I think args can't end in nil, for example: [lua]local Person = { name = "Bob" } function Person:Greet(...) print( self.name, ...) end local bf = BindFunction(Person.Greet, Person, nil) bf("yay") Person:Greet(nil, "yay")[/lua] Admittedly haven't tested this, but I'm sure the first one skips the nil. Edit: This can be solved by "hard-coding" the number of arguments from args into a RunString that generates the function you'll return. select("#", ...) will correctly handle nils as actual values. You also won't have to use table.Copy or table.Add at any point, so executing the function will be faster! However, generating it will be slower probably, since you'd be compiling a string. I'm geeking out about this, I don't even think binding like this is useful. :happy:
you can use select("#",...) to get the length of a vararg and go from there I wrote a quick [URL="https://gist.github.com/SwadicalRag/2ff9f46b1d9a182ebab2b23a84edb075"]vararg helper library[/URL], using that, you can rewrite BindFunction as: [CODE] local varg = (include or dofile)("varg.lua") function BindFunction(fn,...) local base = varg(...) return function(...) return fn(base + varg(...)) end end [/CODE] [editline]3rd March 2017[/editline] I made a typo, I meant [CODE] return fn((base + varg(...)):Unpack()) [/CODE] (I can't edit my post for some reason :/)
Your library is pretty cool, but for what it's worth my method would run the bind without any overhead, whereas yours would concatenate the args and unpack them every time.
[QUOTE=sannys;51898923]It's not only for self references. It's used to prepend extra arguments. [url]https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind[/url] I see what you mean with the args becoming increasingly large, though. This should do it. [lua]function BindFunction(func, object, ...) local args = {...} return function(...) return func(object, unpack(table.Add(table.Copy(args), {...}))) end end[/lua][/QUOTE] Why are you making a copy of args? It seems like it would breaking things in weird and wonderful ways.
[QUOTE=sannys;51898628]A function binding function. [lua]function BindFunction(func, object, ...) local args = {...} return function(...) return func(object, unpack(table.Add(table.Copy(args), {...}))) end end[/lua][/QUOTE] [DEL]or more simply put without the unnecessary table fuckery:[/DEL] read below as to why this is wrong [code] function bind( func, ... ) local a = { ... } return function( ... ) return func( unpack( a ), ... ) end end [/code]
[QUOTE=ZeBull;51902973]or more simply put without the unnecessary table fuckery: [code] function bind( func, ... ) local a = { ... } return function( ... ) return func( unpack( a ), ... ) end end [/code][/QUOTE] Unless the result list comes as the last expression in the argument list, then it will be adjusted to the first element only. So this will only pass the first element of `a`
apply damage on npc_helicopter [lua] aa("EntityFireBullets", function(client, data) local oldCallback = data.Callback data.Callback = function(client, trace, dmgInfo) if (oldCallback) then oldCallback(client, trace, dmgInfo) end if (trace) then local target = trace.Entity if (IsValid(target) and target:IsNPC()) then if (target:GetClass() == "npc_helicopter") then local dmg = DamageInfo() dmg:SetDamage(data.Damage) dmg:SetAttacker(client) dmg:SetDamageType(DMG_AIRBOAT) target:TakeDamageInfo(dmg) target:SetNW2Int("health", target:Health()) end end end end return true end)[/lua]
[QUOTE=rebel1324;51944400]apply damage on npc_helicopter [lua] aa("EntityFireBullets", function(client, data) local oldCallback = data.Callback data.Callback = function(client, trace, dmgInfo) if (oldCallback) then oldCallback(client, trace, dmgInfo) end if (trace) then local target = trace.Entity if (IsValid(target) and target:IsNPC()) then if (target:GetClass() == "npc_helicopter") then local dmg = DamageInfo() dmg:SetDamage(data.Damage) dmg:SetAttacker(client) dmg:SetDamageType(DMG_AIRBOAT) target:TakeDamageInfo(dmg) target:SetNW2Int("health", target:Health()) end end end end return true end)[/lua][/QUOTE] Doesn't the helicopter already take damage though?
You can go try shooting bullets on helicopter now. If you successfully take down the heli without any mods and rpg, I'll give you ferrari
[url]https://gist.github.com/meepdarknessmeep/a2bb9c61e2cca03e2a37b2d305b1e0e0[/url] This loads a non-power-of-2 PNG into a Garry's Mod texture without flipping the fuck out. It's async so you could implement it with coroutine in mind. It will be used in an upcoming release of mine.
Super duper simple table replace function... Is there already a function for this on the wiki? I made it cause I couldn't find one lol. [CODE]local function tableReplace(Table, pos, value) if (Table[pos]:IsValid()) then table.remove(Table, pos) table.insert(Table, pos, value) else table.insert(Table, pos, value) end end[/CODE]
[QUOTE=MrRalgoman;52087959]Super duper simple table replace function... Is there already a function for this on the wiki? I made it cause I couldn't find one lol. [CODE]local function tableReplace(Table, pos, value) if (Table[pos]:IsValid()) then table.remove(Table, pos) table.insert(Table, pos, value) else table.insert(Table, pos, value) end end[/CODE][/QUOTE] [code] table[pos] = value [/code]
[QUOTE=MPan1;52087969][code] table[pos] = value [/code][/QUOTE] Well hey, you learn something new everyday.
Useful for debugging (especially from console via lua_run[_cl]): [lua]function Print(...) if select("#", ...) == 1 and type(...) == "table" then PrintTable(...) else print(...) end end[/lua] Print(somevar) will use PrintTable if somevar is a table, and in all other cases, will use the regular print. I'm trying to make it good enough and test it enough to make a pull request, I think this is useful enough to be in vanilla gmod.
[QUOTE=NeatNit;52224652]Useful for debugging (especially from console via lua_run[_cl]): [lua]function Print(...) if select("#", ...) == 1 and type(...) == "table" then PrintTable(...) else print(...) end end[/lua] Print(somevar) will use PrintTable if somevar is a table, and in all other cases, will use the regular print. I'm trying to make it good enough and test it enough to make a pull request, I think this is useful enough to be in vanilla gmod.[/QUOTE] this is so useful and simple yet i never thought about it lol
[QUOTE=NeatNit;52224652]Useful for debugging (especially from console via lua_run[_cl]): :snip: Print(somevar) will use PrintTable if somevar is a table, and in all other cases, will use the regular print. I'm trying to make it good enough and test it enough to make a pull request, I think this is useful enough to be in vanilla gmod.[/QUOTE] You can get rid of the `select` call if you give it a first argument, i.e. [code] function Print(val, ...) if ... == nil and type(val) == "table" then PrintTable(val) else print(val, ...) end end [/code] Edit: Though this'd make it break if given nil as second argument, edge case failures wew
[QUOTE=bigdogmat;52224930]You can get rid of the `select` call if you give it a first argument, i.e. [code] function Print(val, ...) if ... == nil and type(val) == "table" then PrintTable(val) else print(val, ...) end end [/code] Edit: Though this'd make it break if given nil as second argument, edge case failures wew[/QUOTE] It would also not allow no value for val, e.g. Print() would behave like print(nil) instead of print(). Varargs are tricky business. If this is about performance, forget all about it. print, PrintTable and now Print are all debugging tools. As long as they don't significantly freeze the game (which they don't), there is no reason to optimize them.
Pretty sure it's done: [lua]--[[--------------------------------------------------------- Either print or PrintTable, depending on the arguments -----------------------------------------------------------]] function Print(...) if select("#", ...) == 1 -- only 1 element and type(...) == "table" -- which is a table and (getmetatable(...) == nil or not debug.getmetatable(...).__tostring) -- that has no custom tostring function then PrintTable(...) else print(...) end end[/lua] Pull request: [url]https://github.com/garrynewman/garrysmod/pull/1365[/url] My tests: [code]] lua_run Print(Print) > Print(Print)... function: 0x22934080 ] lua_run Print(Entity(1)) > Print(Entity(1))... Player [1][NeatNit] ] lua_run Print(Entity(1):GetEyeTrace()) > Print(Entity(1):GetEyeTrace())... AllSolid = false Entity = Entity [0][worldspawn] Fraction = 0.0038898063357919 FractionLeftSolid = 0 Hit = true HitBox = 0 HitGroup = 0 HitNoDraw = false HitNonWorld = false HitNormal = 0.000000 0.000000 1.000000 HitPos = 1054.024658 -613.926025 64.031250 HitSky = false HitTexture = BUILDING_TEMPLATE/ROOF_TEMPLATE001A HitWorld = true MatType = 67 Normal = -0.864618 0.017805 -0.502114 PhysicsBone = 0 StartPos = 1164.229858 -616.195435 128.031250 StartSolid = false SurfaceProps = 30 ] lua_run Print(Entity(1):GetEyeTrace().Entity) > Print(Entity(1):GetEyeTrace().Entity)... Entity [0][worldspawn] ] lua_run Print(Entity(1):GetEyeTrace().Entity:GetPos()) > Print(Entity(1):GetEyeTrace().Entity:GetPos())... 0.000000 0.000000 0.000000 ] lua_run Print({"Hello, ", "World!"}) > Print({"Hello, ", "World!"})... 1 = Hello, 2 = World! ] lua_run Print(setmetatable({"Hello, ", "World!"}, nil)) > Print(setmetatable({"Hello, ", "World!"}, nil))... 1 = Hello, 2 = World! ] lua_run Print(setmetatable({"Hello, ", "World!"}, {})) > Print(setmetatable({"Hello, ", "World!"}, {}))... 1 = Hello, 2 = World! ] lua_run Print(setmetatable({"Hello, ", "World!"}, {__tostring=function(a) return a[1]..a[2] end})) > Print(setmetatable({"Hello, ", "World!"}, {__tostring=function(a) return a[1]..a[2] end}))... Hello, World! ] lua_run print(setmetatable({"Hello, ", "World!"}, {})) > print(setmetatable({"Hello, ", "World!"}, {}))... table: 0x0f7a37f8 ] lua_run print(setmetatable({"Hello, ", "World!"}, {__tostring=function(a) return a[1]..a[2] end})) > print(setmetatable({"Hello, ", "World!"}, {__tostring=function(a) return a[1]..a[2] end}))... Hello, World! ] lua_run Print(setmetatable({"Hello, ", "World!"}, {__tostring=function(a) return a[1]..a[2] end, __metatable = "nonya business"})) > Print(setmetatable({"Hello, ", "World!"}, {__tostring=function(a) return a[1]..a[2] end, __metatable = "nonya business"}))... Hello, World! ] lua_run Print(getmetatable(setmetatable({"Hello, ", "World!"}, {__tostring=function(a) return a[1]..a[2] end, __metatable = "nonya business"}))) > Print(getmetatable(setmetatable({"Hello, ", "World!"}, {__tostring=function(a) return a[1]..a[2] end, __metatable = "nonya business"})))... nonya business[/code]
Invert player movement through input without messing up ladder interaction [lua]local CUserCmd = FindMetaTable( "CUserCmd" ) function CUserCmd:AddKey( keys ) local newbuttons = bit.bor( self:GetButtons(), keys ) self:SetButtons( newbuttons ) end hook.Add( "CreateMove", "InvertPlayerMovement", function( cmd ) local fr, sd = cmd:GetForwardMove(), cmd:GetSideMove() local frA, bkA, lfA, rtA if cmd:KeyDown( IN_FORWARD ) then -- If the player is walking forward cmd:RemoveKey( IN_FORWARD ) -- Make us think he's not going forward cmd:SetForwardMove( -math.abs(fr) ) -- Make the player walk backwards bkA = true -- We'll need to change the input later end -- Do the same for all other directions if cmd:KeyDown( IN_BACK ) then cmd:RemoveKey( IN_BACK ) cmd:SetForwardMove( math.abs(fr) ) frA = true end if cmd:KeyDown( IN_MOVERIGHT ) then cmd:RemoveKey( IN_MOVERIGHT ) cmd:SetSideMove( -math.abs(sd) ) lfA = true end if cmd:KeyDown( IN_MOVELEFT ) then cmd:RemoveKey( IN_MOVELEFT ) cmd:SetSideMove( math.abs(sd) ) rtA = true end -- Change the input if bkA then cmd:AddKey( IN_BACK ) end if frA then cmd:AddKey( IN_FORWARD ) end if lfA then cmd:AddKey( IN_MOVELEFT ) end if rtA then cmd:AddKey( IN_MOVERIGHT ) end end )[/lua]
-snip, was wrong-
[QUOTE=code_gs;52231792]You're still setting the side and forward moves incorrectly. If someone holds front and back, they're going to move backwards because of how you ordered the speed calls.[/QUOTE] Why would that be the case? GetForwardMove will return 0 and you won't move at all, as I store the value before messing with the input
:snip: [QUOTE=bobbleheadbob;52397964][url]https://wiki.garrysmod.com/page/Global/SortedPairs[/url] :)[/QUOTE] :(
[QUOTE=JasonMan34;52396726]Iterate through a string-indexed table in alphabetical order: Compared to: [/QUOTE] [url]https://wiki.garrysmod.com/page/Global/SortedPairs[/url] :)
I'm sure I've posted this before, but just in case I haven't, here's two things someone might find useful: [URL="https://github.com/MysteryPancake/GMod-Rainbows"]A bunch of functions for drawing rainbow stuff[/URL] [URL="https://github.com/MysteryPancake/GMod-Binding"]A module for binding keys and mouse and controller buttons to run stuff[/URL]
[url]https://gist.github.com/meepdarknessmeep/447587d427553c0277d9cb09d4199258[/url] Something I made in 2015 to statically draw something and store the draw so it doesn't eat frames. Last time I tried it it saved me a ton of fps on even small drawing functions. I also tried it wrong (not making the function outside the hook). I've included the license and an example.
Sorry, you need to Log In to post a reply to this thread.