diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/OdinSdkConfig.xml b/.idea/OdinSdkConfig.xml
new file mode 100644
index 0000000..cfb49e8
--- /dev/null
+++ b/.idea/OdinSdkConfig.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
new file mode 100644
index 0000000..1f0ef49
--- /dev/null
+++ b/.idea/editor.xml
@@ -0,0 +1,580 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/Odin_SDK.xml b/.idea/libraries/Odin_SDK.xml
new file mode 100644
index 0000000..fc0afde
--- /dev/null
+++ b/.idea/libraries/Odin_SDK.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..dd7d771
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..0034a38
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,16 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "cppvsdbg",
+ "request": "launch",
+ "name": "Debug",
+ "program": "${workspaceFolder}/game.exe",
+ "args": [],
+ "cwd": "${workspaceFolder}"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..7d71ffc
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,19 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "type": "shell",
+ "command": "make",
+ // "problemMatcher": [
+ // "$msvc"
+ // ],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 61153b4..982bac1 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
run:
- odin run src -out:game.exe -debug -use-separate-modules -sanitize:address
+ odin run src -out:game.exe -debug
build:
odin build src -out:game.exe -debug -use-separate-modules
@@ -8,3 +8,4 @@ clean:
rm *.exe
rm -rf *.dSYM/
+all: build
diff --git a/assets/sounds/pickup_sound.ogg b/assets/sounds/pickup_sound.ogg
deleted file mode 100644
index 47648df..0000000
Binary files a/assets/sounds/pickup_sound.ogg and /dev/null differ
diff --git a/src/main.odin b/src/main.odin
index 545c8b0..95a8d04 100644
--- a/src/main.odin
+++ b/src/main.odin
@@ -27,7 +27,8 @@ world_mouse: rl.Vector2
screen_game_area: rl.Rectangle
game_render_buffer_area := rl.Rectangle{0, 0, GAME_SCREEN_WIDTH, -GAME_SCREEN_HEIGHT}
-pickup_sound := rl.LoadSound("assets/sounds/pickup_sound.ogg")
+pickup_sound: rl.Sound
+pickup_sound_data: []u8 : #load("../assets/sounds/pickup_sound.mp3")
player_spritesheet_sprite: []u8 : #load("../assets/player.png")
tomato_spritesheet_sprite: []u8 : #load("../assets/tomato.png")
@@ -44,6 +45,18 @@ load_texture_from_memory :: proc(data: []u8) -> (texture: rl.Texture2D) {
return texture
}
+load_sound_from_memory :: proc(
+ data: []u8,
+ sound_extension: cstring = ".mp3",
+) -> (
+ sound: rl.Sound,
+) {
+ wav := rl.LoadWaveFromMemory(sound_extension, raw_data(data), auto_cast len(data))
+ defer rl.UnloadWave(wav)
+ sound = rl.LoadSoundFromWave(wav)
+ return
+}
+
sell_button_texture: rl.Texture2D
background_texture: rl.Texture2D
tile_dirt_dry_texture: rl.Texture2D
@@ -64,11 +77,19 @@ 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.SetConfigFlags({.VSYNC_HINT, .WINDOW_RESIZABLE, .MSAA_4X_HINT})
rl.InitWindow(1280, 720, "Raylib Minimal")
rl.SetTargetFPS(rl.GetMonitorRefreshRate(rl.GetCurrentMonitor()))
rl.InitAudioDevice()
+ if rl.IsAudioDeviceReady() {
+ fmt.println("Audio working!")
+ } else {
+ fmt.println("Audio borked!")
+ }
+
+ pickup_sound = load_sound_from_memory(pickup_sound_data)
+
game_render_buffer = rl.LoadRenderTexture(
auto_cast GAME_SCREEN_WIDTH,
auto_cast GAME_SCREEN_HEIGHT,
@@ -110,13 +131,13 @@ main :: proc() {
rl.PlaySound(pickup_sound)
- for !rl.WindowShouldClose() {
- free_all(context.temp_allocator)
- update()
- draw()
+ if true {for !rl.WindowShouldClose() {
+ free_all(context.temp_allocator)
+ update()
+ draw()
+ }
+
}
-
-
rl.UnloadRenderTexture(game_render_buffer) // Unload render texture
rl.CloseAudioDevice()
rl.CloseWindow()
@@ -128,15 +149,16 @@ update :: proc() {
t.tween_update(&tween_ctx_vec2, auto_cast rl.GetFrameTime())
// :scaling update
+ screen_width := rl.GetScreenWidth()
+ screen_height := rl.GetScreenHeight()
screen_scale = min(
- f32(rl.GetScreenWidth()) / GAME_SCREEN_WIDTH,
- f32(rl.GetScreenHeight()) / GAME_SCREEN_HEIGHT,
+ f32(screen_width) / GAME_SCREEN_WIDTH,
+ f32(screen_height) / GAME_SCREEN_HEIGHT,
)
mouse := rl.GetMousePosition()
virtual_mouse := rl.Vector2 {
- (mouse.x - (f32(rl.GetScreenWidth()) - (GAME_SCREEN_WIDTH * screen_scale)) * 0.5) /
- screen_scale,
- (mouse.y - (f32(rl.GetScreenHeight()) - (GAME_SCREEN_HEIGHT * screen_scale)) * 0.5) /
+ (mouse.x - (f32(screen_width) - (GAME_SCREEN_WIDTH * screen_scale)) * 0.5) / screen_scale,
+ (mouse.y - (f32(screen_height) - (GAME_SCREEN_HEIGHT * screen_scale)) * 0.5) /
screen_scale,
}
virtual_mouse = rl.Vector2Clamp(
@@ -385,35 +407,15 @@ 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)
- }
- }
- }
+ 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},
+ },
+ )
}
}
@@ -444,7 +446,9 @@ 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)
- draw_animation(data.animation, e.pos)
+ if data.has_plant {
+ draw_animation(data.animation, e.pos)
+ }
}
draw_item :: proc(e: Entity) {
data, ok := e.data.(ItemData)
diff --git a/tween/tween.odin b/tween/tween.odin
new file mode 100644
index 0000000..08e77b9
--- /dev/null
+++ b/tween/tween.odin
@@ -0,0 +1,239 @@
+package tween
+
+import "base:intrinsics"
+import "core:math/ease"
+import "core:time"
+
+TweenContext :: struct($T: typeid) {
+ tweens: [dynamic]Tween(T),
+}
+
+Tween :: struct($T: typeid) {
+ value: ^T,
+ start: T,
+ diff: T,
+ goal: T,
+ delay: f64, // in seconds
+ duration: time.Duration,
+ progress: f64,
+ rate: f64,
+ type: ease.Ease,
+ inited: bool,
+ id: int,
+
+ // callbacks, data can be set, will be pushed to callback
+ data: rawptr, // by default gets set to value input
+ on_start: proc(ctx: ^TweenContext(T), data: rawptr),
+ on_update: proc(ctx: ^TweenContext(T), data: rawptr),
+ on_complete: proc(ctx: ^TweenContext(T), data: rawptr),
+}
+
+@(require_results)
+context_init_f :: proc(
+ $T: typeid,
+) -> TweenContext(T) where intrinsics.type_is_float(T) {
+ return {tweens = make([dynamic]Tween(T))}
+}
+
+@(require_results)
+context_init_v :: proc(
+ $T: typeid/[$N]$E,
+) -> TweenContext(T) where intrinsics.type_is_float(E) {
+ return {tweens = make([dynamic]Tween(T))}
+}
+
+context_init :: proc {
+ context_init_f,
+ context_init_v,
+}
+
+// delete map content
+context_destroy :: proc(ctx: TweenContext($T)) {
+ delete(ctx.tweens)
+}
+
+// clear map content, stops all animations
+context_clear :: proc(ctx: ^TweenContext($T)) {
+ clear(&ctx.tweens)
+}
+
+// append / overwrite existing tween value to parameters
+// rest is initialized in tween_init, inside update
+// return value can be used to set callbacks
+@(require_results)
+tween_to :: proc(
+ ctx: ^TweenContext($T),
+ value: ^T,
+ goal: T,
+ type: ease.Ease = .Quadratic_Out,
+ duration: time.Duration = time.Second,
+ delay: f64 = 0,
+ id: int = 0,
+) -> (
+ tween: ^Tween(T),
+) {
+ append(&ctx.tweens, Tween(T){})
+ n := len(ctx.tweens) - 1
+ tween = &ctx.tweens[n]
+
+ tween^ = {
+ value = value,
+ goal = goal,
+ duration = duration,
+ delay = delay,
+ type = type,
+ data = value,
+ id = id,
+ }
+
+ return
+}
+
+// init internal properties
+_tween_init :: proc(tween: ^Tween($T), duration: time.Duration) {
+ tween.inited = true
+ tween.start = tween.value^
+ tween.diff = tween.goal - tween.value^
+ s := time.duration_seconds(duration)
+ tween.rate = duration > 0 ? 1.0 / s : 0
+ tween.progress = duration > 0 ? 0 : 1
+}
+
+_tween_init_f :: proc(
+ tween: ^Tween($T),
+ duration: time.Duration,
+) where intrinsics.type_is_float(T) {
+ _tween_init(tween, duration)
+}
+
+_tween_init_v :: proc(
+ tween: ^Tween($T/[$N]$E),
+ duration: time.Duration,
+) where intrinsics.type_is_float(E) {
+ _tween_init(tween, duration)
+}
+
+tween_init :: proc {
+ _tween_init_f,
+ _tween_init_v,
+}
+
+_tween_interpolate :: proc(tween: ^Tween($T), dt, delay_remainder: f64) {
+ tween.progress += tween.rate * (dt + delay_remainder)
+ x := tween.progress >= 1 ? 1 : ease.ease(tween.type, tween.progress)
+ tween.value^ = tween.start + tween.diff * T(x)
+}
+
+tween_interpolate_f :: proc(
+ tween: ^Tween($T),
+ dt, delay_remainder: f64,
+) where intrinsics.type_is_float(T) {
+ _tween_interpolate(tween, dt, delay_remainder)
+}
+
+tween_interpolate_v :: proc(
+ tween: ^Tween($T/[$N]$E),
+ dt, delay_remainder: f64,
+) where intrinsics.type_is_float(E) {
+ _tween_interpolate(tween, dt, delay_remainder)
+}
+
+tween_interpolate :: proc {
+ tween_interpolate_f,
+ tween_interpolate_v,
+}
+
+// update all tweens, wait for their delay if one exists
+// calls callbacks in all stages, when they're filled
+// deletes tween from the map after completion
+_tween_update :: proc(ctx: ^TweenContext($T), dt: f64) {
+ for &tween in ctx.tweens {
+ delay_remainder := f64(0)
+
+ // Update delay if necessary.
+ if tween.delay > 0 {
+ tween.delay -= dt
+
+ if tween.delay < 0 {
+ // We finished the delay, but in doing so consumed part of this frame's `dt` budget.
+ // Keep track of it so we can apply it to this tween without affecting others.
+ delay_remainder = tween.delay
+ // We're done with this delay.
+ tween.delay = 0
+ }
+ }
+
+ // We either had no delay, or the delay has been consumed.
+ if tween.delay <= 0 {
+ if !tween.inited {
+ tween_init(&tween, tween.duration)
+
+ if tween.on_start != nil {
+ tween.on_start(ctx, tween.data)
+ }
+ }
+
+ // If part of the `dt` budget was consumed this frame, then `delay_remainder` will be
+ // that remainder, a negative value. Adding it to `dt` applies what's left of the `dt`
+ // to the tween so it advances properly, instead of too much or little.
+ tween_interpolate(&tween, dt, delay_remainder)
+ // tween.progress += tween.rate * (dt + delay_remainder)
+ // x := tween.progress >= 1 ? 1 : ease(tween.type, tween.progress)
+ // tween.value^ = tween.start + tween.diff * T(x)
+
+ if tween.on_update != nil {
+ tween.on_update(ctx, tween.data)
+ }
+
+ if tween.progress >= 1 {
+ // append keys to array that will be deleted after the loop
+
+ if tween.on_complete != nil {
+ tween.on_complete(ctx, tween.data)
+ }
+ }
+ }
+ }
+}
+
+tween_update_f :: proc(ctx: ^TweenContext($T), dt: f64) where intrinsics.type_is_float(T) {
+ _tween_update(ctx, dt)
+}
+
+tween_update_v :: proc(ctx: ^TweenContext($T/[$N]$E), dt: f64) where intrinsics.type_is_float(E) {
+ _tween_update(ctx, dt)
+}
+
+tween_update :: proc {
+ tween_update_v,
+ tween_update_f,
+}
+
+// stop a specific key inside the map
+// returns true when it successfully removed the key
+// @(require_results)
+tween_stop :: proc(ctx: ^TweenContext($T), id: int) -> bool {
+ removed_tweens := false
+ for i := 0; i < len(ctx.tweens); {
+ if tween.id == id {
+ unordered_remove(&ctx.tweens, i)
+ removed_tweens = true
+ } else {
+ i += 1
+ }
+ }
+
+ return false
+}
+
+// Returns the first tween animation with that id
+// If no tween exists with the given id, will return 0
+@(require_results)
+tween_time_left :: proc(flux: TweenContext($T), id: int) -> f64 {
+ for tween in ctx.tweens {
+ if tween.id == id {
+ return ((1 - tween.progress) * tween.rate) + tween.delay
+ }
+ }
+ return 0
+}