Make tile ids strongly typed between bg/mid/fg

This commit is contained in:
crumblingstatue 2023-04-17 13:41:51 +02:00
parent cdf73d0739
commit 1e665378f8
15 changed files with 448 additions and 178 deletions

56
Cargo.lock generated
View file

@ -158,6 +158,17 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitfield-struct"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc92b6a6ac8f8176ab4764440f5129ad73d557c532b7f5c324a71846fd25a9c6"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.15",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -296,9 +307,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.7" version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
@ -400,7 +411,7 @@ dependencies = [
[[package]] [[package]]
name = "egui-inspect" name = "egui-inspect"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/crumblingstatue/egui-inspect.git#5cf1f05c0f4f6625eb9a46ee0faebdb27919f9bf" source = "git+https://github.com/crumblingstatue/egui-inspect.git#77b02370966aad6be6eeb94ce88ae9268986fc0d"
dependencies = [ dependencies = [
"egui", "egui",
"egui-inspect-derive", "egui-inspect-derive",
@ -409,11 +420,11 @@ dependencies = [
[[package]] [[package]]
name = "egui-inspect-derive" name = "egui-inspect-derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/crumblingstatue/egui-inspect.git#5cf1f05c0f4f6625eb9a46ee0faebdb27919f9bf" source = "git+https://github.com/crumblingstatue/egui-inspect.git#77b02370966aad6be6eeb94ce88ae9268986fc0d"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 1.0.109", "syn 2.0.15",
] ]
[[package]] [[package]]
@ -504,7 +515,7 @@ dependencies = [
"flume", "flume",
"half", "half",
"lebe", "lebe",
"miniz_oxide", "miniz_oxide 0.6.2",
"rayon-core", "rayon-core",
"smallvec", "smallvec",
"zune-inflate", "zune-inflate",
@ -519,6 +530,15 @@ dependencies = [
"instant", "instant",
] ]
[[package]]
name = "fdeflate"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d329bdeac514ee06249dabc27877490f17f5d371ec693360768b838e19f3ae10"
dependencies = [
"simd-adler32",
]
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.0.25" version = "1.0.25"
@ -526,7 +546,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide 0.6.2",
] ]
[[package]] [[package]]
@ -930,6 +950,7 @@ name = "mantle-diver"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bitfield-struct",
"clap", "clap",
"derivative", "derivative",
"directories", "directories",
@ -980,6 +1001,16 @@ dependencies = [
"adler", "adler",
] ]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
"simd-adler32",
]
[[package]] [[package]]
name = "nanorand" name = "nanorand"
version = "0.7.0" version = "0.7.0"
@ -1166,14 +1197,15 @@ checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]] [[package]]
name = "png" name = "png"
version = "0.17.7" version = "0.17.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" checksum = "aaeebc51f9e7d2c150d3f3bfeb667f2aa985db5ef1e3d212847bdedb488beeaa"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"crc32fast", "crc32fast",
"fdeflate",
"flate2", "flate2",
"miniz_oxide", "miniz_oxide 0.7.1",
] ]
[[package]] [[package]]
@ -1978,9 +2010,9 @@ dependencies = [
[[package]] [[package]]
name = "zune-inflate" name = "zune-inflate"
version = "0.2.51" version = "0.2.53"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a01728b79fb9b7e28a8c11f715e1cd8dc2cda7416a007d66cac55cebb3a8ac6b" checksum = "440a08fd59c6442e4b846ea9b10386c38307eae728b216e1ab2c305d1c9daaf8"
dependencies = [ dependencies = [
"simd-adler32", "simd-adler32",
] ]

View file

@ -27,6 +27,7 @@ directories = "5.0.0"
texture_packer = "0.25.0" texture_packer = "0.25.0"
walkdir = "2.3.3" walkdir = "2.3.3"
image = "0.24.6" image = "0.24.6"
bitfield-struct = "0.4.1"
[dependencies.s2dc] [dependencies.s2dc]
#git = "https://github.com/crumblingstatue/s2dc.git" #git = "https://github.com/crumblingstatue/s2dc.git"

11
design/data.md Normal file
View file

@ -0,0 +1,11 @@
Tile
9 bits bg
9 bits mid
9 bits fg 32 bits
3 bits slope
2 bits fg state (0: fg has ore, 1: bg has ore, 2: both have ores)
5 bits liquid type
6 bits paint 16 bits
5 bits reserved

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -23,6 +23,7 @@ use crate::{
inventory::{ItemId, Slot, TileLayer, UseAction}, inventory::{ItemId, Slot, TileLayer, UseAction},
math::{center_offset, TILE_SIZE}, math::{center_offset, TILE_SIZE},
res::Res, res::Res,
tiles::TileId,
CliArgs, CliArgs,
}; };
@ -158,9 +159,12 @@ impl App {
.clamp(-terminal_velocity, terminal_velocity); .clamp(-terminal_velocity, terminal_velocity);
let mut on_screen_tile_ents = Vec::new(); let mut on_screen_tile_ents = Vec::new();
for_each_tile_on_screen(self.game.camera_offset, self.rt.size(), |tp, _sp| { 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; let tile = self.game.world.tile_at_mut(tp, &self.game.worldgen).mid;
let tdef = &self.game.tile_db[tid]; if tile.empty() {
let Some(bb) = tdef.bb else { return;
}
let tdef = &self.game.tile_db[tile];
let Some(bb) = tdef.layer.bb else {
return; return;
}; };
let x = tp.x as i32 * TILE_SIZE as i32; let x = tp.x as i32 * TILE_SIZE as i32;
@ -173,7 +177,7 @@ impl App {
); );
on_screen_tile_ents.push(TileColEn { on_screen_tile_ents.push(TileColEn {
col: en, col: en,
platform: tdef.platform, platform: tdef.layer.platform,
}); });
}); });
imm_dbg!(on_screen_tile_ents.len()); imm_dbg!(on_screen_tile_ents.len());
@ -264,27 +268,25 @@ impl App {
let def = &self.game.itemdb.db[active_slot.id as usize]; let def = &self.game.itemdb.db[active_slot.id as usize];
let t = self.game.world.tile_at_mut(mouse_tpos, &self.game.worldgen); let t = self.game.world.tile_at_mut(mouse_tpos, &self.game.worldgen);
match &def.use_action { match &def.use_action {
UseAction::PlaceTile { layer, id } => match layer { UseAction::PlaceBgTile { id } => {
TileLayer::Bg => { if t.bg.empty() {
if t.bg == 0 { t.bg = *id
t.bg = *id
}
} }
TileLayer::Mid => { }
if t.mid == 0 { UseAction::PlaceMidTile { id } => {
t.mid = *id if t.mid.empty() {
} t.mid = *id
} }
TileLayer::Fg => { }
if t.fg == 0 { UseAction::PlaceFgTile { id } => {
t.fg = *id if t.fg.empty() {
} t.fg = *id
} }
}, }
UseAction::RemoveTile { layer } => match layer { UseAction::RemoveTile { layer } => match layer {
TileLayer::Bg => t.bg = 0, TileLayer::Bg => t.bg = TileId::EMPTY,
TileLayer::Mid => t.mid = 0, TileLayer::Mid => t.mid = TileId::EMPTY,
TileLayer::Fg => t.fg = 0, TileLayer::Fg => t.fg = TileId::EMPTY,
}, },
} }
} }

View file

@ -17,7 +17,7 @@ use crate::{
math::{smoothwave, wp_to_tp, WorldPos}, math::{smoothwave, wp_to_tp, WorldPos},
res::Res, res::Res,
tiles::TileDb, tiles::TileDb,
world::{Tile, TilePos, World}, world::{TilePos, World},
worldgen::Worldgen, worldgen::Worldgen,
}; };
@ -77,11 +77,11 @@ impl GameState {
for_each_tile_on_screen(self.camera_offset, rt.size(), |tp, sp| { for_each_tile_on_screen(self.camera_offset, rt.size(), |tp, sp| {
let tile = self.world.tile_at_mut(tp, &self.worldgen); let tile = self.world.tile_at_mut(tp, &self.worldgen);
s.set_position(sp.to_sf_vec()); s.set_position(sp.to_sf_vec());
if tile.bg != Tile::EMPTY { if !tile.bg.empty() {
s.set_texture_rect(self.tile_db[tile.bg].tex_rect.to_sf()); s.set_texture_rect(self.tile_db[tile.bg].tex_rect.to_sf());
rt.draw(&s); rt.draw(&s);
} }
if tile.mid != Tile::EMPTY { if !tile.mid.empty() {
s.set_texture_rect(self.tile_db[tile.mid].tex_rect.to_sf()); s.set_texture_rect(self.tile_db[tile.mid].tex_rect.to_sf());
if let Some(light) = self.tile_db[tile.mid].light { if let Some(light) = self.tile_db[tile.mid].light {
let pos = ScreenVec { let pos = ScreenVec {
@ -92,7 +92,7 @@ impl GameState {
} }
rt.draw(&s); rt.draw(&s);
} }
if tile.fg != Tile::EMPTY { if !tile.fg.empty() {
s.set_texture_rect(self.tile_db[tile.fg].tex_rect.to_sf()); s.set_texture_rect(self.tile_db[tile.fg].tex_rect.to_sf());
rt.draw(&s); rt.draw(&s);
} }

View file

@ -1,6 +1,9 @@
use egui_inspect::derive::Inspect; use egui_inspect::derive::Inspect;
use crate::{math::IntRect, world::TileId}; use crate::{
math::IntRect,
tiles::{BgTileId, FgTileId, MidTileId},
};
/// We won't have more than 65535 different items /// We won't have more than 65535 different items
pub type ItemId = u16; pub type ItemId = u16;
@ -63,7 +66,9 @@ pub enum TileLayer {
#[derive(Debug)] #[derive(Debug)]
pub enum UseAction { pub enum UseAction {
PlaceTile { layer: TileLayer, id: TileId }, PlaceBgTile { id: BgTileId },
PlaceMidTile { id: MidTileId },
PlaceFgTile { id: FgTileId },
RemoveTile { layer: TileLayer }, RemoveTile { layer: TileLayer },
} }
@ -80,9 +85,8 @@ impl Default for ItemDb {
name: String::from("Dirt Block"), name: String::from("Dirt Block"),
graphic_name: String::from("tiles/dirt"), graphic_name: String::from("tiles/dirt"),
tex_rect: IntRect::default(), tex_rect: IntRect::default(),
use_action: UseAction::PlaceTile { use_action: UseAction::PlaceMidTile {
layer: TileLayer::Mid, id: MidTileId::DIRT,
id: 3,
}, },
consumable: true, consumable: true,
}, },
@ -90,9 +94,8 @@ impl Default for ItemDb {
name: String::from("Torch"), name: String::from("Torch"),
graphic_name: String::from("tiles/torch"), graphic_name: String::from("tiles/torch"),
tex_rect: IntRect::default(), tex_rect: IntRect::default(),
use_action: UseAction::PlaceTile { use_action: UseAction::PlaceMidTile {
layer: TileLayer::Mid, id: MidTileId::TORCH,
id: 4,
}, },
consumable: true, consumable: true,
}, },
@ -100,9 +103,8 @@ impl Default for ItemDb {
name: String::from("Platform"), name: String::from("Platform"),
graphic_name: String::from("tiles/platform"), graphic_name: String::from("tiles/platform"),
tex_rect: IntRect::default(), tex_rect: IntRect::default(),
use_action: UseAction::PlaceTile { use_action: UseAction::PlaceMidTile {
layer: TileLayer::Mid, id: MidTileId::PLATFORM,
id: 5,
}, },
consumable: true, consumable: true,
}, },
@ -119,9 +121,8 @@ impl Default for ItemDb {
name: String::from("Panzerium"), name: String::from("Panzerium"),
graphic_name: String::from("tiles/panzerium"), graphic_name: String::from("tiles/panzerium"),
tex_rect: IntRect::default(), tex_rect: IntRect::default(),
use_action: UseAction::PlaceTile { use_action: UseAction::PlaceMidTile {
layer: TileLayer::Mid, id: MidTileId::PANZERIUM,
id: 6,
}, },
consumable: true, consumable: true,
}, },

View file

@ -1,26 +1,186 @@
pub mod tiledb_edit_ui; pub mod tiledb_edit_ui;
use std::ops::Index; use std::{fmt::Debug, marker::PhantomData, ops::Index};
use egui_inspect::derive::Inspect; use egui_inspect::{derive::Inspect, Inspect};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
graphics::{ScreenSc, ScreenVec}, graphics::ScreenVec,
math::{IntRect, TILE_SIZE}, math::{IntRect, TILE_SIZE},
texture_atlas::RectMap, texture_atlas::RectMap,
world::TileId,
}; };
#[derive(Serialize, Deserialize, Default, Debug, Inspect)] #[derive(Inspect, PartialEq, Eq)]
pub struct TileDef { pub struct TileId<Layer>(pub u16, PhantomData<Layer>);
pub bb: Option<TileBb>,
impl<Layer> Copy for TileId<Layer> {}
impl<Layer> Clone for TileId<Layer> {
fn clone(&self) -> Self {
Self(self.0, PhantomData)
}
}
impl<Layer> Debug for TileId<Layer> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("TileId").field(&self.0).finish()
}
}
impl<Layer> TileId<Layer> {
pub fn empty(&self) -> bool {
self.0 == 0
}
pub const EMPTY: Self = Self(0, PhantomData);
}
#[derive(Debug)]
pub enum Bg {}
#[derive(Debug)]
pub enum Mid {}
#[derive(Debug)]
pub enum Fg {}
impl Bg {
pub fn unknown_def() -> TileDef<Self> {
TileDef {
light: Some(ScreenVec { x: 0, y: 0 }),
graphic_name: String::from("tiles/unknown_bg"),
tex_rect: IntRect {
x: 0,
y: 0,
w: 0,
h: 0,
},
layer: (),
}
}
}
impl Mid {
pub fn unknown_def() -> TileDef<Self> {
TileDef {
light: Some(ScreenVec { x: 0, y: 0 }),
graphic_name: String::from("tiles/unknown_mid"),
tex_rect: IntRect {
x: 0,
y: 0,
w: 0,
h: 0,
},
layer: MidDef {
platform: true,
bb: Some(TileBb {
x: 0,
y: 0,
w: 32,
h: 32,
}),
},
}
}
}
impl Fg {
pub fn unknown_def() -> TileDef<Self> {
TileDef {
light: Some(ScreenVec { x: 0, y: 0 }),
graphic_name: String::from("tiles/unknown_fg"),
tex_rect: IntRect {
x: 0,
y: 0,
w: 0,
h: 0,
},
layer: (),
}
}
}
pub trait TileLayer {
/// Definitions specific to this layer
type SpecificDef;
}
impl TileLayer for Bg {
type SpecificDef = ();
}
impl TileLayer for Mid {
type SpecificDef = MidDef;
}
impl TileLayer for Fg {
type SpecificDef = ();
}
pub type BgTileId = TileId<Bg>;
pub type MidTileId = TileId<Mid>;
pub type FgTileId = TileId<Fg>;
impl BgTileId {
pub const DIRT: Self = Self(1, PhantomData);
pub const STONE: Self = Self(2, PhantomData);
}
impl MidTileId {
pub const DIRT: Self = Self(1, PhantomData);
pub const STONE: Self = Self(2, PhantomData);
pub const TORCH: Self = Self(3, PhantomData);
pub const PLATFORM: Self = Self(4, PhantomData);
pub const PANZERIUM: Self = Self(5, PhantomData);
pub const UNBREAKANIUM: Self = Self(6, PhantomData);
}
impl FgTileId {
pub const COAL: Self = Self(1, PhantomData);
}
#[derive(Serialize, Deserialize, Inspect)]
pub struct TileDef<Layer: TileLayer>
where
Layer::SpecificDef: Debug + Inspect,
{
/// Whether the tile emits light, and the light source offset /// Whether the tile emits light, and the light source offset
pub light: Option<ScreenVec>, pub light: Option<ScreenVec>,
/// Platform behavior: Horizontally passable, vertically passable upwards
pub platform: bool,
pub graphic_name: String, pub graphic_name: String,
pub tex_rect: IntRect, pub tex_rect: IntRect,
pub layer: Layer::SpecificDef,
}
#[derive(Debug, Inspect, Default, Serialize, Deserialize)]
pub struct MidDef {
/// Platform behavior: Horizontally passable, vertically passable upwards
pub platform: bool,
/// Collision bounding box
pub bb: Option<TileBb>,
}
impl<Layer: TileLayer> Debug for TileDef<Layer>
where
Layer::SpecificDef: Debug + Inspect,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TileDef")
.field("light", &self.light)
.field("graphic_name", &self.graphic_name)
.field("tex_rect", &self.tex_rect)
.field("layer", &self.layer)
.finish()
}
}
impl<Layer: TileLayer> Default for TileDef<Layer>
where
Layer::SpecificDef: Default + Debug + Inspect,
{
fn default() -> Self {
Self {
light: Default::default(),
graphic_name: Default::default(),
tex_rect: Default::default(),
layer: Layer::SpecificDef::default(),
}
}
} }
const DEFAULT_TILE_BB: TileBb = TileBb { const DEFAULT_TILE_BB: TileBb = TileBb {
@ -40,49 +200,54 @@ pub struct TileBb {
#[derive(Serialize, Deserialize, Debug, Inspect)] #[derive(Serialize, Deserialize, Debug, Inspect)]
pub struct TileDb { pub struct TileDb {
db: Vec<TileDef>, unknown_bg: TileDef<Bg>,
unknown_mid: TileDef<Mid>,
unknown_fg: TileDef<Fg>,
bg: Vec<TileDef<Bg>>,
mid: Vec<TileDef<Mid>>,
fg: Vec<TileDef<Fg>>,
} }
impl Default for TileDb { impl Default for TileDb {
fn default() -> Self { fn default() -> Self {
let unknown = TileDef {
bb: None,
light: Some(ScreenVec {
x: TILE_SIZE as ScreenSc / 2,
y: TILE_SIZE as ScreenSc / 2,
}),
platform: false,
graphic_name: String::from("tiles/unknown"),
tex_rect: IntRect::default(),
};
Self { Self {
db: vec![EMPTY, unknown], unknown_bg: Bg::unknown_def(),
unknown_mid: Mid::unknown_def(),
unknown_fg: Fg::unknown_def(),
bg: vec![],
mid: vec![],
fg: vec![],
} }
} }
} }
const EMPTY: TileDef = TileDef { impl Index<BgTileId> for TileDb {
bb: None, type Output = TileDef<Bg>;
light: None,
// Rendering empty tile is actually special cased, and no rendering is done.
// But just in case, put the offset to UNKNOWN
tex_rect: IntRect {
x: 0,
y: 0,
w: 0,
h: 0,
},
platform: false,
graphic_name: String::new(),
};
impl Index<TileId> for TileDb { fn index(&self, index: BgTileId) -> &Self::Output {
type Output = TileDef; self.bg
.get(index.0 as usize - 1)
.unwrap_or(&self.unknown_bg)
}
}
fn index(&self, index: TileId) -> &Self::Output { impl Index<MidTileId> for TileDb {
self.db.get(index as usize).unwrap_or_else(|| { type Output = TileDef<Mid>;
&self.db[1] // Unknown tile def is stored at index 1
}) fn index(&self, index: MidTileId) -> &Self::Output {
self.mid
.get(index.0 as usize - 1)
.unwrap_or(&self.unknown_mid)
}
}
impl Index<FgTileId> for TileDb {
type Output = TileDef<Fg>;
fn index(&self, index: FgTileId) -> &Self::Output {
self.fg
.get(index.0 as usize - 1)
.unwrap_or(&self.unknown_fg)
} }
} }
@ -115,14 +280,38 @@ impl TileDb {
} }
pub(crate) fn update_rects(&mut self, rects: &RectMap) { pub(crate) fn update_rects(&mut self, rects: &RectMap) {
for def in &mut self.db { update_rect_def(&mut self.unknown_bg, rects);
if !def.graphic_name.is_empty() { update_rect_def(&mut self.unknown_mid, rects);
if let Some(rect) = rects.get(&def.graphic_name) { update_rect_def(&mut self.unknown_fg, rects);
def.tex_rect = *rect; update_rect_db(&mut self.bg, rects);
} else { update_rect_db(&mut self.mid, rects);
log::error!("Missing texture for {}", def.graphic_name); update_rect_db(&mut self.fg, rects);
} }
} }
}
fn update_rect_db<Layer: TileLayer>(db: &mut Vec<TileDef<Layer>>, rects: &RectMap)
where
Layer::SpecificDef: Debug + Inspect,
{
for def in db {
update_rect_def(def, rects);
}
}
fn update_rect_def<Layer: TileLayer>(
def: &mut TileDef<Layer>,
rects: &std::collections::HashMap<String, IntRect>,
) where
Layer::SpecificDef: Debug + Inspect,
{
if !def.graphic_name.is_empty() {
if let Some(rect) = rects.get(def.graphic_name.as_str()) {
def.tex_rect = *rect;
log::info!("Updated rect for {}: {:?}", def.graphic_name.as_str(), rect);
} else {
log::error!("Missing texture for {:?}", def.graphic_name.as_str());
}
} else {
log::warn!("Empty graphic name!");
} }
} }

View file

@ -1,57 +1,91 @@
use std::fmt::Debug;
use egui_inspect::Inspect;
use crate::{ use crate::{
graphics::{ScreenSc, ScreenVec}, graphics::{ScreenSc, ScreenVec},
math::TILE_SIZE, math::TILE_SIZE,
}; };
use super::{TileDb, DEFAULT_TILE_BB}; use super::{Bg, Fg, Mid, TileDb, TileDef, TileLayer, DEFAULT_TILE_BB};
pub fn tiledb_edit_ui(ctx: &egui::Context, tile_db: &mut TileDb) { pub fn tiledb_edit_ui(ctx: &egui::Context, tile_db: &mut TileDb) {
egui::Window::new("Tiledb editor").show(ctx, |ui| { egui::Window::new("Tiledb editor").show(ctx, |ui| {
ui.label(format!("Number of tile defs: {}", tile_db.db.len()));
ui.separator(); ui.separator();
egui::ScrollArea::vertical() egui::ScrollArea::vertical()
.max_height(400.0) .max_height(400.0)
.show(ui, |ui| { .show(ui, |ui| {
for (i, def) in tile_db.db.iter_mut().enumerate() { ui.heading("Bg");
ui.label(i.to_string()); db_ui(&mut tile_db.bg, ui);
match &mut def.light { ui.heading("Mid");
Some(light) => { db_ui(&mut tile_db.mid, ui);
ui.label("x"); ui.heading("Fg");
ui.add(egui::DragValue::new(&mut light.x)); db_ui(&mut tile_db.fg, ui);
ui.label("y");
ui.add(egui::DragValue::new(&mut light.y));
}
None => {
if ui.button("Insert light emit").clicked() {
def.light = Some(ScreenVec {
x: TILE_SIZE as ScreenSc / 2,
y: TILE_SIZE as ScreenSc / 2,
});
}
}
}
match &mut def.bb {
Some(bb) => {
ui.label("x");
ui.add(egui::DragValue::new(&mut bb.x));
ui.label("y");
ui.add(egui::DragValue::new(&mut bb.y));
ui.label("w");
ui.add(egui::DragValue::new(&mut bb.w));
ui.label("h");
ui.add(egui::DragValue::new(&mut bb.h));
}
None => {
if ui.button("Insert bb").clicked() {
def.bb = Some(DEFAULT_TILE_BB);
}
}
}
}
}); });
ui.separator();
if ui.button("Add new default").clicked() {
tile_db.db.push(super::TileDef::default());
}
}); });
} }
trait SpecialUi {
fn special_ui(&mut self, ui: &mut egui::Ui);
}
impl SpecialUi for TileDef<Mid> {
fn special_ui(&mut self, ui: &mut egui::Ui) {
match &mut self.layer.bb {
Some(bb) => {
ui.label("x");
ui.add(egui::DragValue::new(&mut bb.x));
ui.label("y");
ui.add(egui::DragValue::new(&mut bb.y));
ui.label("w");
ui.add(egui::DragValue::new(&mut bb.w));
ui.label("h");
ui.add(egui::DragValue::new(&mut bb.h));
}
None => {
if ui.button("Insert bb").clicked() {
self.layer.bb = Some(DEFAULT_TILE_BB);
}
}
}
}
}
impl SpecialUi for TileDef<Bg> {
fn special_ui(&mut self, _ui: &mut egui::Ui) {}
}
impl SpecialUi for TileDef<Fg> {
fn special_ui(&mut self, _ui: &mut egui::Ui) {}
}
fn db_ui<Layer: TileLayer + Debug>(db: &mut Vec<TileDef<Layer>>, ui: &mut egui::Ui)
where
<Layer as TileLayer>::SpecificDef: Debug + Default + Inspect,
TileDef<Layer>: SpecialUi,
{
for (i, def) in db.iter_mut().enumerate() {
ui.label(i.to_string());
match &mut def.light {
Some(light) => {
ui.label("x");
ui.add(egui::DragValue::new(&mut light.x));
ui.label("y");
ui.add(egui::DragValue::new(&mut light.y));
}
None => {
if ui.button("Insert light emit").clicked() {
def.light = Some(ScreenVec {
x: TILE_SIZE as ScreenSc / 2,
y: TILE_SIZE as ScreenSc / 2,
});
}
}
}
def.special_ui(ui);
}
ui.separator();
if ui.button("Add new default").clicked() {
db.push(Default::default());
}
}

View file

@ -13,7 +13,11 @@ use fnv::FnvHashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
math::WorldPos, player::Player, world::reg_chunk_existence::ExistenceBitset, worldgen::Worldgen, math::WorldPos,
player::Player,
tiles::{BgTileId, FgTileId, MidTileId, TileId},
world::reg_chunk_existence::ExistenceBitset,
worldgen::Worldgen,
}; };
use self::serialization::save_chunk; use self::serialization::save_chunk;
@ -203,9 +207,9 @@ type ChunkTiles = [Tile; CHUNK_N_TILES];
fn default_chunk_tiles() -> ChunkTiles { fn default_chunk_tiles() -> ChunkTiles {
[Tile { [Tile {
bg: 0, bg: TileId::EMPTY,
mid: 0, mid: TileId::EMPTY,
fg: 0, fg: TileId::EMPTY,
}; CHUNK_N_TILES] }; CHUNK_N_TILES]
} }
@ -229,7 +233,7 @@ impl Chunk {
// Unbreakable layer at bottom // Unbreakable layer at bottom
if pos.y > 798 { if pos.y > 798 {
for b in &mut tiles { for b in &mut tiles {
b.mid = Tile::UNBREAKANIUM; b.mid = MidTileId::UNBREAKANIUM;
} }
} }
Self { tiles } Self { tiles }
@ -269,21 +273,14 @@ fn chunk_exists(reg_path: &Path, pos: ChunkPos) -> bool {
crate::bitmanip::nth_bit_set(bitset.0, idx as usize) crate::bitmanip::nth_bit_set(bitset.0, idx as usize)
} }
pub type TileId = u16;
#[derive(Clone, Copy, Debug, Inspect)] #[derive(Clone, Copy, Debug, Inspect)]
pub struct Tile { pub struct Tile {
/// Background wall behind entities /// Background wall behind entities
pub bg: TileId, pub bg: BgTileId,
/// The solid wall on the same level as entities /// The solid wall on the same level as entities
pub mid: TileId, pub mid: MidTileId,
/// A layer on top of the mid wall. Usually ores or decorative pieces. /// A layer on top of the mid wall. Usually ores or decorative pieces.
pub fg: TileId, pub fg: FgTileId,
}
impl Tile {
pub const EMPTY: TileId = 0;
pub const UNBREAKANIUM: TileId = 5;
} }
pub const REGION_CHUNK_EXTENT: u8 = 8; pub const REGION_CHUNK_EXTENT: u8 = 8;

View file

@ -42,9 +42,9 @@ pub(super) fn save_chunk(pos: &ChunkPos, chk: &Chunk, world_dir: &Path) {
let byte_idx = loc_byte_idx(loc_idx); let byte_idx = loc_byte_idx(loc_idx);
for (i, tile) in chk.tiles.iter().enumerate() { for (i, tile) in chk.tiles.iter().enumerate() {
let off = byte_idx + (i * TILE_BYTES); let off = byte_idx + (i * TILE_BYTES);
region_tile_data[off..off + 2].copy_from_slice(&tile.bg.to_le_bytes()); region_tile_data[off..off + 2].copy_from_slice(&tile.bg.0.to_le_bytes());
region_tile_data[off + 2..off + 4].copy_from_slice(&tile.mid.to_le_bytes()); region_tile_data[off + 2..off + 4].copy_from_slice(&tile.mid.0.to_le_bytes());
region_tile_data[off + 4..off + 6].copy_from_slice(&tile.fg.to_le_bytes()); region_tile_data[off + 4..off + 6].copy_from_slice(&tile.fg.0.to_le_bytes());
} }
f.rewind().unwrap(); f.rewind().unwrap();
f.write_all(&u64::to_le_bytes(existence_bitset.0)[..]) f.write_all(&u64::to_le_bytes(existence_bitset.0)[..])
@ -65,9 +65,9 @@ impl Chunk {
let mut tiles = default_chunk_tiles(); let mut tiles = default_chunk_tiles();
for (i, t) in tiles.iter_mut().enumerate() { for (i, t) in tiles.iter_mut().enumerate() {
let off = byte_idx + (i * TILE_BYTES); let off = byte_idx + (i * TILE_BYTES);
t.bg = u16::from_le_bytes(data[off..off + 2].try_into().unwrap()); t.bg.0 = u16::from_le_bytes(data[off..off + 2].try_into().unwrap());
t.mid = u16::from_le_bytes(data[off + 2..off + 4].try_into().unwrap()); t.mid.0 = u16::from_le_bytes(data[off + 2..off + 4].try_into().unwrap());
t.fg = u16::from_le_bytes(data[off + 4..off + 6].try_into().unwrap()); t.fg.0 = u16::from_le_bytes(data[off + 4..off + 6].try_into().unwrap());
} }
Self { tiles } Self { tiles }
} }
@ -83,7 +83,7 @@ fn test_chunk_seri() {
tiles: super::default_chunk_tiles(), tiles: super::default_chunk_tiles(),
}; };
for t in &mut chk.tiles { for t in &mut chk.tiles {
t.bg = 1; t.bg = crate::tiles::BgTileId::DIRT;
} }
save_chunk(&ChunkPos { x: 2, y: 0 }, &chk, "testworld".as_ref()); save_chunk(&ChunkPos { x: 2, y: 0 }, &chk, "testworld".as_ref());
save_chunk(&ChunkPos { x: 3, y: 0 }, &chk, "testworld".as_ref()); save_chunk(&ChunkPos { x: 3, y: 0 }, &chk, "testworld".as_ref());

View file

@ -8,7 +8,10 @@ use worldgen::{
}, },
}; };
use crate::world::{ChunkPos, Tile as Tl, CHUNK_EXTENT}; use crate::{
tiles::{BgTileId, FgTileId, MidTileId, TileId},
world::{ChunkPos, Tile as Tl, CHUNK_EXTENT},
};
pub struct Worldgen { pub struct Worldgen {
world: World<crate::world::Tile>, world: World<crate::world::Tile>,
@ -33,35 +36,35 @@ impl Worldgen {
// Dirt coal // Dirt coal
.add( .add(
Tile::new(Tl { Tile::new(Tl {
bg: 9, bg: BgTileId::DIRT,
mid: 3, mid: MidTileId::DIRT,
fg: 6, fg: FgTileId::COAL,
}) })
.when(constraint!(nm.clone(), < -0.8)), .when(constraint!(nm.clone(), < -0.8)),
) )
// Dirt // Dirt
.add( .add(
Tile::new(Tl { Tile::new(Tl {
bg: 9, bg: BgTileId::DIRT,
mid: 3, mid: MidTileId::DIRT,
fg: 0, fg: TileId::EMPTY,
}) })
.when(constraint!(nm.clone(), < -0.1)), .when(constraint!(nm.clone(), < -0.1)),
) )
// Stone // Stone
.add( .add(
Tile::new(Tl { Tile::new(Tl {
bg: 7, bg: BgTileId::STONE,
mid: 2, mid: MidTileId::STONE,
fg: 0, fg: TileId::EMPTY,
}) })
.when(constraint!(nm, < 0.45)), .when(constraint!(nm, < 0.45)),
) )
// Dirt wall // Dirt wall
.add(Tile::new(Tl { .add(Tile::new(Tl {
bg: 9, bg: BgTileId::DIRT,
mid: 0, mid: TileId::EMPTY,
fg: 0, fg: TileId::EMPTY,
})); }));
Self { world } Self { world }
} }

BIN
tiles.dat

Binary file not shown.