local function withmultiple (t)
local mt = getmetatable(t) or { }
function mt.__call (self, _, i)
local env = getfenv(2)
local f = debug.getinfo(2, "f").func
return coroutine.wrap(function (t, obj)
repeat
obj = next(t, obj)
if obj then
local prevother = obj.other
obj.other = env
setfenv(f, obj)
coroutine.yield(obj)
obj.other = prevother
end
until not obj
setfenv(f, env)
end), self, i
end
return setmetatable(t, mt)
end
local function withsingle (t)
local mt = getmetatable(t) or { }
function mt.__call (self, _, i)
local env = getfenv(2)
local f = debug.getinfo(2, "f").func
return coroutine.wrap(function ()
local prevother = self.other
self.other = env
setfenv(f, self)
coroutine.yield(self)
self.other = prevother
setfenv(f, env)
end)
end
return setmetatable(t, mt)
end
local objecttypes = { }
local objects = withmultiple(setmetatable({ }, { __index = objecttypes}))
-- this might also work with __index'd methods but i don't want to find out
local function Object (data)
data = withsingle(data or { })
data.id = tostring(data)
data.x = data.x or 0
data.y = data.y or 0
data.type = data.type or "friend"
data.other = "test"
function data.shout () return "HELLO I AM " .. tostring(data):upper() end
function data.setcode (f) data.code = setfenv(f, data) end
data.code = data.code or data.setcode(function () end)
objects[data] = true
if not objecttypes[data.type] then
objecttypes[data.type] = withmultiple { }
end
objects[data.type][data] = true
getmetatable(data).__index = _G
return data
end
Object { x = 16, y = 384, type = "enemy" }
Object { x = 48, y = 48, type = "enemy" }
Object { x = 24, y = 128 }
Object { x = 256, y = 0 }
Object { x = 64, y = 64, type = "enemy" }
local PARENT = Object { x = 56, y = 56, type = "player" }
PARENT.setcode(function (self)
print "----- all objects -----"
for _ in objects() do
print(shout(), x, y, type, other == PARENT and "ok" or "oops")
end
print "----- just enemies -----"
for _ in objects.enemy() do
print(shout(), x, y, type, other == PARENT and "ok" or "oops")
end
print "----- just this object -----"
for _ in self() do
print(shout(), x, y, type, other == PARENT and "ok" or "oops")
end
print "----- nested -----"
for _ in objects.enemy() do
local distance = math.sqrt((x - other.x)^2 + (y - other.y)^2)
if distance < 20 then
for _ in other() do
print(id .. " got shot by " .. other.id)
end
end
end
print "----- check that 'other' is restored when 'with' finishes -----"
for o in pairs(objects) do
print(o, o.other == "test" and "ok" or "oops")
end
end)
-- main loop goes here
PARENT:code()
-- i've never used gm and the manual doesn't really make clear whether the object calling the with is included in 'all' or whatever object type is provided. this code assumes it is... anyway i think i'll stick to explicit referencing
-- also lua users please pretend this code never existed. i don't know if it even works in all cases or is technically correct (i probably missed some things)