Inventory system

Show off your games, demos and other (playable) creations.
User avatar
steVeRoll
Party member
Posts: 131
Joined: Sun Feb 14, 2016 1:13 pm

Inventory system

Post by steVeRoll »

I am kinda proud of myself because this is my first ever complete project which uses modules correctly.
This is an inventory system where you can move and replace items in the inventory and see their name and description.
I even think that soon this will be an inventory api that is easy to implement.
And before you say anything -
I know, the code is terrible.
So if you have any improvements (and I know you do) - fell free to tell me about them.
I also haven't found any bugs yet (!!!), so you can tell me about any you find.
I hope you like it!

Instructions -
Click on an item once to select it. Click anywhere you want in the inventory to move the selected item there.
When you roll over an item, info about it will be displayed on the right.
Attachments
inventory.love
(7.78 KiB) Downloaded 504 times
User avatar
zorg
Party member
Posts: 3444
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Inventory system

Post by zorg »

Not bad!

Though to be honest, when you said "uses modules currently", at first, i was worried you might've been using the lua module stuff, which has been deprecated since forever; glad it's not the case. :3

One thing though, in many other threads one specific line of code surfaced:

Code: Select all

local classOrWhatever = {}
classOrWhatever.__index = classOrWhatever
Now, some might say that this does absolutely nothing (apart from creating a circular reference), and on the whole, it doesn't; but it has exactly one use:

Code: Select all

local new = function(possiblyArgs) -- a constructor for object instances, or something similar
  local instance = setmetatable({}, classOrWhatever)
  return instance
end
Whether one would write the setmetatable call on a separate line from the local empty table creation or not is mostly irrelevant; it won't make any dents in code speed.

However, the __index metamethod is used to look up stuff from another table, if the table the metamethod is defined for doesn't have it.

The issue with the above way is that you're basically denying further lookups; if you don't plan on having "class" hierarchies, then it's fine, but i myself prefer to keep my options open, hence a slightly different code:

Code: Select all

local classOrWhatever = {}

-- define functions working on instances with self param or colon notation, whichever one finds more understandable.

local mtClassOrWhatever = {__index = classOrWhatever} -- The metatable that the instances used is defined separately from the table where funtions (and possibly class fields/variables) are located.

local new = function(possiblyArgs)
  local instance = {}
  setmetatable(instance, mtClassOrWhatever)
  return instance
end

return new -- either this, or you make the constructor part of the classOrWhatever table, and return that... you can also go further and have a __call metatable defined, which would call new when you'd do classOrWhatever() where you required this file.
end
This way, if i ever wanted these instances to look up a function from another class after it's not found in classOrWhatever, i could just set the metatable for that table to be the other class, which i'd require in this file.

Sorry if this is a bit much at once, but i thought i'd share my insights. :P
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
steVeRoll
Party member
Posts: 131
Joined: Sun Feb 14, 2016 1:13 pm

Re: Inventory system

Post by steVeRoll »

zorg wrote: Sat Dec 17, 2016 4:37 pm Not bad!

Though to be honest, when you said "uses modules currently", at first, i was worried you might've been using the lua module stuff, which has been deprecated since forever; glad it's not the case. :3

One thing though, in many other threads one specific line of code surfaced:

Code: Select all

local classOrWhatever = {}
classOrWhatever.__index = classOrWhatever
Now, some might say that this does absolutely nothing (apart from creating a circular reference), and on the whole, it doesn't; but it has exactly one use:

Code: Select all

local new = function(possiblyArgs) -- a constructor for object instances, or something similar
  local instance = setmetatable({}, classOrWhatever)
  return instance
end
Whether one would write the setmetatable call on a separate line from the local empty table creation or not is mostly irrelevant; it won't make any dents in code speed.

However, the __index metamethod is used to look up stuff from another table, if the table the metamethod is defined for doesn't have it.

The issue with the above way is that you're basically denying further lookups; if you don't plan on having "class" hierarchies, then it's fine, but i myself prefer to keep my options open, hence a slightly different code:

Code: Select all

local classOrWhatever = {}

-- define functions working on instances with self param or colon notation, whichever one finds more understandable.

local mtClassOrWhatever = {__index = classOrWhatever} -- The metatable that the instances used is defined separately from the table where funtions (and possibly class fields/variables) are located.

local new = function(possiblyArgs)
  local instance = {}
  setmetatable(instance, mtClassOrWhatever)
  return instance
end

return new -- either this, or you make the constructor part of the classOrWhatever table, and return that... you can also go further and have a __call metatable defined, which would call new when you'd do classOrWhatever() where you required this file.
end
This way, if i ever wanted these instances to look up a function from another class after it's not found in classOrWhatever, i could just set the metatable for that table to be the other class, which i'd require in this file.

Sorry if this is a bit much at once, but i thought i'd share my insights. :P
Thank you for your reply zorg! I know I should've replied earlier, but now I became interested when you said "...the lua module stuff, which has been deprecated since forever". Can you explain to me what you meant by that, and what kind of modules I have been using in this code?
Thanks!
User avatar
s-ol
Party member
Posts: 1077
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Re: Inventory system

Post by s-ol »

zorg wrote: Sat Dec 17, 2016 4:37 pm Not bad!

Though to be honest, when you said "uses modules currently", at first, i was worried you might've been using the lua module stuff, which has been deprecated since forever; glad it's not the case. :3

One thing though, in many other threads one specific line of code surfaced:

Code: Select all

local classOrWhatever = {}
classOrWhatever.__index = classOrWhatever
Now, some might say that this does absolutely nothing (apart from creating a circular reference), and on the whole, it doesn't; but it has exactly one use:

Code: Select all

local new = function(possiblyArgs) -- a constructor for object instances, or something similar
  local instance = setmetatable({}, classOrWhatever)
  return instance
end
Whether one would write the setmetatable call on a separate line from the local empty table creation or not is mostly irrelevant; it won't make any dents in code speed.

However, the __index metamethod is used to look up stuff from another table, if the table the metamethod is defined for doesn't have it.

The issue with the above way is that you're basically denying further lookups; if you don't plan on having "class" hierarchies, then it's fine, but i myself prefer to keep my options open, hence a slightly different code:

Code: Select all

local classOrWhatever = {}

-- define functions working on instances with self param or colon notation, whichever one finds more understandable.

local mtClassOrWhatever = {__index = classOrWhatever} -- The metatable that the instances used is defined separately from the table where funtions (and possibly class fields/variables) are located.

local new = function(possiblyArgs)
  local instance = {}
  setmetatable(instance, mtClassOrWhatever)
  return instance
end

return new -- either this, or you make the constructor part of the classOrWhatever table, and return that... you can also go further and have a __call metatable defined, which would call new when you'd do classOrWhatever() where you required this file.
end
This way, if i ever wanted these instances to look up a function from another class after it's not found in classOrWhatever, i could just set the metatable for that table to be the other class, which i'd require in this file.

Sorry if this is a bit much at once, but i thought i'd share my insights. :P
you can btw still do that the way he is doing, if I understand your post correctly:

Code: Select all

local Base = {}
Base.__index = Base

function Base.new()
  local self = setmetatable({}, Base)
  self.bar = 2
  return self
end

function Base:foo()
  print(self:getBar())
end

function Base:getBar()
  -- (not something i would normally write but good for illustration)
  return self.bar
end

--

local Sub = setmetatable({}, Base)
Sub.__index = Sub

function Sub.new()
  local self = setmetatable(Base.new(), Sub)
  self.spam = 'eggs'
  return self
end

function Sub:foo()
  Base.foo(self)
  print('but also ' .. self.spam)
end

--

sub = Sub.new()
sub:getBar() -- 2
sub:foo() -- prints '2 but also eggs'

This is the way I've been doing it for a while, and I do prefer it because I think the metatable - 'instance' mapping should be one to many and not many to many.

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
User avatar
zorg
Party member
Posts: 3444
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Inventory system

Post by zorg »

steVeRoll: In the past, lua had a module keyword that was probably used for modules... i honestly don't know much about it, since when that was indeed a thing, i wasn't using lua yet. Point is, you are not using that, which basically is good. That's all i meant. :3

s-ol: Call it personal preference, but i just don't like mixing metatable stuff into the same table that i'd set itself as a metatable on.
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
s-ol
Party member
Posts: 1077
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Re: Inventory system

Post by s-ol »

zorg wrote: Sat Feb 18, 2017 3:37 pm s-ol: Call it personal preference, but i just don't like mixing metatable stuff into the same table that i'd set itself as a metatable on.
Sure, just wanted to show it's not necessarily a worse approach :)

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: Inventory system

Post by airstruck »

zorg wrote:s-ol: Call it personal preference, but i just don't like mixing metatable stuff into the same table that i'd set itself as a metatable on.
That used to bug the hell out of me too, but I've come around to it in at least one case. I can't find any shorter way to write this boilerplate:

Code: Select all

local function new (t, ...) t = setmetatable({}, t) t:init(...) return t end
local Foo = { __call = new }
Foo.__index = Foo
But yeah, as s-ol mentioned using the table as its own meta-index doesn't prevent creating inheritance chains. Can simply add a line like setmetatable(Foo, SuperFoo). Not sure if that's what you meant by "denying further lookups," but that part did make it sound like it would prevent inheritance somehow rather than just being about personal preference.
User avatar
zorg
Party member
Posts: 3444
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Inventory system

Post by zorg »

Yeah, it was probably just me making a mistake in my thought processes; after i thought about it a bit.
I was thinking about something like

Code: Select all

local newB, ClassB = require "ClassB"

local ClassA = setmetatable({}, {__index = ClassB})
-- fill it up with functions
local newA = function()
  local instance = setmetatable({}, {__index = ClassA})
  -- fill it up with instance fields
  return instance
end

return newA, ClassA
So basically, instances have their metatable be their own class' metatable, but the class metatable itself inherits from another class.
Also, the bigger reason i always want to define -one- variable that has {__index = something} in it is because if i do that in the constructor function, the metatables themselves will be different tables in memory, as opposed to re-using a single one.
Please do correct me if i'm wrong though! :3
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
User avatar
s-ol
Party member
Posts: 1077
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

Re: Inventory system

Post by s-ol »

zorg wrote: Sun Feb 19, 2017 3:58 pm So basically, instances have their metatable be their own class' metatable, but the class metatable itself inherits from another class.
Also, the bigger reason i always want to define -one- variable that has {__index = something} in it is because if i do that in the constructor function, the metatables themselves will be different tables in memory, as opposed to re-using a single one.
Interesting, you would rather have that? Sharing the same mt between all my instances is the exact reasoning that makes me prefer this style.

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
  baby:hurt(me)
end
User avatar
airstruck
Party member
Posts: 650
Joined: Thu Jun 04, 2015 7:11 pm
Location: Not being time thief.

Re: Inventory system

Post by airstruck »

Sharing the same mt between all my instances is the exact reasoning that makes me prefer this style.
But you can share the same metatable for all instances either way.

Code: Select all

local Foo = {}
local FooMeta = { __index = Foo }
local function new (_, ...) local t = setmetatable({}, FooMeta) t:init(...) return t end
setmetatable(Foo, { _call = new })
One nice thing about using the table as its own meta-index is it makes it easy to write that "new" function in a reusable way.

Code: Select all

local function new (t, ...) t = setmetatable({}, t) t:init(...) return t end

local Vehicle = {}
Vehicle.__index = Vehicle
setmetatable(Vehicle, { __call = new })

local Bike = {}
Bike.__index = Bike
setmetatable(Bike, { __call = new, __index = Vehicle })
The drawback is that it's surprisingly difficult to reason about for such a simple thing. If someone like zorg who pretty well knows what he's doing can get confused by it (yes, it's happened to me too), you can bet that people who are new to Lua might not fully understand what's going on. That might be a good enough reason to keep the prototype table separate from the metatable outside of special cases like in this example (i.e. "class boilerplate").
Post Reply

Who is online

Users browsing this forum: No registered users and 6 guests