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:
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

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_HEIGHT: f32 = 270
TILE_SIZE :: 16
PICKUP_DISTANCE :: 15

View file

@ -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)
}
}

View file

@ -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)
}
}
}
}

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")
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)
}

View file

@ -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)
}
}
}