import TerrainGenerator from './TerrainGenerator' ; import RectGrid , { Grid } from '../core/Grid' ; import { createTileData , Point , Tile , TileData } from '../core/Components' ; const LIMIT = 2 ; export const NO_SKEW = 0 ; export const VERTICAL_SKEW = 1 ; export const HORIZONTAL_SKEW = 2 ; interface Chamber { topLeft : Point ; bottomRight : Point ; } class TerrainMazeGenerator extends TerrainGenerator { private readonly divideWidth : ( width : number , height : number ) => boolean ; constructor ( width , height , tileData ? , ignore ? , slant ? ) { super ( width , height , tileData , ignore ) ; if ( slant === undefined || slant === NO_SKEW ) { this . divideWidth = divideWidthNoSkew ; } else if ( slant === VERTICAL_SKEW ) { this . divideWidth = divideWidthVSkew ; } else if ( slant === HORIZONTAL_SKEW ) { this . divideWidth = divideWidthHSkew ; } else { throw new Error ( "Invalid recursive maze division skew type" ) ; } } param topLeft param bottomRight generateTerrain ( topLeft ? , bottomRight ? ) { const grid = new RectGrid ( this . width , this . height ) ; if ( topLeft === undefined ) { topLeft = { x : 1 , y : 1 } ; } if ( bottomRight === undefined ) { bottomRight = { x : grid . getWidth ( ) - 2 , y : grid . getHeight ( ) - 2 } ; } for ( let x = topLeft . x - 1 ; x <= bottomRight . x + 1 ; x ++ ) { this . draw ( grid , { point : { x : x , y : topLeft . y - 1 } , data : this . getSolid ( ) } ) ; this . draw ( grid , { point : { x : x , y : bottomRight . y + 1 } , data : this . getSolid ( ) } ) ; } for ( let y = topLeft . y - 1 ; y <= bottomRight . y + 1 ; y ++ ) { this . draw ( grid , { point : { x : topLeft . x - 1 , y : y } , data : this . getSolid ( ) } ) ; this . draw ( grid , { point : { x : bottomRight . x + 1 , y : y } , data : this . getSolid ( ) } ) ; } this . divide ( grid , { topLeft : topLeft , bottomRight : bottomRight , } ) ; return grid ; } param grid param tiles private drawArr ( grid , tiles ) { for ( const tile of tiles ) { this . draw ( grid , tile ) ; } } param grid param chamber divide ( grid , chamber ) { const width = widthOf ( chamber ) ; const height = heightOf ( chamber ) ; const min = chamber . topLeft ; const max = chamber . bottomRight ; if ( this . divideWidth ( width , height ) ) { if ( width > LIMIT ) { const randX = getMidPoint ( min . x , max . x ) ; const toDraw = [ ] ; for ( let y = min . y ; y <= max . y ; y ++ ) { toDraw . push ( { point : { x : randX , y : y } , data : this . getTerrain ( ) } ) ; } let edgeBlocked = false ; if ( canDrawHole ( grid . get ( { x : randX , y : min . y - 1 } ) ) ) { toDraw . push ( { point : { x : randX , y : min . y } , data : createTileData ( false ) } ) ; edgeBlocked = true ; } if ( canDrawHole ( grid . get ( { x : randX , y : max . y + 1 } ) ) ) { toDraw . push ( { point : { x : randX , y : max . y } , data : createTileData ( false ) } ) ; edgeBlocked = true ; } if ( ! edgeBlocked ) { const randY = getRand ( min . y , max . y ) ; toDraw . push ( { point : { x : randX , y : randY } , data : createTileData ( false ) } ) ; } this . drawArr ( grid , toDraw ) ; const leftChamber = { topLeft : chamber . topLeft , bottomRight : { x : randX - 1 , y : chamber . bottomRight . y } , } ; const rightChamber = { topLeft : { x : randX + 1 , y : chamber . topLeft . y } , bottomRight : chamber . bottomRight , } ; this . divide ( grid , leftChamber ) ; this . divide ( grid , rightChamber ) ; } } else { if ( height > LIMIT ) { const randY = getMidPoint ( min . y , max . y ) ; const toDraw = [ ] ; for ( let x = min . x ; x <= max . x ; x ++ ) { toDraw . push ( { point : { x : x , y : randY } , data : this . getTerrain ( ) } ) ; } let edgeBlocked = false ; if ( canDrawHole ( grid . get ( { x : min . x - 1 , y : randY } ) ) ) { toDraw . push ( { point : { x : min . x , y : randY } , data : createTileData ( false ) } ) ; edgeBlocked = true ; } if ( canDrawHole ( grid . get ( { x : max . x + 1 , y : randY } ) ) ) { toDraw . push ( { point : { x : max . x , y : randY } , data : createTileData ( false ) } ) ; edgeBlocked = true ; } if ( ! edgeBlocked ) { const randX = getRand ( min . x , max . x ) ; toDraw . push ( { point : { x : randX , y : randY } , data : createTileData ( false ) } ) ; } this . drawArr ( grid , toDraw ) ; const topChamber = { topLeft : chamber . topLeft , bottomRight : { x : chamber . bottomRight . x , y : randY - 1 } , } ; const bottomChamber = { topLeft : { x : chamber . topLeft . x , y : randY + 1 } , bottomRight : chamber . bottomRight , } ; this . divide ( grid , topChamber ) ; this . divide ( grid , bottomChamber ) ; } } } } function divideWidthNoSkew ( width , height ) { return width >= height ; } function divideWidthHSkew ( width , height ) { return width >= height * 2 ; } function divideWidthVSkew ( width , height ) { return width * 2 >= height ; } function widthOf ( chamber ) { return chamber . bottomRight . x - chamber . topLeft . x + 1 ; } function heightOf ( chamber ) { return chamber . bottomRight . y - chamber . topLeft . y + 1 ; } function canDrawHole ( tile ) { return tile . data . pathCost === 1 && ! tile . data . isSolid ; } param min param max function getMidPoint ( min , max ) { const range = max - min ; if ( range >= 20 ) { return ( ( min + max ) / 2 ) >> 0 ; } else if ( range > 5 ) { const mid = ( ( min + max ) / 2 ) >> 0 ; const points = [ mid , mid + 1 ] ; return points [ getRand ( 0 , points . length - 1 ) ] ; } else { return getRand ( min + 1 , max - 1 ) ; } } param min param max function getRand ( min , max ) { return Math . floor ( Math . random ( ) * ( max + 1 - min ) + min ) ; } export default TerrainMazeGenerator ;