diff --git a/Cargo.lock b/Cargo.lock index 512d975..dd30eea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -29,6 +29,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + [[package]] name = "anyhow" version = "1.0.70" @@ -106,6 +115,12 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "cairo-sys-rs" version = "0.16.3" @@ -163,7 +178,7 @@ checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -205,7 +220,7 @@ source = "git+https://github.com/crumblingstatue/egui-inspect.git#5cf1f05c0f4f66 dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", ] [[package]] @@ -225,6 +240,19 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8ecd80612937e0267909d5351770fe150004e24dab93954f69ca62eecd3f77e" +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "epaint" version = "0.21.0" @@ -474,6 +502,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "indexmap" version = "1.9.3" @@ -504,6 +538,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "is-terminal" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" +dependencies = [ + "hermit-abi", + "io-lifetimes", + "rustix", + "windows-sys 0.48.0", +] + [[package]] name = "js-sys" version = "0.3.61" @@ -571,13 +617,17 @@ dependencies = [ "egui", "egui-inspect", "egui-sfml", + "env_logger", "fnv", "gamedebug_core", "hecs", + "log", "num-traits", "rand", "rfd", + "rmp-serde", "s2dc", + "serde", "sfml", "sfml-xt", "worldgen", @@ -689,6 +739,12 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "paste" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" + [[package]] name = "pin-project-lite" version = "0.2.9" @@ -792,6 +848,23 @@ dependencies = [ "bitflags", ] +[[package]] +name = "regex" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "rfd" version = "0.11.3" @@ -817,6 +890,28 @@ dependencies = [ "windows", ] +[[package]] +name = "rmp" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b13be192e0220b8afb7222aa5813cb62cc269ebb5cac346ca6487681d2913e" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + [[package]] name = "rustix" version = "0.37.8" @@ -850,6 +945,20 @@ name = "serde" version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.13", +] [[package]] name = "serde_spanned" @@ -924,6 +1033,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "system-deps" version = "6.0.4" @@ -937,6 +1057,15 @@ dependencies = [ "version-compare", ] +[[package]] +name = "termcolor" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" +dependencies = [ + "winapi-util", +] + [[package]] name = "toml" version = "0.7.3" @@ -1028,7 +1157,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -1062,7 +1191,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1105,6 +1234,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index 354b5bc..0f4059c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,10 @@ rand = "0.8.5" rfd = "0.11.3" sfml = "0.20.0" worldgen = "0.5.3" +rmp-serde = "1.1.1" +serde = { version = "1.0.159", features = ["derive"] } +log = "0.4.17" +env_logger = "0.10.0" [dependencies.s2dc] git = "https://github.com/crumblingstatue/s2dc.git" diff --git a/res/tiles.png b/res/tiles.png index d54a977..74d44ea 100644 Binary files a/res/tiles.png and b/res/tiles.png differ diff --git a/src/app.rs b/src/app.rs index 24a7d01..63053ae 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,8 +1,5 @@ -use std::fmt::{self}; - use anyhow::Context; -use egui_inspect::inspect; -use egui_sfml::{egui, SfEgui}; +use egui_sfml::SfEgui; use gamedebug_core::{imm, imm_dbg}; use sfml::{ audio::SoundSource, @@ -15,13 +12,13 @@ use sfml::{ }; use crate::{ - debug::DebugState, - game::{for_each_tile_on_screen, Biome, GameState, LightSource}, + debug::{self, DebugState}, + game::{for_each_tile_on_screen, Biome, GameState}, graphics::{self, ScreenPos, ScreenPosScalar}, input::Input, - math::{center_offset, px_per_frame_to_km_h, WorldPos, M_PER_PX, TILE_SIZE}, + math::{center_offset, TILE_SIZE}, res::Res, - world::{Tile, TilePosScalar, CHUNK_EXTENT}, + world::{TilePosScalar, CHUNK_EXTENT}, }; /// Application level state (includes game and ui state, etc.) @@ -76,6 +73,7 @@ impl App { self.input.clear_pressed(); gamedebug_core::inc_frame(); } + self.game.tile_db.try_save(); } fn do_event_handling(&mut self) { @@ -132,7 +130,7 @@ impl App { let mut on_screen_tile_ents = Vec::new(); for_each_tile_on_screen(self.game.camera_offset, self.rt.size(), |tp, _sp| { let tid = self.game.world.tile_at_mut(tp, &self.game.worldgen).mid; - if tid == Tile::EMPTY { + if !self.game.tile_db[tid].solid { return; } let tsize = TILE_SIZE as i32; @@ -186,7 +184,12 @@ impl App { wpos.x = wpos.x.saturating_add_signed(loc.x.into()); wpos.y = wpos.y.saturating_add_signed(loc.y.into()); let mouse_tpos = wpos.tile_pos(); - imm!("Mouse @ tile {}, {}", mouse_tpos.x, mouse_tpos.y); + imm!( + "Mouse @ tile {}, {} ({:?})", + mouse_tpos.x, + mouse_tpos.y, + self.game.world.tile_at_mut(mouse_tpos, &self.game.worldgen) + ); imm!( "@ chunk {}, {}", mouse_tpos.x / CHUNK_EXTENT as TilePosScalar, @@ -207,8 +210,6 @@ impl App { } else { t.bg = self.game.tile_to_place; } - } else if self.input.mid_pressed { - self.game.light_sources.push(LightSource { pos: wpos }); } if self.game.camera_offset.y > 643_000 { self.game.current_biome = Biome::Underground; @@ -274,15 +275,13 @@ impl App { self.rw.draw_with_renderstates(&spr, &rst); self.sf_egui .do_frame(|ctx| { - if self.debug.panel { - debug_panel_ui( - &mut self.debug, - &mut self.game, - ctx, - &mut self.res, - &mut self.scale, - ); - } + debug::do_debug_ui( + ctx, + &mut self.debug, + &mut self.game, + &mut self.res, + &mut self.scale, + ); }) .unwrap(); self.sf_egui.draw(&mut self.rw, None); @@ -300,94 +299,3 @@ fn viewport_center_offset(rw_size: Vector2u, rt_size: Vector2u, scale: u8) -> Sc y: y as ScreenPosScalar, } } - -fn debug_panel_ui( - debug: &mut DebugState, - mut game: &mut GameState, - ctx: &egui::Context, - res: &mut Res, - mut scale: &mut u8, -) { - egui::Window::new("Debug (F12)").show(ctx, |ui| { - if debug.freecam { - ui.label("Cam x"); - ui.add(egui::DragValue::new(&mut game.camera_offset.x)); - ui.label("Cam y"); - ui.add(egui::DragValue::new(&mut game.camera_offset.y)); - let co = game.camera_offset; - ui.label(format!( - "Cam Depth: {}", - LengthDisp(co.y as f32 - WorldPos::SURFACE as f32) - )); - ui.label(format!( - "Cam offset from center: {}", - LengthDisp(co.x as f32 - WorldPos::CENTER as f32) - )); - } else { - let tp = game.player.center_tp(); - imm_dbg!(tp); - ui.label(format!( - "Player Depth: {}", - LengthDisp(game.player.feet_y() as f32 - WorldPos::SURFACE as f32) - )); - ui.label(format!( - "Player offset from center: {}", - LengthDisp(game.player.col_en.en.pos.x as f32 - WorldPos::CENTER as f32) - )); - ui.label(format!( - "Hspeed: {} ({} km/h)", - game.player.hspeed, - px_per_frame_to_km_h(game.player.hspeed) - )); - ui.label(format!( - "Vspeed: {} ({} km/h)", - game.player.vspeed, - px_per_frame_to_km_h(game.player.vspeed) - )); - } - ui.label("Music volume"); - let mut vol = res.surf_music.volume(); - ui.add(egui::DragValue::new(&mut vol)); - res.surf_music.set_volume(vol); - ui.separator(); - egui::ScrollArea::both() - .id_source("insp_scroll") - .max_height(240.) - .max_width(340.0) - .show(ui, |ui| { - inspect! { - ui, - scale, game - } - }); - ui.separator(); - egui::ScrollArea::vertical().show(ui, |ui| { - gamedebug_core::for_each_imm(|info| match info { - gamedebug_core::Info::Msg(msg) => { - ui.label(msg); - } - gamedebug_core::Info::Rect(_, _, _, _, _) => todo!(), - }); - }); - gamedebug_core::clear_immediates(); - }); -} - -struct LengthDisp(f32); - -impl fmt::Display for LengthDisp { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let meters = self.0 * M_PER_PX; - if meters.abs() > 1000. { - let km = if meters.is_sign_negative() { - (meters / 1000.).ceil() - } else { - (meters / 1000.).floor() - }; - let m = meters % 1000.; - write!(f, "{km} km, {m} m") - } else { - write!(f, "{meters} m") - } - } -} diff --git a/src/debug.rs b/src/debug.rs index 9576ffa..ddb2c83 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -1,11 +1,20 @@ -use sfml::window::Key; +use egui_inspect::inspect; +use sfml::{audio::SoundSource, window::Key}; -use crate::input::Input; +use crate::{ + game::GameState, + input::Input, + math::{px_per_frame_to_km_h, WorldPos}, + res::Res, + stringfmt::LengthDisp, + tiles::tiledb_edit_ui::tiledb_edit_ui, +}; #[derive(Default, Debug)] pub struct DebugState { - pub panel: bool, + panel: bool, pub freecam: bool, + tiledb_edit: bool, } impl DebugState { @@ -18,3 +27,90 @@ impl DebugState { } } } + +fn debug_panel_ui( + debug: &mut DebugState, + mut game: &mut GameState, + ctx: &egui::Context, + res: &mut Res, + mut scale: &mut u8, +) { + egui::Window::new("Debug (F12)").show(ctx, |ui| { + if debug.freecam { + ui.label("Cam x"); + ui.add(egui::DragValue::new(&mut game.camera_offset.x)); + ui.label("Cam y"); + ui.add(egui::DragValue::new(&mut game.camera_offset.y)); + let co = game.camera_offset; + ui.label(format!( + "Cam Depth: {}", + LengthDisp(co.y as f32 - WorldPos::SURFACE as f32) + )); + ui.label(format!( + "Cam offset from center: {}", + LengthDisp(co.x as f32 - WorldPos::CENTER as f32) + )); + } else { + ui.label(format!( + "Player Depth: {}", + LengthDisp(game.player.feet_y() as f32 - WorldPos::SURFACE as f32) + )); + ui.label(format!( + "Player offset from center: {}", + LengthDisp(game.player.col_en.en.pos.x as f32 - WorldPos::CENTER as f32) + )); + ui.label(format!( + "Hspeed: {} ({} km/h)", + game.player.hspeed, + px_per_frame_to_km_h(game.player.hspeed) + )); + ui.label(format!( + "Vspeed: {} ({} km/h)", + game.player.vspeed, + px_per_frame_to_km_h(game.player.vspeed) + )); + } + ui.label("Music volume"); + let mut vol = res.surf_music.volume(); + ui.add(egui::DragValue::new(&mut vol)); + res.surf_music.set_volume(vol); + ui.separator(); + egui::ScrollArea::both() + .id_source("insp_scroll") + .max_height(240.) + .max_width(340.0) + .show(ui, |ui| { + inspect! { + ui, + scale, + game, + debug.tiledb_edit + } + }); + ui.separator(); + egui::ScrollArea::vertical().show(ui, |ui| { + gamedebug_core::for_each_imm(|info| match info { + gamedebug_core::Info::Msg(msg) => { + ui.label(msg); + } + gamedebug_core::Info::Rect(_, _, _, _, _) => todo!(), + }); + }); + gamedebug_core::clear_immediates(); + }); +} + +pub(crate) fn do_debug_ui( + ctx: &egui::Context, + debug: &mut DebugState, + game: &mut GameState, + res: &mut Res, + scale: &mut u8, +) { + if debug.panel { + debug_panel_ui(debug, game, ctx, res, scale); + } + if debug.tiledb_edit { + tiledb_edit_ui(ctx, &mut game.tile_db); + } +} diff --git a/src/game.rs b/src/game.rs index b1640be..1abf5c3 100644 --- a/src/game.rs +++ b/src/game.rs @@ -3,17 +3,16 @@ mod player; use derivative::Derivative; use egui_inspect::derive::Inspect; use sfml::{ - graphics::{ - Color, Rect, RectangleShape, RenderTarget, RenderTexture, Shape, Sprite, Transformable, - }, + graphics::{Color, RectangleShape, RenderTarget, RenderTexture, Shape, Sprite, Transformable}, system::{Clock, Vector2u}, SfBox, }; use crate::{ graphics::{ScreenPos, ScreenPosScalar}, - math::{wp_to_tp, WorldPos}, + math::{wp_to_tp, WorldPos, TILE_SIZE}, res::Res, + tiles::TileDb, world::{Tile, TileId, TilePos, World}, worldgen::Worldgen, }; @@ -37,11 +36,12 @@ pub struct GameState { #[opaque] pub clock: SfBox, pub light_sources: Vec, + pub tile_db: TileDb, } #[derive(Debug, Inspect)] pub struct LightSource { - pub pos: WorldPos, + pub pos: ScreenPos, } #[derive(PartialEq, Eq, Clone, Copy, Debug, Inspect)] @@ -52,20 +52,27 @@ pub enum Biome { impl GameState { pub(crate) fn draw_world(&mut self, rt: &mut RenderTexture, res: &mut Res) { + self.light_sources.clear(); let mut s = Sprite::with_texture(&res.tile_atlas); for_each_tile_on_screen(self.camera_offset, rt.size(), |tp, sp| { let tile = self.world.tile_at_mut(tp, &self.worldgen); s.set_position(sp.to_sf_vec()); if tile.bg != Tile::EMPTY { - s.set_texture_rect(Rect::new((tile.bg - 1) as i32 * 32, 0, 32, 32)); + s.set_texture_rect(self.tile_db[tile.bg].atlas_offset.to_sf_rect()); rt.draw(&s); } if tile.mid != Tile::EMPTY { - s.set_texture_rect(Rect::new((tile.mid - 1) as i32 * 32, 0, 32, 32)); + s.set_texture_rect(self.tile_db[tile.mid].atlas_offset.to_sf_rect()); + if self.tile_db[tile.mid].emits_light { + let mut pos = sp; + pos.x += (TILE_SIZE / 2) as i16; + pos.y += (TILE_SIZE / 2) as i16; + self.light_sources.push(LightSource { pos: sp }); + } rt.draw(&s); } if tile.fg != Tile::EMPTY { - s.set_texture_rect(Rect::new((tile.fg - 1) as i32 * 32, 0, 32, 32)); + s.set_texture_rect(self.tile_db[tile.fg].atlas_offset.to_sf_rect()); rt.draw(&s); } }); @@ -102,14 +109,10 @@ impl GameState { 255, )); for ls in &self.light_sources { - let (x, y) = ( - ls.pos.x as i32 - self.camera_offset.x as i32, - ls.pos.y as i32 - self.camera_offset.y as i32, - ); let mut s = Sprite::with_texture(&res.light_texture); s.set_scale((4., 4.)); s.set_origin((128., 128.)); - s.set_position((x as f32, y as f32)); + s.set_position((ls.pos.x.into(), ls.pos.y.into())); lightmap.draw(&s); } } @@ -152,6 +155,7 @@ impl Default for GameState { ambient_light: 0, clock: Clock::start(), light_sources: Vec::new(), + tile_db: TileDb::load_or_default(), } } } diff --git a/src/game/player.rs b/src/game/player.rs index 88fab45..c4ed36e 100644 --- a/src/game/player.rs +++ b/src/game/player.rs @@ -34,6 +34,7 @@ impl Player { jumps_left: 0, } } + #[allow(dead_code)] pub fn center_tp(&self) -> TilePos { TilePos { x: (self.col_en.en.pos.x / TILE_SIZE as i32) as TilePosScalar, diff --git a/src/graphics.rs b/src/graphics.rs index 5540901..78dc868 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -1,3 +1,4 @@ +use egui_inspect::derive::Inspect; use sfml::{ graphics::RenderWindow, system::Vector2f, @@ -23,7 +24,7 @@ impl ScreenRes { } // We assume this game won't be played above 32767*32767 resolution -#[derive(Default, Clone, Copy, Debug)] +#[derive(Default, Clone, Copy, Debug, Inspect)] pub struct ScreenPos { pub x: ScreenPosScalar, pub y: ScreenPosScalar, diff --git a/src/main.rs b/src/main.rs index da54089..02a0827 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,8 @@ mod graphics; mod input; mod math; mod res; +mod stringfmt; +mod tiles; mod world; mod worldgen; @@ -18,6 +20,9 @@ fn try_main() -> anyhow::Result<()> { } fn main() { + env_logger::builder() + .filter_level(log::LevelFilter::Info) + .init(); if let Err(e) = try_main() { rfd::MessageDialog::new() .set_title("Fatal error") diff --git a/src/stringfmt.rs b/src/stringfmt.rs new file mode 100644 index 0000000..d67b5bb --- /dev/null +++ b/src/stringfmt.rs @@ -0,0 +1,22 @@ +use std::fmt; + +use crate::math::M_PER_PX; + +pub struct LengthDisp(pub f32); + +impl fmt::Display for LengthDisp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let meters = self.0 * M_PER_PX; + if meters.abs() > 1000. { + let km = if meters.is_sign_negative() { + (meters / 1000.).ceil() + } else { + (meters / 1000.).floor() + }; + let m = meters % 1000.; + write!(f, "{km} km, {m} m") + } else { + write!(f, "{meters} m") + } + } +} diff --git a/src/tiles.rs b/src/tiles.rs new file mode 100644 index 0000000..eaeefbd --- /dev/null +++ b/src/tiles.rs @@ -0,0 +1,106 @@ +pub mod tiledb_edit_ui; + +use std::ops::Index; + +use egui_inspect::derive::Inspect; +use log::warn; +use serde::{Deserialize, Serialize}; +use sfml::graphics::IntRect; + +use crate::{math::TILE_SIZE, world::TileId}; + +#[derive(Serialize, Deserialize, Default, Debug, Inspect)] +pub struct TileDef { + pub solid: bool, + pub emits_light: bool, + pub atlas_offset: AtlasOffset, +} + +#[derive(Serialize, Deserialize, Debug, Inspect)] +pub struct TileDb { + db: Vec, +} + +impl Default for TileDb { + fn default() -> Self { + Self { + // Add empty/air tile + db: vec![EMPTY], + } + } +} + +const EMPTY: TileDef = TileDef { + solid: false, + emits_light: false, + // Rendering empty tile is actually special cased, and no rendering is done. + // But just in case, put the offset to UNKNOWN + atlas_offset: UNKNOWN_ATLAS_OFF, +}; + +impl Index for TileDb { + type Output = TileDef; + + fn index(&self, index: TileId) -> &Self::Output { + self.db.get(index as usize).unwrap_or(&UNKNOWN_TILE) + } +} + +#[derive(Debug, Inspect, Serialize, Deserialize)] +pub struct AtlasOffset { + pub x: u16, + pub y: u16, +} +impl AtlasOffset { + pub(crate) fn to_sf_rect(&self) -> IntRect { + IntRect { + left: self.x as i32, + top: self.y as i32, + width: TILE_SIZE as i32, + height: TILE_SIZE as i32, + } + } +} + +impl Default for AtlasOffset { + fn default() -> Self { + UNKNOWN_ATLAS_OFF + } +} + +const UNKNOWN_ATLAS_OFF: AtlasOffset = AtlasOffset { x: 320, y: 0 }; + +static UNKNOWN_TILE: TileDef = TileDef { + solid: true, + emits_light: true, + atlas_offset: UNKNOWN_ATLAS_OFF, +}; + +const PATH: &str = "tiles.dat"; + +impl TileDb { + pub fn load_or_default() -> Self { + match std::fs::read(PATH) { + Ok(data) => match rmp_serde::from_slice(&data) { + Ok(db) => db, + Err(e) => { + warn!("Failed to load tile database: {e}\nCreating default."); + Default::default() + } + }, + Err(e) => { + warn!("Failed to load tile database: {e}\nCreating default."); + Default::default() + } + } + } + pub fn try_save(&self) { + match rmp_serde::to_vec(self) { + Ok(vec) => match std::fs::write(PATH, vec) { + Ok(()) => {} + Err(e) => warn!("Failed to save tile db: {e}"), + }, + Err(e) => warn!("Failed to save tile db: {e}"), + } + } +} diff --git a/src/tiles/tiledb_edit_ui.rs b/src/tiles/tiledb_edit_ui.rs new file mode 100644 index 0000000..8ced76b --- /dev/null +++ b/src/tiles/tiledb_edit_ui.rs @@ -0,0 +1,9 @@ +use super::TileDb; + +pub fn tiledb_edit_ui(ctx: &egui::Context, tile_db: &mut TileDb) { + egui::Window::new("Tiledb editor").show(ctx, |ui| { + if ui.button("Add new default").clicked() { + tile_db.db.push(super::TileDef::default()); + } + }); +} diff --git a/tiles.dat b/tiles.dat new file mode 100644 index 0000000..e0697de Binary files /dev/null and b/tiles.dat differ