1

I am making a roguelike in Love2D as a hobby project. My approach is to try and use as much of the native capabilities of Lua and the Love2D (0.10.1) API as possible, without relying on fancy libraries like middleclass or HUMP, so as to learn more about the language.

After reading PiL's chapters on OOP and seeing the power there, I decided to set up a Mob class (using metamethods to emulate class functionality) that encompasses the players, monsters, and other NPCs (anything that can move). So, far, it's working beautifully, I can create all kinds of instances easily that share methods and all that stuff. But there's a lot of things I don't know how to do, yet, and one of them is holding my prototype up from further progress.

Setting up collision with the map itself wasn't too bad. My maps are tables full of tables full of integers, with 0 being the floor. The game draws "." and "#" and "+" and such to denote various inanimate objects, from each table. Player 1 moves using the numpad, and their position is tracked by dividing their raw pixel position by 32 to create a grid of 32x32 "tiles". Then, inside love.keypressed(key), I have lines like:

if key == "kp8" and currentmap[player1.grid_y - 1][player1.grid_x] == 0 then
        player1.grid_y = player1.grid_y - 1

and so on, with elseifs for each key the player can press. This prevents them from walking through anything that isn't an open floor tile in the map itself.

But, I'm trying to implement some kind of "collision detection" to prevent MOBs from walking through each other and to use in writing the rules for combat, and this is trickier. I had a method in place to calculate the distance between mobs, but I'm told this might eventually cause rounding errors, plus it had to be written for each combination of mobs I want to test, individually.

What I'd like to know is: Is there a known (preferably elegant) way to get all instances of a particular class to pass some number of values to a table?

What I'd like to do is "ask" every Mob on a given map where they are, and have them "report" self.grid_x and self.grid_y to another layer of map that's just for tracking mobs (1 if self.is_here is true, 0 if not, or similar), that gets updated every turn. Then, I could implement collision rules based on coordinates being equal, or maybe a foo.is_here flag or something.

I have only vague ideas about how to proceed, however. Any help would be appreciated, including (and maybe especially) feedback as to a better way to do what I'm trying to do. Thanks!

Apple Baps
  • 37
  • 6

2 Answers2

0

A simple idea is to store "who is here" information for every cell of the field and update this information on every move of every object.

 function create_game_field()
 -- initialize a table for storing "who is here" information
      who_is_here = {}
      for y = 1,24 do
        who_is_here[y] = {}
        for x = 1,38 do
            who_is_here[y][x] = 0
        end
      end
 end

function Mob:can_move(dx, dy)
     local u = currentmap[self.y + dy][self.x + dx]
     local v = who_is_here[self.y + dy][self.x + dx]
     if u == 0 and v == 0 then
          return true
     else
     end
end

function Mob:move(dx, dy)
-- update "who is here"
     who_is_here[self.y][self.x] = 0
     self.x, self.y = self.x + dx, self.y + dy
     who_is_here[self.y][self.x] = 1
end

function Mob:who_is_there(dx, dy) -- look who is standing on adjacent cell
    return who_is_here[self.y + dy][self.x + dx] -- return mob or nil
end

function Mob:roll_call()
    who_is_here[self.y][self.x] = 1
end

Usage example:

-- player1 spawns in at (6,9) on the grid coords
player1 = Mob:spawn(6,9)
-- player1 added to who_is_here
player1:roll_call()

Then, in love.keypressed(key):

if key == "kp8" and player1:can_move(0, -1) then
    player1:move(0, -1)
end
Apple Baps
  • 37
  • 6
Egor Skriptunoff
  • 23,359
  • 2
  • 34
  • 64
  • Troubleshooting the logic, I ended up needing to rewrite `create_game_field()` to include a second for loop (to make it a matrix instead), and made it a zero matrix. Then I rewrote Mob:can_move to generate two local variables u and v, corresponding to the currentmap check and who_is_here check, then return true if they were both 0. Mob:move didn't need any rewrites, but then in `love.keypressed(key)` i just made it so that `if key == "kp8" and player1:can_move(0, -1) then player1:move(0, -1) end` for clarity. I also needed a function to update who_is_here prior to a mob move. But it works! – Apple Baps Sep 05 '16 at 20:29
0

There are a few ways you could get all your instances data but one of the simpler ones is probably to have them all be added to a table when they are created. Providing you add the entire table for that instance, all the values will update in the main table because it acts like a collection of pointers.

function mob:new( x, y, type )
    self.x = 100
    self.y = 200
    self.type = type
    -- any other declarations you need
    table.insert(allMobs, self)
    return self
end

Here we insert all the mobs into the table 'allMobs'. Once we have that we can simply iterate through and get all our coordinates.

for i, v in ipairs(allMobs) do
    local x, y = v.x, v.y
    -- Do whatever you need with the coordinates. Add them to another table, compare
    -- them to others, etc.
end

Now we have a table with all our mobs in it and a way to access each of their positions. If you have any further inquiries then let me know.

Adam
  • 81
  • 5
  • I'll be using a method like this in determining turn order, I think. Thanks for the reminder that `table.insert()` exists, I hadn't looked too closely at that section of the documentation! :) – Apple Baps Sep 05 '16 at 20:24