package world import ( "fmt" "io" "edgaru089.ml/go/gl01/internal/util/itype" ) // World holds a number of Chunks and Entities. type World struct { Chunks map[itype.Vec2i]*Chunk Seed int32 } // NewWorld creates a new, empty, world with no chunks. func NewWorld() *World { var w World w.Chunks = make(map[itype.Vec2i]*Chunk) return &w } // The default World. var DefaultWorld World func init() { DefaultWorld.Chunks = make(map[itype.Vec2i]*Chunk) } // Chunk returns the chunk at [x, z], or nil if nonexistent. func (w *World) Chunk(x, z int) *Chunk { return w.Chunks[itype.Vec2i{x, z}] } // SetChunk sets the chunk [x, z] to c. func (w *World) SetChunk(x, z int, c *Chunk) { if old, ok := w.Chunks[itype.Vec2i{x, z}]; ok { old.FreeRender() } c.X = x c.Z = z c.renderChanged = true c.world = w w.Chunks[itype.Vec2i{x, z}] = c c.InitRender() } func BlockPosToChunk(blockpos itype.Vec3i) (x, z int) { if blockpos[0] >= 0 { x = blockpos[0] / ChunkSizeX } else { x = (blockpos[0]+1)/ChunkSizeX - 1 } if blockpos[2] >= 0 { z = blockpos[2] / ChunkSizeZ } else { z = (blockpos[2]+1)/ChunkSizeZ - 1 } return } func BlockPosToInChunk(blockpos itype.Vec3i) (x, y, z int) { if blockpos[0] >= 0 { x = blockpos[0] % ChunkSizeX } else { x = ChunkSizeX + (blockpos[0] % ChunkSizeX) if x == ChunkSizeX { x = 0 } } y = blockpos[1] if blockpos[2] >= 0 { z = blockpos[2] % ChunkSizeZ } else { z = ChunkSizeZ + (blockpos[2] % ChunkSizeZ) if z == ChunkSizeZ { z = 0 } } return } // Break breaks the block. func (w *World) Break(pos itype.Vec3i) { cx, cz := BlockPosToChunk(pos) cix, ciy, ciz := BlockPosToInChunk(pos) c, ok := w.Chunks[itype.Vec2i{cx, cz}] if !ok || ciy < 0 || ciy >= ChunkSizeY { return } c.SetBlock(cix, ciy, ciz, 0, 0) } // Block returns the block at the position [pos], or an empty Block{} if it does not exist. func (w *World) Block(pos itype.Vec3i) Block { cx, cz := BlockPosToChunk(pos) cix, ciy, ciz := BlockPosToInChunk(pos) c, ok := w.Chunks[itype.Vec2i{cx, cz}] if !ok || ciy < 0 || ciy >= ChunkSizeY { return Block{} } return Block{ Id: int(c.Id[cix][ciy][ciz]), Aux: int(c.Aux[cix][ciy][ciz]), Dataset: nil, Behaviour: GetBlockBehaviour(int(c.Id[cix][ciy][ciz])), } } // SetBlock sets the block at the given position, returning its Block. // // It fails if the chunk is not loaded, and an empty Block{} is returned. func (w *World) SetBlock(pos itype.Vec3i, id, aux int, dataset itype.Dataset) Block { cx, cz := BlockPosToChunk(pos) cix, ciy, ciz := BlockPosToInChunk(pos) c, ok := w.Chunks[itype.Vec2i{cx, cz}] if !ok || ciy < 0 || ciy >= ChunkSizeY { return Block{} } c.SetBlock(cix, ciy, ciz, id, aux) return Block{ Id: int(c.Id[cix][ciy][ciz]), Aux: int(c.Aux[cix][ciy][ciz]), Dataset: nil, Behaviour: GetBlockBehaviour(int(c.Id[cix][ciy][ciz])), } } // LoadChunkFromGob loads (or overwrites) chunk [id.x, id.z] from the Gob-encoding file. func (w *World) LoadChunkFromGob(id itype.Vec2i, file io.Reader) (err error) { c := &Chunk{} if err != nil { return fmt.Errorf("World: load chunk (%d,%d) from gob file: %s", id[0], id[1], err) } if old, ok := w.Chunks[id]; ok { old.FreeRender() } c.LoadFromGobIndexed(file, id[0], id[1]) w.Chunks[id] = c c.InitRender() return nil } // Render should be called from WorldRenderer, setting shader and texture context // for the Render function, which merely calls BindVertexArray and DrawArrays. func (w *World) Render() { for _, c := range w.Chunks { c.Render() } } // RenderWater calls DrawArrays drawing the semi-transparant layer of the world. func (w *World) RenderWater() { for _, c := range w.Chunks { c.RenderWater() } }