aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbuild.sh4
-rw-r--r--res/img/player.asebin5482 -> 5482 bytes
-rwxr-xr-xrun_debug.sh2
-rw-r--r--[-rwxr-xr-x]src/assets.odin106
-rw-r--r--src/draw/sprite.odin94
-rw-r--r--src/main.odin15
-rw-r--r--src/platform.odin18
-rw-r--r--src/player.odin50
-rw-r--r--src/resources.odin61
-rw-r--r--src/sprite.odin106
-rw-r--r--src/tiled/json.odin208
-rw-r--r--src/tiled/tiled.odin628
-rw-r--r--src/tiled/world.odin88
-rw-r--r--src/world.odin87
-rw-r--r--tools/compile_assets/loaders.odin97
-rw-r--r--tools/compile_assets/main.odin129
-rw-r--r--tools/compile_assets/tiled.odin447
17 files changed, 969 insertions, 1171 deletions
diff --git a/build.sh b/build.sh
index 3a96bab..0399d37 100755
--- a/build.sh
+++ b/build.sh
@@ -1,2 +1,2 @@
-sokol-shdc -i src/draw/default.glsl -o src/draw/default_shader.odin -l glsl430:metal_macos:hlsl5 -f sokol_odin
-odin build src -vet -disallow-do -o:speed
+odin run ./tools/compile_assets -- res src/assets.odin &&
+odin build src -disallow-do -o:speed $1
diff --git a/res/img/player.ase b/res/img/player.ase
index 47a7dfa..5aaf62e 100644
--- a/res/img/player.ase
+++ b/res/img/player.ase
Binary files differ
diff --git a/run_debug.sh b/run_debug.sh
index c56ad41..c1cb61d 100755
--- a/run_debug.sh
+++ b/run_debug.sh
@@ -1 +1 @@
-./run.sh "-debug -sanitize:address"
+./run.sh "-sanitize:address"
diff --git a/src/assets.odin b/src/assets.odin
index e74dcf8..b2222bb 100755..100644
--- a/src/assets.odin
+++ b/src/assets.odin
@@ -15,11 +15,12 @@ Image_Id :: enum {
Animation_Id :: enum {
PLAYER,
+ NONE,
}
-Map_Id :: enum {
- ROOM_BEGIN_1,
+Room_Id :: enum {
ROOM_BEGIN,
+ ROOM_BEGIN_1,
}
Tileset_Id :: enum {
@@ -29,50 +30,87 @@ Tileset_Id :: enum {
Resource_Id :: union {
Image_Id,
Animation_Id,
- Map_Id,
+ Room_Id,
Tileset_Id,
}
-images: [Image_Id]Image_Resource
-animations: [Animation_Id]Animation_Resource
-maps: [Map_Id]Map_Resource
-tilesets: [Tileset_Id]Tileset_Resource
+images: [Image_Id]Image_Resource = {
+ .TILESETS = {data = #load("../.compiled-res/tilesets.qoi"), anim = .NONE},
+ .PLAYER = {data = #load("../.compiled-res/player-sheet.qoi"), anim = .PLAYER},
+}
-path_to_id: map[string]Resource_Id
+animations: [Animation_Id]Animation_Resource = {
+ .PLAYER = {frame_count = 23, frame_durations = {100, 100, 100, 100, 100, 100, 75, 75, 75, 75, 75, 75, 75, 75, 100, 100, 100, 100, 100, 100, 100, 100, 100}, tags = {"idle"={from = 0, to = 5}, "jump_trans"={from = 16, to = 16}, "run"={from = 6, to = 13}, "jump_down"={from = 17, to = 18}, "jump_up"={from = 14, to = 15}, "sleep"={from = 19, to = 22}}},
+ .NONE = {frame_count=1, frame_durations={100}, tags={}},
+}
-load_resources :: proc() {
- load_images()
- load_anims()
- load_maps()
- load_tilesets()
+rooms: [Room_Id]Room_Resource = {
+ .ROOM_BEGIN = {width=30, height=20, tile_width=16, tile_height=16, layers={{40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 27, 31, 31, 19, 19, 28, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 21, 31, 31, 31, 11, 0, 0, 0, 0, 7, 40, 40, 40, 40, 40, 40, 40, 40, 27, 31, 19, 31, 19, 19, 19, 31, 19, 31, 19, 19, 17, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 40, 40, 40, 21, 31, 31, 31, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 40, 40, 21, 19, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 21, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 27, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 32, 0, 0, 0, 0, 0, 0, 5, 9, 20, 9, 20, 20, 20, 18, 20, 20, 18, 18, 9, 18, 9, 18, 6, 0, 0, 0, 0, 0, 7, 32, 0, 0, 0, 0, 0, 0, 7, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 8, 0, 0, 0, 0, 0, 7, 11, 0, 0, 0, 0, 0, 0, 7, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 38, 9, 9, 18, 18, 9, 39, 0, 0, 0, 5, 9, 20, 9, 44, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 0, 0, 0, 7, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 20, 9, 18, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}}, objects={}, background_image=nil},
+ .ROOM_BEGIN_1 = {width=30, height=20, tile_width=16, tile_height=16, layers={{40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 21, 19, 19, 19, 19, 31, 31, 28, 40, 40, 40, 40, 40, 40, 40, 27, 31, 19, 31, 31, 31, 19, 19, 31, 31, 31, 31, 19, 19, 19, 17, 0, 0, 0, 0, 0, 0, 10, 28, 40, 40, 21, 19, 31, 31, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 21, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 40, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 27, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 41, 9, 20, 20, 20, 18, 18, 18, 20, 8, 0, 0, 0, 0, 0, 0, 5, 18, 9, 9, 20, 20, 20, 18, 9, 18, 9, 18, 18, 9, 39, 40, 40, 40, 40, 40, 40, 40, 40, 43, 20, 20, 9, 20, 9, 9, 39, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40}}, objects={}, background_image=nil},
+}
- // Allow conversion from paths to a resource id, since it's a better way to
- // reference resources in other resources (JSON is a good example).
- path_to_id["res/tileset.tsj"] = Tileset_Id.TILESET
- path_to_id["res/img/player.ase"] = Image_Id.PLAYER
- path_to_id["res/img/tilesets.png"] = Image_Id.TILESETS
- path_to_id["res/room_begin.tmj"] = Map_Id.ROOM_BEGIN
- path_to_id["res/room_begin_1.tmj"] = Map_Id.ROOM_BEGIN_1
+tilesets: [Tileset_Id]Tileset_Resource = {
+ .TILESET = {tiles={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44}, image=.TILESETS},
}
-@(private="file")
-load_images :: proc() {
- images[.TILESETS] = {data = #load("/home/xswan/demonchime/.compiled-res/tilesets.qoi")}
- images[.PLAYER] = {data = #load("/home/xswan/demonchime/.compiled-res/player-sheet.qoi")}
+tiles: []Tile_Resource = {
+ {tileset=.TILESET, rect=Rect{start = {0, 0}, size = {16, 16}}, id=1, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {16, 0}, size = {16, 16}}, id=2, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {32, 0}, size = {16, 16}}, id=3, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {48, 0}, size = {16, 16}}, id=4, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {64, 0}, size = {16, 16}}, id=5, collisions={Rect{start = {8, 8}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {80, 0}, size = {16, 16}}, id=6, collisions={Rect{start = {0, 8}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {96, 0}, size = {16, 16}}, id=7, collisions={Rect{start = {8, 0}, size = {8, 16}}}},
+ {tileset=.TILESET, rect=Rect{start = {112, 0}, size = {16, 16}}, id=8, collisions={Rect{start = {0, 0}, size = {8, 16}}}},
+ {tileset=.TILESET, rect=Rect{start = {128, 0}, size = {16, 16}}, id=9, collisions={Rect{start = {0, 8}, size = {16, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {144, 0}, size = {16, 16}}, id=10, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {160, 0}, size = {16, 16}}, id=11, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {0, 16}, size = {16, 16}}, id=12, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {16, 16}, size = {16, 16}}, id=13, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {32, 16}, size = {16, 16}}, id=14, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {48, 16}, size = {16, 16}}, id=15, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {64, 16}, size = {16, 16}}, id=16, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {80, 16}, size = {16, 16}}, id=17, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {96, 16}, size = {16, 16}}, id=18, collisions={Rect{start = {0, 8}, size = {16, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {112, 16}, size = {16, 16}}, id=19, collisions={Rect{start = {0, 0}, size = {16, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {128, 16}, size = {16, 16}}, id=20, collisions={Rect{start = {0, 8}, size = {16, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {144, 16}, size = {16, 16}}, id=21, collisions={Rect{start = {8, 0}, size = {8, 8}}, Rect{start = {0, 8}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {160, 16}, size = {16, 16}}, id=22, collisions={Rect{start = {8, 8}, size = {8, 8}}, Rect{start = {0, 0}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {0, 32}, size = {16, 16}}, id=23, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {16, 32}, size = {16, 16}}, id=24, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {32, 32}, size = {16, 16}}, id=25, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {48, 32}, size = {16, 16}}, id=26, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {64, 32}, size = {16, 16}}, id=27, collisions={Rect{start = {8, 0}, size = {8, 8}}, Rect{start = {0, 8}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {80, 32}, size = {16, 16}}, id=28, collisions={Rect{start = {8, 8}, size = {8, 8}}, Rect{start = {0, 0}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {96, 32}, size = {16, 16}}, id=29, collisions={Rect{start = {0, 8}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {112, 32}, size = {16, 16}}, id=30, collisions={Rect{start = {8, 8}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {128, 32}, size = {16, 16}}, id=31, collisions={Rect{start = {0, 0}, size = {16, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {144, 32}, size = {16, 16}}, id=32, collisions={Rect{start = {0, 0}, size = {8, 16}}}},
+ {tileset=.TILESET, rect=Rect{start = {160, 32}, size = {16, 16}}, id=33, collisions={Rect{start = {8, 0}, size = {8, 16}}}},
+ {tileset=.TILESET, rect=Rect{start = {0, 48}, size = {16, 16}}, id=34, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {16, 48}, size = {16, 16}}, id=35, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {32, 48}, size = {16, 16}}, id=36, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {48, 48}, size = {16, 16}}, id=37, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {64, 48}, size = {16, 16}}, id=38, collisions={Rect{start = {0, 8}, size = {16, 8}}, Rect{start = {0, 0}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {80, 48}, size = {16, 16}}, id=39, collisions={Rect{start = {0, 8}, size = {16, 8}}, Rect{start = {8, 0}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {96, 48}, size = {16, 16}}, id=40, collisions={}},
+ {tileset=.TILESET, rect=Rect{start = {112, 48}, size = {16, 16}}, id=41, collisions={Rect{start = {8, 8}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {128, 48}, size = {16, 16}}, id=42, collisions={Rect{start = {0, 8}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {144, 48}, size = {16, 16}}, id=43, collisions={Rect{start = {0, 8}, size = {16, 8}}, Rect{start = {0, 0}, size = {8, 8}}}},
+ {tileset=.TILESET, rect=Rect{start = {160, 48}, size = {16, 16}}, id=44, collisions={Rect{start = {0, 8}, size = {16, 8}}, Rect{start = {8, 0}, size = {8, 8}}}},
}
-@(private="file")
-load_anims :: proc() {
- animations[.PLAYER] = {frame_count = 23, frame_durations = {100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100}, tags = {"jump_trans"={from = 16, to = 16}, "jump_down"={from = 17, to = 18}, "idle"={from = 0, to = 5}, "sleep"={from = 19, to = 22}, "jump_up"={from = 14, to = 15}, "run"={from = 6, to = 13}}}
+world: []Room_Position_Resource = {
+ {id=.ROOM_BEGIN_1, x=-528, y=-64, width=480, height=320},
+ {id=.ROOM_BEGIN, x=-48, y=-64, width=480, height=320},
}
-@(private="file")
-load_maps :: proc() {
- maps[.ROOM_BEGIN_1] = #load("/home/xswan/demonchime/res/room_begin_1.tmj")
- maps[.ROOM_BEGIN] = #load("/home/xswan/demonchime/res/room_begin.tmj")
+path_to_id: map[string]Resource_Id = {
+ "res/img/player.ase" = Image_Id.PLAYER,
+ "res/img/tilesets.png" = Image_Id.TILESETS,
+ "res/room_begin.tmj" = Room_Id.ROOM_BEGIN,
+ "res/room_begin_1.tmj" = Room_Id.ROOM_BEGIN_1,
}
-@(private="file")
-load_tilesets :: proc() {
- tilesets[.TILESET] = #load("/home/xswan/demonchime/res/tileset.tsj")
+load_resources :: proc() {
}
diff --git a/src/draw/sprite.odin b/src/draw/sprite.odin
index ebe2e08..3a90dfe 100644
--- a/src/draw/sprite.odin
+++ b/src/draw/sprite.odin
@@ -4,97 +4,3 @@ import "core:log"
import rl "vendor:raylib"
-Sprite :: struct {
- image: rl.Texture2D,
- anim: Animation,
- active_anim: u32,
- frame_time: f32,
- current_frame: i32,
-
- width: i32,
- height: i32,
-
- pos: Vec2,
- offset: Vec2,
- rotation: f32,
- scale: Vec2,
-
- just_finished_loop: bool,
-}
-
-init_sprite :: proc(
- sprite: ^Sprite,
- path: string,
- anim: Animation = {},
-) -> bool {
- byte_path := make([]u8, len(path) + 1, context.temp_allocator)
- copy(byte_path, path)
- byte_path[len(path)] = 0
-
- sprite.image = rl.LoadTexture(transmute(cstring)&byte_path[0])
- sprite.anim = anim
-
- sprite.width = i32(int(sprite.image.width) / len(sprite.anim.frames))
- sprite.height = i32(sprite.image.height)
-
- sprite.scale = Vec2{1, 1}
-
- log.debugf(
- "loaded sprite '%v' %vx%v (%vx%v)",
- path,
- sprite.width, sprite.height,
- sprite.image.width, sprite.image.height,
- )
-
- return true
-}
-
-destroy_sprite :: proc(sprite: Sprite) {
- rl.UnloadTexture(sprite.image)
-}
-
-set_sprite_active_tag :: proc(sprite: ^Sprite, tag_name: string) {
- sprite.active_anim = sprite.anim.frame_tags_indices[tag_name]
-}
-
-update_sprite :: proc(sprite: ^Sprite, dt: f32) {
- sprite.just_finished_loop = false
-
- sprite.frame_time += dt
-
- tag := sprite.anim.frame_tags[sprite.active_anim]
- frame := sprite.anim.frames[sprite.current_frame]
- duration := f32(frame.duration) / 1000
-
- if sprite.frame_time > duration {
- sprite.frame_time -= duration
- sprite.current_frame += 1
- }
-
- if sprite.current_frame < tag.from {
- sprite.current_frame = tag.to
- sprite.just_finished_loop = true
- }
- if sprite.current_frame > tag.to {
- sprite.current_frame = tag.from
- sprite.just_finished_loop = true
- }
-}
-
-sprite :: proc(
- sprite: Sprite,
-) {
- frame := sprite.anim.frames[sprite.current_frame]
-
- texture_quad(
- sprite.image,
- Rect {
- Vec2{f32(frame.rect.x), f32(frame.rect.y)},
- Vec2{f32(frame.rect.w), f32(frame.rect.h)},
- },
- sprite.pos,
- sprite.offset,
- sprite.rotation,
- sprite.scale,
- )
-}
diff --git a/src/main.odin b/src/main.odin
index 508f97c..bed9861 100644
--- a/src/main.odin
+++ b/src/main.odin
@@ -9,12 +9,13 @@ import "base:runtime"
import "core:log"
import "core:fmt"
import "core:mem"
+import os "core:os/os2"
import rl "vendor:raylib"
import "draw"
import "phys"
-import "tiled"
+// import "tiled"
Vec2 :: [2]f32
@@ -55,6 +56,11 @@ logger: log.Logger
// }
// }
+die :: proc(msg: string, args: ..any, #any_int exit_code := 1) {
+ log.fatalf(msg, args)
+ os.exit(exit_code)
+}
+
init :: proc() {
state.camera.zoom = 1
// state.camera.offset = {draw.SCREEN_WIDTH/2, draw.SCREEN_HEIGHT/2}
@@ -71,8 +77,7 @@ init :: proc() {
// }
// state.room = room
- tiled.load_world("res/map.world")
- tiled.open_new_room_at({0, 0})
+ open_room(.ROOM_BEGIN)
setup_map_collisions()
}
@@ -85,7 +90,7 @@ frame :: proc() {
rl.BeginMode2D(state.camera)
draw_player()
- tiled.draw_map(tiled.current_room.tmap)
+ draw_room(current_room.id)
// draw_platforms()
rl.EndMode2D()
@@ -102,7 +107,6 @@ frame :: proc() {
cleanup :: proc() {
delete_player()
delete_entity_list(state.platform_list)
- tiled.delete_world()
phys.destroy_world()
}
@@ -159,3 +163,4 @@ main :: proc() {
cleanup()
}
+
diff --git a/src/platform.odin b/src/platform.odin
index bc0e103..a071468 100644
--- a/src/platform.odin
+++ b/src/platform.odin
@@ -2,7 +2,6 @@ package demonchime
import "draw"
import "phys"
-import "tiled"
Platform :: struct {
handle: Entity_Handle,
@@ -40,20 +39,17 @@ setup_map_collisions :: proc() {
make_map_collisions :: proc(
x: i32,
y: i32,
- tile: tiled.Tile,
- tile_set: tiled.Tile_Set,
+ tile_id: u32,
) {
- objs, is_object_layer := tile.object_group.layer.(tiled.Object_Layer)
- if !is_object_layer {
- return
- }
+ tile := tiles[tile_id]
- for obj in objs {
+ for coll in tile.collisions {
make_platform({
- Vec2{f32(x), f32(y)} + obj.position,
- obj.size,
+ coll.start + {f32(x), f32(y)},
+ coll.size,
})
}
}
- tiled.iterate_map_tiles(tiled.current_room.tmap, make_map_collisions)
+
+ iterate_room_tiles(current_room.id, make_map_collisions)
}
diff --git a/src/player.odin b/src/player.odin
index de5a66b..e7fd214 100644
--- a/src/player.odin
+++ b/src/player.odin
@@ -5,9 +5,7 @@ import "core:math"
import rl "vendor:raylib"
-import "draw"
import "phys"
-import "tiled"
PLAYER_SPEED :: 100
PLAYER_ACCEL :: 10
@@ -29,8 +27,7 @@ Player_State :: enum {
// 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,
+ sprite: Sprite,
jump_buffer: f32,
coyote_time: f32,
@@ -45,13 +42,6 @@ player: struct {
}
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()
- return
- }
-
handle, body := phys.make_body(
phys.Rect{{-4, 17}, {8, 16}},
)
@@ -59,7 +49,7 @@ init_player :: proc() {
phys.set_body_position(handle, Vec2{50, 100})
- draw.init_sprite(&player.sprite, player.anim.image_path, player.anim)
+ init_sprite(&player.sprite, .PLAYER)
player.sprite.offset = Vec2{
math.floor(f32(player.sprite.width / 2)),
@@ -70,8 +60,7 @@ init_player :: proc() {
delete_player :: proc() {
phys.remove_body(player.body_handle)
- draw.destroy_sprite(player.sprite)
- draw.delete_anim_data(player.anim)
+ destroy_sprite(player.sprite)
}
@(private="file")
@@ -93,10 +82,10 @@ default_state :: proc(dt: f32) {
input := get_input_dir()
if input != 0 {
- draw.set_sprite_active_tag(&player.sprite, "run")
+ set_sprite_active_tag(&player.sprite, "run")
player.sprite.scale.x = math.sign(input)
} else {
- draw.set_sprite_active_tag(&player.sprite, "idle")
+ set_sprite_active_tag(&player.sprite, "idle")
}
if is_keybind_just_down(actions.jump) {
@@ -116,11 +105,11 @@ default_state :: proc(dt: f32) {
} else {
switch body.vel.y {
case -math.INF_F32..<-50:
- draw.set_sprite_active_tag(&player.sprite, "jump_up")
+ set_sprite_active_tag(&player.sprite, "jump_up")
case 50..<math.INF_F32:
- draw.set_sprite_active_tag(&player.sprite, "jump_down")
+ set_sprite_active_tag(&player.sprite, "jump_down")
case:
- draw.set_sprite_active_tag(&player.sprite, "jump_trans")
+ set_sprite_active_tag(&player.sprite, "jump_trans")
}
}
@@ -176,6 +165,7 @@ dash_state :: proc(dt: f32) {
}
}
+@(private="file")
exit_dash :: proc() {
player.state = .DEFAULT
@@ -189,21 +179,21 @@ exit_dash :: proc() {
change_rooms :: proc() {
body := phys.get_body(player.body_handle)
- width := f32(tiled.current_room.room.width)
- height := f32(tiled.current_room.room.height)
+ width := f32(current_room.width)
+ height := f32(current_room.height)
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),
+ prev_room_pos := Vec2{
+ f32(current_room.x),
+ f32(current_room.y),
}
- changed := tiled.open_new_room_at({i32(body.pos.x), i32(body.pos.y)})
+ changed := open_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),
+ new_room_pos := Vec2{
+ f32(current_room.x),
+ f32(current_room.y),
}
diff := prev_room_pos - new_room_pos
@@ -226,7 +216,7 @@ update_player :: proc(dt: f32) {
body := phys.get_body(player.body_handle)
player.sprite.pos = body.pos
- draw.update_sprite(&player.sprite, dt)
+ update_sprite(&player.sprite, dt)
player.dash_cooldown -= dt
player.jump_buffer -= dt
@@ -236,5 +226,5 @@ update_player :: proc(dt: f32) {
}
draw_player :: proc() {
- draw.sprite(player.sprite)
+ draw_sprite(player.sprite)
}
diff --git a/src/resources.odin b/src/resources.odin
index 6120812..799ba75 100644
--- a/src/resources.odin
+++ b/src/resources.odin
@@ -19,9 +19,68 @@ Animation_Resource :: struct {
tags: map[string]Tag_Resource,
}
-Map_Resource :: struct {
+Object_Resource :: struct {
+ pos: Vec2,
+ parallax: Vec2,
+ properties: map[string]any,
+}
+
+Room_Resource :: struct {
+ width: i32,
+ height: i32,
+
+ tile_width: i32,
+ tile_height: i32,
+
+ layers: [][]u32,
+
+ objects: []Object_Resource,
+
+ background_image: Maybe(Image_Id),
+}
+
+Tile_Resource :: struct {
+ tileset: Tileset_Id,
+ rect: Rect,
+
+ id: u32,
+
+ collisions: []Rect,
}
Tileset_Resource :: struct {
+ tiles: []u32,
+ image: Image_Id,
+}
+
+Room_Position_Resource :: struct {
+ id: Room_Id,
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
}
+get_image :: proc(id: Image_Id) -> rl.Texture2D {
+ img_res := &images[id]
+
+ if !rl.IsTextureValid(img_res.texture) {
+ rl_img := rl.LoadImageFromMemory(
+ ".qoi",
+ raw_data(img_res.data),
+ i32(len(img_res.data)),
+ )
+ if !rl.IsImageValid(rl_img) {
+ die("Could not load asset %v", id)
+ }
+
+ img_res.texture = rl.LoadTextureFromImage(rl_img);
+ rl.UnloadImage(rl_img)
+ }
+
+ return img_res.texture
+}
+
+get_room :: proc(id: Room_Id) -> Room_Resource {
+ return rooms[id]
+}
diff --git a/src/sprite.odin b/src/sprite.odin
new file mode 100644
index 0000000..af2fdf9
--- /dev/null
+++ b/src/sprite.odin
@@ -0,0 +1,106 @@
+package demonchime
+
+import "core:log"
+
+import rl "vendor:raylib"
+
+import "draw"
+
+Sprite :: struct {
+ image: rl.Texture2D,
+ anim_id: Animation_Id,
+ current_tag: Tag_Resource,
+ frame_time: f32,
+ current_frame: i32,
+
+ width: i32,
+ height: i32,
+
+ pos: Vec2,
+ offset: Vec2,
+ rotation: f32,
+ scale: Vec2,
+
+ just_finished_loop: bool,
+}
+
+init_sprite :: proc(
+ sprite: ^Sprite,
+ image_id: Image_Id,
+ anim_id: Maybe(Animation_Id) = nil,
+) -> bool {
+ sprite.image = get_image(image_id)
+
+ switch id in anim_id {
+ case Animation_Id:
+ sprite.anim_id = id
+ case nil:
+ sprite.anim_id = images[image_id].anim
+ }
+
+ anim := animations[sprite.anim_id]
+
+ sprite.width = i32(f32(sprite.image.width) / f32(anim.frame_count))
+ sprite.height = i32(sprite.image.height)
+
+ log.debug(sprite.width, anim.frame_count, sprite.anim_id)
+
+ sprite.scale = Vec2{1, 1}
+
+ return true
+}
+
+destroy_sprite :: proc(sprite: Sprite) {
+ rl.UnloadTexture(sprite.image)
+}
+
+set_sprite_active_tag :: proc(sprite: ^Sprite, tag_name: string) {
+ anim := animations[sprite.anim_id]
+ sprite.current_tag = anim.tags[tag_name]
+}
+
+update_sprite :: proc(sprite: ^Sprite, dt: f32) {
+ sprite.just_finished_loop = false
+
+ sprite.frame_time += dt
+
+ anim := animations[sprite.anim_id]
+
+ duration := f32(anim.frame_durations[sprite.current_frame]) / 1000
+
+ if sprite.frame_time > duration {
+ sprite.frame_time -= duration
+ sprite.current_frame += 1
+ }
+
+ tag := &sprite.current_tag
+
+ if sprite.current_frame < tag.from {
+ sprite.current_frame = tag.to
+ sprite.just_finished_loop = true
+ }
+ if sprite.current_frame > tag.to {
+ sprite.current_frame = tag.from
+ sprite.just_finished_loop = true
+ }
+}
+
+draw_sprite :: proc(
+ sprite: Sprite,
+) {
+ rect := draw.Rect{
+ {f32(sprite.width * sprite.current_frame), 0},
+ {f32(sprite.width), f32(sprite.height)},
+ }
+
+ draw.texture_quad(
+ sprite.image,
+ rect,
+ sprite.pos,
+ sprite.offset,
+ sprite.rotation,
+ sprite.scale,
+ )
+
+ // log.debug(rect)
+}
diff --git a/src/tiled/json.odin b/src/tiled/json.odin
deleted file mode 100644
index 874430e..0000000
--- a/src/tiled/json.odin
+++ /dev/null
@@ -1,208 +0,0 @@
-package tiled
-
-Json_Map :: struct {
- backgroundcolor: string,
- class: string,
- compressionlevel: i32,
- width: i32,
- height: i32,
- infinite: bool,
- layers: []Json_Layer,
- nextlayerid: i32,
- nextobjectid: i32,
- orientation: string,
- parallaxoriginx: f64,
- parallaxoriginy: f64,
- properties: []Json_Property,
- renderorder: string,
- staggeraxis: string,
- staggerindex: string,
- tilewidth: i32,
- tileheight: i32,
- tilesets: []Json_Tile_Set,
-}
-
-Json_Layer :: struct {
- class: string,
- compression: string,
- data: union {
- []i32,
- string,
- },
- draworder: string,
- encoding: string,
- id: i32,
- image: string,
- image_width: i32,
- image_height: i32,
- layers: []Json_Layer,
- // locked: bool,
- name: string,
- objects: []Json_Object,
- offsetx: f64,
- offsety: f64,
- opacity: f32,
- parallaxx: f64,
- parallaxy: f64,
- properties: []Json_Property,
- repeatx: bool,
- repeaty: bool,
- tintcolor: string,
- transparentcolor: string,
- type: string,
- visible: bool,
- x: i32,
- y: i32,
- width: i32,
- height: i32,
-}
-
-Json_Object :: struct {
- ellipse: bool,
- gid: i32,
- width: f64,
- height: f64,
- id: i32,
- name: string,
- point: bool,
- polygon: []Json_Point,
- polyline: []Json_Point,
- properties: []Json_Property,
- rotation: f64,
- template: string,
- text: Maybe(struct {
- fontfamily: string,
- color: string,
- text: string,
- halign: string,
- valign: string,
- pixelsize: i32,
- wrap: bool,
- underline: bool,
- bold: bool,
- italic: bool,
- kerning: bool,
- strikeout: bool,
- }),
- type: string,
- visible: bool,
- x: f64,
- y: f64,
-}
-
-Json_Tile_Set :: struct {
- background_color: string,
- class: string,
- rows: i32,
- columns: i32,
- fillmode: string,
- firstgid: i32,
- grid: Maybe(Json_Grid),
- image: string,
- imagewidth: i32,
- imageheight: i32,
- margin: i32,
- name: string,
- objectalignment: string,
- properties: []Json_Property,
- source: string,
- spacing: i32,
- terrains: []Json_Terrain,
- tilecount: i32,
- tilewidth: i32,
- tileheight: i32,
- tileoffset: Json_Tile_Offset,
- tilerendersize: string,
- tiles: []Json_Tile,
- transformations: Json_Transformations,
- transparentcolor: string,
- type: string,
- wangsets: []Json_Wang_Set,
-}
-
-Json_Grid :: struct {
- width: i32,
- height: i32,
- orientation: string,
-}
-
-Json_Tile_Offset :: struct {
- x: i32,
- y: i32,
-}
-
-Json_Transformations :: struct {
- hflip: bool,
- vflip: bool,
- rotate: bool,
- preferuntransformed: bool,
-}
-
-Json_Tile :: struct {
- animation: []Json_Frame,
- id: i32,
- image: string,
- imagewidth: i32,
- imageheight: i32,
- x: i32,
- y: i32,
- width: i32,
- height: i32,
- objectgroup: Json_Layer,
- probability: f64,
- properties: []Json_Property,
- terrain: []i32,
- type: string,
-}
-
-Json_Frame :: struct {
- duration: i32,
- tileid: i32,
-}
-
-Json_Terrain :: struct {
- name: string,
- properties: []Json_Property,
- tile: i32,
-}
-
-Json_Wang_Set :: struct {
- class: string,
- colors: []Json_Wang_Color,
- name: string,
- properties: []Json_Property,
- tile: i32,
- type: string,
- wangtiles: []Json_Wang_Tile,
-}
-
-Json_Wang_Color :: struct {
- class: string,
- color: string,
- name: string,
- probability: f64,
- properties: []Json_Property,
- tile: i32,
-}
-
-Json_Wang_Tile :: struct {
- tileid: i32,
- wangid: []u8,
-}
-
-Json_Object_Template :: struct {
- tileset: Maybe(Json_Tile_Set),
- object: Json_Object,
-}
-
-Json_Property :: struct {
- name: string,
- type: string,
- propertytype: string,
- value: any,
-}
-
-Json_Point :: struct {
- x: f64,
- y: f64,
-}
diff --git a/src/tiled/tiled.odin b/src/tiled/tiled.odin
deleted file mode 100644
index d84c296..0000000
--- a/src/tiled/tiled.odin
+++ /dev/null
@@ -1,628 +0,0 @@
-package tiled
-
-import os "core:os/os2"
-import mem "core:mem"
-import strings "core:strings"
-import "core:path/filepath"
-import "core:log"
-import "core:encoding/json"
-
-import rl "vendor:raylib"
-
-Color :: [4]f32
-
-Error :: enum {
- NONE = 0,
- COULD_NOT_LOAD,
- NON_ORTHOGONAL_NOT_SUPPORTED,
- INFINITE_NOT_SUPPORTED,
- ELLIPSE_OBJ_NOT_SUPPORTED,
- POLYGON_OBJ_NOT_SUPPORTED,
- POLYLINE_OBJ_NOT_SUPPORTED,
- TEXT_OBJ_NOT_SUPPORTED,
-}
-
-Axis :: enum {
- X,
- Y,
-}
-
-Properties :: map[string]any
-
-Map :: struct {
- // Currently, only orthogonal maps are allowed. Any non-orthogonal maps
- // throw an error.
- // Infinite maps will also throw an error for now.
-
- background_color: Color,
- class: string,
-
- width: i32,
- height: i32,
-
- layers: []Layer,
-
- next_layer_id: i32,
- next_object_id: i32,
-
- parallax_origin_x: f64,
- parallax_origin_y: f64,
-
- properties: Properties,
-
- // stagger_axis: Axis,
- // stagger_offset: i32, // To replace stagger index.
-
- tile_width: i32,
- tile_height: i32,
-
- tile_sets: []Tile_Set,
-
- tiles: [dynamic]Tile,
-}
-
-Layer :: struct {
- // Layers that aren't visible are entirely ignored.
- name: string,
- properties: Properties,
-
- parallax_x: f64,
- parallax_y: f64,
-
- tint: Color,
- id: i32,
-
- layer: union #no_nil {
- Tile_Layer,
- Image_Layer,
- Object_Layer,
- }
-}
-
-Tile_Layer :: struct {
- // Data will automatically become uncompressed.
- width: i32,
- height: i32,
- data: []i32,
-}
-
-Image_Layer :: struct {
- texture: rl.Texture2D,
- transparent_color: Color,
- repeat_x: bool,
- repeat_y: bool,
-}
-
-Object_Layer :: []Object
-
-Object_Type :: enum {
- POINT,
- TILE,
- RECT,
-}
-
-Object :: struct {
- // For now, anything that *isn't* one of these is ignored and
- // a warning is thrown:
- // - Point
- // - Tile
- // - Rectangle
- //
- // Objects that aren't visible will be ignored entirely.
-
- type: Object_Type,
- name: string,
- class: string,
- properties: Properties,
- position: [2]f32,
- size: [2]f32,
- tile_id: i32,
-}
-
-Tile_Set :: struct {
- // For now, any autotiling information is ignored.
-
- properties: Properties,
-
- rows: i32,
- columns: i32,
-
- first_gid: i32,
-
- texture: rl.Texture2D,
- margin: i32,
- spacing: i32,
-
- tile_count: i32,
- tile_width: i32,
- tile_height: i32,
- tile_offset: [2]i32, // in pixels
-
- tiles: []i32, // array of GIDs
-}
-
-Tile :: struct {
- // For now, animations are ignored. Support in the future is somewhat likely.
- // And similar to above, autotiling information is ignored.
-
- is_image_collection: bool,
-
- tile_set: i32, // index for the tile set in Map
-
- id: i32,
- gid: i32,
-
- texture: Maybe(rl.Texture2D), // If the tile is part of an image collection.
-
- // Where on the texture is this tile
- src_start: [2]i32,
- src_size: [2]i32,
-
- object_group: Layer, // Object layer for collisions.
-
- properties: Properties,
-}
-
-load_map :: proc(path: string) -> (Map, Error) {
- defer free_all(context.temp_allocator)
-
- jmap_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 {}, .COULD_NOT_LOAD
- }
-
- jmap: Json_Map
- unmarshal_err := json.unmarshal(
- jmap_text,
- &jmap,
- allocator = context.temp_allocator,
- )
- if unmarshal_err != nil {
- log.errorf("Failed to unmarshal file %v (%v)", path, unmarshal_err)
- return {}, .COULD_NOT_LOAD
- }
-
- // log.debug(jmap)
-
- tmap: Map
- err := convert_json_map(jmap, &tmap, path)
- if err != .NONE {
- return {}, err
- }
- return tmap, .NONE
-}
-
-delete_map :: proc(tmap: Map) {
- delete(tmap.class)
- delete(tmap.properties)
-
- for layer in tmap.layers {
- delete_layer(layer)
- }
- delete(tmap.layers)
-
- for tile_set in tmap.tile_sets {
- delete(tile_set.properties)
- delete(tile_set.tiles) // all tiles are deleted later
- rl.UnloadTexture(tile_set.texture)
- }
- delete(tmap.tile_sets)
-
- for tile in tmap.tiles {
- delete(tile.properties)
-
- if tile.texture != nil {
- rl.UnloadTexture(tile.texture.(rl.Texture2D))
- }
-
- delete_layer(tile.object_group)
- }
- delete(tmap.tiles)
-}
-
-@(private)
-delete_layer :: proc(layer: Layer) {
- delete(layer.name)
- delete(layer.properties)
-
- switch l in layer.layer {
- case Tile_Layer:
- delete(l.data)
- case Image_Layer:
- rl.UnloadTexture(l.texture)
- case Object_Layer:
- for obj in l {
- delete(obj.name)
- delete(obj.class)
- delete(obj.properties)
- }
- delete(l)
- }
-}
-
-@(private)
-convert_json_color :: proc(jcolor: string) -> Color {
- return Color{1, 1, 1, 1}
-}
-
-@(private)
-convert_json_properties :: proc(jprops: []Json_Property) -> Properties {
- props := make(Properties)
-
- for jprop in jprops {
- props[jprop.name] = jprop.value
- }
-
- return props
-}
-
-@(private)
-convert_json_map :: proc(jmap: Json_Map, tmap: ^Map, path: string) -> Error {
- // ensure the map is orthogonal
- if strings.compare(jmap.orientation, "orthogonal") != 0 {
- return .NON_ORTHOGONAL_NOT_SUPPORTED
- }
-
- // ensure the map is not infinite
- if jmap.infinite {
- return .INFINITE_NOT_SUPPORTED
- }
-
- tmap.background_color = convert_json_color(jmap.backgroundcolor)
- tmap.class = strings.clone(jmap.class)
-
- tmap.width = jmap.width
- tmap.height = jmap.height
-
- tmap.tile_width = jmap.tilewidth
- tmap.tile_height = jmap.tileheight
-
- tmap.next_layer_id = jmap.nextlayerid
- tmap.next_object_id = jmap.nextobjectid
-
- tmap.parallax_origin_x = jmap.parallaxoriginx
- tmap.parallax_origin_y = jmap.parallaxoriginy
-
- tmap.properties = convert_json_properties(jmap.properties)
-
- // TODO: flatten groups
- layer_count := len(jmap.layers)
- for jlayer in jmap.layers {
- layer_count += len(jlayer.layers) // take into consideration groups
- }
-
- tmap.layers = make([]Layer, layer_count)
- current_index := 0
-
- for jlayer in jmap.layers {
- if !jlayer.visible {
- continue // ignore hidden layers
- }
-
- // FIXME: Some duplicated code down here
- if strings.compare(jlayer.type, "group") == 0 {
- for jlayer2 in jlayer.layers {
- if !jlayer2.visible {
- continue // ignore hidden layers
- }
-
- layer, err := convert_json_layer(jlayer2)
- if err != .NONE {
- return err
- }
- tmap.layers[current_index] = layer
- current_index += 1
- }
- continue
- }
-
- layer, err := convert_json_layer(jlayer)
- if err != .NONE {
- return err
- }
- tmap.layers[current_index] = layer
- current_index += 1
- }
-
- tmap.tile_sets = make([]Tile_Set, len(jmap.tilesets))
-
- res_dir := filepath.dir(path, allocator = context.temp_allocator)
-
- for jtile_set, i in jmap.tilesets {
- tile_set, err := convert_json_tile_set(tmap, res_dir, jtile_set)
- if err != .NONE {
- return err
- }
- tmap.tile_sets[i] = tile_set
-
- for j in 0..<len(tmap.tiles) {
- tmap.tiles[int(tile_set.first_gid) + j].tile_set = i32(i)
- }
- }
-
- return .NONE
-}
-
-Map_Iterate_Callback :: proc(i32, i32, Tile, Tile_Set)
-
-iterate_map_tiles :: proc(tmap: Map, callback: Map_Iterate_Callback) {
- for layer in tmap.layers {
- tile_layer, ok := layer.layer.(Tile_Layer)
- if !ok {
- continue
- }
-
- x: i32 = 0
- y: i32 = 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)
- }
-
- x += tmap.tile_width
- if x > (tmap.width - 1) * tmap.tile_width {
- x = 0
- y += tmap.tile_height
- }
- }
- }
-}
-
-test_cells :: proc(tmap: Map) {
- for layer in tmap.layers {
- tile_layer, ok := layer.layer.(Tile_Layer)
- if !ok {
- continue
- }
-
- x: i32 = 0
- y: i32 = 0
-
- for cell, i in tile_layer.data {
- if cell > 0 {
- log.debug(i, [2]i32{x, y}, cell)
- }
-
- x += tmap.tile_width
- if x > (tmap.width - 1) * tmap.tile_width {
- x = 0
- y += tmap.tile_height
- }
- }
- }
-}
-
-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})
- defer delete(path)
-
- byte_path := make([]u8, len(path) + 1, context.temp_allocator)
- copy(byte_path, path)
- byte_path[len(path)] = 0
- return rl.LoadTexture(transmute(cstring)&byte_path[0])
-}
-
-@(private)
-convert_json_layer :: proc(jlayer: Json_Layer) -> (Layer, Error) {
- // TODO: uncompress tile data
-
- layer: Layer
-
- layer.name = strings.clone(jlayer.name)
-
- layer.parallax_x = jlayer.parallaxx
- layer.parallax_y = jlayer.parallaxy
-
- layer.id = jlayer.id
-
- layer.tint = convert_json_color(jlayer.tintcolor)
-
- layer.properties = convert_json_properties(jlayer.properties)
-
- switch jlayer.type {
- case "tilelayer":
- tile_layer := Tile_Layer{
- width = jlayer.width,
- height = jlayer.height,
- }
-
- data := jlayer.data.([]i32)
- tile_layer.data = make([]i32, len(data))
- copy(tile_layer.data, data)
-
- layer.layer = tile_layer
-
- case "objectgroup":
- obj_layer := make(Object_Layer, len(jlayer.objects))
-
- for jobj, i in jlayer.objects {
- if !jobj.visible {
- continue // ignore hidden objects
- }
-
- obj, err := convert_json_object(jobj)
- if err != .NONE {
- return {}, err
- }
- obj_layer[i] = obj
- }
-
- layer.layer = obj_layer
-
- case "imagelayer":
- image_layer := Image_Layer{
- texture = load_image(jlayer.image, "res"),
- transparent_color = convert_json_color(jlayer.transparentcolor),
- repeat_x = jlayer.repeatx,
- repeat_y = jlayer.repeaty,
- }
-
- layer.layer = image_layer
-
- case "group":
- // groups are handled elsewhere
- }
-
- return layer, .NONE
-}
-
-@(private)
-convert_json_object :: proc(jobj: Json_Object) -> (Object, Error) {
- obj: Object
-
- if jobj.ellipse {
- return {}, .ELLIPSE_OBJ_NOT_SUPPORTED
- }
- if jobj.polygon != nil {
- return {}, .POLYGON_OBJ_NOT_SUPPORTED
- }
- if jobj.polyline != nil {
- return {}, .POLYLINE_OBJ_NOT_SUPPORTED
- }
- if jobj.text != nil {
- return {}, .TEXT_OBJ_NOT_SUPPORTED
- }
-
- obj.position = {
- f32(jobj.x),
- f32(jobj.y),
- }
- obj.size = {
- f32(jobj.width),
- f32(jobj.height),
- }
- obj.tile_id = jobj.gid
-
- obj.name = strings.clone(jobj.name)
- obj.class = strings.clone(jobj.type)
- obj.properties = convert_json_properties(jobj.properties)
-
- return obj, .NONE
-}
-
-@(private)
-convert_json_tile_set :: proc(
- tmap: ^Map,
- res_dir: string,
- jtile_set: Json_Tile_Set,
-) -> (Tile_Set, Error) {
- if len(jtile_set.source) > 0 {
- // Tileset links somewhere else
- path := strings.concatenate({res_dir, "/", jtile_set.source})
- defer delete(path)
-
- data, err := os.read_entire_file(path, allocator = context.temp_allocator)
- if err != nil {
- log.errorf("Could not load external tile set '%v'", path)
- return {}, .COULD_NOT_LOAD
- }
-
- new_jtile_set: Json_Tile_Set
- marshal_err := json.unmarshal(
- data,
- &new_jtile_set,
- allocator = context.temp_allocator,
- )
- if marshal_err != nil {
- log.errorf("Could not unmarshal external tile set '%v'", path)
- return {}, .COULD_NOT_LOAD
- }
-
- return convert_json_tile_set(tmap, res_dir, new_jtile_set)
- }
-
- tile_set: Tile_Set
-
- tile_set.rows = jtile_set.rows
- tile_set.columns = jtile_set.columns
-
- tile_set.first_gid = jtile_set.firstgid
-
- tile_set.texture = load_image(jtile_set.image, res_dir)
- tile_set.margin = jtile_set.margin
- tile_set.spacing = jtile_set.spacing
-
- tile_set.tile_count = jtile_set.tilecount
- tile_set.tile_width = jtile_set.tilewidth
- tile_set.tile_height = jtile_set.tileheight
- tile_set.tile_offset = {
- jtile_set.tileoffset.x,
- jtile_set.tileoffset.y,
- }
-
- tile_set.properties = convert_json_properties(jtile_set.properties)
-
- tile_set.tiles = make([]i32, jtile_set.tilecount)
-
- // Ignore margin and spacing for now
- // TODO: account for margin and spacing
- tile_x := tile_set.texture.width / tile_set.tile_width
- tile_y := tile_set.texture.height / tile_set.tile_height
-
- current_index := 0
- for y in 0..<tile_y {
- for x in 0..<tile_x {
- tile: Tile
-
- tile.id = i32(current_index)
-
- tile.src_start = {
- x * tile_set.tile_width,
- y * tile_set.tile_height,
- }
- tile.src_size = {
- tile_set.tile_width,
- tile_set.tile_height,
- }
-
- current_index += 1
-
- tile.gid = i32(len(tmap.tiles))
- append(&tmap.tiles, tile)
-
- tile_set.tiles[current_index - 1] = tile.gid
- }
- }
-
- for jtile in jtile_set.tiles {
- ttile := &tmap.tiles[tile_set.tiles[jtile.id]]
- object_group, err := convert_json_layer(jtile.objectgroup)
- if err != .NONE {
- return {}, err
- }
- ttile.object_group = object_group
- ttile.properties = convert_json_properties(jtile.properties)
- }
-
- return tile_set, .NONE
-}
diff --git a/src/tiled/world.odin b/src/tiled/world.odin
deleted file mode 100644
index 0a13716..0000000
--- a/src/tiled/world.odin
+++ /dev/null
@@ -1,88 +0,0 @@
-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() {
- for room in world {
- delete(room.file_name)
- }
-
- 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
-}
diff --git a/src/world.odin b/src/world.odin
new file mode 100644
index 0000000..d0e6c74
--- /dev/null
+++ b/src/world.odin
@@ -0,0 +1,87 @@
+package demonchime
+
+import "core:log"
+
+import rl "vendor:raylib"
+
+current_room: Room_Position_Resource
+
+open_room_at :: proc(pos: [2]i32) -> bool {
+ pos := pos
+ pos += {current_room.x, current_room.y}
+
+ log.debug("trying to change room...", pos)
+ for room_pos in world {
+ if room_pos.id == current_room.id {
+ continue
+ }
+
+ if pos.x >= room_pos.x && pos.x <= room_pos.x + room_pos.width \
+ && pos.y >= room_pos.y && pos.y <= room_pos.y + room_pos.height {
+ current_room = room_pos
+ return true
+ }
+ }
+ return false
+}
+
+open_room :: proc(id: Room_Id) -> bool {
+ for room_pos in world {
+ if room_pos.id == id {
+ current_room = room_pos
+ return true
+ }
+ }
+
+ return false
+}
+
+draw_room :: proc(id: Room_Id) {
+ draw_tile :: proc(x: i32, y: i32, tile_id: u32) {
+ tile := tiles[tile_id]
+ tileset := tilesets[tile.tileset]
+ rl.DrawTexturePro(
+ get_image(tileset.image),
+ rl.Rectangle{
+ x = f32(tile.rect.start.x),
+ y = f32(tile.rect.start.y),
+ width = f32(tile.rect.size.x),
+ height = f32(tile.rect.size.y),
+ },
+ rl.Rectangle{
+ x = f32(x),
+ y = f32(y),
+ width = f32(tile.rect.size.x),
+ height = f32(tile.rect.size.y),
+ },
+ {0, 0},
+ 0,
+ rl.WHITE,
+ )
+ }
+
+ iterate_room_tiles(id, draw_tile)
+}
+
+Map_Iterate_Callback :: proc(i32, i32, u32)
+
+iterate_room_tiles :: proc(id: Room_Id, callback: Map_Iterate_Callback) {
+ room := get_room(id)
+
+ for layer in room.layers {
+ x: i32 = 0
+ y: i32 = 0
+
+ for cell, i in layer {
+ if cell > 0 {
+ callback(x, y, cell - 1)
+ }
+
+ x += room.tile_width
+ if x > (room.width - 1) * room.tile_width {
+ x = 0
+ y += room.tile_height
+ }
+ }
+ }
+}
diff --git a/tools/compile_assets/loaders.odin b/tools/compile_assets/loaders.odin
index 136ba07..648fe49 100644
--- a/tools/compile_assets/loaders.odin
+++ b/tools/compile_assets/loaders.odin
@@ -11,20 +11,35 @@ import "core:image/qoi"
import ase "aseprite"
import aseutil "aseprite/utils"
-load_map :: proc(path: string, _: ^os.File, _: ^os.File) {
- line := fmt.aprintf("#load(\"%v\")", path)
- maps[filepath.stem(path)] = line
- paths_to_res_type[path] = "Map_Id"
+load_world :: proc(path: string, file: ^os.File, _: ^os.File) {
+ load_json_world(path)
+}
+
+load_room :: proc(path: string, file: ^os.File, _: ^os.File) {
+ load_json_room(path, file)
+ paths_to_res_type[path] = "Room_Id"
}
load_tileset :: proc(path: string, _: ^os.File, _: ^os.File) {
- line := fmt.aprintf("#load(\"%v\")", path)
- tilesets[filepath.stem(path)] = line
- paths_to_res_type[path] = "Tileset_Id"
+ load_json_tileset(path)
}
load_qoi :: proc(path: string, qoi: ^os.File, output: ^os.File) {
- line := fmt.aprintf("{{data = #load(%w)}}", path)
+ rel_path, rel_err := filepath.rel(
+ output_dir,
+ path,
+ context.temp_allocator,
+ )
+ if rel_err != nil {
+ die(
+ "Could not find relative path from %v to %v (%v)",
+ output_dir,
+ path,
+ rel_err,
+ )
+ }
+
+ line := fmt.aprintf("{{data = #load(%w), anim = .NONE}}", rel_path)
images[filepath.stem(path)] = line
paths_to_res_type[path] = "Image_Id"
@@ -60,22 +75,39 @@ load_png :: proc(path: string, png_file: ^os.File, output: ^os.File) {
die("Could not convert PNG to QOI (%v)", qoi_err)
}
- absolute_path, abs_ok := filepath.abs(
+ abs_path, found_abs := filepath.abs(
compiled_path,
- allocator = context.temp_allocator,
+ context.temp_allocator
)
- if !abs_ok {
- die("Could not find absolute path to a compiled file (%v)", compiled_path)
+ if !found_abs {
+ die("Could not find absolute path for %v", compiled_path)
}
- line := fmt.aprintf("{{data = #load(%w)}}", absolute_path)
+ rel_path, rel_err := filepath.rel(
+ output_dir,
+ abs_path,
+ context.temp_allocator,
+ )
+ if rel_err != nil {
+ die(
+ "Could not find relative path from %v to %v (%v)",
+ output_dir,
+ compiled_path,
+ rel_err,
+ )
+ }
+
+ line := fmt.aprintf("{{data = #load(%w), anim = .NONE}}", rel_path)
images[filepath.stem(path)] = line
paths_to_res_type[path] = "Image_Id"
}
@(private="file")
-load_sprite_sheet :: proc(path: string, doc: ^ase.Document) {
+load_sprite_sheet :: proc(
+ path: string,
+ doc: ^ase.Document
+) {
sprite_sheet, ss_err := aseutil.create_sprite_sheet(doc, {
size = {int(doc.header.width), int(doc.header.height)},
count = int(doc.header.frames),
@@ -89,8 +121,6 @@ load_sprite_sheet :: proc(path: string, doc: ^ase.Document) {
}
defer aseutil.destroy(sprite_sheet)
- fmt.println("loaded ss")
-
pixels := make(
[]image.RGBA_Pixel,
sprite_sheet.width * sprite_sheet.height,
@@ -126,15 +156,34 @@ load_sprite_sheet :: proc(path: string, doc: ^ase.Document) {
die("Could not save spritesheet %v (%v)", path, qoi_err)
}
- absolute_path, abs_ok := filepath.abs(
+ abs_path, found_abs := filepath.abs(
compiled_path,
- allocator = context.temp_allocator,
+ context.temp_allocator
+ )
+ if !found_abs {
+ die("Could not find absolute path for %v", compiled_path)
+ }
+
+ rel_path, rel_err := filepath.rel(
+ output_dir,
+ abs_path,
+ context.temp_allocator,
)
- if !abs_ok {
- die("Could not find absolute path to a compiled file (%v)", compiled_path)
+ if rel_err != nil {
+ die(
+ "Could not find relative path from %v to %v (%v)",
+ output_dir,
+ compiled_path,
+ rel_err,
+ )
}
- line := fmt.aprintf("{{data = #load(%w)}}", absolute_path)
+ anim_name := strings.to_screaming_snake_case(
+ filepath.stem(path),
+ allocator = context.temp_allocator,
+ )
+
+ line := fmt.aprintf("{{data = #load(%w), anim = .%v}}", rel_path, anim_name)
images[filepath.stem(path)] = line
paths_to_res_type[path] = "Image_Id"
}
@@ -191,9 +240,9 @@ load_ase :: proc(path: string, ase_file: ^os.File, output: ^os.File) {
die("Could not unmarshal aseprite file %v (%v)", path, unmarshal_err)
}
- // Load sprite sheet
- load_sprite_sheet(path, &doc)
-
// Load animation
load_animation(path, &doc)
+
+ // Load sprite sheet
+ load_sprite_sheet(path, &doc)
}
diff --git a/tools/compile_assets/main.odin b/tools/compile_assets/main.odin
index 60c8a8c..68c5625 100644
--- a/tools/compile_assets/main.odin
+++ b/tools/compile_assets/main.odin
@@ -27,8 +27,8 @@ Image_Id :: enum {
Animation_Id :: enum {
<anim-enum>}
-Map_Id :: enum {
-<map-enum>}
+Room_Id :: enum {
+<room-enum>}
Tileset_Id :: enum {
<tileset-enum>}
@@ -36,49 +36,46 @@ Tileset_Id :: enum {
Resource_Id :: union {
Image_Id,
Animation_Id,
- Map_Id,
+ Room_Id,
Tileset_Id,
}
-images: [Image_Id]Image_Resource
-animations: [Animation_Id]Animation_Resource
-maps: [Map_Id]Map_Resource
-tilesets: [Tileset_Id]Tileset_Resource
+images: [Image_Id]Image_Resource = {
+<image-load>}
-path_to_id: map[string]Resource_Id
+animations: [Animation_Id]Animation_Resource = {
+<anim-load>}
-load_resources :: proc() {
- load_images()
- load_anims()
- load_maps()
- load_tilesets()
+rooms: [Room_Id]Room_Resource = {
+<room-load>}
- // Allow conversion from paths to a resource id, since it's a better way to
- // reference resources in other resources (JSON is a good example).
-<resource-paths>}
+tilesets: [Tileset_Id]Tileset_Resource = {
+<tileset-load>}
-@(private="file")
-load_images :: proc() {
-<image-load>}
+tiles: []Tile_Resource = {
+<tiles-load>}
-@(private="file")
-load_anims :: proc() {
-<anim-load>}
+world: []Room_Position_Resource = {
+<room-positions>}
-@(private="file")
-load_maps :: proc() {
-<maps-load>}
+path_to_id: map[string]Resource_Id = {
+<resource-paths>}
-@(private="file")
-load_tilesets :: proc() {
-<tileset-load>}
+load_resources :: proc() {
+}
`
images: map[string]string
animations: map[string]string
-maps: map[string]string
+rooms: map[string]string
+
+tiles: [dynamic]string
tilesets: map[string]string
+world: [dynamic]string
+
+output_dir: string
+
paths_to_res_type: map[string]string
die :: proc(msg: string, args: ..any, exit_code := 1) {
@@ -168,13 +165,11 @@ create_loads :: proc(
for element in elements {
load = strings.concatenate({
load,
- " ",
- map_name,
- "[.",
+ " .",
strings.to_upper_snake_case(element, context.temp_allocator),
- "] = ",
+ " = ",
elements[element],
- "\n",
+ ",\n",
}, allocator = context.temp_allocator)
}
return set_placeholder(content, placeholder, load)
@@ -199,9 +194,29 @@ main :: proc() {
print_help()
}
+ // add dummy
+ append(&tiles, "")
+
+ // Need to copy, cause you can't just free executable memory, you fool
+ no_anim_line := "{frame_count=1, frame_durations={100}, tags={}}"
+ no_anim_line_owned := make([]u8, len(no_anim_line))
+ copy_from_string(no_anim_line_owned, no_anim_line)
+ animations["NONE"] = string(no_anim_line_owned)
+
input_dir := os.args[1]
output_file_path := os.args[2]
+ abs_output_path, found_abs := filepath.abs(
+ output_file_path,
+ context.temp_allocator
+ )
+ if !found_abs {
+ die("Could not find absolute path to output file")
+ }
+
+ output_dir = filepath.dir(abs_output_path)
+ defer delete(output_dir)
+
output_file, open_err := os.open(output_file_path, {.Write, .Create, .Trunc})
if open_err != nil {
die("Could not create output file '%v' (%v)", output_file_path, open_err)
@@ -233,8 +248,8 @@ main :: proc() {
loaders: map[string]Asset_Loader
defer delete(loaders)
- loaders["tmj"] = load_map
- // loaders["world"] = load_json
+ loaders["tmj"] = load_room
+ loaders["world"] = load_world
loaders["tsj"] = load_tileset
loaders["qoi"] = load_qoi
loaders["png"] = load_png
@@ -248,7 +263,7 @@ main :: proc() {
loader, has := &loaders[ext]
if !has {
fmt.printfln(
- "%-25v Skipped\tNo loader for '%v'",
+ "%-25v Skipped\tNo loader for '.%v'",
file.name,
ext,
)
@@ -262,7 +277,6 @@ main :: proc() {
}
defer os.close(f)
- fmt.printfln("%-25v Loading...", file.name)
loader^(file.fullpath, f, output_file)
fmt.printfln("%-25v Loaded", file.name)
@@ -276,14 +290,39 @@ main :: proc() {
content = create_enum(content, "<image-enum>", images)
content = create_enum(content, "<anim-enum>", animations)
- content = create_enum(content, "<map-enum>", maps)
+ content = create_enum(content, "<room-enum>", rooms)
content = create_enum(content, "<tileset-enum>", tilesets)
content = create_loads(content, "images", "<image-load>", images)
content = create_loads(content, "animations", "<anim-load>", animations)
- content = create_loads(content, "maps", "<maps-load>", maps)
+ content = create_loads(content, "rooms", "<room-load>", rooms)
content = create_loads(content, "tilesets", "<tileset-load>", tilesets)
+ tile_loads := ""
+ for tile, i in tiles {
+ if i == 0 {
+ continue
+ }
+ tile_loads = strings.concatenate({
+ tile_loads,
+ " ",
+ tile,
+ ",\n",
+ }, allocator = context.temp_allocator)
+ }
+ content = set_placeholder(content, "<tiles-load>", tile_loads)
+
+ room_pos_loads := ""
+ for room_pos, i in world {
+ room_pos_loads = strings.concatenate({
+ room_pos_loads,
+ " ",
+ room_pos,
+ ",\n",
+ }, allocator = context.temp_allocator)
+ }
+ content = set_placeholder(content, "<room-positions>", room_pos_loads)
+
res_paths := ""
cwd, _ := os.get_working_directory(context.temp_allocator)
@@ -300,13 +339,13 @@ main :: proc() {
rel_path, _ := filepath.rel(cwd, file.fullpath, context.temp_allocator)
res_paths = strings.concatenate({
res_paths,
- " path_to_id[\"",
+ " \"",
rel_path,
- "\"] = ",
+ "\" = ",
res_type,
".",
res_name,
- "\n",
+ ",\n",
}, allocator = context.temp_allocator)
}
content = set_placeholder(content, "<resource-paths>", res_paths)
@@ -319,8 +358,8 @@ main :: proc() {
for anim in animations {
delete(animations[anim])
}
- for tmap in maps {
- delete(maps[tmap])
+ for room in rooms {
+ delete(rooms[room])
}
free_all(context.temp_allocator)
diff --git a/tools/compile_assets/tiled.odin b/tools/compile_assets/tiled.odin
new file mode 100644
index 0000000..7752273
--- /dev/null
+++ b/tools/compile_assets/tiled.odin
@@ -0,0 +1,447 @@
+package assets_gen
+
+import os "core:os/os2"
+import "core:fmt"
+import "core:encoding/json"
+import "core:path/filepath"
+import "core:strings"
+
+Rect :: struct {
+ start: [2]f32,
+ size: [2]f32,
+}
+
+Json_Map :: struct {
+ backgroundcolor: string,
+ class: string,
+ compressionlevel: i32,
+ width: i32,
+ height: i32,
+ infinite: bool,
+ layers: []Json_Layer,
+ nextlayerid: i32,
+ nextobjectid: i32,
+ orientation: string,
+ parallaxoriginx: f64,
+ parallaxoriginy: f64,
+ properties: []Json_Property,
+ renderorder: string,
+ staggeraxis: string,
+ staggerindex: string,
+ tilewidth: i32,
+ tileheight: i32,
+ tilesets: []Json_Tileset,
+}
+
+Json_Layer :: struct {
+ class: string,
+ compression: string,
+ data: union {
+ []i32,
+ string,
+ },
+ draworder: string,
+ encoding: string,
+ id: i32,
+ image: string,
+ image_width: i32,
+ image_height: i32,
+ layers: []Json_Layer,
+ // locked: bool,
+ name: string,
+ objects: []Json_Object,
+ offsetx: f64,
+ offsety: f64,
+ opacity: f32,
+ parallaxx: f64,
+ parallaxy: f64,
+ properties: []Json_Property,
+ repeatx: bool,
+ repeaty: bool,
+ tintcolor: string,
+ transparentcolor: string,
+ type: string,
+ visible: bool,
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+}
+
+Json_Object :: struct {
+ ellipse: bool,
+ gid: i32,
+ width: f64,
+ height: f64,
+ id: i32,
+ name: string,
+ point: bool,
+ polygon: []Json_Point,
+ polyline: []Json_Point,
+ properties: []Json_Property,
+ rotation: f64,
+ template: string,
+ text: Maybe(struct {
+ fontfamily: string,
+ color: string,
+ text: string,
+ halign: string,
+ valign: string,
+ pixelsize: i32,
+ wrap: bool,
+ underline: bool,
+ bold: bool,
+ italic: bool,
+ kerning: bool,
+ strikeout: bool,
+ }),
+ type: string,
+ visible: bool,
+ x: f64,
+ y: f64,
+}
+
+Json_Tileset :: struct {
+ background_color: string,
+ class: string,
+ rows: i32,
+ columns: i32,
+ fillmode: string,
+ firstgid: i32,
+ grid: Maybe(Json_Grid),
+ image: string,
+ imagewidth: i32,
+ imageheight: i32,
+ margin: i32,
+ name: string,
+ objectalignment: string,
+ properties: []Json_Property,
+ source: string,
+ spacing: i32,
+ terrains: []Json_Terrain,
+ tilecount: i32,
+ tilewidth: i32,
+ tileheight: i32,
+ tileoffset: Json_Tile_Offset,
+ tilerendersize: string,
+ tiles: []Json_Tile,
+ transformations: Json_Transformations,
+ transparentcolor: string,
+ type: string,
+ wangsets: []Json_Wang_Set,
+}
+
+Json_Grid :: struct {
+ width: i32,
+ height: i32,
+ orientation: string,
+}
+
+Json_Tile_Offset :: struct {
+ x: i32,
+ y: i32,
+}
+
+Json_Transformations :: struct {
+ hflip: bool,
+ vflip: bool,
+ rotate: bool,
+ preferuntransformed: bool,
+}
+
+Json_Tile :: struct {
+ animation: []Json_Frame,
+ id: i32,
+ image: string,
+ imagewidth: i32,
+ imageheight: i32,
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+ objectgroup: Json_Layer,
+ probability: f64,
+ properties: []Json_Property,
+ terrain: []i32,
+ type: string,
+}
+
+Json_Frame :: struct {
+ duration: i32,
+ tileid: i32,
+}
+
+Json_Terrain :: struct {
+ name: string,
+ properties: []Json_Property,
+ tile: i32,
+}
+
+Json_Wang_Set :: struct {
+ class: string,
+ colors: []Json_Wang_Color,
+ name: string,
+ properties: []Json_Property,
+ tile: i32,
+ type: string,
+ wangtiles: []Json_Wang_Tile,
+}
+
+Json_Wang_Color :: struct {
+ class: string,
+ color: string,
+ name: string,
+ probability: f64,
+ properties: []Json_Property,
+ tile: i32,
+}
+
+Json_Wang_Tile :: struct {
+ tileid: i32,
+ wangid: []u8,
+}
+
+Json_Object_Template :: struct {
+ tileset: Maybe(Json_Tileset),
+ object: Json_Object,
+}
+
+Json_Property :: struct {
+ name: string,
+ type: string,
+ propertytype: string,
+ value: any,
+}
+
+Json_Point :: struct {
+ x: f64,
+ y: f64,
+}
+
+Json_World_Room :: struct {
+ file_name: string `json:"fileName"`,
+ x: i32,
+ y: i32,
+ width: i32,
+ height: i32,
+}
+
+Json_World :: struct {
+ maps: []Json_World_Room,
+}
+
+Gids :: struct {
+ first: u32,
+ last: u32,
+}
+
+gids: map[string]Gids
+
+load_json_world :: proc(path: string) {
+ json_text, read_err := os.read_entire_file(path, context.temp_allocator)
+ if read_err != nil {
+ die("Could not load world %v (%v)", path, read_err)
+ }
+
+ json_dat: Json_World
+ unmarshal_err := json.unmarshal(
+ json_text,
+ &json_dat,
+ allocator = context.temp_allocator,
+ )
+ if unmarshal_err != nil {
+ die("Could not parse world %v (%v)", path, unmarshal_err)
+ }
+
+ for room in json_dat.maps {
+ room_name := strings.to_screaming_snake_case(
+ filepath.stem(room.file_name),
+ allocator = context.temp_allocator,
+ )
+
+ line := fmt.aprintf(
+ "{{id=.%v, x=%w, y=%w, width=%w, height=%w}}",
+ room_name,
+ room.x,
+ room.y,
+ room.width,
+ room.height,
+ )
+ append(&world, line)
+ }
+}
+
+create_tile :: proc(
+ tileset_enum: string,
+ rect: Rect,
+ collisions: []Rect,
+) -> string {
+ line := fmt.aprintf(
+ "{{tileset=.%v, rect=%w, id=%w, collisions=%w}}",
+ tileset_enum,
+ rect,
+ len(tiles),
+ collisions,
+ )
+ append(&tiles, line)
+ return line
+}
+
+load_json_tileset :: proc(path: string) {
+ json_text, read_err := os.read_entire_file(path, context.temp_allocator)
+ if read_err != nil {
+ die("Could not load tileset %v (%v)", path, read_err)
+ }
+
+ json_dat: Json_Tileset
+ unmarshal_err := json.unmarshal(
+ json_text,
+ &json_dat,
+ allocator = context.temp_allocator,
+ )
+ if unmarshal_err != nil {
+ die("Failed to parse tileset %v (%v)", path, unmarshal_err)
+ }
+
+ tileset_enum := strings.to_screaming_snake_case(
+ filepath.stem(path),
+ allocator = context.temp_allocator,
+ )
+
+ image_enum := strings.to_screaming_snake_case(
+ filepath.stem(json_dat.image),
+ allocator = context.temp_allocator,
+ )
+
+ tiles_x := json_dat.imagewidth / json_dat.tilewidth
+ tiles_y := json_dat.imageheight / json_dat.tileheight
+
+ local_tiles := make([dynamic]string, allocator = context.temp_allocator)
+
+ tile_size: [2]f32 = {f32(json_dat.tilewidth), f32(json_dat.tileheight)}
+
+ first_gid := u32(len(tiles))
+
+ gids[filepath.stem(path)] = {
+ first = first_gid,
+ last = u32(len(tiles) + int(json_dat.tilecount)),
+ }
+
+ collisions := make([][dynamic]Rect, json_dat.tilecount)
+ defer {
+ for collision in collisions {
+ delete(collision)
+ }
+ delete(collisions)
+ }
+
+ for tile in json_dat.tiles {
+ collision := &collisions[tile.id]
+ for object in tile.objectgroup.objects {
+ append(
+ collision,
+ Rect{
+ start = {f32(object.x), f32(object.y)},
+ size = {f32(object.width), f32(object.height)}
+ }
+ )
+ }
+ }
+
+ for y in 0..<tiles_y {
+ for x in 0..<tiles_x {
+ append(&local_tiles, create_tile(
+ tileset_enum,
+ Rect{
+ start = {f32(x * json_dat.tilewidth), f32(y * json_dat.tileheight)},
+ size = tile_size,
+ },
+ collisions[y * tiles_x + x][:],
+ ))
+ }
+ }
+
+ gids := make([]u32, len(local_tiles))
+ for tile, i in local_tiles {
+ gids[i] = first_gid + u32(i)
+ }
+
+ line := fmt.aprintf("{{tiles=%w, image=.%v}}", gids, image_enum)
+ tilesets[filepath.stem(path)] = line
+}
+
+load_json_room :: proc(path: string, file: ^os.File) {
+ json_text, read_err := os.read_entire_file(file, context.temp_allocator)
+ if read_err != nil {
+ die("Could not load room %v (%v)", path, read_err)
+ }
+
+ json_dat: Json_Map
+ unmarshal_err := json.unmarshal(
+ json_text,
+ &json_dat,
+ allocator = context.temp_allocator,
+ )
+ if unmarshal_err != nil {
+ die("Failed to parse room %v (%v)", path, unmarshal_err)
+ }
+
+ width := json_dat.width
+ height := json_dat.height
+
+ tile_width := json_dat.tilewidth
+ tile_height := json_dat.tileheight
+
+ tiled_to_real_gid: map[i32]u32
+ defer delete(tiled_to_real_gid)
+
+ // map tiled's map-based GIDs to my actually global GIDs
+ id: i32 = 1
+ for tileset in json_dat.tilesets {
+ tileset_name := filepath.stem(tileset.source)
+ gid, exists := gids[tileset_name]
+ if !exists {
+ load_json_tileset(strings.concatenate({
+ filepath.dir(path),
+ "/",
+ tileset.source,
+ }))
+ gid = gids[tileset_name]
+ }
+
+ for i in gid.first..<gid.last {
+ tiled_to_real_gid[id] = i
+ id += 1
+ }
+ }
+
+ layers: [dynamic][]u32
+ defer delete(layers)
+
+ for layer in json_dat.layers {
+ if strings.compare(layer.type, "tilelayer") != 0 {
+ continue
+ }
+
+ json_layer_dat, ok := layer.data.([]i32)
+ if !ok {
+ continue
+ }
+
+ data := make([]u32, len(json_layer_dat), allocator = context.temp_allocator)
+
+ for cell, i in json_layer_dat {
+ data[i] = tiled_to_real_gid[cell]
+ }
+
+ append(&layers, data)
+ }
+
+ line := fmt.aprintf(
+ "{{width=%v, height=%v, tile_width=%v, tile_height=%v, layers=%w, objects={{}}, background_image=nil}}",
+ width, height,
+ tile_width, tile_height,
+ layers[:],
+ )
+ rooms[filepath.stem(path)] = line
+}