steel-lord/mapping/gamemap.go

164 lines
4.3 KiB
Go
Raw Normal View History

2019-07-02 23:55:29 -07:00
package mapping
2019-07-12 13:46:26 -07:00
import (
"github.com/kettek/goro"
2019-07-16 18:52:08 -07:00
2019-07-17 20:05:05 -07:00
"steel/interfaces"
2019-07-12 13:46:26 -07:00
)
2019-07-19 16:04:18 -07:00
// GameMap is our map type for holding our tiles and dimensions.
2019-07-02 23:55:29 -07:00
type GameMap struct {
2019-07-19 16:04:18 -07:00
width, height int
tiles [][]Tile
2019-07-02 23:55:29 -07:00
}
2019-07-19 21:08:46 -07:00
// NewGameMap initializes a GameMap's tiles to match provided width and height and sets up a few tiles to block movement and sight. Returns a GameMap interface.
2019-07-19 16:04:18 -07:00
func NewGameMap(width, height int) interfaces.GameMap {
2019-07-19 21:08:46 -07:00
g := &GameMap{
2019-07-19 16:04:18 -07:00
width: width,
height: height,
}
g.tiles = make([][]Tile, g.width)
2019-07-02 23:55:29 -07:00
2019-07-19 16:04:18 -07:00
for x := range g.tiles {
g.tiles[x] = make([]Tile, g.height)
for y := range g.tiles[x] {
g.tiles[x][y].Flags = BlockMovement | BlockSight
2019-07-10 19:44:16 -07:00
}
2019-07-17 20:05:05 -07:00
}
2019-07-19 16:04:18 -07:00
return g
2019-07-12 13:46:26 -07:00
}
2019-07-16 18:52:08 -07:00
// MakeMap creates a new randomized map. This is built according to the passed arguments.
2019-07-17 20:05:05 -07:00
func (g *GameMap) MakeMap(maxRooms, roomMinSize, roomMaxSize int, player interfaces.Entity) {
2019-07-19 16:04:18 -07:00
var rooms []Rect
2019-07-16 18:52:08 -07:00
for r := 0; r < maxRooms; r++ {
// Generate a random width and height.
width := roomMinSize + goro.Random.Intn(roomMaxSize)
height := roomMinSize + goro.Random.Intn(roomMaxSize)
// Generate a random position within the map boundaries.
2019-07-19 16:04:18 -07:00
x := goro.Random.Intn(g.width - width - 1)
y := goro.Random.Intn(g.height - height - 1)
2019-07-16 18:52:08 -07:00
// Create a Rect according to our generated sizes.
room := NewRect(x, y, width, height)
2019-07-17 20:05:05 -07:00
2019-07-16 18:52:08 -07:00
// Iterate through our existing rooms to check for intersection with our new room.
intersects := false
for _, otherRoom := range rooms {
if room.Intersect(otherRoom) {
intersects = true
break
}
}
// Add the room if there is no intersection found.
if !intersects {
g.CreateRoom(room)
roomCenterX, roomCenterY := room.Center()
2019-07-17 20:05:05 -07:00
// Always place the player in the center of the first room.
if len(rooms) == 0 {
player.SetX(roomCenterX)
player.SetY(roomCenterY)
2019-07-16 18:52:08 -07:00
} else {
2019-07-17 20:05:05 -07:00
prevCenterX, prevCenterY := rooms[len(rooms)-1].Center()
// Flip a coin if we should tunnel horizontally or vertically first.
if goro.Random.Intn(1) == 1 {
g.CreateHTunnel(prevCenterX, roomCenterX, prevCenterY)
g.CreateVTunnel(prevCenterY, roomCenterY, roomCenterX)
} else {
g.CreateVTunnel(prevCenterY, roomCenterY, prevCenterX)
g.CreateHTunnel(prevCenterX, roomCenterX, roomCenterY)
}
2019-07-16 18:52:08 -07:00
}
// Append our new room to our rooms list.
rooms = append(rooms, room)
}
}
2019-07-12 13:46:26 -07:00
}
// CreateRoom creates a room from a provided rect.
2019-07-19 16:04:18 -07:00
func (g *GameMap) CreateRoom(r Rect) {
2019-07-12 13:46:26 -07:00
for x := r.X1 + 1; x < r.X2; x++ {
for y := r.Y1 + 1; y < r.Y2; y++ {
if g.InBounds(x, y) {
2019-07-19 21:08:46 -07:00
g.tiles[x][y] = Tile{}
2019-07-12 13:46:26 -07:00
}
}
2019-07-02 23:55:29 -07:00
}
2019-07-12 13:46:26 -07:00
}
2019-07-02 23:55:29 -07:00
2019-07-19 21:08:46 -07:00
//CreateHTunnel creates a horizontal tunnel from x1 to/from x1 starting at y.
2019-07-12 13:46:26 -07:00
func (g *GameMap) CreateHTunnel(x1, x2, y int) {
for x := goro.MinInt(x1, x2); x <= goro.MaxInt(x1, x2); x++ {
if g.InBounds(x, y) {
2019-07-19 21:08:46 -07:00
g.tiles[x][y] = Tile{}
2019-07-12 13:46:26 -07:00
}
}
}
2019-07-17 20:05:05 -07:00
// CreateVTunnel creates a vertical tunnel from y1 to/from y2 starting at x.
2019-07-12 13:46:26 -07:00
func (g *GameMap) CreateVTunnel(y1, y2, x int) {
for y := goro.MinInt(y1, y2); y <= goro.MaxInt(y1, y2); y++ {
if g.InBounds(x, y) {
2019-07-19 21:08:46 -07:00
g.tiles[x][y] = Tile{}
2019-07-12 13:46:26 -07:00
}
}
2019-07-02 23:55:29 -07:00
}
2019-07-19 16:04:18 -07:00
// Explored returns if the tile at x by y has been explored.
func (g *GameMap) Explored(x, y int) bool {
if g.InBounds(x, y) {
return g.tiles[x][y].Flags&Explored != 0
}
return false
}
// SetExplored sets the explored state of the tile at x and y to the passed explored bool.
func (g *GameMap) SetExplored(x, y int, explored bool) {
if g.InBounds(x, y) {
if explored {
g.tiles[x][y].Flags = g.tiles[x][y].Flags | Explored
} else {
g.tiles[x][y].Flags = g.tiles[x][y].Flags &^ Explored
}
}
}
2019-07-02 23:55:29 -07:00
// IsBlocked returns if the given coordinates are blocking movement.
func (g *GameMap) IsBlocked(x, y int) bool {
// Always block if ourside our GameMap's bounds.
2019-07-16 18:52:08 -07:00
if !g.InBounds(x, y) {
2019-07-02 23:55:29 -07:00
return true
}
2019-07-19 21:08:46 -07:00
return g.tiles[x][y].Flags&BlockMovement != 0
2019-07-19 16:04:18 -07:00
}
// IsOpaque returns if the given coordinates are blocking sight.
func (g *GameMap) IsOpaque(x, y int) bool {
if !g.InBounds(x, y) {
return true
}
return g.tiles[x][y].Flags&BlockSight != 0
2019-07-16 18:52:08 -07:00
}
2019-07-02 23:55:29 -07:00
2019-07-19 21:08:46 -07:00
// Width returns the width of the map.
func (g *GameMap) Width() int {
return g.width
}
// Height returns the height of the map.
func (g *GameMap) Height() int {
return g.height
}
2019-07-12 13:46:26 -07:00
// InBounds returns if the given coordinates are within the map's bounds.
2019-07-17 20:05:05 -07:00
func (g *GameMap) InBounds(x, y int) bool {
2019-07-19 16:04:18 -07:00
if x < 0 || x >= g.width || y < 0 || y >= g.height {
2019-07-12 13:46:26 -07:00
return false
}
return true
}