~johanvandegriff/ruegolike

1e2ec350d77628da2f55adccfb987893875e761f — Johan Vandegriff 1 year, 5 months ago e9fb6d0
added multiple levels and up/down stairs
4 files changed, 61 insertions(+), 41 deletions(-)

M DEV.md
M display.go
M generate.go
M main.go
M DEV.md => DEV.md +3 -2
@@ 36,9 36,10 @@ Update: looks like RogueBasin has a similar [article](http://roguebasin.roguelik
### generation
* generate better looking levels
* up stairs and down stairs should be connected (flood fill)
* generate 25 levels
* can have multiple up/down stairs?
* generate 32 levels
* player starts on upstairs of level 0
* option: have up stairs on level 0 or not
* first level has no up stairs
* last level has no down stairs

### display

M display.go => display.go +15 -14
@@ 26,7 26,7 @@ func isXYInRange(x, y int) bool {
	return x >= 0 && x < width && y >= 0 && y < height
}

func blocksLight(x, y, octant, originX, originY int, level *[width][height]int32) bool {
func blocksLight(x, y, octant, originX, originY int, level *[height][width]int32) bool {
	nx, ny := originX, originY
	switch octant {
	case 0:


@@ 54,10 54,10 @@ func blocksLight(x, y, octant, originX, originY int, level *[width][height]int32
		nx += x
		ny += y
	}
	return !isXYInRange(nx, ny) || level[nx][ny] != '.'
	return !isXYInRange(nx, ny) || level[ny][nx] == '#'
}

func setVisible(x, y, octant, originX, originY int, visible *[width][height]bool, explored *[width][height]bool) {
func setVisible(x, y, octant, originX, originY int, visible *[height][width]bool, explored *[height][width]bool) {
	nx, ny := originX, originY
	switch octant {
	case 0:


@@ 86,8 86,8 @@ func setVisible(x, y, octant, originX, originY int, visible *[width][height]bool
		ny += y
	}
	if isXYInRange(nx, ny) {
		visible[nx][ny] = true
		explored[nx][ny] = true
		visible[ny][nx] = true
		explored[ny][nx] = true
	}
}



@@ 112,10 112,10 @@ func isSlopeLess(a, b slope) bool {

//calculate visible and explored tiles with shadowcasting
//see http://www.adammil.net/blog/v125_Roguelike_Vision_Algorithms.html#mine
func shadowcast(originX, originY, rangeLimit int, visible *[width][height]bool, explored *[width][height]bool, level *[width][height]int32) {
func shadowcast(originX, originY, rangeLimit int, visible *[height][width]bool, explored *[height][width]bool, level *[height][width]int32) {
	for x := 0; x < width; x++ {
		for y := 0; y < height; y++ {
			visible[x][y] = false
			visible[y][x] = false
		}
	}
	//loop through each octant


@@ 124,7 124,7 @@ func shadowcast(originX, originY, rangeLimit int, visible *[width][height]bool, 
	}
}

func shadowcastAux(octant, originX, originY, rangeLimit, x int, top, bottom slope, visible *[width][height]bool, explored *[width][height]bool, level *[width][height]int32) {
func shadowcastAux(octant, originX, originY, rangeLimit, x int, top, bottom slope, visible *[height][width]bool, explored *[height][width]bool, level *[height][width]int32) {
	// throughout this function there are references to various parts of tiles. a tile's coordinates refer to its
	// center, and the following diagram shows the parts of the tile and the vectors from the origin that pass through
	// those parts. given a part of a tile with vector u, a vector v passes above it if v > u and below it if v < u


@@ 305,7 305,7 @@ func shadowcastAux(octant, originX, originY, rangeLimit, x int, top, bottom slop
}

//Display - display the current level on the ascii screen
func Display(s tcell.Screen, playerPos Position, visible *[width][height]bool, explored *[width][height]bool, level *[width][height]int32) {
func Display(s tcell.Screen, playerPos Position, visible *[height][width]bool, explored1 *[height][width]bool, level *[height][width]int32) {
	playerX := playerPos.x
	playerY := playerPos.y



@@ 313,19 313,20 @@ func Display(s tcell.Screen, playerPos Position, visible *[width][height]bool, e
	style2 := tcell.StyleDefault.Foreground(tcell.ColorDarkSlateGray).Background(tcell.ColorBlack)

	rangeLimit := 7
	shadowcast(playerX, playerY, rangeLimit, visible, explored, level)
	shadowcast(playerX, playerY, rangeLimit, visible, explored1, level)

	//display the level
	for x := 0; x < width; x++ {
		for y := 0; y < height; y++ {
			if explored[x][y] {
				if visible[x][y] {
					s.SetContent(x+offsetX, y+offsetY, level[x][y], nil, style1)
			if explored1[y][x] {
				if visible[y][x] {
					s.SetContent(x+offsetX, y+offsetY, level[y][x], nil, style1)
				} else {
					s.SetContent(x+offsetX, y+offsetY, level[x][y], nil, style2)
					s.SetContent(x+offsetX, y+offsetY, level[y][x], nil, style2)
				}
			} else {
				s.SetContent(x+offsetX, y+offsetY, ' ', nil, style2)
				// s.SetContent(x+offsetX, y+offsetY, level[y][x], nil, style2)
			}
		}
	}

M generate.go => generate.go +23 -10
@@ 3,19 3,32 @@ package main
import "math/rand"

//Generate - generate all the levels in the game
func Generate() ([width][height]int32, [width][height]bool, Position) {
	var level [width][height]int32
	var explored [width][height]bool
func Generate() ([depth][height][width]int32, [depth][height][width]bool, Position) {
	var levels [depth][height][width]int32
	var explored [depth][height][width]bool

	//simple terrain generation
	for x := 0; x < width; x++ {
	var stairX, stairY int
	for z := 0; z < depth; z++ {
		for y := 0; y < height; y++ {
			if rand.Intn(100) < 40 {
				level[x][y] = '#' //wall, 40%
			} else {
				level[x][y] = '.' //empty, 60%
			for x := 0; x < width; x++ {
				if rand.Intn(100) < 40 {
					levels[z][y][x] = '#' //wall, 40%
				} else {
					levels[z][y][x] = '.' //empty, 60%
				}
			}
		}
		if z > 0 {
			levels[z][stairY][stairX] = '<'
		}
		if z < depth-1 {
			for ok := true; ok; ok = levels[0][stairY][stairX] != '.' {
				stairX = rand.Intn(width)
				stairY = rand.Intn(height)
			}
			levels[z][stairY][stairX] = '>'
		}
	}
	// level[5][4] = '£'
	// level[5][6] = '#'


@@ 24,10 37,10 @@ func Generate() ([width][height]int32, [width][height]bool, Position) {
	//start the player on an empty square
	var playerX, playerY int
	//do while
	for ok := true; ok; ok = level[playerX][playerY] != '.' {
	for ok := true; ok; ok = levels[0][playerY][playerX] != '.' {
		playerX = rand.Intn(width)
		playerY = rand.Intn(height)
	}

	return level, explored, Position{playerX, playerY, 0}
	return levels, explored, Position{playerX, playerY, 0}
}

M main.go => main.go +20 -15
@@ 9,7 9,7 @@ import (
	"github.com/gdamore/tcell"
)

const width, height = 48, 16
const width, height, depth = 48, 16, 32
const offsetX, offsetY = 1, 2

//Position - the x, y, and z (depth) of a location in the game


@@ 43,9 43,9 @@ func main() {

	// invert := tcell.StyleDefault.Foreground(tcell.ColorBlack).Background(tcell.ColorWhite)

	level, explored, playerPos := Generate()
	levels, explored, playerPos := Generate()

	var visible [width][height]bool
	var visible [height][width]bool
	s.Clear()
	for {
		//player movement


@@ 88,21 88,26 @@ func main() {
			} else if ev.Rune() == '9' {
				deltaX, deltaY = 1, -1
			}
			if deltaX != 0 || deltaY != 0 {
				newPlayerX := playerPos.x + deltaX
				newPlayerY := playerPos.y + deltaY

			newPlayerX := playerPos.x + deltaX
			newPlayerY := playerPos.y + deltaY

			if newPlayerX >= 0 && newPlayerX < width &&
				newPlayerY >= 0 && newPlayerY < height &&
				level[newPlayerX][newPlayerY] == '.' {
				playerPos.x = newPlayerX
				playerPos.y = newPlayerY
				EmitStr(s, 15, 0, style1, "    ")
			} else {
				EmitStr(s, 15, 0, style1, "oof!")
				if newPlayerX >= 0 && newPlayerX < width &&
					newPlayerY >= 0 && newPlayerY < height &&
					levels[playerPos.z][newPlayerY][newPlayerX] != '#' {
					playerPos.x = newPlayerX
					playerPos.y = newPlayerY
					EmitStr(s, 15, 0, style1, "    ")
				} else {
					EmitStr(s, 15, 0, style1, "oof!")
				}
			} else if ev.Rune() == '>' && levels[playerPos.z][playerPos.y][playerPos.x] == '>' {
				playerPos.z++
			} else if ev.Rune() == '<' && levels[playerPos.z][playerPos.y][playerPos.x] == '<' {
				playerPos.z--
			}
		}

		Display(s, playerPos, &visible, &explored, &level)
		Display(s, playerPos, &visible, &explored[playerPos.z], &levels[playerPos.z])
	}
}