mapgen moving to delanay -> minimum spanning tree for rooms connection

This commit is contained in:
2019-11-11 01:47:16 +03:00
parent b734e538f4
commit e30aa33715
16 changed files with 380 additions and 73 deletions

View File

@ -82,7 +82,3 @@ func (l *Level) SetAllInvisible() {
}
}
type Room struct {
*types.Rect
Center types.Coords
}

View File

@ -5,12 +5,38 @@ import (
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util"
)
//fixme move to config
var minRoomSize = 3
var maxRoomSize = 22
var maxrooms = 50
//fixme make closure to stack them
var fges = map[int]types.RectFill{
1: types.RectFill{
Top: func() *gamemap.Tile { return gamemap.NewWall() },
Bottom: func() *gamemap.Tile { return gamemap.NewWall() },
Left: func() *gamemap.Tile { return gamemap.NewWall() },
Right: func() *gamemap.Tile { return gamemap.NewWall() },
BottomLeft: func() *gamemap.Tile { return gamemap.NewWall() },
BottomRight: func() *gamemap.Tile { return gamemap.NewWall() },
TopLeft: func() *gamemap.Tile { return gamemap.NewWall() },
TopRight: func() *gamemap.Tile { return gamemap.NewWall() },
Body: func() *gamemap.Tile { return gamemap.NewFloor() },
},
2: types.RectFill{
Top: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Bottom: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Left: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Right: func() *gamemap.Tile { return gamemap.NewWaterTile() },
BottomLeft: func() *gamemap.Tile { return gamemap.NewWaterTile() },
BottomRight: func() *gamemap.Tile { return gamemap.NewWaterTile() },
TopLeft: func() *gamemap.Tile { return gamemap.NewWaterTile() },
TopRight: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Body: func() *gamemap.Tile { return gamemap.NewDeepWaterTile() },
},
}
func DefaultGen(l *gamemap.Level) (*gamemap.Level, []*gamemap.Room) {
rng := util.NewRNG()
@ -24,35 +50,26 @@ func DefaultGen(l *gamemap.Level) (*gamemap.Level, []*gamemap.Room) {
rooms := make([]*gamemap.Room, 0)
//one wall around whole level guaranteed
levelBoundary := types.NewRect(l.X + 1, l.Y + 1, l.W - 2, l.H - 2)
for i := 0; i < maxrooms; i++ {
newRoom := &gamemap.Room{
Rect: types.NewRect(
rng.Range(l.X, l.W),
rng.Range(l.Y, l.H),
rng.Range(minRoomSize, maxRoomSize),
rng.Range(minRoomSize, maxRoomSize),
)}
newRoom.Center = types.Coords{newRoom.X + newRoom.W / 2, newRoom.Y + newRoom.H / 2}
failed := false
if !levelBoundary.InBounds(types.Coords{newRoom.X, newRoom.Y}) {
failed = true
var failed = false
var fillage types.RectFill
fillage = fges[rng.GetWeightedEntity(map[int]int{1: 10, 2: 1})]
newRoom := gamemap.NewRandomRectRoom(
rng,
rng.Range(minRoomSize, maxRoomSize),
rng.Range(minRoomSize, maxRoomSize),
fillage,
)
where := types.Coords{
rng.Range(1, l.W -2 - newRoom.W),
rng.Range(1, l.H - 2 - newRoom.H),
}
if !failed && !l.InBounds(types.Coords{newRoom.X + newRoom.W, newRoom.Y + newRoom.H}) {
failed = true
}
if !failed {
for _, otherRoom := range rooms {
if otherRoom.Intersects(newRoom.Rect) {
failed = true
break
}
for _, otherRoom := range rooms {
if otherRoom.Intersects(newRoom.Rect) {
failed = true
break
}
}
@ -60,61 +77,31 @@ func DefaultGen(l *gamemap.Level) (*gamemap.Level, []*gamemap.Room) {
rooms = append(rooms, newRoom)
}
newRoom.BlitToLevel(l, where)
//addStairs(rooms)
//itemize(rooms)
}
fges := map[int]types.RectFill{
1: types.RectFill{
Top: func() *gamemap.Tile { return gamemap.NewWall() },
Bottom: func() *gamemap.Tile { return gamemap.NewWall() },
Left: func() *gamemap.Tile { return gamemap.NewWall() },
Right: func() *gamemap.Tile { return gamemap.NewWall() },
BottomLeft: func() *gamemap.Tile { return gamemap.NewWall() },
BottomRight: func() *gamemap.Tile { return gamemap.NewWall() },
TopLeft: func() *gamemap.Tile { return gamemap.NewWall() },
TopRight: func() *gamemap.Tile { return gamemap.NewWall() },
Body: func() *gamemap.Tile { return gamemap.NewFloor() },
},
2: types.RectFill{
Top: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Bottom: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Left: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Right: func() *gamemap.Tile { return gamemap.NewWaterTile() },
BottomLeft: func() *gamemap.Tile { return gamemap.NewWaterTile() },
BottomRight: func() *gamemap.Tile { return gamemap.NewWaterTile() },
TopLeft: func() *gamemap.Tile { return gamemap.NewWaterTile() },
TopRight: func() *gamemap.Tile { return gamemap.NewWaterTile() },
Body: func() *gamemap.Tile { return gamemap.NewDeepWaterTile() },
},
}
var fillage types.RectFill
for _, room := range rooms {
fillage = fges[rng.GetWeightedEntity(map[int]int{1:10, 2:1})]
room.Blit(fillage, l)
}
for idx, room := range rooms {
if idx > 0 {
connectRooms(l, room, rooms[idx-1], fillage, rng.Range(0,1))
connectRooms(l, room, rooms[idx-1], fillage, rng.Range(0, 1))
}
}
return l, rooms
}
func connectRooms (l *gamemap.Level, room, otherRoom *gamemap.Room, fillage types.RectFill, toss int) {
func connectRooms(l *gamemap.Level, room, otherRoom *gamemap.Room, fillage types.RectFill, toss int) {
if toss == 0 {
digHTunnel(l, room.Center.X,otherRoom.Center.X,room.Center.Y, fillage)
digVTunnel(l, room.Center.Y,otherRoom.Center.Y,otherRoom.Center.X, fillage)
digHTunnel(l, room.Center.X, otherRoom.Center.X, room.Center.Y, fillage)
digVTunnel(l, room.Center.Y, otherRoom.Center.Y, otherRoom.Center.X, fillage)
} else {
digVTunnel(l, room.Center.Y, otherRoom.Center.Y, room.Center.Y, fillage)
digHTunnel(l, room.Center.X, otherRoom.Center.X, otherRoom.Center.Y, fillage)
}
}
func digHTunnel(l *gamemap.Level, x1,x2,y int, fillage types.RectFill) {
func digHTunnel(l *gamemap.Level, x1, x2, y int, fillage types.RectFill) {
var start, finish int
if x1 < x2 {
start = x1
@ -131,7 +118,7 @@ func digHTunnel(l *gamemap.Level, x1,x2,y int, fillage types.RectFill) {
}
}
func digVTunnel(l *gamemap.Level, y1,y2,x int, fillage types.RectFill) {
func digVTunnel(l *gamemap.Level, y1, y2, x int, fillage types.RectFill) {
var start, finish int
if y1 < y2 {
start = y1

124
engine/gamemap/prefab.go Normal file
View File

@ -0,0 +1,124 @@
package gamemap
import (
"encoding/json"
"fmt"
"io/ioutil"
"lab.zaar.be/thefish/alchemyst-go/engine/items"
"lab.zaar.be/thefish/alchemyst-go/engine/mob"
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util"
)
type PrefabFile struct {
DefaultTileLegend map[string]string `json:"default_tile_legend"`
DefaultMobsLegend map[string]string `json:"default_mobs_legend"`
DefaultItemLegend map[string]string `json:"default_item_legend"`
Prefabs []PrefabRecord
}
type PrefabRecord struct {
name string
Size struct {
X int `json:"x"`
Y int `json:"y"`
} `json:"Size"`
TileLegend map[string]string `json:"default_tile_legend"`
MobsLegend map[string]string `json:"default_mobs_legend"`
ItemLegend map[string]string `json:"default_item_legend"`
Body []string `json:"body"`
}
func LoadPrefabFile(filename string) (*PrefabFile, error) {
data, err := ioutil.ReadFile(filename)
if err!= nil {
return nil, err
}
instance := &PrefabFile{}
err = json.Unmarshal(data, instance)
if err != nil {
return nil, err
}
return instance, nil
}
type PrefabLoader struct {
ctx util.ClientCtx
}
func NewPrefabLoader(ctx util.ClientCtx) PrefabLoader {
return PrefabLoader{ctx: ctx}
}
func (pfbl PrefabLoader) PrefabRoomsList() []Room {
rooms := make([]Room, 0)
file, err := LoadPrefabFile("./assets/prefabs/test.json")
if err !=nil {
panic(err)
}
for _, rawPrefab := range file.Prefabs {
//prepare actual legends
currentTileLegend := file.DefaultTileLegend
currentMobsLegend := file.DefaultMobsLegend
currentItemLegend := file.DefaultItemLegend
fmt.Printf("%v",rawPrefab)
for k,v := range rawPrefab.TileLegend {
currentTileLegend[k] = v
}
for k,v := range rawPrefab.MobsLegend {
currentMobsLegend[k] = v
}
for k,v := range rawPrefab.ItemLegend {
currentItemLegend[k] = v
}
room := Room{
Rect:&types.Rect{0, 0, rawPrefab.Size.X, rawPrefab.Size.Y},
Center: types.Coords{rawPrefab.Size.X / 2, rawPrefab.Size.Y / 2}, //fixme
Geometry: make([]func()*Tile, rawPrefab.Size.X*rawPrefab.Size.Y),
Mobs: make([]mob.Mob, rawPrefab.Size.X*rawPrefab.Size.Y),
Items: make([]items.Carried, rawPrefab.Size.X*rawPrefab.Size.Y),
Connectors: make([]types.Coords, rawPrefab.Size.X*rawPrefab.Size.Y),
}
//make geometry
var f func() *Tile
for j := 0; j < room.H; j++ {
str := rawPrefab.Body[j]
if len(str) != room.W {
continue;
}
for i:=0; i < room.W; i++ {
ok := false
shortName := currentTileLegend[string(str[i])]
if shortName == "any" {
continue
}
if shortName == "connector" {
f = NewFloor
room.Connectors = append(room.Connectors, types.Coords{i,j})
} else {
f, ok = TileTypeMap[shortName]
}
if (!ok) {
pfbl.ctx.Logger().Warn().Msgf("Unknown tile: %s", shortName)
}
room.Geometry[i+ j*room.W] = f
}
}
//add room to list
rooms = append(rooms, room)
}
return rooms
}
var TileTypeMap = map[string]func()*Tile{
"wall": NewWall,
"floor": NewFloor,
"decorated_wall": NewDecoratedWall,
"water": NewWaterTile,
"deep_water": NewDeepWaterTile,
}

85
engine/gamemap/room.go Normal file
View File

@ -0,0 +1,85 @@
package gamemap
import (
"errors"
"lab.zaar.be/thefish/alchemyst-go/engine/items"
"lab.zaar.be/thefish/alchemyst-go/engine/mob"
"lab.zaar.be/thefish/alchemyst-go/engine/types"
"lab.zaar.be/thefish/alchemyst-go/util"
)
var invalidBlit = errors.New("trying to blit on existing good tile")
type Room struct {
*types.Rect
Center types.Coords
Geometry []func() *Tile
Mobs []mob.Mob
Items []items.Carried
Connectors []types.Coords
}
func (r *Room) Put (x, y int, tileFunc interface{}) {
tf := tileFunc.(func() *Tile)
if tf == nil {
return //fixme error
}
if r.InBounds(types.Coords{x, y}) {
r.Geometry[x+y*r.W] = tf
}
}
func (room *Room) BlitToLevel(l *Level, where types.Coords) error {
//copy tiles like this:
//https://stackoverflow.com/questions/21011023/copy-pointer-values-a-b-in-golang
for j := 0; j < room.H; j++ {
for i := 0; i < room.W; i++ {
underlyingTile := l.GetTileByXY(i+where.X, j+where.Y)
tileFunc := room.Geometry[i+j*room.W]
if tileFunc == nil {
continue
}
//check underlying tile
if underlyingTile != nil ||
underlyingTile.Name != "Wall" {
return invalidBlit
}
l.Put(i+where.X, j+where.Y, tileFunc)
}
}
//update room coords?
room.X = where.X
room.Y = where.Y
//update connector?
for i, _ := range room.Connectors {
room.Connectors[i].X += where.X
room.Connectors[i].Y += where.Y
}
return nil
}
func NewRandomRectRoom(rng *util.RNG, w, h int, fillage types.RectFill) *Room {
newRoom := &Room{
Rect: types.NewRect(
0,
0,
w,
h,
),
Center: types.Coords{w / 2, h /2 },
}
newRoom.Blit(fillage, newRoom)
//add connectors
newRoom.Connectors = append(
newRoom.Connectors,
types.Coords{rng.Range(1, w - 2), 0},
types.Coords{rng.Range(1, w - 2), h -1},
types.Coords{0, rng.Range(1, h - 2)},
types.Coords{w - 1, rng.Range(1, h - 2)},
)
return newRoom
}