roguelike help

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
imgifty
Prole
Posts: 13
Joined: Wed Nov 20, 2013 12:58 pm

roguelike help

Post by imgifty »

Hey guys
Im trying to make a fairly simple roguelike and I need some help, because I somehow can't figure out some thing. On the bottom I attached my game, so you could look at it.
I followed kikito's tile tutorial: https://github.com/kikito/love-tile-tutorial/wiki , but now I have some questions that I'd like to get answered.

At the moment I got a working mainmenu, a string map that get's displayed with quads and I would like to add the other essential stuff. At the moment I got a player stored in a table and I got just simple movement. I still dont have any collision with the Map features and I really want to add that, yet I'm not really sure how I would implement it. I want to check:

Code: Select all

if tileTable[columnIndex][rowIndex] == "#" then
do something -> not walkable dont know how to do that
end
I also would really like to know how I would go about the player? Should I store everythign in tables or even classes? Also would it be possible to make "pseudo random map" by just having different stringmaps and then opening them randomly?
How would I go best about generating monsters or different tiles, that can do something, like coins items etc.

Sorry if my english is bad but I'd be really thankful if you could help me. Oh yea I had to upload it on a nother website because the file was over 10mb.

http://www.file-upload.net/download-870 ... .love.html
User avatar
Sheepolution
Party member
Posts: 264
Joined: Mon Mar 04, 2013 9:31 am
Location: The Netherlands
Contact:

Re: roguelike help

Post by Sheepolution »

imgifty wrote: At the moment I got a player stored in a table and I got just simple movement. I still dont have any collision with the Map features and I really want to add that, yet I'm not really sure how I would implement it. I want to check:

Code: Select all

if tileTable[columnIndex][rowIndex] == "#" then
do something -> not walkable dont know how to do that
end
You should make a variable that keeps track of the current tile you're at. Currently your player starts at tile 791. The map is 50 tiles width. So when you move up, you do playerTile = playerTile - 50 , and + 50 for down. And -1 / +1 for left / right.
If you have that you can use it to determine if the tile you want to go to is a "#". I haven't looked into much, but you should be able to do something like..

Code: Select all

--If the current tile, - 50, which is the player going up, is NOT a wall..
if map[playerTile - 50] ~= "#" then
   playertest.y = playertest.y - tile
   playerTile = playerTile - 50
end
imgifty wrote: I also would really like to know how I would go about the player? Should I store everythign in tables or even classes?
I always recommend people using classes. But you can use whatever works for you.
imgifty wrote: Also would it be possible to make "pseudo random map" by just having different stringmaps and then opening them randomly?
You could make a table where you put in all the levels, then you choose one randomly by level = levelList[math.random(#levelList)]
imgifty wrote: How would I go best about generating monsters or different tiles, that can do something, like coins items etc.
You could make multiple layers. Although that might get a bit too advanced, but then you make an entity layer, collision layer, background layer.
imgifty
Prole
Posts: 13
Joined: Wed Nov 20, 2013 12:58 pm

Re: roguelike help

Post by imgifty »

Hey there thank you very much with your help sheepolution! I used this here from what u suggested me and it almost works. I still have to edit the rest tomorrow.
Though the only way I could do it was with an 2d array without the 791 tile you were telling me about. I also wanted to ask how i should manage global variables? Do i just load all of my numberse in the love.load()? For example I put it in my at the moment tile location:

Code: Select all

playertilex = 26
	playertiley = 1
to my love.load() it didnt seem to work otherwise when I had it in the function playercheck...

Otherwise I also want to maybe use my player in classes hwo would I go about that? I read a OOP tutorial for lua but it didnt make much sense at the moment. I also dont see any benefits that it would give me :s Also could someone elaborate on adding items, monsters? Can I randomly place them into my 2d array where eg. the tile is '.' ? Do i have to make a tile for them own?

Code: Select all

function playercheck(dx,dy)

	dx = 0
	dy = 0

	--playertile = 791

	
	love.graphics.setColor(0,255,0)
	love.graphics.print("$",(playertilex-1)*12+200,(playertiley-1)*12)

	if tileTable[playertilex][playertiley-1] == '#' then --UP
		love.graphics.setColor(255,0,0)
		--return false
		love.graphics.print("$",(playertilex-1)*12+200,(playertiley-2)*12)
		--playertile = playertile - 50
	elseif tileTable[playertilex][playertiley+1] == '#' then --DOWN
		love.graphics.setColor(255,0,0)
		--return false
		love.graphics.print("$",(playertilex-1)*12+200,(playertiley)*12)

	elseif tileTable[playertilex+1][playertiley] == '#' then --RIGHT
		love.graphics.setColor(255,0,0)
		--return false
		love.graphics.print("$",(playertilex)*12+200,(playertiley-1)*12)
	
	elseif tileTable[playertilex-1][playertiley] == '#' then --LEFT
		love.graphics.setColor(255,0,0)
		--return false
		love.graphics.print("$",(playertilex-2)*12+200,(playertiley-1)*12)

	end

	if tileTable[playertilex + dx][playertiley + dy] == '#' then
		return false
	end
		return true
	

end
Oh yea in the love.mouse stuff I then added playercheck(dx,dy) for every direction.

Hmm weird my code doesnt really work atm. I set the tile location with the green dollar sign and if it has a wall around it it should draw a red dollar sign but it doesnt always work weird. Ill look at it tomorrow i guess

THANKS for your help
Attachments
luatest.love
(33.83 KiB) Downloaded 185 times
imgifty
Prole
Posts: 13
Joined: Wed Nov 20, 2013 12:58 pm

Re: roguelike help

Post by imgifty »

Okay I edited now. Now it shows wich tiles next to the player are solid or not. Yet I still got a problem with the movement. It still somehow moves into the solid tile and from there on you can't move anymore. It's weird and I'm not really sure what I'm doing wrong.

I attached my game file. Not really sure what's causing the problem. It seems to know wich one is solid ,becaues once I move into the solid block it isnt able to move anymore.

Help please :shock: Thanks
Attachments
luatest.love
(31.68 KiB) Downloaded 180 times
User avatar
SneakySnake
Citizen
Posts: 94
Joined: Fri May 31, 2013 2:01 pm
Contact:

Re: roguelike help

Post by SneakySnake »

Code: Select all

function playercheck(dx,dy)

	--TILETABLE[Y][X] FK ME

	local dx,dy = 0,0
The function parameters are never used, since the local dx and dy override them.
And both always have the value 0.
imgifty
Prole
Posts: 13
Joined: Wed Nov 20, 2013 12:58 pm

Re: roguelike help

Post by imgifty »

Okay thanks the walking works now. I would now like to add maybe random items on the map? Like goldcoins that spawn on the map. How would I do that?

I thought about this:

Code: Select all

 if tileTable[columnIndex][rowIndex] == "." and love.math.random(10) == 10 then
		love.graphics.setColor(255,0,0)
		love.graphics.rectangle("fill",12*(columnIndex-1)+200,12*(rowIndex-1),12,12)
	end
Soo I check the tiles on my 2d array that are '.' and then with a change of 10% print out a rectangle onthat spot. Somehow the code doesnt work is there a better way to do this? I added this to the newMap function.

Code: Select all

function newMap(tileWidth, tileHeight, tilesetPath, tileString, quadInfo)
  
  tileW = tileWidth
  tileH = tileHeight
  tileset = love.graphics.newImage(tilesetPath)
  
  local tilesetW, tilesetH = tileset:getWidth(), tileset:getHeight()
  
  quads = {}
  
  for _,info in ipairs(quadInfo) do
    -- info[1] = the character, info[2] = x, info[3] = y
    quads[info[1]] = love.graphics.newQuad(info[2], info[3], tileW, tileH, tilesetW, tilesetH)
  end
  
  tileTable = {}
  
  local width = #(tileString:match("[^\n]+"))

  for x = 1,width,1 do tileTable[x] = {} end

  local rowIndex,columnIndex = 1,1
  for row in tileString:gmatch("[^\n]+") do
    assert(#row == width, 'Map is not aligned: width of row ' .. tostring(rowIndex) .. ' should be ' .. tostring(width) .. ', but it is ' .. tostring(#row))
    columnIndex = 1
    for character in row:gmatch(".") do
      tileTable[columnIndex][rowIndex] = character
      

      if tileTable[columnIndex][rowIndex] == "." and love.math.random(10) == 10 then
		love.graphics.setColor(255,0,0)
		love.graphics.rectangle("fill",12*(columnIndex-1)+200,12*(rowIndex-1),12,12)
	end

	columnIndex = columnIndex + 1

	
      --love.graphics.setColor(255,255,255)
      --love.graphics.print(character,100,100)

    end
    rowIndex=rowIndex+1
  end

end

Could I then also just edit the '.' character value in my 2d array?

Thanks for your help guys :awesome:
User avatar
nfey
Citizen
Posts: 63
Joined: Tue Feb 18, 2014 9:50 am
Location: Romania

Re: roguelike help

Post by nfey »

I understand you added the first code block to the newMap() function. Where does the newMap() function get called?

Until you reply, I'll chime in with some general advice:

The love.graphics.whatever calls are used to draw stuff. You should only use these in the love.draw() function. But in order to call these methods in love.draw() you need to have the information for what you're trying to draw (in your case, the pickups and the rectangles representing them), right? The instinct is to calculate these parameters (location for pickup, etc.) on the spot and then draw them. But the problem is that the draw function is called dozens of times per second. If you have a math.random() call inside the love.draw() call, calculating the chance of something being drawn, this means that you will get a different result for every screen redraw (i.e. every frame).

The solution is to separate your initial game state loading (love.load()), your game state changing logic(love.update()) and your drawing logic (love.draw()). In the case of your pickup, the flow would be like this: you calculate the chance for the pickup to be spawned in love.load() and save the result in a variable (or on the array representing the world map); you update this information from the world map in love.update(), if the player walks over the pickup for example you need to delete it from the world and change the player's state somehow - his health or power or whatever; then, in love.draw(), you use the information from the world map to draw the pickup, and the pickup's state is being updated already in love.update() so you will always draw the correct representation.

I don't know where you're calling your newMap() method, but it seems to me like you're mixing up these 3 phases.
imgifty
Prole
Posts: 13
Joined: Wed Nov 20, 2013 12:58 pm

Re: roguelike help

Post by imgifty »

nfey wrote:I understand you added the first code block to the newMap() function. Where does the newMap() function get called?

Until you reply, I'll chime in with some general advice:

The love.graphics.whatever calls are used to draw stuff. You should only use these in the love.draw() function. But in order to call these methods in love.draw() you need to have the information for what you're trying to draw (in your case, the pickups and the rectangles representing them), right? The instinct is to calculate these parameters (location for pickup, etc.) on the spot and then draw them. But the problem is that the draw function is called dozens of times per second. If you have a math.random() call inside the love.draw() call, calculating the chance of something being drawn, this means that you will get a different result for every screen redraw (i.e. every frame).

The solution is to separate your initial game state loading (love.load()), your game state changing logic(love.update()) and your drawing logic (love.draw()). In the case of your pickup, the flow would be like this: you calculate the chance for the pickup to be spawned in love.load() and save the result in a variable (or on the array representing the world map); you update this information from the world map in love.update(), if the player walks over the pickup for example you need to delete it from the world and change the player's state somehow - his health or power or whatever; then, in love.draw(), you use the information from the world map to draw the pickup, and the pickup's state is being updated already in love.update() so you will always draw the correct representation.

I don't know where you're calling your newMap() method, but it seems to me like you're mixing up these 3 phases.
Hey there I solved that problem I had. I now can add different items and pick them up. Now I wanted to add enemies that get spawned randomly on the map. I got pretty close I think, but somehow it only draws one enemy instead of all and then when I press space again for a new enemy it just overwrites the old one.
I am first randomly selecting a tilex and y and then check if they are ' ' and yea that part doesnt really work. Then I make a table with the x and y.
When space is pressed it inserts into a empty table and writes the enemy table we just created. To draw then I iterate through the enemies table and draw them.

Code: Select all

--enemies.lua
require'map'

function createEnemy()
	
	local maxtilesx = 50
	local maxtilesy = 30
	local tileheight = 12
	local enemyx = math.random(1,maxtilesx)
	local enemyy = math.random(1,maxtilesy)

	--[[while checkEnemy(enemyx,enemyy) == false do
		enemyx = math.random(1,maxtilesx)
		enemyy = math.random(1,maxtilesy)
	end

	enemy = {}

	enemy.x = enemyx
	enemy.y = enemyy

	return enemy]]


	if checkEnemy(enemyx,enemyy) == true then
		createEnemy()
	elseif checkEnemy(enemyx,enemyy) == false then
		enemy = {}
		enemyx = math.random(1,maxtilesx)
		enemyy = math.random(1,maxtilesy)
		--enemy.x = 12*(enemyx-1)+200
		--enemy.y = 12*(enemyy-1)
		enemy.x = enemyx
		enemy.y = enemyy
	end

	return enemy
end

Code: Select all

--map.lua has more in it with the map generation etc. 
function checkEnemy(columnIndex,rowIndex)
	if tileTable[columnIndex][rowIndex] ~= ' ' then
		return true
	end

	return false
end

Code: Select all

--output.lua
-- gets called in love.draw()

function output.enemies()
	
	local tileW, tileH = 12,12
	local tilesetW, tilesetH = tilesheet:getWidth(), tilesheet:getHeight()
	enemyquad = love.graphics.newQuad(12,  0, tileW, tileH, tilesetW, tilesetH)



	for i, v in ipairs(enemies) do

			local x,y = (enemy.x-1)*tileW+200, (enemy.y-1)*tileH+0
		
		
		love.graphics.draw(tilesheet,enemyquad,x,y) -- Then draw them.
	end

end

Also they seem to spawn in places where the floor isnt a space ' '.
Attachments
helplua.love
(6.01 MiB) Downloaded 178 times
User avatar
SneakySnake
Citizen
Posts: 94
Joined: Fri May 31, 2013 2:01 pm
Contact:

Re: roguelike help

Post by SneakySnake »

imgifty wrote:somehow it only draws one enemy instead of all and then when I press space again for a new enemy it just overwrites the old one.

Code: Select all

function output.enemies()

	local tileW, tileH = 12,12
	local tilesetW, tilesetH = tilesheet:getWidth(), tilesheet:getHeight()
	enemyquad = love.graphics.newQuad(12,  0, tileW, tileH, tilesetW, tilesetH)



	for i, v in ipairs(enemies) do

			local x,y = (enemy.x-1)*tileW+200, (enemy.y-1)*tileH+0


		love.graphics.draw(tilesheet,enemyquad,x,y) -- Then draw them.
	end

end
Notice how you are using enemy.x, enemy.y instead of v.x, v.y.
The reason why this does not cause a nil error is because you have a global enemy variable that you create in the createEnemy() function. You really should not use global variables, unless truly necessary (which is very rarely the case).
imgifty wrote: Also they seem to spawn in places where the floor isnt a space ' '.

Code: Select all

elseif checkEnemy(enemyx,enemyy) == false then
		enemy = {}
		enemyx = math.random(1,maxtilesx)
		enemyy = math.random(1,maxtilesy)
		--enemy.x = 12*(enemyx-1)+200
		--enemy.y = 12*(enemyy-1)
		enemy.x = enemyx
		enemy.y = enemyy
	end
Here, after you have determined that the position is indeed a ' ', you go ahead and assign a new, completely random position, rendering the check you just did useless.
imgifty
Prole
Posts: 13
Joined: Wed Nov 20, 2013 12:58 pm

Re: roguelike help

Post by imgifty »

Thanks the enemies work now. I also added a random enemy generator. Could anyone explain to me how I woudl implement an inventory system? I mean a really simple one for example like this:

Every item you pick up always lands in your inventory. The inventory you have only has one space for every type of item so say weapon, armor etc. So once you pick up a new armor your old one gets deleted.

How would I add that? I guess I have to make one big table with all the items then one with the inventory. Then when I pick up an item check wether that type of item exists already. If it does remove old one from table and insert new item. Adjust new stats: Dmg, critchance etc.

Could anyone give me an example how I would do that? Thanks

Oh yea also for my monsters I also wanted to use the same tiles im using for my map. Now the problem is I somehow cant access those quads. :?

I tried doing it like in the beggining of my drawMap but it didnt seem to work. I dont want to define everything again for my items too i want all quads in one place. Enemies, items, tiles.

Thanks for your help. Also I added enemies that I can fight and fight me. How can I make them follow me? I heard from Jumper but I have no idea how I would implement it in my project.

I also could add portals that dont do much but are cool :P

Thanks for your help! :awesome: :ultrahappy:

I attached my current game. Image
Attachments
game.love
(149.11 KiB) Downloaded 172 times
Post Reply

Who is online

Users browsing this forum: Amazon [Bot] and 5 guests