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

View file

@ -27,6 +27,7 @@ directories = "5.0.0"
texture_packer = "0.25.0"
walkdir = "2.3.3"
image = "0.24.6"
bitfield-struct = "0.4.1"
[dependencies.s2dc]
#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},
math::{center_offset, TILE_SIZE},
res::Res,
tiles::TileId,
CliArgs,
};
@ -158,9 +159,12 @@ impl App {
.clamp(-terminal_velocity, terminal_velocity);
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;
let tdef = &self.game.tile_db[tid];
let Some(bb) = tdef.bb else {
let tile = self.game.world.tile_at_mut(tp, &self.game.worldgen).mid;
if tile.empty() {
return;
}
let tdef = &self.game.tile_db[tile];
let Some(bb) = tdef.layer.bb else {
return;
};
let x = tp.x as i32 * TILE_SIZE as i32;
@ -173,7 +177,7 @@ impl App {
);
on_screen_tile_ents.push(TileColEn {
col: en,
platform: tdef.platform,
platform: tdef.layer.platform,
});
});
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 t = self.game.world.tile_at_mut(mouse_tpos, &self.game.worldgen);
match &def.use_action {
UseAction::PlaceTile { layer, id } => match layer {
TileLayer::Bg => {
if t.bg == 0 {
t.bg = *id
}
UseAction::PlaceBgTile { id } => {
if t.bg.empty() {
t.bg = *id
}
TileLayer::Mid => {
if t.mid == 0 {
t.mid = *id
}
}
UseAction::PlaceMidTile { id } => {
if t.mid.empty() {
t.mid = *id
}
TileLayer::Fg => {
if t.fg == 0 {
t.fg = *id
}
}
UseAction::PlaceFgTile { id } => {
if t.fg.empty() {
t.fg = *id
}
},
}
UseAction::RemoveTile { layer } => match layer {
TileLayer::Bg => t.bg = 0,
TileLayer::Mid => t.mid = 0,
TileLayer::Fg => t.fg = 0,
TileLayer::Bg => t.bg = TileId::EMPTY,
TileLayer::Mid => t.mid = TileId::EMPTY,
TileLayer::Fg => t.fg = TileId::EMPTY,
},
}
}

View file

@ -17,7 +17,7 @@ use crate::{
math::{smoothwave, wp_to_tp, WorldPos},
res::Res,
tiles::TileDb,
world::{Tile, TilePos, World},
world::{TilePos, World},
worldgen::Worldgen,
};
@ -77,11 +77,11 @@ impl GameState {
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 {
if !tile.bg.empty() {
s.set_texture_rect(self.tile_db[tile.bg].tex_rect.to_sf());
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());
if let Some(light) = self.tile_db[tile.mid].light {
let pos = ScreenVec {
@ -92,7 +92,7 @@ impl GameState {
}
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());
rt.draw(&s);
}

View file

@ -1,6 +1,9 @@
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
pub type ItemId = u16;
@ -63,7 +66,9 @@ pub enum TileLayer {
#[derive(Debug)]
pub enum UseAction {
PlaceTile { layer: TileLayer, id: TileId },
PlaceBgTile { id: BgTileId },
PlaceMidTile { id: MidTileId },
PlaceFgTile { id: FgTileId },
RemoveTile { layer: TileLayer },
}
@ -80,9 +85,8 @@ impl Default for ItemDb {
name: String::from("Dirt Block"),
graphic_name: String::from("tiles/dirt"),
tex_rect: IntRect::default(),
use_action: UseAction::PlaceTile {
layer: TileLayer::Mid,
id: 3,
use_action: UseAction::PlaceMidTile {
id: MidTileId::DIRT,
},
consumable: true,
},
@ -90,9 +94,8 @@ impl Default for ItemDb {
name: String::from("Torch"),
graphic_name: String::from("tiles/torch"),
tex_rect: IntRect::default(),
use_action: UseAction::PlaceTile {
layer: TileLayer::Mid,
id: 4,
use_action: UseAction::PlaceMidTile {
id: MidTileId::TORCH,
},
consumable: true,
},
@ -100,9 +103,8 @@ impl Default for ItemDb {
name: String::from("Platform"),
graphic_name: String::from("tiles/platform"),
tex_rect: IntRect::default(),
use_action: UseAction::PlaceTile {
layer: TileLayer::Mid,
id: 5,
use_action: UseAction::PlaceMidTile {
id: MidTileId::PLATFORM,
},
consumable: true,
},
@ -119,9 +121,8 @@ impl Default for ItemDb {
name: String::from("Panzerium"),
graphic_name: String::from("tiles/panzerium"),
tex_rect: IntRect::default(),
use_action: UseAction::PlaceTile {
layer: TileLayer::Mid,
id: 6,
use_action: UseAction::PlaceMidTile {
id: MidTileId::PANZERIUM,
},
consumable: true,
},

View file

@ -1,26 +1,186 @@
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 crate::{
graphics::{ScreenSc, ScreenVec},
graphics::ScreenVec,
math::{IntRect, TILE_SIZE},
texture_atlas::RectMap,
world::TileId,
};
#[derive(Serialize, Deserialize, Default, Debug, Inspect)]
pub struct TileDef {
pub bb: Option<TileBb>,
#[derive(Inspect, PartialEq, Eq)]
pub struct TileId<Layer>(pub u16, PhantomData<Layer>);
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
pub light: Option<ScreenVec>,
/// Platform behavior: Horizontally passable, vertically passable upwards
pub platform: bool,
pub graphic_name: String,
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 {
@ -40,49 +200,54 @@ pub struct TileBb {
#[derive(Serialize, Deserialize, Debug, Inspect)]
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 {
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 {
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 {
bb: None,
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<BgTileId> for TileDb {
type Output = TileDef<Bg>;
impl Index<TileId> for TileDb {
type Output = TileDef;
fn index(&self, index: BgTileId) -> &Self::Output {
self.bg
.get(index.0 as usize - 1)
.unwrap_or(&self.unknown_bg)
}
}
fn index(&self, index: TileId) -> &Self::Output {
self.db.get(index as usize).unwrap_or_else(|| {
&self.db[1] // Unknown tile def is stored at index 1
})
impl Index<MidTileId> for TileDb {
type Output = TileDef<Mid>;
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) {
for def in &mut self.db {
if !def.graphic_name.is_empty() {
if let Some(rect) = rects.get(&def.graphic_name) {
def.tex_rect = *rect;
} else {
log::error!("Missing texture for {}", def.graphic_name);
}
}
}
update_rect_def(&mut self.unknown_bg, rects);
update_rect_def(&mut self.unknown_mid, rects);
update_rect_def(&mut self.unknown_fg, rects);
update_rect_db(&mut self.bg, rects);
update_rect_db(&mut self.mid, rects);
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::{
graphics::{ScreenSc, ScreenVec},
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) {
egui::Window::new("Tiledb editor").show(ctx, |ui| {
ui.label(format!("Number of tile defs: {}", tile_db.db.len()));
ui.separator();
egui::ScrollArea::vertical()
.max_height(400.0)
.show(ui, |ui| {
for (i, def) in tile_db.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,
});
}
}
}
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.heading("Bg");
db_ui(&mut tile_db.bg, ui);
ui.heading("Mid");
db_ui(&mut tile_db.mid, ui);
ui.heading("Fg");
db_ui(&mut tile_db.fg, ui);
});
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 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;
@ -203,9 +207,9 @@ type ChunkTiles = [Tile; CHUNK_N_TILES];
fn default_chunk_tiles() -> ChunkTiles {
[Tile {
bg: 0,
mid: 0,
fg: 0,
bg: TileId::EMPTY,
mid: TileId::EMPTY,
fg: TileId::EMPTY,
}; CHUNK_N_TILES]
}
@ -229,7 +233,7 @@ impl Chunk {
// Unbreakable layer at bottom
if pos.y > 798 {
for b in &mut tiles {
b.mid = Tile::UNBREAKANIUM;
b.mid = MidTileId::UNBREAKANIUM;
}
}
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)
}
pub type TileId = u16;
#[derive(Clone, Copy, Debug, Inspect)]
pub struct Tile {
/// Background wall behind entities
pub bg: TileId,
pub bg: BgTileId,
/// 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.
pub fg: TileId,
}
impl Tile {
pub const EMPTY: TileId = 0;
pub const UNBREAKANIUM: TileId = 5;
pub fg: FgTileId,
}
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);
for (i, tile) in chk.tiles.iter().enumerate() {
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 + 2..off + 4].copy_from_slice(&tile.mid.to_le_bytes());
region_tile_data[off + 4..off + 6].copy_from_slice(&tile.fg.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.0.to_le_bytes());
region_tile_data[off + 4..off + 6].copy_from_slice(&tile.fg.0.to_le_bytes());
}
f.rewind().unwrap();
f.write_all(&u64::to_le_bytes(existence_bitset.0)[..])
@ -65,9 +65,9 @@ impl Chunk {
let mut tiles = default_chunk_tiles();
for (i, t) in tiles.iter_mut().enumerate() {
let off = byte_idx + (i * TILE_BYTES);
t.bg = 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.fg = u16::from_le_bytes(data[off + 4..off + 6].try_into().unwrap());
t.bg.0 = u16::from_le_bytes(data[off..off + 2].try_into().unwrap());
t.mid.0 = u16::from_le_bytes(data[off + 2..off + 4].try_into().unwrap());
t.fg.0 = u16::from_le_bytes(data[off + 4..off + 6].try_into().unwrap());
}
Self { tiles }
}
@ -83,7 +83,7 @@ fn test_chunk_seri() {
tiles: super::default_chunk_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: 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 {
world: World<crate::world::Tile>,
@ -33,35 +36,35 @@ impl Worldgen {
// Dirt coal
.add(
Tile::new(Tl {
bg: 9,
mid: 3,
fg: 6,
bg: BgTileId::DIRT,
mid: MidTileId::DIRT,
fg: FgTileId::COAL,
})
.when(constraint!(nm.clone(), < -0.8)),
)
// Dirt
.add(
Tile::new(Tl {
bg: 9,
mid: 3,
fg: 0,
bg: BgTileId::DIRT,
mid: MidTileId::DIRT,
fg: TileId::EMPTY,
})
.when(constraint!(nm.clone(), < -0.1)),
)
// Stone
.add(
Tile::new(Tl {
bg: 7,
mid: 2,
fg: 0,
bg: BgTileId::STONE,
mid: MidTileId::STONE,
fg: TileId::EMPTY,
})
.when(constraint!(nm, < 0.45)),
)
// Dirt wall
.add(Tile::new(Tl {
bg: 9,
mid: 0,
fg: 0,
bg: BgTileId::DIRT,
mid: TileId::EMPTY,
fg: TileId::EMPTY,
}));
Self { world }
}

BIN
tiles.dat

Binary file not shown.