2020-06-11 20:42:59 +01:00
|
|
|
package atlas
|
2020-06-07 18:08:34 +01:00
|
|
|
|
2020-06-07 18:33:44 +01:00
|
|
|
import (
|
|
|
|
"log"
|
2020-06-07 22:36:11 +01:00
|
|
|
"math/rand"
|
2020-06-09 18:08:07 +01:00
|
|
|
|
|
|
|
"github.com/mdiluz/rove/pkg/maths"
|
2020-06-26 19:45:24 +01:00
|
|
|
"github.com/mdiluz/rove/pkg/objects"
|
2020-06-09 18:08:07 +01:00
|
|
|
"github.com/mdiluz/rove/pkg/vector"
|
2020-06-07 18:33:44 +01:00
|
|
|
)
|
2020-06-07 18:08:34 +01:00
|
|
|
|
|
|
|
// Chunk represents a fixed square grid of tiles
|
|
|
|
type Chunk struct {
|
|
|
|
// Tiles represents the tiles within the chunk
|
2020-06-12 22:51:18 +01:00
|
|
|
Tiles []byte `json:"tiles"`
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|
|
|
|
|
2020-06-27 14:48:21 +01:00
|
|
|
// SpawnContent will create a chunk and fill it with spawned tiles
|
|
|
|
func (c *Chunk) SpawnContent(size int) {
|
|
|
|
c.Tiles = make([]byte, size*size)
|
|
|
|
for i := 0; i < len(c.Tiles); i++ {
|
|
|
|
c.Tiles[i] = objects.Empty
|
|
|
|
}
|
|
|
|
|
|
|
|
// For now, fill it randomly with objects
|
|
|
|
for i := range c.Tiles {
|
|
|
|
if rand.Intn(16) == 0 {
|
|
|
|
c.Tiles[i] = objects.LargeRock
|
|
|
|
} else if rand.Intn(32) == 0 {
|
|
|
|
c.Tiles[i] = objects.SmallRock
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 18:08:34 +01:00
|
|
|
// Atlas represents a grid of Chunks
|
|
|
|
type Atlas struct {
|
|
|
|
// Chunks represents all chunks in the world
|
|
|
|
// This is intentionally not a 2D array so it can be expanded in all directions
|
|
|
|
Chunks []Chunk `json:"chunks"`
|
|
|
|
|
2020-06-27 14:48:21 +01:00
|
|
|
// CurrentSize is the current width/height of the given atlas
|
|
|
|
CurrentSize int `json:"currentSize"`
|
2020-06-07 18:08:34 +01:00
|
|
|
|
|
|
|
// ChunkSize is the dimensions of each chunk
|
|
|
|
ChunkSize int `json:"chunksize"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewAtlas creates a new empty atlas
|
2020-06-27 14:48:21 +01:00
|
|
|
func NewAtlas(chunkSize int) Atlas {
|
|
|
|
return Atlas{
|
|
|
|
CurrentSize: 0,
|
|
|
|
Chunks: nil,
|
|
|
|
ChunkSize: chunkSize,
|
2020-06-07 18:57:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 18:08:34 +01:00
|
|
|
// SetTile sets an individual tile's kind
|
2020-06-27 14:48:21 +01:00
|
|
|
func (a *Atlas) SetTile(v vector.Vector, tile byte) {
|
|
|
|
// Get the chunk, expand, and spawn it if needed
|
|
|
|
c := a.toChunkWithGrow(v)
|
|
|
|
chunk := a.Chunks[c]
|
|
|
|
if chunk.Tiles == nil {
|
|
|
|
chunk.SpawnContent(a.ChunkSize)
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|
|
|
|
|
2020-06-11 20:34:30 +01:00
|
|
|
local := a.toChunkLocal(v)
|
2020-06-07 18:08:34 +01:00
|
|
|
tileId := local.X + local.Y*a.ChunkSize
|
2020-06-27 14:48:21 +01:00
|
|
|
|
|
|
|
// Sanity check
|
|
|
|
if tileId >= len(chunk.Tiles) || tileId < 0 {
|
|
|
|
log.Fatalf("Local tileID is not in valid chunk, somehow, this means something is very wrong")
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|
2020-06-27 14:48:21 +01:00
|
|
|
|
|
|
|
// Set the chunk back
|
|
|
|
chunk.Tiles[tileId] = tile
|
|
|
|
a.Chunks[c] = chunk
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetTile will return an individual tile
|
2020-06-27 14:48:21 +01:00
|
|
|
func (a *Atlas) GetTile(v vector.Vector) byte {
|
|
|
|
// Get the chunk, expand, and spawn it if needed
|
|
|
|
c := a.toChunkWithGrow(v)
|
|
|
|
chunk := a.Chunks[c]
|
|
|
|
if chunk.Tiles == nil {
|
|
|
|
chunk.SpawnContent(a.ChunkSize)
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|
|
|
|
|
2020-06-11 20:34:30 +01:00
|
|
|
local := a.toChunkLocal(v)
|
2020-06-07 18:08:34 +01:00
|
|
|
tileId := local.X + local.Y*a.ChunkSize
|
2020-06-27 14:48:21 +01:00
|
|
|
|
|
|
|
// Sanity check
|
|
|
|
if tileId >= len(chunk.Tiles) || tileId < 0 {
|
|
|
|
log.Fatalf("Local tileID is not in valid chunk, somehow, this means something is very wrong")
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|
|
|
|
|
2020-06-27 14:48:21 +01:00
|
|
|
return chunk.Tiles[tileId]
|
|
|
|
}
|
|
|
|
|
|
|
|
// toChunkWithGrow will expand the atlas for a given tile, returns the new chunk
|
|
|
|
func (a *Atlas) toChunkWithGrow(v vector.Vector) int {
|
|
|
|
for {
|
|
|
|
// Get the chunk, and grow looping until we have a valid chunk
|
|
|
|
chunk := a.toChunk(v)
|
|
|
|
if chunk >= len(a.Chunks) || chunk < 0 {
|
|
|
|
a.grow()
|
|
|
|
} else {
|
|
|
|
return chunk
|
|
|
|
}
|
|
|
|
}
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|
|
|
|
|
2020-06-11 20:34:30 +01:00
|
|
|
// toChunkLocal gets a chunk local coordinate for a tile
|
|
|
|
func (a *Atlas) toChunkLocal(v vector.Vector) vector.Vector {
|
2020-06-09 18:08:07 +01:00
|
|
|
return vector.Vector{X: maths.Pmod(v.X, a.ChunkSize), Y: maths.Pmod(v.Y, a.ChunkSize)}
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|
|
|
|
|
2020-06-27 14:48:21 +01:00
|
|
|
// GetChunkID gets the current chunk ID for a position in the world
|
2020-06-11 20:34:30 +01:00
|
|
|
func (a *Atlas) toChunk(v vector.Vector) int {
|
|
|
|
local := a.toChunkLocal(v)
|
2020-06-07 18:08:34 +01:00
|
|
|
// Get the chunk origin itself
|
|
|
|
origin := v.Added(local.Negated())
|
|
|
|
// Divided it by the number of chunks
|
|
|
|
origin = origin.Divided(a.ChunkSize)
|
|
|
|
// Shift it by our size (our origin is in the middle)
|
2020-06-27 14:48:21 +01:00
|
|
|
origin = origin.Added(vector.Vector{X: a.CurrentSize / 2, Y: a.CurrentSize / 2})
|
2020-06-07 18:08:34 +01:00
|
|
|
// Get the ID based on the final values
|
2020-06-27 14:48:21 +01:00
|
|
|
return (a.CurrentSize * origin.Y) + origin.X
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|
|
|
|
|
2020-06-11 20:34:30 +01:00
|
|
|
// chunkOrigin gets the chunk origin for a given chunk index
|
|
|
|
func (a *Atlas) chunkOrigin(chunk int) vector.Vector {
|
2020-06-09 18:08:07 +01:00
|
|
|
v := vector.Vector{
|
2020-06-27 14:48:21 +01:00
|
|
|
X: maths.Pmod(chunk, a.CurrentSize) - (a.CurrentSize / 2),
|
|
|
|
Y: (chunk / a.CurrentSize) - (a.CurrentSize / 2),
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return v.Multiplied(a.ChunkSize)
|
|
|
|
}
|
|
|
|
|
2020-06-27 14:48:21 +01:00
|
|
|
// grow will expand the current atlas in all directions by one chunk
|
|
|
|
func (a *Atlas) grow() error {
|
|
|
|
// Create a new atlas
|
|
|
|
newAtlas := NewAtlas(a.ChunkSize)
|
2020-06-07 22:30:03 +01:00
|
|
|
|
2020-06-27 14:48:21 +01:00
|
|
|
// Expand by one on each axis
|
|
|
|
newAtlas.CurrentSize = a.CurrentSize + 2
|
2020-06-07 18:08:34 +01:00
|
|
|
|
2020-06-27 14:48:21 +01:00
|
|
|
// Allocate the new atlas chunks
|
|
|
|
// These chunks will have nil tile slices
|
|
|
|
newAtlas.Chunks = make([]Chunk, newAtlas.CurrentSize*newAtlas.CurrentSize)
|
2020-06-07 18:08:34 +01:00
|
|
|
|
2020-06-27 14:48:21 +01:00
|
|
|
// Copy all old chunks into the new atlas
|
2020-06-07 18:08:34 +01:00
|
|
|
for index, chunk := range a.Chunks {
|
|
|
|
// Calculate the new chunk location and copy over the data
|
2020-06-11 20:34:30 +01:00
|
|
|
newAtlas.Chunks[newAtlas.toChunk(a.chunkOrigin(index))] = chunk
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|
|
|
|
|
2020-06-07 18:33:44 +01:00
|
|
|
// Copy the new atlas data into this one
|
|
|
|
*a = newAtlas
|
|
|
|
|
2020-06-07 18:08:34 +01:00
|
|
|
// Return the new atlas
|
2020-06-07 18:33:44 +01:00
|
|
|
return nil
|
2020-06-07 18:08:34 +01:00
|
|
|
}
|