2022-01-20 21:58:50 +08:00
|
|
|
package world
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
|
2024-08-02 18:38:33 +08:00
|
|
|
"edgaru089.ink/go/gl01/internal/util/itype"
|
2022-01-20 21:58:50 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
2022-01-28 15:24:03 +08:00
|
|
|
c.world = w
|
2022-01-20 21:58:50 +08:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2022-02-24 20:20:33 +08:00
|
|
|
// 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)
|
|
|
|
}
|
|
|
|
|
2022-01-20 21:58:50 +08:00
|
|
|
// 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])),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-28 15:24:03 +08:00
|
|
|
// 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])),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-20 21:58:50 +08:00
|
|
|
// 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()
|
|
|
|
}
|
|
|
|
}
|
2022-01-29 00:43:11 +08:00
|
|
|
|
|
|
|
// RenderWater calls DrawArrays drawing the semi-transparant layer of the world.
|
|
|
|
func (w *World) RenderWater() {
|
|
|
|
for _, c := range w.Chunks {
|
|
|
|
c.RenderWater()
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|