2

I'm trying to implement a speed-based turn system for a roguelike. I've set up a Mob class using metamethods, so that assigning the following to a variable will spawn a mob into the map at certain grid coordinates:

function Mob:spawn(x,y,m)
    local mob = {}
    setmetatable(mob, Mob)
    mob.x = x
    mob.y = y
    mob.is_monster = m
    return mob
end

Once that's done, I call the following:

function Mob:roll_call()
    who_is_here[self.y][self.x] = self.is_monster
    self.turn_counter = self.turn_counter * math.random(0.9, 1.1)
    table.insert(allTurnCounters, self.turn_counter)
end

This puts the mob's self.turn_counter into a table. Meanwhile, in another module, I've defined these two functions, the heart of the problem:

function turn.decrement_counters(dt) -- runs in Dungeon.update(dt) and subtracts from allTurnCounters
    for i = 1,#allMobsSpawned do
            if allTurnCounters[i] <= 0 then
                    allTurnCounters[i] = 0
                    turn_active = true
                    whose_turn = i
                    return
            elseif allTurnCounters[i] > 0 then
                    allTurnCounters[i] = allTurnCounters[i] - (10 * dt)
            end
    end
end

function turn.whose_is_it() -- called when an entry in allTurnCounters goes zero
    if whose_turn == 1 then -- spots 1 and 2 in the spawn list are only ever for players
            player1.my_turn = true -- turns on player 1's keys
    elseif whose_turn == 2 then
            player2.my_turn = true -- turns on player 2's keys
    elseif whose_turn >= 3 then -- above 3 we're in monster territory

    end
end

I've decided that the first two instances of Mob to be initialized will always be players 1 and 2, assigned to the variables player1 and player2, respectively. And, as it is, it works fine for passing control back and forth between players! But obviously, that's not enough for a fully-featured game. I need monsters, too.

The allTurnCounters table gets new entries, in order, from every mob that spawns (a class which includes both the players and the monsters, so they can share stats). Here's my question: How can I get Lua to dynamically retrieve the name of the table associated with a given turn_counter/value within that table, and use it to take turn priority, even in the event that I don't know what's been procedurally spawned ahead of time or what place it will occupy in the spawn order?

I have 3 ideas, none of which I'm solid on how to implement. One method would be something like sending the entire instance table to another table, rather than just their turn_counters, and then somehow grabbing a pair of values (the table itself and my_turn within the table), updating my_turn's value directly.

Another method might be to use the environment _G... somehow. I'm still poring over Chapter 14 of PiL trying to adapt it to my purposes, but value = _G[varname] seems to be a powerful bit of code I might be able to use for this. Not sure how, just yet.

My last idea was to maybe write some kind of string-sensing find-replace that can grab some other value in each mob's table and then pop it on the front of my_turn. Like, assigning some value with a known pattern for each mob type, that I can use in a string.find and then a string.gsub, to like... manually make the code line read as intended. Seems inelegant, though.

I had good luck with asking my previous Lua/Love2D question here, so I figured let's toss it out there while I'm thinking!

Apple Baps
  • 37
  • 6
  • "How can I get Lua to dynamically retrieve the name of the table associated with a given turn_counter/value within that table[...]?" - 1) tables don't have names, 2) in your code there is no association between the entries in allTurnCounters and other tables/names/anything. – user253751 Sep 08 '16 at 01:18
  • What I mean by "name" is the variable associated with the table, the one used to initialize that instance of the Mob class. So, for instance, player1, or small_ant1 or whatever. I'm aware that those can be redefined and that the reference isn't fixed. But I'm unsure how to use that knowledge constructively for this application, which is why I ask. And that's right, there is not an association between allTurnCounters and anything else. Can you suggest a good way to fix this? – Apple Baps Sep 08 '16 at 02:51

2 Answers2

1

Here is my suggestion for how you should implement this:

  1. Instead of allTurnCounters[i], give mobs a turn_counter property and use allMobsSpawned[i].turn_counter. Then, delete allTurnCounters.

  2. Instead of storing the mob number in whose_turn, store the mob itself. (Note: when I say "the mob itself", it's short for "a reference to the mob itself")

    So instead of

    whose_turn = i
    

    you would have:

    whose_turn = allMobsSpawned[i]
    
  3. Now whose_turn holds the mob whose turn it is. You can easily check whose_turn == player1, whose_turn == player2, etc. As a bonus, it doesn't rely on the players being the first mobs any more.

You can access the mob's properties through whose_turn - if whose_turn == player1 is true for example then whose_turn.x accesses the same field as player1.x

user253751
  • 57,427
  • 7
  • 48
  • 90
  • I figured out a slightly different solution on my own in the intervening time. But your answer has a great suggestion that I'll be using to make my own answer less janky. – Apple Baps Sep 08 '16 at 06:16
0

Here's a somewhat janky solution that can be made more elegant by incorporating the method from the other answer. This is what I came up with on my own while waiting for an answer.

-- in "Mob.lua" module
function Mob:roll_call()
    who_is_here[self.y][self.x] = self.is_monster
    self.turn_counter = self.turn_counter * math.random(0.9, 1.1)
    table.insert(allMobs, {self.name, self.turn_counter})
    if self.is_monster == true then
        table.insert(allMonsters, {self.name, self.turn_counter})
    end
end

    function Mob:move(dx, dy)
     who_is_here[self.y][self.x] = nil
       self.x, self.y = self.x + dx, self.y + dy
       who_is_here[self.y][self.x] = self.is_monster
       self.turn_counter = 1 * self.speed
       for k,v in ipairs(allMobs) do
         if v[1] == self.name then
            v[2] = self.turn_counter
         end
     end
     self.my_turn = false -- flags turn is over
     turn_active = false -- flags no active turn
    end

-- in "turn.lua" module 
function turn.decrement_counters(dt)
    if turn_active == false then
        for k,v in ipairs(allMobs) do
            v[2] = v[2] - (10 * dt)
            if v[2] < 0 then
                v[2] = 0
                turn_active = true
                whose_turn = v[1]
                return
            end
        end
    else turn.whose_is_it()
    end
end

function turn.whose_is_it()
    if whose_turn == player1.name then
        player1.my_turn = true
    elseif whose_turn == player2.name then
        player2.my_turn = true
    elseif whose_turn == ant1.name then
        ant1.my_turn = true
    end
end

turn.whose_is_it() is the part that will need refining. If I use immibis' method of assigning allMobs[i].turn_counter, that will simplify things considerably and allow for future expansion. This answer only works for player1, player2, and an ant called ant1 in particular.

Apple Baps
  • 37
  • 6