added scythe
This commit is contained in:
parent
6993054d63
commit
9504812ed5
10 changed files with 305 additions and 116 deletions
5
Makefile
5
Makefile
|
|
@ -1,9 +1,12 @@
|
|||
run:
|
||||
odin run src -out:game.exe -debug
|
||||
odin run src -out:game.exe -debug -use-separate-modules -vet
|
||||
|
||||
build:
|
||||
odin build src -out:game.exe -debug -use-separate-modules
|
||||
|
||||
check:
|
||||
odin check src -vet -debug
|
||||
|
||||
clean:
|
||||
rm *.exe
|
||||
rm -rf *.dSYM/
|
||||
|
|
|
|||
BIN
assets/assets.aseprite
Normal file
BIN
assets/assets.aseprite
Normal file
Binary file not shown.
BIN
assets/hoe.png
Normal file
BIN
assets/hoe.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 203 B |
BIN
assets/scythe.png
Normal file
BIN
assets/scythe.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 237 B |
14
atlas_packer/atlas_packer.odin
Normal file
14
atlas_packer/atlas_packer.odin
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package atlas_packer
|
||||
|
||||
import "core:fmt"
|
||||
import "core:os"
|
||||
import "vendor:stb/rect_pack"
|
||||
|
||||
main :: proc() {
|
||||
if len(os.args) != 3 {
|
||||
fmt.panicf(
|
||||
"Invalid amount of args provided, valid call must look like: atlas_packer.exe <folder of images> <image extension>\nProvided args: %v",
|
||||
os.args[1:],
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -14,15 +14,16 @@ PlayerData :: struct {
|
|||
}
|
||||
|
||||
ItemData :: struct {
|
||||
id: int,
|
||||
id: InventoryItem,
|
||||
count: int,
|
||||
texture: rl.Texture2D,
|
||||
}
|
||||
|
||||
TileData :: struct {
|
||||
is_tiled: bool,
|
||||
has_plant: bool,
|
||||
is_watered: bool,
|
||||
plant_id: int,
|
||||
plant_id: InventoryItem,
|
||||
animation: Animation,
|
||||
plant_grown: bool,
|
||||
}
|
||||
|
|
@ -88,23 +89,6 @@ update_entities :: proc() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
data.has_plant = false
|
||||
rl.PlaySound(pickup_sound)
|
||||
item := create_entity(
|
||||
Entity {
|
||||
pos = e.pos,
|
||||
kind = .Item,
|
||||
data = ItemData{id = data.plant_id, count = 3, texture = tomato_texture},
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
draw_entities :: proc() {
|
||||
for e in entities {
|
||||
if .Allocated not_in e.flags {continue}
|
||||
|
|
@ -125,20 +109,13 @@ entity_to_handle :: proc(e: Entity) -> EntityHandle {
|
|||
}
|
||||
|
||||
draw_player :: proc(e: Entity) {
|
||||
data, ok := e.data.(PlayerData)
|
||||
draw_animation(data.animation, e.pos)
|
||||
}
|
||||
draw_tile :: proc(e: Entity) {
|
||||
data, ok := e.data.(TileData)
|
||||
//rl.DrawRectangle(auto_cast e.pos.x, auto_cast e.pos.y, TILE_SIZE, TILE_SIZE, rl.BLACK)
|
||||
rl.DrawTextureV(tile_dirt_dry_texture, e.pos, rl.WHITE)
|
||||
if data.has_plant {
|
||||
data, ok := e.data.(PlayerData);if ok {
|
||||
draw_animation(data.animation, e.pos)
|
||||
}
|
||||
}
|
||||
draw_item :: proc(e: Entity) {
|
||||
data, ok := e.data.(ItemData)
|
||||
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})
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
package game
|
||||
|
||||
import t "../tween"
|
||||
import "core:time"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
SELL_BUTTON_POS :: rl.Vector2{432, 16}
|
||||
TOOL_POS :: rl.Vector2{32, 32}
|
||||
|
||||
game_camera := rl.Camera2D {
|
||||
zoom = 1,
|
||||
|
|
@ -25,5 +27,88 @@ animations: [dynamic]^Animation
|
|||
|
||||
entities: [256]Entity
|
||||
|
||||
current_tick: time.Tick
|
||||
|
||||
tween_ctx_f32: t.TweenContext(f32)
|
||||
tween_ctx_vec2: t.TweenContext([2]f32)
|
||||
|
||||
player_inventory: [InventoryItem]int
|
||||
|
||||
InventoryItem :: enum {
|
||||
//Tools
|
||||
hoe,
|
||||
scythe,
|
||||
watering_can,
|
||||
pickaxe,
|
||||
axe,
|
||||
//Plants
|
||||
tomato,
|
||||
tomato_seed,
|
||||
tomato_seedling,
|
||||
//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,
|
||||
}
|
||||
|
||||
HotbarKind :: enum {
|
||||
tools,
|
||||
seeds,
|
||||
}
|
||||
|
||||
Hotbar :: struct {
|
||||
kind: HotbarKind,
|
||||
tool_slots: []InventoryItem,
|
||||
seed_slots: []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) {
|
||||
total_slots := hotbar.kind == .tools ? len(hotbar.tool_slots) : len(hotbar.seed_slots)
|
||||
hotbar.current_slot += 1
|
||||
if hotbar.current_slot >= total_slots {
|
||||
hotbar.current_slot = 0
|
||||
}
|
||||
}
|
||||
if rl.IsKeyPressed(.TAB) {
|
||||
// TODO: preserve position for each hotbar slot
|
||||
hotbar.kind = hotbar.kind == .seeds ? .tools : .seeds
|
||||
hotbar.current_slot = 0
|
||||
}
|
||||
}
|
||||
|
||||
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]]^
|
||||
rl.DrawTexturePro(
|
||||
hotbar_item_texture,
|
||||
{0, 0, 16, 16},
|
||||
{TOOL_POS.x, TOOL_POS.y, 32, 32},
|
||||
{},
|
||||
0,
|
||||
rl.WHITE,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
get_current_hand_item :: proc() -> InventoryItem {
|
||||
return(
|
||||
hotbar.kind == .tools ? hotbar.tool_slots[hotbar.current_slot] : hotbar.seed_slots[hotbar.current_slot] \
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,40 @@
|
|||
package game
|
||||
|
||||
import t "../tween"
|
||||
import sa "core:container/small_array"
|
||||
import "core:fmt"
|
||||
import "core:math"
|
||||
import "core:math/linalg"
|
||||
import "core:slice"
|
||||
import "core:log"
|
||||
import "core:mem"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
main :: proc() {
|
||||
context.logger = log.create_console_logger(
|
||||
log.Level.Debug,
|
||||
log.Options{.Level, .Procedure, .Line, .Terminal_Color},
|
||||
)
|
||||
defer log.destroy_console_logger(context.logger)
|
||||
|
||||
when ODIN_DEBUG {
|
||||
track: mem.Tracking_Allocator
|
||||
mem.tracking_allocator_init(&track, context.allocator)
|
||||
context.allocator = mem.tracking_allocator(&track)
|
||||
|
||||
defer {
|
||||
if len(track.allocation_map) > 0 {
|
||||
log.errorf("=== %v allocations not freed: ===\n", len(track.allocation_map))
|
||||
for _, entry in track.allocation_map {
|
||||
log.errorf("- %v bytes @ %v\n", entry.size, entry.location)
|
||||
}
|
||||
}
|
||||
if len(track.bad_free_array) > 0 {
|
||||
log.errorf("=== %v incorrect frees: ===\n", len(track.bad_free_array))
|
||||
for entry in track.bad_free_array {
|
||||
log.errorf("- %p @ %v\n", entry.memory, entry.location)
|
||||
}
|
||||
}
|
||||
mem.tracking_allocator_destroy(&track)
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
||||
}
|
||||
|
||||
|
|
@ -25,8 +51,6 @@ run :: proc() {
|
|||
}
|
||||
|
||||
load_resources()
|
||||
rl.SetSoundVolume(pickup_sound, 0.1)
|
||||
rl.PlaySound(pickup_sound)
|
||||
|
||||
for !rl.WindowShouldClose() {
|
||||
free_all(context.temp_allocator)
|
||||
|
|
@ -70,6 +94,8 @@ update :: proc() {
|
|||
GAME_SCREEN_HEIGHT * screen_scale,
|
||||
}
|
||||
|
||||
update_hotbar()
|
||||
|
||||
// :animation update
|
||||
update_entities()
|
||||
|
||||
|
|
@ -99,7 +125,7 @@ update :: proc() {
|
|||
|
||||
}
|
||||
if rl.IsMouseButtonDown(.LEFT) {
|
||||
spawn_tile_under_mouse()
|
||||
interact_with_tile_under_mouse(.LEFT)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +138,8 @@ draw :: proc() {
|
|||
|
||||
draw_entities()
|
||||
|
||||
draw_hotbar()
|
||||
|
||||
rl.DrawTextureV(sell_button_texture, SELL_BUTTON_POS, rl.WHITE)
|
||||
rl.EndMode2D()
|
||||
rl.EndTextureMode()
|
||||
|
|
@ -127,60 +155,3 @@ draw :: proc() {
|
|||
)
|
||||
rl.EndDrawing()
|
||||
}
|
||||
|
||||
load_resources :: proc() {
|
||||
tween_ctx_f32 = t.context_init(f32)
|
||||
tween_ctx_vec2 = t.context_init([2]f32)
|
||||
|
||||
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
|
||||
|
||||
player_animation = Animation {
|
||||
texture = load_texture_from_memory(player_spritesheet_sprite),
|
||||
num_frames = 2,
|
||||
frame_length = 0.5,
|
||||
loop = true,
|
||||
}
|
||||
tomato_animation = Animation {
|
||||
texture = load_texture_from_memory(tomato_spritesheet_sprite),
|
||||
num_frames = 4,
|
||||
frame_length = 0.1,
|
||||
loop = false,
|
||||
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)
|
||||
tile_dirt_dry_texture = load_texture_from_memory(tile_dirt_dry_sprite)
|
||||
tile_dirt_wet_texture = load_texture_from_memory(tile_dirt_wet_sprite)
|
||||
pickup_sound = load_sound_from_memory(pickup_sound_data)
|
||||
|
||||
|
||||
e := create_entity(
|
||||
Entity {
|
||||
pos = {GAME_SCREEN_WIDTH / 2, GAME_SCREEN_HEIGHT / 2},
|
||||
kind = .Player,
|
||||
data = PlayerData{animation = player_animation},
|
||||
},
|
||||
)
|
||||
player_handle = entity_to_handle(e^)
|
||||
}
|
||||
|
||||
free_resources :: proc() {
|
||||
t.context_destroy(tween_ctx_f32)
|
||||
t.context_destroy(tween_ctx_vec2)
|
||||
|
||||
rl.UnloadSound(pickup_sound)
|
||||
|
||||
rl.UnloadTexture(tomato_texture)
|
||||
rl.UnloadTexture(sell_button_texture)
|
||||
rl.UnloadTexture(background_texture)
|
||||
rl.UnloadTexture(tile_dirt_dry_texture)
|
||||
rl.UnloadTexture(tile_dirt_wet_texture)
|
||||
|
||||
rl.UnloadRenderTexture(game_render_buffer) // Unload render texture
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package game
|
||||
|
||||
import t "../tween"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
pickup_sound_data: []u8 : #load("../assets/sounds/pickup_sound.mp3")
|
||||
|
|
@ -11,6 +12,21 @@ 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")
|
||||
hoe_sprite: []u8 : #load("../assets/hoe.png")
|
||||
scythe_sprite: []u8 : #load("../assets/scythe.png")
|
||||
|
||||
pickup_sound: rl.Sound
|
||||
|
||||
sell_button_texture: rl.Texture2D
|
||||
background_texture: rl.Texture2D
|
||||
tile_dirt_dry_texture: rl.Texture2D
|
||||
tile_dirt_wet_texture: rl.Texture2D
|
||||
tomato_texture: rl.Texture2D
|
||||
hoe_texture: rl.Texture2D
|
||||
scythe_texture: rl.Texture2D
|
||||
|
||||
player_animation: Animation
|
||||
tomato_animation: Animation
|
||||
|
||||
load_texture_from_memory :: proc(data: []u8) -> (texture: rl.Texture2D) {
|
||||
img := rl.LoadImageFromMemory(".png", raw_data(data), auto_cast len(data))
|
||||
|
|
@ -31,13 +47,63 @@ load_sound_from_memory :: proc(
|
|||
return
|
||||
}
|
||||
|
||||
pickup_sound: rl.Sound
|
||||
|
||||
sell_button_texture: rl.Texture2D
|
||||
background_texture: rl.Texture2D
|
||||
tile_dirt_dry_texture: rl.Texture2D
|
||||
tile_dirt_wet_texture: rl.Texture2D
|
||||
tomato_texture: rl.Texture2D
|
||||
load_resources :: proc() {
|
||||
tween_ctx_f32 = t.context_init(f32)
|
||||
tween_ctx_vec2 = t.context_init([2]f32)
|
||||
|
||||
player_animation: Animation
|
||||
tomato_animation: Animation
|
||||
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
|
||||
|
||||
player_animation = Animation {
|
||||
texture = load_texture_from_memory(player_spritesheet_sprite),
|
||||
num_frames = 2,
|
||||
frame_length = 0.5,
|
||||
loop = true,
|
||||
}
|
||||
tomato_animation = Animation {
|
||||
texture = load_texture_from_memory(tomato_spritesheet_sprite),
|
||||
num_frames = 4,
|
||||
frame_length = 0.1,
|
||||
loop = false,
|
||||
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)
|
||||
tile_dirt_dry_texture = load_texture_from_memory(tile_dirt_dry_sprite)
|
||||
tile_dirt_wet_texture = load_texture_from_memory(tile_dirt_wet_sprite)
|
||||
hoe_texture = load_texture_from_memory(hoe_sprite)
|
||||
scythe_texture = load_texture_from_memory(scythe_sprite)
|
||||
|
||||
pickup_sound = load_sound_from_memory(pickup_sound_data)
|
||||
rl.SetSoundVolume(pickup_sound, 0.1)
|
||||
|
||||
e := create_entity(
|
||||
Entity {
|
||||
pos = {GAME_SCREEN_WIDTH / 2, GAME_SCREEN_HEIGHT / 2},
|
||||
kind = .Player,
|
||||
data = PlayerData{animation = player_animation},
|
||||
},
|
||||
)
|
||||
player_handle = entity_to_handle(e^)
|
||||
}
|
||||
|
||||
free_resources :: proc() {
|
||||
t.context_destroy(tween_ctx_f32)
|
||||
t.context_destroy(tween_ctx_vec2)
|
||||
|
||||
rl.UnloadSound(pickup_sound)
|
||||
|
||||
rl.UnloadTexture(tomato_texture)
|
||||
rl.UnloadTexture(sell_button_texture)
|
||||
rl.UnloadTexture(background_texture)
|
||||
rl.UnloadTexture(tile_dirt_dry_texture)
|
||||
rl.UnloadTexture(tile_dirt_wet_texture)
|
||||
|
||||
rl.UnloadRenderTexture(game_render_buffer) // Unload render texture
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package game
|
||||
|
||||
import "core:fmt"
|
||||
import "core:math"
|
||||
import rl "vendor:raylib"
|
||||
|
||||
|
|
@ -9,26 +10,98 @@ Tile :: struct {
|
|||
animation: Animation,
|
||||
}
|
||||
|
||||
spawn_tile_under_mouse :: proc() {
|
||||
interact_with_tile_under_mouse :: proc(mouse_button: rl.MouseButton) {
|
||||
tile_position := get_tile_position(world_mouse)
|
||||
old_tile: bool
|
||||
for e in entities {
|
||||
current_hand_item := get_current_hand_item()
|
||||
|
||||
tile_entity: ^Entity
|
||||
for &e in entities {
|
||||
if .Allocated not_in e.flags {continue}
|
||||
if e.kind == .Tile && e.pos == tile_position {
|
||||
old_tile = true
|
||||
tile_entity = &e
|
||||
}
|
||||
}
|
||||
if !old_tile {
|
||||
create_entity(
|
||||
Entity {
|
||||
pos = tile_position,
|
||||
kind = .Tile,
|
||||
data = TileData{has_plant = true, plant_id = 0, animation = tomato_animation},
|
||||
},
|
||||
)
|
||||
if tile_entity == nil {
|
||||
tile_entity = create_entity(Entity{pos = tile_position, kind = .Tile, data = TileData{}})
|
||||
}
|
||||
|
||||
tile_data, ok := &tile_entity.data.(TileData);if !ok {
|
||||
fmt.panicf("Valid tile entity has invalid tile data!")
|
||||
}
|
||||
if mouse_button == .LEFT {
|
||||
switch hotbar.kind {
|
||||
case .tools:
|
||||
#partial switch current_hand_item {
|
||||
case .hoe:
|
||||
if !tile_data.is_tiled {
|
||||
tile_data.is_tiled = true
|
||||
}
|
||||
case .watering_can:
|
||||
if tile_data.is_tiled && !tile_data.is_watered {
|
||||
tile_data.is_watered = true
|
||||
}
|
||||
case .scythe:
|
||||
if tile_data.has_plant && tile_data.plant_grown {
|
||||
tile_data.has_plant = false
|
||||
tile_data.plant_grown = false
|
||||
|
||||
rl.PlaySound(pickup_sound)
|
||||
create_entity(
|
||||
Entity {
|
||||
pos = tile_entity.pos,
|
||||
kind = .Item,
|
||||
data = ItemData {
|
||||
id = get_seed_to_plant_id(tile_data.plant_id),
|
||||
count = 3,
|
||||
texture = get_plant_texture(tile_data.plant_id)^,
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
case .seeds:
|
||||
if !tile_data.has_plant {
|
||||
tile_data.has_plant = true
|
||||
tile_data.plant_id = current_hand_item
|
||||
tile_data.animation = get_plant_animation(current_hand_item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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_seed_to_plant_id :: proc(seed: InventoryItem) -> InventoryItem {
|
||||
return .tomato
|
||||
}
|
||||
|
||||
get_plant_animation :: proc(plant: InventoryItem) -> Animation {
|
||||
return tomato_animation
|
||||
}
|
||||
|
||||
get_plant_texture :: proc(plant: InventoryItem) -> ^rl.Texture2D {
|
||||
return ITEM_TEXTURES[plant]
|
||||
}
|
||||
|
||||
update_tile :: proc(e: Entity, data: ^TileData) {
|
||||
if !data.is_tiled {return}
|
||||
|
||||
update_animation(&data.animation)
|
||||
if data.has_plant && data.animation.done && data.plant_grown == false {
|
||||
data.plant_grown = true
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue