// nextSpot picks where the sentinel will be going to by considering // all the surrounding spaces and picking from the valid ones. func (s *sentinel) nextSpot(plan grid.Grid) *gridSpot { at := s.next was := s.prev w, h := plan.Size() // using knowledge that the grid starts at 0, 0 and goes to size, -size. // and that the outside border is also valid. choices := []*gridSpot{} if at.x >= -1 && at.y >= -1 && at.x <= w && at.y <= h { if s.isValidSpot(plan, w, h, was, at.x+1, at.y) { choices = append(choices, &gridSpot{at.x + 1, at.y}) } if s.isValidSpot(plan, w, h, was, at.x-1, at.y) { choices = append(choices, &gridSpot{at.x - 1, at.y}) } if s.isValidSpot(plan, w, h, was, at.x, at.y+1) { choices = append(choices, &gridSpot{at.x, at.y + 1}) } if s.isValidSpot(plan, w, h, was, at.x, at.y-1) { choices = append(choices, &gridSpot{at.x, at.y - 1}) } } if len(choices) > 0 { way := 0 if len(choices) > 1 { way = rand.Intn(len(choices)) } return choices[way] } return was // backtrack should never happen. }
// isValidSpot checks that a spot is valid for a sentinel, i.e. not a wall or the // previous location. func (s *sentinel) isValidSpot(plan grid.Grid, w, h int, old *gridSpot, x, y int) bool { if x == old.x && y == old.y { // can't use previous position. return false } if x >= 0 && y >= 0 && x < w && y < h { // exclude walls. return plan.IsOpen(x, y) } if x >= -1 && y >= -1 && x <= w && y <= h { // outside edge ok. return true } return false // anywhere else is a no-go zone. }
// buildFloorPlan creates the level layout. func (lvl *level) buildFloorPlan(root vu.Pov, hd *hud, plan grid.Grid) { width, height := plan.Size() for x := 0; x < width; x++ { for y := 0; y < height; y++ { xc := float64(x * lvl.units) yc := float64(-y * lvl.units) band := plan.Band(x, y) / 3 if x == width/2 && y == height/2 { lvl.gcx, lvl.gcy = x, y // remember the maze center location lvl.center = root.NewPov().SetLocation(xc, 0, yc) m := lvl.center.NewModel("uvra").LoadMesh("tile").AddTex("drop1") m.SetAlpha(0.7) m.SetUniform("spin", 1.0) m.SetUniform("fd", lvl.fade) } else if plan.IsOpen(x, y) { // the floor tiles. tileLabel := lvl.tileLabel(band) tile := root.NewPov().SetLocation(xc, 0, yc) m := tile.NewModel("uva").LoadMesh("tile").AddTex(tileLabel) m.SetAlpha(0.7) m.SetUniform("fd", lvl.fade) // remember the tile locations for drop spots inside the maze. lvl.cc.addDropLocation(x, y) } else { // draw flat on the y plane with the maze extending into the screen. wm := lvl.wallMeshLabel(band) wt := lvl.wallTextureLabel(band) wall := root.NewPov().SetLocation(xc, 0, yc) m := wall.NewModel("uva").LoadMesh(wm).AddTex(wt) m.SetUniform("fd", lvl.fade) lvl.walls = append(lvl.walls, wall) // add the wall to the minimap hd.addWall(xc, yc) } } } // add core drop locations around the outside of the maze. for x := -1; x < width+1; x++ { lvl.cc.addDropLocation(x, -1) lvl.cc.addDropLocation(x, height) } for y := 0; y < height; y++ { lvl.cc.addDropLocation(-1, y) lvl.cc.addDropLocation(width, y) } }