odin-raylib-game/game/world.odin

268 lines
6.7 KiB
Odin

package game
import rl "vendor:raylib"
import "core:fmt"
import "core:os"
import "core:path/filepath"
CELL_SIZE :: 16
CHUNK_SIZE :: 32
WORLD_DATA_PATH :: "data/worlds"
World :: struct {
data_dir: string,
chunks: map[Vec2i]Chunk
}
Chunk :: struct #packed {
position: Vec2i,
tiles: [CHUNK_SIZE][CHUNK_SIZE]Tile,
}
create_world :: proc(name:string) -> World {
data_dir := fmt.tprintf("%v/%v", WORLD_DATA_PATH, name)
if !os.is_dir(data_dir) {
fmt.printfln("Data dir: %v does not exist", data_dir)
os.make_directory(data_dir)
}
chunk_dir := fmt.tprintf("%v/%v", data_dir, "chunks")
if !os.is_dir(chunk_dir) {
os.make_directory(chunk_dir)
}
return World {
data_dir = data_dir,
chunks = make(map[Vec2i]Chunk),
}
}
load_world :: proc(name:string) -> World {
dir := fmt.tprintf("%v/%v", WORLD_DATA_PATH, name)
if !os.is_dir(dir) {
panic("Couldnt load world")
}
return World {
data_dir = dir,
chunks = make(map[Vec2i]Chunk),
}
}
save_world :: proc(w:^World) {
if !os.is_dir(w.data_dir) {
panic("World has invalid data_path")
}
fmt.printfln("Saving world %v", w.data_dir)
for chunk in w.chunks {
save_chunk(get_chunk(w, chunk), w)
}
}
serialize_chunk :: proc(c:^Chunk) -> []byte {
data : [dynamic]byte
// Position
pos_bytes := serialize_vec2i(c.position)
append(&data, ..pos_bytes[:])
// Tiles
for y in 0..<CHUNK_SIZE {
for x in 0..<CHUNK_SIZE {
tile_bytes := serialize_tile(c.tiles[x][y])
fmt.printfln("Serialized tile size: %v", len(tile_bytes))
append(&data, ..tile_bytes[:])
}
}
fmt.printfln("Serialized chunk size: %v", len(data)) // Print final serialized size
fmt.printfln("Before serialization, Chunk position: %v", c.position)
return data[:]
}
deserialize_chunk :: proc(data: []byte) -> Chunk {
chunk: Chunk
// Position
chunk.position = deserialize_vec2i(data[0:8])
// Tiles
offset := 8
tile_size := size_of(Tile)
// Check total expected size of data
expected_size := offset + (CHUNK_SIZE * CHUNK_SIZE * tile_size)
if len(data) != expected_size {
fmt.printfln("Error: Data size mismatch. Expected size: %v, Actual size: %v", expected_size, len(data))
fmt.printfln("Chunk position: %v", chunk.position)
return chunk // Return empty or invalid chunk to handle the error.
}
for y in 0..<CHUNK_SIZE {
for x in 0..<CHUNK_SIZE {
start := offset + (y * CHUNK_SIZE + x) * tile_size
end := start + tile_size
fmt.printfln("Start:%v, End:%v", start, end)
if end > len(data) {
fmt.printfln("Out of bounds access. Start:%v, End:%v | Data Length: %v", start, end, len(data))
break
}
chunk.tiles[x][y] = deserialize_tile(data[start:end])
}
}
fmt.printfln("Expected chunk size: %v | Serialized chunk data size: %v", size_of(Chunk), len(data))
return chunk
}
save_chunk :: proc(c:^Chunk, w:^World) {
chunk_dir := fmt.tprintf("%v/%v", w.data_dir, "chunks")
if !os.is_dir(chunk_dir) {
os.make_directory(chunk_dir)
}
filename := fmt.tprintf("%v/%v_%v.chunk", chunk_dir, c.position.x, c.position.y)
data := serialize_chunk(c)
fmt.printfln("Writing chunk data: %v bytes", len(data))
err := os.write_entire_file_or_err(filename, data)
if err != nil {
fmt.printfln("Failed to save chunk: %v", err)
}
}
load_chunk :: proc(pos:Vec2i, w:^World) -> Chunk {
chunk_dir := fmt.tprintf("%v/%v", w.data_dir, "chunks")
if !os.is_dir(chunk_dir) {
}
filename := fmt.tprintf("%v/%v_%v.chunk", chunk_dir, pos.x, pos.y)
data, err := os.read_entire_file_from_filename_or_err(filename)
if err != nil {
fmt.printfln("Chunk %v file does not exist, creating new chunk", pos)
return generate_chunk(pos)
}
return deserialize_chunk(data)
}
unload_chunk :: proc(pos:Vec2i, w:^World) {
_, exists := w.chunks[pos]
if exists {
save_chunk(get_chunk(w, pos), w)
delete_key(&w.chunks, pos)
}
}
generate_chunk :: proc(pos:Vec2i) -> Chunk {
chunk := Chunk {position = pos}
for x in 0..<CHUNK_SIZE {
for y in 0..<CHUNK_SIZE {
chunk.tiles[x][y] = Tile {
type = .FOLIAGE,
tilemap_pos = {0,2},
interaction = .NOTHING,
resource = .NOTHING,
color = rl.GREEN
}
}
}
center_pos := Vec2i{CHUNK_SIZE/2, CHUNK_SIZE/2}
// set_chunk_tile(&chunk, tree_tile, center_pos)
return chunk
}
get_chunk :: proc(w:^World, chunk_pos:Vec2i) -> ^Chunk {
chunk, exists := w.chunks[chunk_pos]
if !exists {
w.chunks[chunk_pos] = load_chunk(chunk_pos, w)
}
return &w.chunks[chunk_pos]
}
get_chunk_from_world_pos :: proc(w:^World, pos:rl.Vector2) -> ^Chunk {
chunk_pos := world_pos_to_chunk_pos(pos)
return get_chunk(w, chunk_pos)
}
world_pos_to_chunk_pos :: proc(pos:rl.Vector2) -> Vec2i {
chunk_pos := vec2_to_vec2i({pos.x / CHUNK_SIZE, pos.y / CHUNK_SIZE})
return chunk_pos
}
get_local_chunk_pos :: proc(pos:Vec2i) -> Vec2i {
return Vec2i {
(pos.x % CHUNK_SIZE + CHUNK_SIZE) % CHUNK_SIZE,
(pos.y % CHUNK_SIZE + CHUNK_SIZE) % CHUNK_SIZE,
}
}
get_world_tile :: proc(w:^World, pos:Vec2i) -> ^Tile {
chunk_pos := world_pos_to_chunk_pos(vec2i_to_vec2(pos))
local_pos := get_local_chunk_pos(pos)
chunk := get_chunk(w, chunk_pos)
return get_chunk_tile(chunk, local_pos)
}
get_chunk_tile :: proc(c:^Chunk, pos:Vec2i) -> ^Tile {
return &c.tiles[pos.x][pos.y]
}
set_chunk_tile :: proc(c:^Chunk, t:Tile, pos:Vec2i) {
c.tiles[pos.x][pos.y] = t
}
draw_world :: proc(w:^World) {
for chunk_pos, chunk in w.chunks {
for x in 0..<CHUNK_SIZE {
for y in 0..<CHUNK_SIZE {
tile := chunk.tiles[x][y]
world_x := chunk_pos.x * CHUNK_SIZE + x
world_y := chunk_pos.y * CHUNK_SIZE + y
pos := rl.Vector2{f32(world_x * CELL_SIZE), f32(world_y * CELL_SIZE)}
if tile.type != .NOTHING {
draw_tile(vec2i_to_vec2(tile.tilemap_pos), pos, tile.color)
}
}
}
}
}
// draw_world :: proc(w:^World) {
// for x in 0..< len(w.grid) {
// for y in 0..< len(w.grid) {
// tile := w.grid[x][y]
// posX := x * TILE_SIZE
// posY := y * TILE_SIZE
// if tile.type != .NOTHING {
// draw_tile(tile.tilemap_pos, {f32(posX), f32(posY)}, tile.color)
// }
// }
// }
// }