Added y sorting and refactoring

This commit is contained in:
Stefan Stefanov 2024-12-31 23:17:24 +02:00
parent 9504812ed5
commit 692c066a1a
9 changed files with 209 additions and 51 deletions

View file

@ -1,5 +1,5 @@
run: run:
odin run src -out:game.exe -debug -use-separate-modules -vet odin run src -out:game.exe -debug -vet
build: build:
odin build src -out:game.exe -debug -use-separate-modules odin build src -out:game.exe -debug -use-separate-modules

Binary file not shown.

BIN
assets/axe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

BIN
assets/watering_can.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 431 B

View file

@ -3,3 +3,5 @@ package game
GAME_SCREEN_WIDTH: f32 = 480 GAME_SCREEN_WIDTH: f32 = 480
GAME_SCREEN_HEIGHT: f32 = 270 GAME_SCREEN_HEIGHT: f32 = 270
TILE_SIZE :: 16 TILE_SIZE :: 16
PICKUP_DISTANCE :: 15

View file

@ -1,6 +1,9 @@
package game package game
import "core:fmt"
import "core:math" import "core:math"
import "core:math/linalg"
import "core:slice"
import rl "vendor:raylib" import rl "vendor:raylib"
EntityKind :: enum { EntityKind :: enum {
@ -50,20 +53,26 @@ EntityFlags :: enum {
} }
create_entity :: proc(entity: Entity) -> ^Entity { create_entity :: proc(entity: Entity) -> ^Entity {
for &e in entities { entity := entity
if .Allocated not_in e.flags { entity.flags = {.Allocated}
e = entity entity.handle = latest_entity_handle
e.flags = {.Allocated} latest_entity_handle += 1
latest_entity_handle += 1 append(&entities, entity)
return &e return &entities[len(entities) - 1]
}
}
assert(false, "Failed to allocate entity, not enough space!!!!")
return nil
} }
destroy_entity :: proc(e: ^Entity) { 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 { handle_to_entity :: proc(handle: EntityHandle) -> ^Entity {
@ -76,6 +85,8 @@ handle_to_entity :: proc(handle: EntityHandle) -> ^Entity {
} }
update_entities :: proc() { update_entities :: proc() {
player_entity, _ := get_player_entity_and_data()
for &e in entities { for &e in entities {
if .Allocated not_in e.flags {continue} if .Allocated not_in e.flags {continue}
@ -85,17 +96,50 @@ update_entities :: proc() {
case PlayerData: case PlayerData:
update_animation(&data.animation) update_animation(&data.animation)
case ItemData: 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() { 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 { for e in entities {
if .Allocated not_in e.flags {continue} if .Allocated not_in e.flags {continue}
// Draw debug circle
//rl.DrawCircleV(e.pos, 1, rl.RED)
switch &data in e.data { switch &data in e.data {
case TileData: case TileData:
draw_tile(e) draw_tile_plant(e)
case PlayerData: case PlayerData:
draw_player(e) draw_player(e)
case ItemData: case ItemData:
@ -110,12 +154,16 @@ entity_to_handle :: proc(e: Entity) -> EntityHandle {
draw_player :: proc(e: Entity) { draw_player :: proc(e: Entity) {
data, ok := e.data.(PlayerData);if ok { 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) { draw_item :: proc(e: Entity) {
data, ok := e.data.(ItemData);if ok { data, ok := e.data.(ItemData);if ok {
t := f32(math.sin(rl.GetTime() * 5)) 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)
} }
} }

View file

@ -25,7 +25,7 @@ player_handle: EntityHandle
tiles: [dynamic]Tile tiles: [dynamic]Tile
animations: [dynamic]^Animation animations: [dynamic]^Animation
entities: [256]Entity entities: [dynamic]Entity
current_tick: time.Tick current_tick: time.Tick
@ -33,6 +33,7 @@ tween_ctx_f32: t.TweenContext(f32)
tween_ctx_vec2: t.TweenContext([2]f32) tween_ctx_vec2: t.TweenContext([2]f32)
player_inventory: [InventoryItem]int player_inventory: [InventoryItem]int
hotbar: Hotbar
InventoryItem :: enum { InventoryItem :: enum {
//Tools //Tools
@ -48,16 +49,7 @@ InventoryItem :: enum {
//Other //Other
} }
ITEM_TEXTURES := [InventoryItem]^rl.Texture2D { 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,
}
HotbarKind :: enum { HotbarKind :: enum {
tools, tools,
@ -66,16 +58,10 @@ HotbarKind :: enum {
Hotbar :: struct { Hotbar :: struct {
kind: HotbarKind, kind: HotbarKind,
tool_slots: []InventoryItem, tool_slots: [dynamic]InventoryItem,
seed_slots: []InventoryItem, seed_slots: [dynamic]InventoryItem,
current_slot: int, current_slot: int,
} }
hotbar := Hotbar {
tool_slots = {.hoe, .scythe, .watering_can},
seed_slots = {.tomato_seed},
}
TOOL_SLOTS := len(hotbar.tool_slots)
update_hotbar :: proc() { update_hotbar :: proc() {
if rl.IsKeyPressed(.E) { if rl.IsKeyPressed(.E) {
@ -95,9 +81,10 @@ update_hotbar :: proc() {
draw_hotbar :: proc() { draw_hotbar :: proc() {
rl.DrawRectangleRec({TOOL_POS.x, TOOL_POS.y, 32, 32}, rl.WHITE) rl.DrawRectangleRec({TOOL_POS.x, TOOL_POS.y, 32, 32}, rl.WHITE)
slots := hotbar.kind == .tools ? hotbar.tool_slots : hotbar.seed_slots 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( rl.DrawTexturePro(
hotbar_item_texture, hotbar_item_texture^,
{0, 0, 16, 16}, {0, 0, 16, 16},
{TOOL_POS.x, TOOL_POS.y, 32, 32}, {TOOL_POS.x, TOOL_POS.y, 32, 32},
{}, {},
@ -107,8 +94,50 @@ draw_hotbar :: proc() {
} }
get_current_hand_item :: proc() -> InventoryItem { get_current_hand_item :: proc() -> (InventoryItem, bool) {
return( slots := hotbar.kind == .tools ? hotbar.tool_slots : hotbar.seed_slots
hotbar.kind == .tools ? hotbar.tool_slots[hotbar.current_slot] : hotbar.seed_slots[hotbar.current_slot] \ 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)
}
}
}
} }

View file

@ -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") tile_dirt_wet_sprite: []u8 : #load("../assets/tile_dirt_wet.png")
hoe_sprite: []u8 : #load("../assets/hoe.png") hoe_sprite: []u8 : #load("../assets/hoe.png")
scythe_sprite: []u8 : #load("../assets/scythe.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 pickup_sound: rl.Sound
@ -24,6 +26,8 @@ tile_dirt_wet_texture: rl.Texture2D
tomato_texture: rl.Texture2D tomato_texture: rl.Texture2D
hoe_texture: rl.Texture2D hoe_texture: rl.Texture2D
scythe_texture: rl.Texture2D scythe_texture: rl.Texture2D
watering_can_texture: rl.Texture2D
axe_texture: rl.Texture2D
player_animation: Animation player_animation: Animation
tomato_animation: Animation tomato_animation: Animation
@ -72,13 +76,18 @@ load_resources :: proc() {
offset = {TILE_SIZE / 2, TILE_SIZE}, 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) 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_dry_texture = load_texture_from_memory(tile_dirt_dry_sprite)
tile_dirt_wet_texture = load_texture_from_memory(tile_dirt_wet_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) hoe_texture = load_texture_from_memory(hoe_sprite)
scythe_texture = load_texture_from_memory(scythe_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) pickup_sound = load_sound_from_memory(pickup_sound_data)
rl.SetSoundVolume(pickup_sound, 0.1) rl.SetSoundVolume(pickup_sound, 0.1)
@ -91,6 +100,29 @@ load_resources :: proc() {
}, },
) )
player_handle = entity_to_handle(e^) 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() { free_resources :: proc() {
@ -99,11 +131,21 @@ free_resources :: proc() {
rl.UnloadSound(pickup_sound) rl.UnloadSound(pickup_sound)
rl.UnloadTexture(tomato_texture)
rl.UnloadTexture(sell_button_texture) rl.UnloadTexture(sell_button_texture)
rl.UnloadTexture(background_texture) rl.UnloadTexture(background_texture)
rl.UnloadTexture(tomato_texture)
rl.UnloadTexture(tile_dirt_dry_texture) rl.UnloadTexture(tile_dirt_dry_texture)
rl.UnloadTexture(tile_dirt_wet_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)
} }

View file

@ -12,7 +12,9 @@ Tile :: struct {
interact_with_tile_under_mouse :: proc(mouse_button: rl.MouseButton) { interact_with_tile_under_mouse :: proc(mouse_button: rl.MouseButton) {
tile_position := get_tile_position(world_mouse) 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 tile_entity: ^Entity
for &e in entities { for &e in entities {
@ -51,8 +53,19 @@ interact_with_tile_under_mouse :: proc(mouse_button: rl.MouseButton) {
pos = tile_entity.pos, pos = tile_entity.pos,
kind = .Item, kind = .Item,
data = ItemData { data = ItemData {
id = get_seed_to_plant_id(tile_data.plant_id), id = get_plant_to_seed_id(tile_data.plant_id),
count = 3, 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)^, 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.has_plant = true
tile_data.plant_id = current_hand_item tile_data.plant_id = current_hand_item
tile_data.animation = get_plant_animation(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} 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 { get_seed_to_plant_id :: proc(seed: InventoryItem) -> InventoryItem {
return .tomato return .tomato
} }
@ -82,7 +101,20 @@ get_plant_animation :: proc(plant: InventoryItem) -> Animation {
} }
get_plant_texture :: proc(plant: InventoryItem) -> ^rl.Texture2D { 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) { 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) { draw_tile :: proc(e: Entity) {
data, ok := e.data.(TileData);if ok { data, ok := e.data.(TileData);if ok {
if !data.is_tiled {return} if !data.is_tiled {return}
tile_tint := data.is_watered ? rl.SKYBLUE : rl.WHITE tile_tint := data.is_watered ? rl.SKYBLUE : rl.WHITE
rl.DrawTextureV(tile_dirt_dry_texture, e.pos, tile_tint) rl.DrawTextureV(tile_dirt_dry_texture, e.pos, tile_tint)
if data.has_plant {
draw_animation(data.animation, e.pos)
}
} }
} }