diff --git a/assets/sounds/pickup_sound.mp3 b/assets/sounds/pickup_sound.mp3 new file mode 100644 index 0000000..5904998 Binary files /dev/null and b/assets/sounds/pickup_sound.mp3 differ diff --git a/assets/sounds/pickup_sound.ogg b/assets/sounds/pickup_sound.ogg new file mode 100644 index 0000000..47648df Binary files /dev/null and b/assets/sounds/pickup_sound.ogg differ diff --git a/assets/sounds/sell_sound.mp3 b/assets/sounds/sell_sound.mp3 new file mode 100644 index 0000000..f4cc225 Binary files /dev/null and b/assets/sounds/sell_sound.mp3 differ diff --git a/assets/tomato_item.png b/assets/tomato_item.png new file mode 100644 index 0000000..7c5f104 Binary files /dev/null and b/assets/tomato_item.png differ diff --git a/src/main.odin b/src/main.odin index fec4ff6..545c8b0 100644 --- a/src/main.odin +++ b/src/main.odin @@ -1,5 +1,6 @@ package game +import t "../tween" import sa "core:container/small_array" import "core:fmt" import "core:math" @@ -26,48 +27,28 @@ world_mouse: rl.Vector2 screen_game_area: rl.Rectangle game_render_buffer_area := rl.Rectangle{0, 0, GAME_SCREEN_WIDTH, -GAME_SCREEN_HEIGHT} -player_sprite: []u8 : #load("../assets/player.png") -tomato_sprite: []u8 : #load("../assets/tomato.png") +pickup_sound := rl.LoadSound("assets/sounds/pickup_sound.ogg") + +player_spritesheet_sprite: []u8 : #load("../assets/player.png") +tomato_spritesheet_sprite: []u8 : #load("../assets/tomato.png") +tomato_item_sprite: []u8 : #load("../assets/tomato_item.png") sell_button_sprite: []u8 : #load("../assets/sell_button.png") background_sprite: []u8 : #load("../assets/background.png") tile_dirt_dry_sprite: []u8 : #load("../assets/tile_dirt_dry.png") tile_dirt_wet_sprite: []u8 : #load("../assets/tile_dirt_wet.png") -player_spritesheet_image := rl.LoadImageFromMemory( - ".png", - raw_data(player_sprite), - auto_cast len(player_sprite), -) -tomato_spritesheet_image := rl.LoadImageFromMemory( - ".png", - raw_data(tomato_sprite), - auto_cast len(tomato_sprite), -) -sell_button_image := rl.LoadImageFromMemory( - ".png", - raw_data(sell_button_sprite), - auto_cast len(sell_button_sprite), -) -background_image := rl.LoadImageFromMemory( - ".png", - raw_data(background_sprite), - auto_cast len(background_sprite), -) +load_texture_from_memory :: proc(data: []u8) -> (texture: rl.Texture2D) { + img := rl.LoadImageFromMemory(".png", raw_data(data), auto_cast len(data)) + defer rl.UnloadImage(img) + texture = rl.LoadTextureFromImage(img) + return texture +} + sell_button_texture: rl.Texture2D background_texture: rl.Texture2D - -tile_dirt_dry_image := rl.LoadImageFromMemory( - ".png", - raw_data(tile_dirt_dry_sprite), - auto_cast len(tile_dirt_dry_sprite), -) -tile_dirt_wet_image := rl.LoadImageFromMemory( - ".png", - raw_data(tile_dirt_wet_sprite), - auto_cast len(tile_dirt_wet_sprite), -) tile_dirt_dry_texture: rl.Texture2D tile_dirt_wet_texture: rl.Texture2D +tomato_texture: rl.Texture2D player_animation: Animation tomato_animation: Animation @@ -79,35 +60,44 @@ animations: [dynamic]^Animation entities: [256]Entity +tween_ctx_f32: t.TweenContext(f32) +tween_ctx_vec2: t.TweenContext([2]f32) + main :: proc() { rl.SetConfigFlags({.VSYNC_HINT, .WINDOW_RESIZABLE, .WINDOW_HIGHDPI, .MSAA_4X_HINT}) rl.InitWindow(1280, 720, "Raylib Minimal") rl.SetTargetFPS(rl.GetMonitorRefreshRate(rl.GetCurrentMonitor())) + rl.InitAudioDevice() game_render_buffer = rl.LoadRenderTexture( auto_cast GAME_SCREEN_WIDTH, auto_cast GAME_SCREEN_HEIGHT, ) rl.SetTextureFilter(game_render_buffer.texture, rl.TextureFilter.POINT) // Texture scale filter to use + tween_ctx_f32 = t.context_init(f32) + tween_ctx_vec2 = t.context_init([2]f32) + defer t.context_destroy(tween_ctx_f32) + defer t.context_destroy(tween_ctx_vec2) + player_animation = Animation { - texture = rl.LoadTextureFromImage(player_spritesheet_image), + texture = load_texture_from_memory(player_spritesheet_sprite), num_frames = 2, frame_length = 0.5, loop = true, } tomato_animation = Animation { - texture = rl.LoadTextureFromImage(tomato_spritesheet_image), + texture = load_texture_from_memory(tomato_spritesheet_sprite), num_frames = 4, - frame_length = 2, + frame_length = 0.1, loop = false, offset = {TILE_SIZE / 2, TILE_SIZE}, } - sell_button_texture = rl.LoadTextureFromImage(sell_button_image) - background_texture = rl.LoadTextureFromImage(background_image) - - tile_dirt_dry_texture = rl.LoadTextureFromImage(tile_dirt_dry_image) - tile_dirt_wet_texture = rl.LoadTextureFromImage(tile_dirt_wet_image) + 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) + tile_dirt_dry_texture = load_texture_from_memory(tile_dirt_dry_sprite) + tile_dirt_wet_texture = load_texture_from_memory(tile_dirt_wet_sprite) e := create_entity( Entity { @@ -118,17 +108,25 @@ main :: proc() { ) player_handle = entity_to_handle(e^) + rl.PlaySound(pickup_sound) + for !rl.WindowShouldClose() { free_all(context.temp_allocator) update() draw() } + rl.UnloadRenderTexture(game_render_buffer) // Unload render texture + rl.CloseAudioDevice() rl.CloseWindow() } update :: proc() { + // :tween update + t.tween_update(&tween_ctx_f32, auto_cast rl.GetFrameTime()) + t.tween_update(&tween_ctx_vec2, auto_cast rl.GetFrameTime()) + // :scaling update screen_scale = min( f32(rl.GetScreenWidth()) / GAME_SCREEN_WIDTH, @@ -155,16 +153,7 @@ update :: proc() { } // :animation update - for &e in entities { - if .Allocated not_in e.flags {continue} - - #partial switch &data in e.data { - case TileData: - update_animation(&data.animation) - case PlayerData: - update_animation(&data.animation) - } - } + update_entities() // :sell hover_sell_button := rl.CheckCollisionPointRec( @@ -179,7 +168,7 @@ update :: proc() { // :player update player := handle_to_entity(player_handle) if player != nil { - pd, ok := player.data.(PlayerData);if ok { + pd, ok := &player.data.(PlayerData);if ok { vel: rl.Vector2 if rl.IsKeyDown(.D) do vel.x += 1 if rl.IsKeyDown(.A) do vel.x -= 1 @@ -202,21 +191,8 @@ draw :: proc() { rl.BeginDrawing() rl.DrawTextureV(background_texture, {}, rl.WHITE) - // :animation draw - //for t in tiles { - // rl.DrawRectangle(auto_cast t.pos.x, auto_cast t.pos.y, TILE_SIZE, TILE_SIZE, rl.BLACK) - // draw_animation(t.animation, t.pos) - //} - for e in entities { - if .Allocated not_in e.flags {continue} - #partial switch &data in e.data { - case TileData: - draw_tile(e) - case PlayerData: - draw_player(e) - } - } + draw_entities() rl.DrawTextureV(sell_button_texture, SELL_BUTTON_POS, rl.WHITE) rl.EndMode2D() @@ -330,16 +306,17 @@ PlayerData :: struct { } ItemData :: struct { - id: int, - count: int, - animation: Animation, + id: int, + count: int, + texture: rl.Texture2D, } TileData :: struct { - has_plant: bool, - is_watered: bool, - plant_id: int, - animation: Animation, + has_plant: bool, + is_watered: bool, + plant_id: int, + animation: Animation, + plant_grown: bool, } EntityData :: union { @@ -389,11 +366,76 @@ handle_to_entity :: proc(handle: EntityHandle) -> ^Entity { return nil } +update_entities :: proc() { + for &e in entities { + if .Allocated not_in e.flags {continue} + + #partial switch &data in e.data { + case TileData: + update_tile(e, &data) + case PlayerData: + update_animation(&data.animation) + case ItemData: + //rl.DrawTextureV(data.texture, e.pos + {0, t * 3}, rl.WHITE) + } + } +} + +update_tile :: proc(e: Entity, data: ^TileData) { + update_animation(&data.animation) + if data.has_plant && data.animation.done && data.plant_grown == false { + data.plant_grown = true + player := handle_to_entity(player_handle) + if player != nil { + item := create_entity( + Entity { + pos = e.pos, + kind = .Item, + data = ItemData{id = data.plant_id, count = 3, texture = tomato_texture}, + }, + ) + tw := t.tween_to(&tween_ctx_vec2, &item.pos, player.pos) + tw.id = item.handle + tw.data = tw + tw.on_update = proc(ctx: ^t.TweenContext([2]f32), data: rawptr) { + player := handle_to_entity(player_handle) + if player != nil { + tween: ^t.Tween([2]f32) = transmute(^t.Tween([2]f32))data + tween.goal = player.pos - {TILE_SIZE, TILE_SIZE} + } + } + tw.on_complete = proc(ctx: ^t.TweenContext([2]f32), data: rawptr) { + tween: ^t.Tween([2]f32) = transmute(^t.Tween([2]f32))data + fmt.println("Done") + rl.PlaySound(pickup_sound) + e := handle_to_entity(tween.id) + if e != nil && e.kind == .Item { + destroy_entity(e) + } + } + } + } +} + +draw_entities :: proc() { + for e in entities { + if .Allocated not_in e.flags {continue} + + switch &data in e.data { + case TileData: + draw_tile(e) + case PlayerData: + draw_player(e) + case ItemData: + draw_item(e) + } + } +} + entity_to_handle :: proc(e: Entity) -> EntityHandle { return e.handle } - draw_player :: proc(e: Entity) { data, ok := e.data.(PlayerData) draw_animation(data.animation, e.pos) @@ -406,5 +448,7 @@ draw_tile :: proc(e: Entity) { } draw_item :: proc(e: Entity) { data, ok := e.data.(ItemData) - draw_animation(data.animation, e.pos) + t := f32(math.sin(rl.GetTime() * 5)) + rl.DrawTextureV(data.texture, e.pos + {0, t * 3}, rl.WHITE) + //draw_animation(data.animation, e.pos + {0, t * 3}) }