package delaunaymst import ( "fmt" "lab.zaar.be/thefish/alchemyst-go/engine/gamemap" "lab.zaar.be/thefish/alchemyst-go/engine/types" "lab.zaar.be/thefish/alchemyst-go/util" "lab.zaar.be/thefish/alchemyst-go/util/appctx" "lab.zaar.be/thefish/alchemyst-go/util/delaunay" ) //fixme move to config var minRoomSize = 5 var maxRoomSize = 25 var maxrooms = 200 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 DelaunayMstGen(ctx appctx.ClientCtx, l *gamemap.Level) (*gamemap.Level, []gamemap.Room) { rng := util.NewRNG() //load prefabs pfLoader := gamemap.NewPrefabLoader(ctx) pfRooms := pfLoader.PrefabRoomsList() //fill with walls for i := 0; i < l.W; i ++ { for j := 0; j < l.H; j++ { l.SetTileByXY(i, j, gamemap.NewWall()) } } rooms := make([]gamemap.Room, 0) prefabUsed := false for i := 0; i < maxrooms; i++ { failed := false var fillage types.RectFill fillage = fges[rng.GetWeightedEntity(map[int]int{1: 10, 2: 1})] var newRoom = gamemap.Room{} if !prefabUsed || rng.Range(0, 5) > 3 { //if prefabUsed { //prefab prefabUsed = true fmt.Printf("\n\n------USING PREFAB-----\n") r := pfRooms[rng.Range(0, len(pfRooms))] //copy to local scope newRoom = gamemap.Room{ Rect: r.Rect, Center: r.Center, Geometry: r.Geometry, Items: r.Items, Mobs: r.Mobs, Connectors: make([]types.Coords,0), } for _, coord := range r.Connectors { newRoom.Connectors = append(newRoom.Connectors, coord) } } else { 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), } newRoom.MoveToCoords(where) for _, otherRoom := range rooms { if otherRoom.Intersects(newRoom.Rect) { failed = true break } } if failed { continue } if len(newRoom.Connectors) > 0 { rooms = append(rooms, newRoom) } } for _, room := range rooms { err := room.BlitToLevel(l) if err != nil { fmt.Printf("err: %v", err) } } centers := make([]types.Coords, 0) for _, room := range rooms { centers = append(centers, room.Center) } edges := delaunay.GetMst(centers, l.W, l.H) fmt.Printf("edges: ", edges) for _, edge := range edges { MedianStraight(rng, l, rooms, centers, edge) } return l, rooms } func MedianStraight(rng *util.RNG, l *gamemap.Level, rooms []gamemap.Room, centers []types.Coords, edge types.Edge) { //find connected rooms var fromRoom, toRoom gamemap.Room for _, room := range rooms { if room.Center == edge.From { fromRoom = room continue } if room.Center == edge.To { toRoom = room continue } if len(fromRoom.Connectors) > 0 && len(toRoom.Connectors) > 0 { break } } midpoint := edge.Midpoint() fromConnector := findNearestCoonector(midpoint, fromRoom) toConnector := findNearestCoonector(midpoint, toRoom) if (!l.InBounds(midpoint)) { //fmt.Printf("rooms: ", rooms) fmt.Printf("\nedges: ", edge) fmt.Printf("\nmidpoint: ", midpoint) panic(fmt.Errorf("midpoint out of level bounds!")) } if (!l.InBounds(fromConnector)) { fmt.Printf("\nfrom room: ", fromRoom.String()) fmt.Printf("\nedges: ", edge) fmt.Printf("\nfromConnector: ", fromConnector) panic(fmt.Errorf("fromConnector out of level bounds!")) } if (!l.InBounds(toConnector)) { fmt.Printf("\nto room: ", toRoom.String()) fmt.Printf("\nedges: ", edge) fmt.Printf("\ntoConnector: ", toConnector) panic(fmt.Errorf("toConnector out of level bounds!")) } connectStraight(rng, l, fromConnector, toConnector, midpoint) } func findNearestCoonector(midpoint types.Coords, room gamemap.Room) types.Coords { var nearest types.Coords for _, con := range room.Connectors { if nearest.X == 0 { nearest = con continue } if midpoint.DistanceTo(con) < midpoint.DistanceTo(nearest) { nearest = con } } return nearest } func connectStraight(rng *util.RNG, l *gamemap.Level, from, to, midpoint types.Coords) { toss := rng.Range(0, 1) if toss == 0 { digHTunnel(l, from.X, midpoint.X, from.Y) digVTunnel(l, from.Y, to.Y, midpoint.X) digHTunnel(l, midpoint.X, to.X, to.Y) } else { digVTunnel(l, from.Y, midpoint.Y, from.X) digHTunnel(l, from.X, to.X, midpoint.Y) digVTunnel(l, midpoint.Y, to.Y, to.X) } } func digHTunnel(l *gamemap.Level, x1, x2, y int) { var start, finish int if x1 < x2 { start = x1 finish = x2 } else { start = x2 finish = x1 } for i := start; i <= finish; i++ { if l.InBounds(types.Coords{i, y}) { l.SetTileByXY(i, y, gamemap.NewFloor()) //l.Tiles[i][y] = gamemap.NewFloor() } } } func digVTunnel(l *gamemap.Level, y1, y2, x int) { var start, finish int if y1 < y2 { start = y1 finish = y2 } else { start = y2 finish = y1 } for i := start; i <= finish; i++ { if l.InBounds(types.Coords{x, i}) { l.SetTileByXY(x, i, gamemap.NewFloor()) } } }