This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Joe3000's profile picture

How to create multiple "enemies" from one table

Started by Joe3000, 03 June 2013 - 10:52 PM
Joe3000 #1
Posted 04 June 2013 - 12:52 AM
So I gave up on Tetris (too dificult) and I just decided to make my own game, it's not complete yet but the basic stuff is there. The one thing I do want is that the "Enemy" will sometimes be more than 1. Say if you get 200 points and then a second enemy comes along. How would I do that? Here is the code….

l,h = term.getSize()
running = true
refreshRate = .5
gtID = os.startTimer(refreshRate)
local score = 00
local health = 100

player = {
  xpos = math.floor(l/2);
  ypos = h -2;
  length = 1;

  update = function(self, key)
    if key == keys.left and self.xpos > 1 then
	  self.xpos = self.xpos - 1
    elseif key == keys.right and self.xpos < l then
	  self.xpos = self.xpos + 1
    elseif key == keys.up and self.ypos > 1 then
	  self.ypos = self.ypos - 1
    elseif key == keys.down and self.ypos < h then
	  self.ypos = self.ypos + 1
    end
  end;

  draw = function(self)
    term.setCursorPos(self.xpos, self.ypos)
    term.setBackgroundColour(colours.white)
    term.write(string.rep(" ", self.length))
  end;
}

food = {
  xpos = math.random(1, l);
  ypos = math.random(1, h);
  amount = 1;

  updateFood = function(self)
  
    if self.xpos == player.xpos and self.ypos == player.ypos then
	  score = score + 20
	  self.amount = 0
	  self.xpos = math.random(1, l)
	  self.ypos = math.random(1, h)
    end
  end;

  drawFood = function(self)
    if self.amount == 0 then
	  self.amount = 1
    elseif self.amount == 1 then
	  term.setCursorPos(self.xpos, self.ypos)
	  term.setBackgroundColor(colours.green)
	  term.write(" ")
    end
  end;
  
}

enemy = {
  xpos = math.random(l);
  ypos = math.random(h);

  updateEnemy = function(self)
    if self.xpos < player.xpos then
	  self.xpos = self.xpos + 1
    elseif self.xpos > player.xpos then
	  self.xpos = self.xpos - 1
    end
    if self.ypos < player.ypos then
	  self.ypos = self.ypos + 1
    elseif self.ypos > player.ypos then
	  self.ypos = self.ypos - 1
    end
  
    if self.xpos == player.xpos and self.ypos == player.ypos then
	  health = health - 20
	  sleep(.01)
    end
  end;

  drawEnemy = function(self)
    term.setCursorPos(self.xpos, self.ypos)
    term.setBackgroundColour(colours.magenta)
    term.write(" ")
  end;

}

function gameDraw()
  term.setBackgroundColour(colours.black)
  term.clear()
  term.setCursorPos(1, 1)
  term.write("Health is "..health)
  term.setCursorPos(1, 2)
  term.write("Score is " ..score)
  player:draw()
  food:drawFood()
  enemy:drawEnemy()
end

function gameUpdate()
  local id, p1 = os.pullEvent()
  food:updateFood()
  if id == "key" then
    if p1 == keys.q then running = false end
    player:update(p1)
  end 
  if id == "timer" and p1 == gtID then
    enemy:updateEnemy()
    gtID = os.startTimer(refreshRate)
  end
end

function gameLoop()
  while running do
    gameUpdate()
    gameDraw()
    if health == 0 then
	  running = false
    end
  end
end

gameLoop()
term.setBackgroundColour(colours.black)
term.setCursorPos(math.floor(l/2) - #"Game Over"/2 + 1, math.floor(h/2))
term.write("Game Over")
sleep(3)
shell.run("clear")
sleep(0.01)
Goof #2
Posted 04 June 2013 - 08:35 AM
Just add the enemy into a table again. (enemy = { ["enemy_1"] = { }, })

local enemy = {
  ["enemy_1"] = {
    xpos = math.random(l);
    ypos = math.random(h);

    updateEnemy = function(self)
      if self.xpos < player.xpos then
            self.xpos = self.xpos + 1
      elseif self.xpos > player.xpos then
            self.xpos = self.xpos - 1
      end
      if self.ypos < player.ypos then
            self.ypos = self.ypos + 1
      elseif self.ypos > player.ypos then
            self.ypos = self.ypos - 1
      end

      if self.xpos == player.xpos and self.ypos == player.ypos then
            health = health - 20
            sleep(.01)
      end
    end;

    drawEnemy = function(self)
      term.setCursorPos(self.xpos, self.ypos)
      term.setBackgroundColour(colours.magenta)
      term.write(" ")
    end;
  },
  ["enemy_2"] = {



  }, 
}
Joe3000 #3
Posted 04 June 2013 - 06:12 PM
is that the only way to do it? I was hoping that I could make certain enemies appear at different times and disapear later
Bomb Bloke #4
Posted 04 June 2013 - 08:02 PM
There are lots of ways of doing it, but defining them manually isn't ideal. You want to do so automatically, a process which is going to be much harder if you've got functions in the table too.

If you want a system that can add enemies as the game goes on, you'll also want a system that can remove them. Two statements come in handy here: #<tablename> returns the amount of indexes in a table before the first one that contains nil. table.getn(<tablename>) returns the highest non-nil index.

So, say you construct the following table:

mytable = {34,nil,12}

#mytable would return 1. table.getn(mytable) would return 3. The actual number of non-nil indexes in the table would be 2, something you'll need to track yourself because Lua doesn't have a function to return that.

With that in mind, consider what truly unique bits of information apply to each enemy: in your current code, there's only two variables that set any enemy apart from another, and that's their X/Y co-ords.

Start your program by defining an empty enemy table, a variable to track how many enemies are in play, and your score:

local enemy = {}
local enemyCount, score = 0, 0

Now we need a function to create an enemy in the table.

local function spawnEnemy()
  -- "#enemy + 1" gets the lowest index in the enemy table that's currently nil.
  enemy[#enemy + 1] = {xpos = math.random(l), ypos = math.random(h)}
  enemyCount = enemyCount + 1
end

And a function to make sure a certain number of enemies are in play, depending on your current score.

local function spawnEnemies()
  -- We want at least five enemies in play no matter what.
  while enemyCount < 5 do spawnEnemy() end

  -- If your score is more then 50, let's make that 10 enemies.
  if score > 50 then while enemyCount < 10 do spawnEnemy() end end

  -- Hit 100, and it's 20 enemies.
  if score > 100 then while enemyCount < 20 do spawnEnemy() end end

  -- etc
end

You could pass arguements to the spawnEnemy() function based on score, and have those added to the enemy table along with their positions. This'd allow you to eg change the colour of enemies as time goes on.

You can probably guess what the enemy despawn function looks like. You can come up with your own conditions as to when it's called. Maybe give each enemy a counter that counts towards 0?

local function killEnemy(deadEnemyIndex)
  if enemy[deadEnemyIndex] ~= nil then  -- You cannot kill that which is dead...
    enemy[deadEnemyIndex] = nil
    enemyCount = enemyCount - 1
  end
end

Your enemy updater function (externalised from all tables) gets rigged to update ALL enemies in play:

local function updateEnemies()
  for i=1,table.getn(enemy) do if enemy[i] ~= nil then  -- Loop through ALL valid enemies in play
    if enemy[i].xpos < player.xpos then
      enemy[i].xpos = enemy[i].xpos + 1
    elseif enemy[i].xpos > player.xpos then
      enemy[i].xpos = enemy[i].xpos - 1
    end
    if enemy[i].ypos < player.ypos then
      enemy[i].ypos = enemy[i].ypos + 1
    elseif enemy[i].ypos > player.ypos then
      enemy[i].ypos = enemy[i].ypos - 1
    end

    if enemy[i].xpos == player.xpos and enemy[i].ypos == player.ypos then
      health = health - 20
      sleep(.01)
    end
  end end
end

Finally, in your game update function, you'll now be running something like this:

  if id == "timer" and p1 == gtID then
    spawnEnemies()
    updateEnemies()
    gtID = os.startTimer(refreshRate)
  end

That should keep you going for a while. :)/>