aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/draw/draw.odin4
-rw-r--r--src/input.odin2
-rw-r--r--src/main.odin86
-rw-r--r--src/phys/world.odin18
-rw-r--r--src/platform.odin33
-rw-r--r--src/player.odin180
-rw-r--r--src/tiled/tiled.odin59
-rw-r--r--src/tiled/world.odin84
8 files changed, 343 insertions, 123 deletions
diff --git a/src/draw/draw.odin b/src/draw/draw.odin
index 3e80d42..122e6fa 100644
--- a/src/draw/draw.odin
+++ b/src/draw/draw.odin
@@ -73,9 +73,7 @@ end_frame :: proc() {
rl.EndDrawing()
}
-rect :: proc(
- rect: Rect,
-) {
+rect :: proc(rect: Rect) {
assert(rect.size.x > 0 && rect.size.y > 0)
rl.DrawRectangle(
diff --git a/src/input.odin b/src/input.odin
index de2af7e..475be92 100644
--- a/src/input.odin
+++ b/src/input.odin
@@ -17,12 +17,14 @@ actions: struct {
move_left: Keybind,
move_right: Keybind,
jump: Keybind,
+ dash: Keybind,
}
init_keybinds :: proc() {
actions.move_left.input = .A
actions.move_right.input = .D
actions.jump.input = .SPACE
+ actions.dash.input = .LEFT_SHIFT
}
is_keybind_down :: proc(keybind: Keybind) -> bool {
diff --git a/src/main.odin b/src/main.odin
index ff794f6..3a90917 100644
--- a/src/main.odin
+++ b/src/main.odin
@@ -24,55 +24,44 @@ Rect :: struct {
}
state: struct {
- player: Player,
+ camera: rl.Camera2D,
platform_list: Entity_List(Platform),
-
- room: tiled.Map,
}
init :: proc() {
+ state.camera.zoom = 1
+ // state.camera.offset = {draw.SCREEN_WIDTH/2, draw.SCREEN_HEIGHT/2}
+
init_keybinds()
draw.init()
- init_player(&state.player)
+ init_player()
- room, err := tiled.load_map("res/room_begin.tmj")
- if err != .NONE {
- log.errorf("Error loading map (%v)", err)
- }
- state.room = room
+ // room, err := tiled.load_map("res/room_begin.tmj")
+ // if err != .NONE {
+ // log.errorf("Error loading map (%v)", err)
+ // }
+ // state.room = room
- make_map_collisions :: proc(
- x: i32,
- y: i32,
- tile: tiled.Tile,
- tile_set: tiled.Tile_Set,
- ) {
- objs, is_object_layer := tile.object_group.layer.(tiled.Object_Layer)
- if !is_object_layer {
- return
- }
+ tiled.load_world("res/map.world")
+ tiled.open_new_room_at({0, 0})
- for obj in objs {
- make_platform({
- Vec2{f32(x), f32(y)} + obj.position,
- obj.size,
- })
- }
- }
-
- tiled.iterate_map_tiles(state.room, make_map_collisions)
+ setup_map_collisions()
}
frame :: proc() {
- update_player(&state.player, rl.GetFrameTime())
+ update_player(rl.GetFrameTime())
draw.new_frame()
+
+ rl.BeginMode2D(state.camera)
+
+ draw_player()
+ tiled.draw_map(tiled.current_room.tmap)
+ draw_platforms()
- draw_player(state.player)
- tiled.draw_map(state.room)
- // draw_platforms()
+ rl.EndMode2D()
fps_text := fmt.caprintf(
"FPS: %v", rl.GetFPS(),
@@ -84,27 +73,27 @@ frame :: proc() {
}
cleanup :: proc() {
- delete_player(state.player)
+ delete_player()
delete_entity_list(state.platform_list)
- tiled.delete_map(state.room)
+ tiled.delete_world()
phys.destroy_world()
}
main :: proc() {
- when ODIN_DEBUG {
- track: mem.Tracking_Allocator
- mem.tracking_allocator_init(&track, context.allocator)
- context.allocator = mem.tracking_allocator(&track)
+ 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 {
- fmt.eprintf(
+ defer {
+ if len(track.allocation_map) > 0 {
+ fmt.eprintf(
"=== %v allocations not freed: ===\n",
len(track.allocation_map),
)
total := 0
- for _, entry in track.allocation_map {
- fmt.eprintf(
+ for _, entry in track.allocation_map {
+ fmt.eprintf(
"- %v bytes @ %v (%v, mode %v)\n",
entry.size,
entry.location,
@@ -112,12 +101,12 @@ main :: proc() {
entry.mode,
)
total += entry.size
- }
+ }
fmt.eprintf("=== a total of %v bytes unfreed ===\n", total)
- }
- mem.tracking_allocator_destroy(&track)
- }
+ }
+ mem.tracking_allocator_destroy(&track)
+ }
}
logger := log.create_console_logger()
@@ -131,6 +120,9 @@ main :: proc() {
init()
for !rl.WindowShouldClose() {
+ // pbody := phys.get_body(player.body_handle)
+ // state.camera.target = {pbody.pos.x, pbody.pos.y}
+
frame()
free_all(context.temp_allocator)
diff --git a/src/phys/world.odin b/src/phys/world.odin
index 8470872..51a85a5 100644
--- a/src/phys/world.odin
+++ b/src/phys/world.odin
@@ -68,11 +68,11 @@ find_bin :: proc(b: Body) -> ^[dynamic]Body_Handle {
}
@(private="file")
-add_to_bins :: proc(b: Body) {
- bin := find_bin(b)
+add_to_bins :: proc(b: ^Body) {
+ bin := find_bin(b^)
idx := i32(len(bin))
append(bin, b.handle)
- world.bodies[b.handle].bin_idx = idx
+ b.bin_idx = idx
}
@(private="file")
@@ -84,8 +84,10 @@ remove_from_bins :: proc(b: Body) {
last := pop(bin)
if last != b.handle {
+ // log.debug(last, b.handle)
+ last_body := get_body(last)
bin[b.bin_idx] = last
- world.bodies[last].bin_idx = b.bin_idx
+ last_body.bin_idx = b.bin_idx
}
}
@@ -113,7 +115,7 @@ add_body :: proc(b: Body) -> (Body_Handle, ^Body) {
body := &world.bodies[world.handles[handle]]
body.handle = handle
- add_to_bins(body^)
+ add_to_bins(body)
return handle, body
}
@@ -125,11 +127,11 @@ remove_body :: proc(h: Body_Handle) {
last := pop(&world.bodies)
if last.handle != h {
- world.bodies[h] = last
+ world.bodies[world.handles[h]] = last
world.handles[last.handle] = world.handles[h]
}
- append(&world.unused_handles, b.handle)
+ append(&world.unused_handles, h)
}
update_body :: proc(h: Body_Handle) {
@@ -207,7 +209,7 @@ set_body_position :: proc(h: Body_Handle, new_pos: Vec2) {
if prev_bin != res_bin {
remove_from_bins(b^)
b.pos = new_pos
- add_to_bins(b^)
+ add_to_bins(b)
} else {
b.pos = new_pos
}
diff --git a/src/platform.odin b/src/platform.odin
index 306df3b..bc0e103 100644
--- a/src/platform.odin
+++ b/src/platform.odin
@@ -2,6 +2,7 @@ package demonchime
import "draw"
import "phys"
+import "tiled"
Platform :: struct {
handle: Entity_Handle,
@@ -24,3 +25,35 @@ draw_platforms :: proc() {
}
draw.renderer.tint = {1, 1, 1, 1}
}
+
+clear_platforms :: proc() {
+ iter := iter_entity_list(state.platform_list)
+ for p in entity_list_iter(&iter) {
+ phys.remove_body(p.body)
+ delete_entity(&state.platform_list, p.handle)
+ }
+}
+
+setup_map_collisions :: proc() {
+ clear_platforms()
+
+ make_map_collisions :: proc(
+ x: i32,
+ y: i32,
+ tile: tiled.Tile,
+ tile_set: tiled.Tile_Set,
+ ) {
+ objs, is_object_layer := tile.object_group.layer.(tiled.Object_Layer)
+ if !is_object_layer {
+ return
+ }
+
+ for obj in objs {
+ make_platform({
+ Vec2{f32(x), f32(y)} + obj.position,
+ obj.size,
+ })
+ }
+ }
+ tiled.iterate_map_tiles(tiled.current_room.tmap, make_map_collisions)
+}
diff --git a/src/player.odin b/src/player.odin
index 5d7ffb4..5c001da 100644
--- a/src/player.odin
+++ b/src/player.odin
@@ -7,25 +7,42 @@ import rl "vendor:raylib"
import "draw"
import "phys"
+import "tiled"
PLAYER_SPEED :: 100
PLAYER_ACCEL :: 10
PLAYER_JUMP_FORCE :: 350
-JUMP_BUFFERING :: 0.07
-COYOTE_TIME :: 0.05
-JUMP_RELEASE_CUT :: -100
+PLAYER_JUMP_BUFFERING :: 0.07
+PLAYER_COYOTE_TIME :: 0.05
+PLAYER_JUMP_RELEASE_CUT :: -100
-Player :: struct {
+PLAYER_DASH_SPEED :: 500
+PLAYER_DASH_TIME :: 0.15
+PLAYER_DASH_COOLDOWN :: 0.3
+
+Player_State :: enum {
+ DEFAULT,
+ DASH,
+}
+
+// there will only ever be one player, so just make it a global :)
+player: struct {
body_handle: phys.Body_Handle,
anim: draw.Animation,
sprite: draw.Sprite,
jump_buffer: f32,
coyote_time: f32,
+
+ dash_cooldown: f32,
+ dash_timer: f32,
+
+ state: Player_State,
+ outside_room: bool,
}
-init_player :: proc(p: ^Player) {
- anim_ok := draw.init_anim_data(&p.anim, "res/player.json")
+init_player :: proc() {
+ anim_ok := draw.init_anim_data(&player.anim, "res/player.json")
if !anim_ok {
log.warn("could not load animation")
rl.CloseWindow()
@@ -33,28 +50,29 @@ init_player :: proc(p: ^Player) {
}
handle, body := phys.make_body(
- phys.Rect{{8, 17}, {16, 16}},
+ phys.Rect{{-4, 17}, {8, 16}},
)
- p.body_handle = handle
+ player.body_handle = handle
phys.set_body_position(handle, Vec2{50, 100})
- draw.init_sprite(&p.sprite, p.anim.image_path, p.anim)
+ draw.init_sprite(&player.sprite, player.anim.image_path, player.anim)
- p.sprite.offset = Vec2{
- -math.floor(f32(p.sprite.width / 2)),
- -f32(p.sprite.height)
+ player.sprite.offset = Vec2{
+ math.floor(f32(player.sprite.width / 2)),
+ -f32(player.sprite.height)
}
}
-delete_player :: proc(p: Player) {
- phys.remove_body(p.body_handle)
+delete_player :: proc() {
+ phys.remove_body(player.body_handle)
- draw.destroy_sprite(p.sprite)
- draw.delete_anim_data(p.anim)
+ draw.destroy_sprite(player.sprite)
+ draw.delete_anim_data(player.anim)
}
-update_player :: proc(p: ^Player, dt: f32) {
+@(private="file")
+get_input_dir :: proc() -> f32 {
input: f32
if is_keybind_down(actions.move_left) {
@@ -64,33 +82,45 @@ update_player :: proc(p: ^Player, dt: f32) {
input += 1
}
- if is_keybind_just_down(actions.jump) {
- p.jump_buffer = JUMP_BUFFERING
- }
+ return input
+}
+
+@(private="file")
+default_state :: proc(dt: f32) {
+ input := get_input_dir()
if input != 0 {
- draw.set_sprite_active_tag(&p.sprite, "run")
- p.sprite.scale.x = math.sign(input)
+ draw.set_sprite_active_tag(&player.sprite, "run")
+ player.sprite.scale.x = math.sign(input)
} else {
- draw.set_sprite_active_tag(&p.sprite, "idle")
+ draw.set_sprite_active_tag(&player.sprite, "idle")
+ }
+
+ if is_keybind_just_down(actions.jump) {
+ player.jump_buffer = PLAYER_JUMP_BUFFERING
+ }
+
+ if is_keybind_just_down(actions.dash) && player.dash_cooldown <= 0 {
+ enter_dash()
+ return
}
- body := phys.get_body(p.body_handle)
-
+ body := phys.get_body(player.body_handle)
+
if .DOWN in body.collisions {
- p.coyote_time = COYOTE_TIME
+ player.coyote_time = PLAYER_COYOTE_TIME
}
- if p.jump_buffer > 0 && p.coyote_time > 0 {
- p.jump_buffer = 0
- p.coyote_time = 0
+ if player.jump_buffer > 0 && player.coyote_time > 0 {
+ player.jump_buffer = 0
+ player.coyote_time = 0
body.vel.y = -PLAYER_JUMP_FORCE
}
if .DOWN not_in body.collisions \
&& !is_keybind_down(actions.jump) \
- && body.vel.y < JUMP_RELEASE_CUT {
- body.vel.y = JUMP_RELEASE_CUT
+ && body.vel.y < PLAYER_JUMP_RELEASE_CUT {
+ body.vel.y = PLAYER_JUMP_RELEASE_CUT
}
body.vel.x = math.lerp(
@@ -100,16 +130,90 @@ update_player :: proc(p: ^Player, dt: f32) {
)
body.vel.y = math.min(body.vel.y + GRAVITY * dt, TERMINAL_VELOCITY)
- phys.update_body(p.body_handle)
+ phys.update_body(player.body_handle)
+}
+
+@(private="file")
+enter_dash :: proc() {
+ body := phys.get_body(player.body_handle)
+ // the sprite x scale is the direction the player is facing :)
+ body.vel = {math.sign(player.sprite.scale.x) * PLAYER_DASH_SPEED, 0}
+
+ player.dash_timer = PLAYER_DASH_TIME
+ player.state = .DASH
+}
+
+@(private="file")
+dash_state :: proc(dt: f32) {
+ phys.update_body(player.body_handle)
+
+ player.dash_timer -= dt
+ body := phys.get_body(player.body_handle)
+
+ if player.dash_timer <= 0 || body.collisions != {} {
+ exit_dash()
+ }
+}
+
+exit_dash :: proc() {
+ player.state = .DEFAULT
+
+ player.dash_cooldown = PLAYER_DASH_COOLDOWN
+
+ body := phys.get_body(player.body_handle)
+ body.vel /= 2
+}
+
+@(private="file")
+change_rooms :: proc() {
+ body := phys.get_body(player.body_handle)
- p.sprite.pos = body.pos
- draw.update_sprite(&p.sprite, dt)
+ width := f32(tiled.current_room.room.width)
+ height := f32(tiled.current_room.room.height)
- p.jump_buffer -= dt
- p.coyote_time -= dt
+ if body.pos.x < 0 || body.pos.x > width \
+ || body.pos.y < 0 || body.pos.y > height {
+ prev_room_pos := [2]f32{
+ f32(tiled.current_room.room.x),
+ f32(tiled.current_room.room.y),
+ }
+ changed := tiled.open_new_room_at({i32(body.pos.x), i32(body.pos.y)})
+
+ if changed {
+ new_room_pos := [2]f32{
+ f32(tiled.current_room.room.x),
+ f32(tiled.current_room.room.y),
+ }
+
+ diff := prev_room_pos - new_room_pos
+ new_pos := body.pos + diff - {0, 0}
+ phys.set_body_position(player.body_handle, new_pos)
+
+ setup_map_collisions()
+ }
+ }
}
-draw_player :: proc(p: Player) {
- draw.sprite(p.sprite)
+update_player :: proc(dt: f32) {
+ switch player.state {
+ case .DEFAULT:
+ default_state(dt)
+ case .DASH:
+ dash_state(dt)
+ }
+
+ body := phys.get_body(player.body_handle)
+
+ player.sprite.pos = body.pos
+ draw.update_sprite(&player.sprite, dt)
+
+ player.dash_cooldown -= dt
+ player.jump_buffer -= dt
+ player.coyote_time -= dt
+
+ change_rooms()
}
+draw_player :: proc() {
+ draw.sprite(player.sprite)
+}
diff --git a/src/tiled/tiled.odin b/src/tiled/tiled.odin
index 373715a..d84c296 100644
--- a/src/tiled/tiled.odin
+++ b/src/tiled/tiled.odin
@@ -183,7 +183,7 @@ load_map :: proc(path: string) -> (Map, Error) {
return {}, .COULD_NOT_LOAD
}
- log.info(jmap)
+ // log.debug(jmap)
tmap: Map
err := convert_json_map(jmap, &tmap, path)
@@ -356,8 +356,8 @@ iterate_map_tiles :: proc(tmap: Map, callback: Map_Iterate_Callback) {
x: i32 = 0
y: i32 = 0
- for cell in tile_layer.data {
- if cell != 0 {
+ for cell, i in tile_layer.data {
+ if cell > 0 {
tile := tmap.tiles[cell - 1]
tile_set := tmap.tile_sets[tile.tile_set]
callback(x, y, tile, tile_set)
@@ -372,7 +372,7 @@ iterate_map_tiles :: proc(tmap: Map, callback: Map_Iterate_Callback) {
}
}
-draw_map :: proc(tmap: Map) {
+test_cells :: proc(tmap: Map) {
for layer in tmap.layers {
tile_layer, ok := layer.layer.(Tile_Layer)
if !ok {
@@ -382,29 +382,9 @@ draw_map :: proc(tmap: Map) {
x: i32 = 0
y: i32 = 0
- for cell in tile_layer.data {
- if cell != 0 {
- tile := tmap.tiles[cell - 1]
- tile_set := tmap.tile_sets[tile.tile_set]
-
- rl.DrawTexturePro(
- tile_set.texture,
- rl.Rectangle{
- x = f32(tile.src_start.x),
- y = f32(tile.src_start.y),
- width = f32(tile.src_size.x),
- height = f32(tile.src_size.y),
- },
- rl.Rectangle{
- x = f32(x),
- y = f32(y),
- width = f32(tile.src_size.x),
- height = f32(tile.src_size.y),
- },
- {0, 0},
- 0,
- rl.WHITE,
- )
+ for cell, i in tile_layer.data {
+ if cell > 0 {
+ log.debug(i, [2]i32{x, y}, cell)
}
x += tmap.tile_width
@@ -416,6 +396,31 @@ draw_map :: proc(tmap: Map) {
}
}
+draw_map :: proc(tmap: Map) {
+ draw_tile :: proc(x: i32, y: i32, tile: Tile, tile_set: Tile_Set) {
+ rl.DrawTexturePro(
+ tile_set.texture,
+ rl.Rectangle{
+ x = f32(tile.src_start.x),
+ y = f32(tile.src_start.y),
+ width = f32(tile.src_size.x),
+ height = f32(tile.src_size.y),
+ },
+ rl.Rectangle{
+ x = f32(x),
+ y = f32(y),
+ width = f32(tile.src_size.x),
+ height = f32(tile.src_size.y),
+ },
+ {0, 0},
+ 0,
+ rl.WHITE,
+ )
+ }
+
+ iterate_map_tiles(tmap, draw_tile)
+}
+
@(private)
load_image :: proc(filename: string, res_dir: string) -> rl.Texture2D {
path := strings.concatenate({res_dir, "/", filename})
diff --git a/src/tiled/world.odin b/src/tiled/world.odin
new file mode 100644
index 0000000..c55097a
--- /dev/null
+++ b/src/tiled/world.odin
@@ -0,0 +1,84 @@
+package tiled
+
+import os "core:os/os2"
+import "core:log"
+import "core:encoding/json"
+import "core:path/filepath"
+import "core:strings"
+
+World_Room :: struct {
+ file_name: string `json:"fileName"`,
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+}
+
+res_dir: string
+world: []World_Room
+current_room: struct {
+ tmap: Map,
+ using room: World_Room,
+}
+
+load_world :: proc(path: string) -> bool {
+ world_text, read_err := os.read_entire_file(path, context.temp_allocator)
+ if read_err != nil {
+ log.errorf("Failed to read file %v (%v)", path, read_err)
+ return false
+ }
+
+ jworld: struct {
+ maps: []World_Room,
+ }
+ unmarshal_err := json.unmarshal(
+ world_text,
+ &jworld,
+ )
+ if unmarshal_err != nil {
+ log.errorf("Failed to unmarshal file %v (%v)", path, unmarshal_err)
+ return false
+ }
+
+ world = jworld.maps
+ res_dir = filepath.dir(path)
+
+ return true
+}
+
+delete_world :: proc() {
+ delete_map(current_room.tmap)
+ delete(world)
+ delete(res_dir)
+}
+
+open_new_room_at :: proc(pos: [2]i32) -> bool {
+ pos := pos
+ pos += {current_room.room.x, current_room.room.y}
+
+ log.debug("trying to change room...", pos)
+ for room in world {
+ if strings.compare(room.file_name, current_room.room.file_name) == 0 {
+ continue
+ }
+
+ if pos.x >= room.x && pos.x <= room.x + room.width \
+ && pos.y >= room.y && pos.y <= room.y + room.height {
+ delete_map(current_room.tmap)
+
+ path := strings.concatenate(
+ {res_dir, "/", room.file_name},
+ allocator = context.temp_allocator,
+ )
+ new_map, err := load_map(path)
+ if err != .NONE {
+ log.error("could not load new room")
+ return false
+ }
+ current_room.tmap = new_map
+ current_room.room = room
+ return true
+ }
+ }
+ return false
+}