diff --git a/game/data/worlds/test_world/chunks/0_0.chunk b/game/data/worlds/test_world/chunks/0_0.chunk new file mode 100644 index 0000000..25da16f Binary files /dev/null and b/game/data/worlds/test_world/chunks/0_0.chunk differ diff --git a/game/data/worlds/test_world/chunks/0_1.chunk b/game/data/worlds/test_world/chunks/0_1.chunk new file mode 100644 index 0000000..aec8710 Binary files /dev/null and b/game/data/worlds/test_world/chunks/0_1.chunk differ diff --git a/game/data/worlds/test_world/chunks/0_2.chunk b/game/data/worlds/test_world/chunks/0_2.chunk new file mode 100644 index 0000000..fad0d1b Binary files /dev/null and b/game/data/worlds/test_world/chunks/0_2.chunk differ diff --git a/game/data/worlds/test_world/chunks/0_3.chunk b/game/data/worlds/test_world/chunks/0_3.chunk new file mode 100644 index 0000000..1b3e4ff Binary files /dev/null and b/game/data/worlds/test_world/chunks/0_3.chunk differ diff --git a/game/data/worlds/test_world/chunks/0_4.chunk b/game/data/worlds/test_world/chunks/0_4.chunk new file mode 100644 index 0000000..833e642 Binary files /dev/null and b/game/data/worlds/test_world/chunks/0_4.chunk differ diff --git a/game/data/worlds/test_world/chunks/1_0.chunk b/game/data/worlds/test_world/chunks/1_0.chunk new file mode 100644 index 0000000..aca5858 Binary files /dev/null and b/game/data/worlds/test_world/chunks/1_0.chunk differ diff --git a/game/data/worlds/test_world/chunks/1_1.chunk b/game/data/worlds/test_world/chunks/1_1.chunk new file mode 100644 index 0000000..308e00e Binary files /dev/null and b/game/data/worlds/test_world/chunks/1_1.chunk differ diff --git a/game/data/worlds/test_world/chunks/1_2.chunk b/game/data/worlds/test_world/chunks/1_2.chunk new file mode 100644 index 0000000..9f85f31 Binary files /dev/null and b/game/data/worlds/test_world/chunks/1_2.chunk differ diff --git a/game/data/worlds/test_world/chunks/1_3.chunk b/game/data/worlds/test_world/chunks/1_3.chunk new file mode 100644 index 0000000..6fdb6bf Binary files /dev/null and b/game/data/worlds/test_world/chunks/1_3.chunk differ diff --git a/game/data/worlds/test_world/chunks/1_4.chunk b/game/data/worlds/test_world/chunks/1_4.chunk new file mode 100644 index 0000000..7d9c153 Binary files /dev/null and b/game/data/worlds/test_world/chunks/1_4.chunk differ diff --git a/game/data/worlds/test_world/chunks/2_0.chunk b/game/data/worlds/test_world/chunks/2_0.chunk new file mode 100644 index 0000000..f712c56 Binary files /dev/null and b/game/data/worlds/test_world/chunks/2_0.chunk differ diff --git a/game/data/worlds/test_world/chunks/2_1.chunk b/game/data/worlds/test_world/chunks/2_1.chunk new file mode 100644 index 0000000..ba6197c Binary files /dev/null and b/game/data/worlds/test_world/chunks/2_1.chunk differ diff --git a/game/data/worlds/test_world/chunks/2_2.chunk b/game/data/worlds/test_world/chunks/2_2.chunk new file mode 100644 index 0000000..a92b811 Binary files /dev/null and b/game/data/worlds/test_world/chunks/2_2.chunk differ diff --git a/game/data/worlds/test_world/chunks/2_3.chunk b/game/data/worlds/test_world/chunks/2_3.chunk new file mode 100644 index 0000000..21d7fce Binary files /dev/null and b/game/data/worlds/test_world/chunks/2_3.chunk differ diff --git a/game/data/worlds/test_world/chunks/2_4.chunk b/game/data/worlds/test_world/chunks/2_4.chunk new file mode 100644 index 0000000..a693344 Binary files /dev/null and b/game/data/worlds/test_world/chunks/2_4.chunk differ diff --git a/game/data/worlds/test_world/chunks/3_0.chunk b/game/data/worlds/test_world/chunks/3_0.chunk new file mode 100644 index 0000000..82d80cc Binary files /dev/null and b/game/data/worlds/test_world/chunks/3_0.chunk differ diff --git a/game/data/worlds/test_world/chunks/3_1.chunk b/game/data/worlds/test_world/chunks/3_1.chunk new file mode 100644 index 0000000..9752d41 Binary files /dev/null and b/game/data/worlds/test_world/chunks/3_1.chunk differ diff --git a/game/data/worlds/test_world/chunks/3_2.chunk b/game/data/worlds/test_world/chunks/3_2.chunk new file mode 100644 index 0000000..33e793f Binary files /dev/null and b/game/data/worlds/test_world/chunks/3_2.chunk differ diff --git a/game/data/worlds/test_world/chunks/3_3.chunk b/game/data/worlds/test_world/chunks/3_3.chunk new file mode 100644 index 0000000..b323b6d Binary files /dev/null and b/game/data/worlds/test_world/chunks/3_3.chunk differ diff --git a/game/data/worlds/test_world/chunks/3_4.chunk b/game/data/worlds/test_world/chunks/3_4.chunk new file mode 100644 index 0000000..1e08d08 Binary files /dev/null and b/game/data/worlds/test_world/chunks/3_4.chunk differ diff --git a/game/data/worlds/test_world/chunks/4_0.chunk b/game/data/worlds/test_world/chunks/4_0.chunk new file mode 100644 index 0000000..b7bbdaf Binary files /dev/null and b/game/data/worlds/test_world/chunks/4_0.chunk differ diff --git a/game/data/worlds/test_world/chunks/4_1.chunk b/game/data/worlds/test_world/chunks/4_1.chunk new file mode 100644 index 0000000..21ce85b Binary files /dev/null and b/game/data/worlds/test_world/chunks/4_1.chunk differ diff --git a/game/data/worlds/test_world/chunks/4_2.chunk b/game/data/worlds/test_world/chunks/4_2.chunk new file mode 100644 index 0000000..82e1402 Binary files /dev/null and b/game/data/worlds/test_world/chunks/4_2.chunk differ diff --git a/game/data/worlds/test_world/chunks/4_3.chunk b/game/data/worlds/test_world/chunks/4_3.chunk new file mode 100644 index 0000000..4fc26c4 Binary files /dev/null and b/game/data/worlds/test_world/chunks/4_3.chunk differ diff --git a/game/data/worlds/test_world/chunks/4_4.chunk b/game/data/worlds/test_world/chunks/4_4.chunk new file mode 100644 index 0000000..75df60b Binary files /dev/null and b/game/data/worlds/test_world/chunks/4_4.chunk differ diff --git a/game/game b/game/game index 77ea0fd..863ddcf 100755 Binary files a/game/game and b/game/game differ diff --git a/game/game.odin b/game/game.odin index f405e7a..fd1c353 100644 --- a/game/game.odin +++ b/game/game.odin @@ -11,30 +11,32 @@ import "core:strings" player : Player world : World -camera : rl.Camera2D - main :: proc() { rl.InitWindow(1280, 720, "Odin game") + flags : rl.ConfigFlags = {.VSYNC_HINT} rl.SetConfigFlags(flags) rl.SetTargetFPS(60) - player.position.x = CELL_SIZE * 5 - player.position.y = CELL_SIZE * 5 - player.mode = .INTERACT - - camera.target = {player.position.x + (CELL_SIZE / 2), player.position.y + (CELL_SIZE / 2)} - camera.zoom = 2 - camera.offset = {f32(rl.GetScreenWidth()) / 2, f32(rl.GetScreenHeight()) / 2} + player = { + position = {CELL_SIZE * 5, CELL_SIZE * 5}, + camera = { + zoom = 2, + target = {player.position.x + (CELL_SIZE / 2), player.position.y + (CELL_SIZE / 2)}, + offset = {f32(rl.GetScreenWidth()) / 2, f32(rl.GetScreenHeight()) / 2}, + }, + mode = .INTERACT, + } load_tilemap() defer unload_tilemap() - fill_world_grid_with_nothing(&world) + world = create_world("test_world") + load_nearby_chunks(&world, player.position) + save_world(&world) - place_random_trees(&world) game_loop() } @@ -50,7 +52,7 @@ game_loop :: proc() { rl.BeginDrawing() rl.ClearBackground(rl.BLACK) - rl.BeginMode2D(camera) + rl.BeginMode2D(player.camera) draw() @@ -59,7 +61,7 @@ game_loop :: proc() { rl.DrawFPS(5,5) player_grid_pos := get_player_grid_position(&player) - player_grid_pos_tile := get_grid_tile(&world, vec2_to_vec2i(player_grid_pos)) + player_grid_pos_tile := get_world_tile(&world, vec2_to_vec2i(player_grid_pos)) status_string := rl.TextFormat("POS: %v : %v | MODE: %v", player_grid_pos, player_grid_pos_tile.type, player.mode) rl.DrawText(status_string, 5, 25, 20, rl.RED) @@ -73,11 +75,7 @@ game_loop :: proc() { } update :: proc() { - - handle_player_input(&player, &world) - handle_window_resize() - camera.target = {player.position.x + (CELL_SIZE / 2), player.position.y + (CELL_SIZE / 2)} - + player_update(&player, &world) } draw :: proc() { @@ -85,16 +83,3 @@ draw :: proc() { draw_player(&player) } -handle_window_resize :: proc() { - if rl.IsWindowResized() { - camera.offset = {f32(rl.GetScreenWidth()) / 2, f32(rl.GetScreenHeight()) / 2} - } -} - -print_grid :: proc() { - for x in 0..< len(world.grid) { - for y in 0..< len(world.grid) { - fmt.printfln("[%d, %d] %v", x, y, world.grid[x][y].type) - } - } -} diff --git a/game/math.odin b/game/math.odin index d036570..4286663 100644 --- a/game/math.odin +++ b/game/math.odin @@ -1,8 +1,8 @@ package game Vec2i :: struct { - x: u32, - y:u32, + x: int, + y: int, } vec2i_to_vec2 :: proc(v2i:Vec2i) -> [2]f32 { @@ -10,5 +10,43 @@ vec2i_to_vec2 :: proc(v2i:Vec2i) -> [2]f32 { } vec2_to_vec2i :: proc(v2:[2]f32) -> Vec2i { - return {u32(v2.x), u32(v2.y)} + return {int(v2.x), int(v2.y)} +} + +serialize_vec2i :: proc(v:Vec2i) -> [8]byte { + data: [8]byte + + x := serialize_int(v.x) + y := serialize_int(v.y) + + for i in 0..<4 { + data[i] = x[i] + } + + for i in 0..<4 { + data[4 + i] = y[i] + } + + return data +} + +deserialize_vec2i :: proc(data:[]byte) -> Vec2i { + x := deserialize_int(data[0:4]) + y := deserialize_int(data[4:8]) + return Vec2i{x,y} +} + +serialize_int :: proc(v:int) -> [4]byte { + data : [4]byte + + data[0] = byte(v) + data[1] = byte(v >> 8) + data[2] = byte(v >> 16) + data[3] = byte(v >> 24) + + return data +} + +deserialize_int :: proc(data:[]byte) -> int { + return int(data[0]) | int(data[1]) << 8 | int(data[2]) << 16 | int(data[3]) << 24 } diff --git a/game/player.odin b/game/player.odin index 40e4b4f..5c50545 100644 --- a/game/player.odin +++ b/game/player.odin @@ -3,10 +3,13 @@ package game import rl "vendor:raylib" import "core:fmt" +CHUNK_UNLOAD_DISTANCE :: 3 + Player :: struct { position : rl.Vector2, move_timer: f32, - mode: InteractMode + mode: InteractMode, + camera: rl.Camera2D } InteractMode :: enum { @@ -14,8 +17,46 @@ InteractMode :: enum { ATTACK, } -handle_player_input :: proc(p : ^Player, w: ^World) { +handle_player_camera :: proc(p:^Player) { + p.camera.target = {p.position.x + (CELL_SIZE / 2), p.position.y + (CELL_SIZE / 2)} + if rl.IsWindowResized() { + p.camera.offset = {f32(rl.GetScreenWidth()) / 2, f32(rl.GetScreenHeight()) / 2} + } +} + +player_update :: proc(p : ^Player, w: ^World) { + handle_player_input(p,w) + handle_player_camera(p) + + +} + +load_nearby_chunks :: proc(w:^World, player_pos:rl.Vector2) { + player_chunk_pos := world_pos_to_chunk_pos(player_pos) + + chunk_radius := 2 // Adjust based on the camera size + + for x := -chunk_radius; x <= chunk_radius; x += 1 { + for y := -chunk_radius; y <= chunk_radius; y += 1 { + chunk_pos := Vec2i{player_chunk_pos.x + x, player_chunk_pos.y + y} + get_chunk(w, chunk_pos) // Ensures chunk is loaded or generated + } + } +} + +unload_far_chunks :: proc(w: ^World, player_pos: Vec2i) { + for chunk_pos in w.chunks { + dist_x := abs(chunk_pos.x - player_pos.x) + dist_y := abs(chunk_pos.y - player_pos.y) + + if dist_x > CHUNK_UNLOAD_DISTANCE || dist_y > CHUNK_UNLOAD_DISTANCE { + unload_chunk(chunk_pos, w) + } + } +} + +handle_player_input :: proc(p:^Player, w:^World) { target_pos := get_player_grid_position(p) dt := rl.GetFrameTime() @@ -31,33 +72,41 @@ handle_player_input :: proc(p : ^Player, w: ^World) { if p.move_timer <= 0 { if rl.IsKeyDown(.D) { target_pos.x += 1 - if !will_collide(target_pos, w) { + if !will_collide(w, target_pos) { player.position.x += CELL_SIZE p.move_timer = move_delay + load_nearby_chunks(w, p.position) + unload_far_chunks(w, vec2_to_vec2i(p.position)) } } if rl.IsKeyDown(.A) { target_pos.x -= 1 - if !will_collide(target_pos, w) { + if !will_collide(w, target_pos) { player.position.x -= CELL_SIZE p.move_timer = move_delay + load_nearby_chunks(w, p.position) + unload_far_chunks(w, vec2_to_vec2i(p.position)) } } if rl.IsKeyDown(.W) { target_pos.y -= 1 - if !will_collide(target_pos, w) { + if !will_collide(w, target_pos) { player.position.y -= CELL_SIZE p.move_timer = move_delay + load_nearby_chunks(w, p.position) + unload_far_chunks(w, vec2_to_vec2i(p.position)) } } if rl.IsKeyDown(.S) { target_pos.y += 1 - if !will_collide(target_pos, w) { + if !will_collide(w, target_pos) { p.move_timer = move_delay player.position.y += CELL_SIZE + load_nearby_chunks(w, p.position) + unload_far_chunks(w, vec2_to_vec2i(p.position)) } } } @@ -74,12 +123,13 @@ draw_player :: proc(player:^Player) { draw_tile({27,0}, player.position, rl.DARKBLUE) } -will_collide :: proc(pos:rl.Vector2, w:^World) -> bool { - if pos.y > WORLD_SIZE * CELL_SIZE || pos.x > WORLD_SIZE * CELL_SIZE { - return false - } +will_collide :: proc(w:^World, pos:rl.Vector2) -> bool { + world_grid_pos := vec2_to_vec2i(pos) + chunk_pos := world_pos_to_chunk_pos(pos) + local_pos := get_local_chunk_pos(world_grid_pos) - tile := get_grid_tile(w, vec2_to_vec2i(pos)) + chunk := get_chunk(w, chunk_pos) + tile := get_chunk_tile(chunk, local_pos) #partial switch tile.type { case .WALL: diff --git a/game/tiles.odin b/game/tiles.odin index ac610ba..f926d3b 100644 --- a/game/tiles.odin +++ b/game/tiles.odin @@ -3,21 +3,132 @@ package game import rl "vendor:raylib" import "core:math/rand" +Tile :: struct #packed { + tilemap_pos: Vec2i, + color: rl.Color, + type: TileType, + interaction: InteractionType, + resource: ResourceType, +} + +TileType :: enum { + NOTHING, + WALL, + FOLIAGE, +} + +ResourceType :: enum { + NOTHING, + TREE, +} + +InteractionType :: enum { + NOTHING, + RESOURCE, + ENEMY, +} +serialize_tile :: proc(t: Tile) -> []byte { + data: [dynamic]byte + + // Tilemap Pos (16 bytes) + tilemap_pos_bytes := serialize_vec2i(t.tilemap_pos) + append(&data, ..tilemap_pos_bytes[:]) + + // Color (4 bytes) + append(&data, byte(t.color.r), byte(t.color.g), byte(t.color.b), byte(t.color.a)) + + // TileType (8 bytes) + tile_type_bytes := serialize_int(int(t.type)) + append(&data, ..tile_type_bytes[:]) + + // Interaction Type (8 bytes) + interaction_bytes := serialize_int(int(t.interaction)) + append(&data, ..interaction_bytes[:]) + + // Resource Type (8 bytes) + resource_bytes := serialize_int(int(t.resource)) + append(&data, ..resource_bytes[:]) + + return data[:] +} + +deserialize_tile :: proc(data: []byte) -> Tile { + t: Tile + + // Tilemap Pos (16 bytes) + t.tilemap_pos = deserialize_vec2i(data[0:16]) + + // Color (4 bytes) + t.color.r = u8(data[16]) + t.color.g = u8(data[17]) + t.color.b = u8(data[18]) + t.color.a = u8(data[19]) + + // TileType (8 bytes) + t.type = TileType(deserialize_int(data[20:28])) + + // Interaction Type (8 bytes) + t.interaction = InteractionType(deserialize_int(data[28:36])) + + // Resource Type (8 bytes) + t.resource = ResourceType(deserialize_int(data[36:44])) + + return t +} + +// serialize_tile :: proc(t: Tile) -> []byte { +// data: [dynamic]byte + +// // TileType +// tile_type_bytes := serialize_int(int(t.type)) +// append(&data, ..tile_type_bytes[:]) + +// // Tilemap Pos +// tilemap_pos_bytes := serialize_vec2i(t.tilemap_pos) +// append(&data, ..tilemap_pos_bytes[:]) + +// // Color +// append(&data, byte(t.color.r), byte(t.color.g), byte(t.color.b), byte(t.color.a)) + +// // Interaction Type +// interaction_bytes := serialize_int(int(t.interaction)) +// append(&data, ..interaction_bytes[:]) + +// // Resource Type +// resource_bytes := serialize_int(int(t.resource)) +// append(&data, ..resource_bytes[:]) + +// return data[:] +// } + +// deserialize_tile :: proc(data:[]byte) -> Tile { +// t: Tile + +// // TileType +// t.type = TileType(deserialize_int(data[0:4])) + +// // TilemapPos +// t.tilemap_pos = deserialize_vec2i(data[4:12]) + +// // Color +// t.color.r = u8(data[12]) +// t.color.g = u8(data[13]) +// t.color.b = u8(data[14]) +// t.color.a = u8(data[15]) + +// // ResourceType +// t.resource = ResourceType(deserialize_int(data[16:20])) + +// // InteractionType +// t.interaction = InteractionType(deserialize_int(data[20:24])) + +// return t +// } + tree_tile := Tile { type = .WALL, tilemap_pos = {0,1}, - color = rl.DARKGREEN -} - -place_random_trees :: proc(w:^World) { - for x in 0..< len(w.grid) { - for y in 0..< len(w.grid) { - - chance := rand.int_max(100) - - if chance <= 5 { - w.grid[x][y] = tree_tile - } - } - } + color = rl.DARKGREEN, + resource = .TREE, + interaction = .RESOURCE, } diff --git a/game/world.odin b/game/world.odin index 616e2af..3a3d479 100644 --- a/game/world.odin +++ b/game/world.odin @@ -2,61 +2,266 @@ package game import rl "vendor:raylib" import "core:fmt" - +import "core:os" +import "core:path/filepath" CELL_SIZE :: 16 -WORLD_SIZE :: 100 +CHUNK_SIZE :: 32 +WORLD_DATA_PATH :: "data/worlds" World :: struct { - grid: [WORLD_SIZE][WORLD_SIZE]Tile + data_dir: string, + chunks: map[Vec2i]Chunk } -Tile :: struct { - type: TileType, - tilemap_pos:rl.Vector2, - color:rl.Color, +Chunk :: struct #packed { + position: Vec2i, + tiles: [CHUNK_SIZE][CHUNK_SIZE]Tile, } -TileType :: enum { - NOTHING, - WALL, - DOOR, - FLOOR, -} - -set_grid_tile :: proc(w:^World, pos:Vec2i, t:Tile) { - w.grid[pos.x][pos.y] = t -} - -get_grid_tile :: proc(w: ^World, pos: Vec2i) -> Tile { - if pos.x < 0 || pos.x >= len(w.grid) || pos.y < 0 || pos.y >= len(w.grid[0]) { - // fmt.printfln("Target [%v] outside of world bounds", pos) - return w.grid[0][0] // Default or error 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), } - return w.grid[pos.x][pos.y] } -fill_world_grid_with_nothing :: proc(w:^World) { - for x in 0..< len(w.grid) { - for y in 0.. 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 { + 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.. 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 { + 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 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 + for chunk_pos, chunk in w.chunks { + for x in 0..