diff --git a/Makefile b/Makefile index 5671082..8c15ed4 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ run: - odin run src -out:game.exe -debug -use-separate-modules -vet + odin run src -out:game.exe -debug -vet build: odin build src -out:game.exe -debug -use-separate-modules diff --git a/assets/assets.aseprite b/assets/assets.aseprite index 0e3fb38..d481efd 100644 Binary files a/assets/assets.aseprite and b/assets/assets.aseprite differ diff --git a/assets/axe.png b/assets/axe.png new file mode 100644 index 0000000..b1a3c15 Binary files /dev/null and b/assets/axe.png differ diff --git a/assets/watering_can.png b/assets/watering_can.png new file mode 100644 index 0000000..af890e7 Binary files /dev/null and b/assets/watering_can.png differ diff --git a/src/constants.odin b/src/constants.odin index c80f632..971d5c5 100644 --- a/src/constants.odin +++ b/src/constants.odin @@ -3,3 +3,5 @@ package game GAME_SCREEN_WIDTH: f32 = 480 GAME_SCREEN_HEIGHT: f32 = 270 TILE_SIZE :: 16 + +PICKUP_DISTANCE :: 15 diff --git a/src/entity.odin b/src/entity.odin index 133ec80..166b6ee 100644 --- a/src/entity.odin +++ b/src/entity.odin @@ -1,6 +1,9 @@ package game +import "core:fmt" import "core:math" +import "core:math/linalg" +import "core:slice" import rl "vendor:raylib" EntityKind :: enum { @@ -50,20 +53,26 @@ EntityFlags :: enum { } create_entity :: proc(entity: Entity) -> ^Entity { - for &e in entities { - if .Allocated not_in e.flags { - e = entity - e.flags = {.Allocated} - latest_entity_handle += 1 - return &e - } - } - assert(false, "Failed to allocate entity, not enough space!!!!") - return nil + entity := entity + entity.flags = {.Allocated} + entity.handle = latest_entity_handle + latest_entity_handle += 1 + append(&entities, entity) + return &entities[len(entities) - 1] } destroy_entity :: proc(e: ^Entity) { - e^ = Entity{} + destroy_entity_by_handle(e.handle) +} + +destroy_entity_by_handle :: proc(handle: EntityHandle) { + idx: int + for ent, i in entities { + if ent.handle == handle { + idx = i + } + } + unordered_remove(&entities, idx) } handle_to_entity :: proc(handle: EntityHandle) -> ^Entity { @@ -76,6 +85,8 @@ handle_to_entity :: proc(handle: EntityHandle) -> ^Entity { } update_entities :: proc() { + player_entity, _ := get_player_entity_and_data() + for &e in entities { if .Allocated not_in e.flags {continue} @@ -85,17 +96,50 @@ update_entities :: proc() { case PlayerData: update_animation(&data.animation) case ItemData: - //rl.DrawTextureV(data.texture, e.pos + {0, t * 3}, rl.WHITE) + dist := linalg.distance(e.pos, player_entity.pos) + if dist <= PICKUP_DISTANCE { + rl.PlaySound(pickup_sound) + player_inventory[data.id] += data.count + destroy_entity(&e) + } } } + init_hotbar_from_inventory() } + +get_player_entity_and_data :: proc() -> (^Entity, PlayerData) { + player_entity := handle_to_entity(player_handle) + if player_entity == nil { + fmt.panicf("Player handle is invalid, how???") + } + player_data, ok := player_entity.data.(PlayerData);if ok { + return player_entity, player_data + } else { + fmt.panicf("Player data is invalid, how???") + } + return nil, {} +} + draw_entities :: proc() { + slice.sort_by(entities[:], proc(a, b: Entity) -> bool { + return a.pos.y < b.pos.y + }) + for e in entities { + if .Allocated not_in e.flags {continue} + #partial switch &data in e.data { + case TileData: + draw_tile(e) + } + } for e in entities { if .Allocated not_in e.flags {continue} + // Draw debug circle + //rl.DrawCircleV(e.pos, 1, rl.RED) + switch &data in e.data { case TileData: - draw_tile(e) + draw_tile_plant(e) case PlayerData: draw_player(e) case ItemData: @@ -110,12 +154,16 @@ entity_to_handle :: proc(e: Entity) -> EntityHandle { draw_player :: proc(e: Entity) { data, ok := e.data.(PlayerData);if ok { - draw_animation(data.animation, e.pos) + draw_animation(data.animation, e.pos + {TILE_SIZE, TILE_SIZE}) } } + draw_item :: proc(e: Entity) { data, ok := e.data.(ItemData);if ok { t := f32(math.sin(rl.GetTime() * 5)) - rl.DrawTextureV(data.texture, e.pos + {0, t * 3}, rl.WHITE) + pos := e.pos + {0, t * 3} + rl.DrawTextureV(data.texture, pos, rl.WHITE) + // Draw debug circle + //rl.DrawCircleV(pos, 1, rl.RED) } } diff --git a/src/globals.odin b/src/globals.odin index 7b3a04b..ccc8c7c 100644 --- a/src/globals.odin +++ b/src/globals.odin @@ -25,7 +25,7 @@ player_handle: EntityHandle tiles: [dynamic]Tile animations: [dynamic]^Animation -entities: [256]Entity +entities: [dynamic]Entity current_tick: time.Tick @@ -33,6 +33,7 @@ tween_ctx_f32: t.TweenContext(f32) tween_ctx_vec2: t.TweenContext([2]f32) player_inventory: [InventoryItem]int +hotbar: Hotbar InventoryItem :: enum { //Tools @@ -48,16 +49,7 @@ InventoryItem :: enum { //Other } -ITEM_TEXTURES := [InventoryItem]^rl.Texture2D { - .hoe = &hoe_texture, - .scythe = &scythe_texture, - .watering_can = &tomato_texture, - .pickaxe = &tomato_texture, - .axe = &tomato_texture, - .tomato = &tomato_texture, - .tomato_seed = &tomato_texture, - .tomato_seedling = &tomato_texture, -} +ITEM_TEXTURES: [InventoryItem]^rl.Texture2D HotbarKind :: enum { tools, @@ -66,16 +58,10 @@ HotbarKind :: enum { Hotbar :: struct { kind: HotbarKind, - tool_slots: []InventoryItem, - seed_slots: []InventoryItem, + tool_slots: [dynamic]InventoryItem, + seed_slots: [dynamic]InventoryItem, current_slot: int, } -hotbar := Hotbar { - tool_slots = {.hoe, .scythe, .watering_can}, - seed_slots = {.tomato_seed}, -} - -TOOL_SLOTS := len(hotbar.tool_slots) update_hotbar :: proc() { if rl.IsKeyPressed(.E) { @@ -95,9 +81,10 @@ update_hotbar :: proc() { draw_hotbar :: proc() { rl.DrawRectangleRec({TOOL_POS.x, TOOL_POS.y, 32, 32}, rl.WHITE) slots := hotbar.kind == .tools ? hotbar.tool_slots : hotbar.seed_slots - hotbar_item_texture := ITEM_TEXTURES[slots[hotbar.current_slot]]^ + if len(slots) == 0 {return} + hotbar_item_texture := ITEM_TEXTURES[slots[hotbar.current_slot]] rl.DrawTexturePro( - hotbar_item_texture, + hotbar_item_texture^, {0, 0, 16, 16}, {TOOL_POS.x, TOOL_POS.y, 32, 32}, {}, @@ -107,8 +94,50 @@ draw_hotbar :: proc() { } -get_current_hand_item :: proc() -> InventoryItem { - return( - hotbar.kind == .tools ? hotbar.tool_slots[hotbar.current_slot] : hotbar.seed_slots[hotbar.current_slot] \ - ) +get_current_hand_item :: proc() -> (InventoryItem, bool) { + slots := hotbar.kind == .tools ? hotbar.tool_slots : hotbar.seed_slots + if len(slots) == 0 { + return nil, false + } else { + return slots[hotbar.current_slot], true + } +} + +init_hotbar_from_inventory :: proc() { + clear(&hotbar.tool_slots) + clear(&hotbar.seed_slots) + for amount, item in player_inventory { + switch item { + case .hoe: + fallthrough + case .scythe: + fallthrough + case .watering_can: + fallthrough + case .pickaxe: + fallthrough + case .axe: + if amount > 0 { + append(&hotbar.tool_slots, item) + } + case .tomato_seed: + if amount > 0 { + append(&hotbar.seed_slots, item) + } + case .tomato: + case .tomato_seedling: + } + } +} + +update_seed_hotbar :: proc() { + clear(&hotbar.seed_slots) + for amount, item in player_inventory { + #partial switch item { + case .tomato_seed: + if amount > 0 { + append(&hotbar.seed_slots, item) + } + } + } } diff --git a/src/resources.odin b/src/resources.odin index cedf723..5a50e05 100644 --- a/src/resources.odin +++ b/src/resources.odin @@ -14,6 +14,8 @@ tile_dirt_dry_sprite: []u8 : #load("../assets/tile_dirt_dry.png") tile_dirt_wet_sprite: []u8 : #load("../assets/tile_dirt_wet.png") hoe_sprite: []u8 : #load("../assets/hoe.png") scythe_sprite: []u8 : #load("../assets/scythe.png") +watering_can_sprite: []u8 : #load("../assets/watering_can.png") +axe_sprite: []u8 : #load("../assets/axe.png") pickup_sound: rl.Sound @@ -24,6 +26,8 @@ tile_dirt_wet_texture: rl.Texture2D tomato_texture: rl.Texture2D hoe_texture: rl.Texture2D scythe_texture: rl.Texture2D +watering_can_texture: rl.Texture2D +axe_texture: rl.Texture2D player_animation: Animation tomato_animation: Animation @@ -72,13 +76,18 @@ load_resources :: proc() { offset = {TILE_SIZE / 2, TILE_SIZE}, } - tomato_texture = load_texture_from_memory(tomato_item_sprite) - sell_button_texture = load_texture_from_memory(sell_button_sprite) background_texture = load_texture_from_memory(background_sprite) + + sell_button_texture = load_texture_from_memory(sell_button_sprite) + tile_dirt_dry_texture = load_texture_from_memory(tile_dirt_dry_sprite) tile_dirt_wet_texture = load_texture_from_memory(tile_dirt_wet_sprite) + tomato_texture = load_texture_from_memory(tomato_item_sprite) + hoe_texture = load_texture_from_memory(hoe_sprite) scythe_texture = load_texture_from_memory(scythe_sprite) + watering_can_texture = load_texture_from_memory(watering_can_sprite) + axe_texture = load_texture_from_memory(axe_sprite) pickup_sound = load_sound_from_memory(pickup_sound_data) rl.SetSoundVolume(pickup_sound, 0.1) @@ -91,6 +100,29 @@ load_resources :: proc() { }, ) player_handle = entity_to_handle(e^) + + // Starting inventory + player_inventory[.tomato_seed] = 5 + player_inventory[.hoe] = 1 + player_inventory[.watering_can] = 1 + player_inventory[.scythe] = 1 + player_inventory[.axe] = 1 + + hotbar.tool_slots = make([dynamic]InventoryItem) + hotbar.seed_slots = make([dynamic]InventoryItem) + init_hotbar_from_inventory() + + ITEM_TEXTURES = [InventoryItem]^rl.Texture2D { + .hoe = &hoe_texture, + .scythe = &scythe_texture, + .watering_can = &watering_can_texture, + .pickaxe = &tomato_texture, + .axe = &axe_texture, + .tomato = &tomato_texture, + .tomato_seed = &tomato_texture, + .tomato_seedling = &tomato_texture, + } + } free_resources :: proc() { @@ -99,11 +131,21 @@ free_resources :: proc() { rl.UnloadSound(pickup_sound) - rl.UnloadTexture(tomato_texture) rl.UnloadTexture(sell_button_texture) rl.UnloadTexture(background_texture) + + rl.UnloadTexture(tomato_texture) rl.UnloadTexture(tile_dirt_dry_texture) rl.UnloadTexture(tile_dirt_wet_texture) - rl.UnloadRenderTexture(game_render_buffer) // Unload render texture + rl.UnloadTexture(hoe_texture) + rl.UnloadTexture(scythe_texture) + rl.UnloadTexture(watering_can_texture) + rl.UnloadTexture(axe_texture) + + rl.UnloadRenderTexture(game_render_buffer) + + delete(hotbar.tool_slots) + delete(hotbar.seed_slots) + delete(entities) } diff --git a/src/tile.odin b/src/tile.odin index 35cc388..0627fdf 100644 --- a/src/tile.odin +++ b/src/tile.odin @@ -12,7 +12,9 @@ Tile :: struct { interact_with_tile_under_mouse :: proc(mouse_button: rl.MouseButton) { tile_position := get_tile_position(world_mouse) - current_hand_item := get_current_hand_item() + current_hand_item, hok := get_current_hand_item();if !hok { + return + } tile_entity: ^Entity for &e in entities { @@ -51,8 +53,19 @@ interact_with_tile_under_mouse :: proc(mouse_button: rl.MouseButton) { pos = tile_entity.pos, kind = .Item, data = ItemData { - id = get_seed_to_plant_id(tile_data.plant_id), + id = get_plant_to_seed_id(tile_data.plant_id), count = 3, + texture = get_seed_texture(tile_data.plant_id)^, + }, + }, + ) + create_entity( + Entity { + pos = tile_entity.pos, + kind = .Item, + data = ItemData { + id = get_seed_to_plant_id(tile_data.plant_id), + count = 1, texture = get_plant_texture(tile_data.plant_id)^, }, }, @@ -64,6 +77,8 @@ interact_with_tile_under_mouse :: proc(mouse_button: rl.MouseButton) { tile_data.has_plant = true tile_data.plant_id = current_hand_item tile_data.animation = get_plant_animation(current_hand_item) + player_inventory[current_hand_item] -= 1 + update_seed_hotbar() } } } @@ -73,6 +88,10 @@ get_tile_position :: proc(wpos: rl.Vector2) -> rl.Vector2 { return {math.floor(wpos.x / TILE_SIZE) * TILE_SIZE, math.floor(wpos.y / TILE_SIZE) * TILE_SIZE} } +get_plant_to_seed_id :: proc(plant: InventoryItem) -> InventoryItem { + return .tomato_seed +} + get_seed_to_plant_id :: proc(seed: InventoryItem) -> InventoryItem { return .tomato } @@ -82,7 +101,20 @@ get_plant_animation :: proc(plant: InventoryItem) -> Animation { } get_plant_texture :: proc(plant: InventoryItem) -> ^rl.Texture2D { - return ITEM_TEXTURES[plant] + //#partial switch plant { + //case .tomato_seed: + // fallthrough + //case .tomato: + // fallthrough + //case .tomato_seedling: + // return ITEM_TEXTURES[.tomato] + //} + //return ITEM_TEXTURES[plant] + return ITEM_TEXTURES[.tomato] +} + +get_seed_texture :: proc(plant: InventoryItem) -> ^rl.Texture2D { + return ITEM_TEXTURES[.tomato_seed] } update_tile :: proc(e: Entity, data: ^TileData) { @@ -94,14 +126,19 @@ update_tile :: proc(e: Entity, data: ^TileData) { } } +draw_tile_plant :: proc(e: Entity) { + data, ok := e.data.(TileData);if ok { + if !data.is_tiled {return} + if data.has_plant { + draw_animation(data.animation, e.pos) + } + } +} draw_tile :: proc(e: Entity) { data, ok := e.data.(TileData);if ok { if !data.is_tiled {return} tile_tint := data.is_watered ? rl.SKYBLUE : rl.WHITE rl.DrawTextureV(tile_dirt_dry_texture, e.pos, tile_tint) - if data.has_plant { - draw_animation(data.animation, e.pos) - } } }