room-creation #15
36
Cargo.lock
generated
|
@ -242,7 +242,7 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"shlex",
|
"shlex",
|
||||||
"syn 2.0.68",
|
"syn 2.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -326,7 +326,7 @@ checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.68",
|
"syn 2.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -369,9 +369,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.104"
|
version = "1.0.105"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "74b6a57f98764a267ff415d50a25e6e166f3831a5071af4995296ea97d210490"
|
checksum = "5208975e568d83b6b05cc0a063c8e7e9acc2b43bee6da15616a5b73e109d7437"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -1111,7 +1111,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.68",
|
"syn 2.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1897,7 +1897,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.68",
|
"syn 2.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1927,7 +1927,7 @@ dependencies = [
|
||||||
"proc-macro-crate",
|
"proc-macro-crate",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.68",
|
"syn 2.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2428,22 +2428,22 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.203"
|
version = "1.0.204"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094"
|
checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.203"
|
version = "1.0.204"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba"
|
checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.68",
|
"syn 2.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2703,9 +2703,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.68"
|
version = "2.0.69"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9"
|
checksum = "201fcda3845c23e8212cd466bfebf0bd20694490fc0356ae8e428e0824a915a6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2738,7 +2738,7 @@ checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.68",
|
"syn 2.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2978,7 +2978,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.68",
|
"syn 2.0.69",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3012,7 +3012,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.68",
|
"syn 2.0.69",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
@ -3696,7 +3696,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.68",
|
"syn 2.0.69",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
BIN
assets/entities/ghost.png
Normal file
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
BIN
assets/furniture/bedroom/ac.png
Normal file
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
BIN
assets/furniture/bedroom/bed.png
Normal file
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
BIN
assets/furniture/bedroom/bookshelf.png
Normal file
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 8.5 KiB |
BIN
assets/furniture/bedroom/couch.png
Normal file
Before Width: | Height: | Size: 8 KiB After Width: | Height: | Size: 8 KiB |
BIN
assets/furniture/bedroom/cupboard.png
Normal file
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
BIN
assets/furniture/bedroom/display_off.png
Normal file
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
BIN
assets/furniture/bedroom/display_on.png
Normal file
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
BIN
assets/furniture/bedroom/drawer.png
Normal file
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
BIN
assets/furniture/bedroom/elec/ac.png
Normal file
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
BIN
assets/furniture/bedroom/elec/display_off.png
Normal file
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
BIN
assets/furniture/bedroom/elec/display_on.png
Normal file
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
BIN
assets/furniture/bedroom/elec/keyboard.png
Normal file
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/elec/laptop_off.png
Normal file
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/elec/laptop_on.png
Normal file
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/elec/mini_ac.png
Normal file
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
BIN
assets/furniture/bedroom/elec/mouse.png
Normal file
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/elec/nightlight.png
Normal file
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
BIN
assets/furniture/bedroom/elec/pc.png
Normal file
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/elec/phone.png
Normal file
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/elec/receiver.png
Normal file
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/elec/tv.png
Normal file
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
BIN
assets/furniture/bedroom/elec/window_ac.png
Normal file
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
BIN
assets/furniture/bedroom/keyboard.png
Normal file
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/laptop_off.png
Normal file
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
BIN
assets/furniture/bedroom/laptop_on.png
Normal file
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
BIN
assets/furniture/bedroom/metal/ac.png
Normal file
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
BIN
assets/furniture/bedroom/metal/bed.png
Normal file
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6 KiB |
BIN
assets/furniture/bedroom/metal/cupboard.png
Normal file
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
BIN
assets/furniture/bedroom/metal/display_off.png
Normal file
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/metal/display_on.png
Normal file
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
BIN
assets/furniture/bedroom/metal/drawer.png
Normal file
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
BIN
assets/furniture/bedroom/metal/keyboard.png
Normal file
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/metal/laptop_off.png
Normal file
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
BIN
assets/furniture/bedroom/metal/laptop_on.png
Normal file
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
BIN
assets/furniture/bedroom/metal/mini_ac.png
Normal file
Before Width: | Height: | Size: 6 KiB After Width: | Height: | Size: 6 KiB |
BIN
assets/furniture/bedroom/metal/mouse.png
Normal file
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
BIN
assets/furniture/bedroom/metal/nightlight.png
Normal file
Before Width: | Height: | Size: 5 KiB After Width: | Height: | Size: 5 KiB |
BIN
assets/furniture/bedroom/metal/nightstand.png
Normal file
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
BIN
assets/furniture/bedroom/metal/pc.png
Normal file
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
BIN
assets/furniture/bedroom/metal/phone.png
Normal file
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/metal/receiver.png
Normal file
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
BIN
assets/furniture/bedroom/metal/tv.png
Normal file
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
BIN
assets/furniture/bedroom/metal/wardrobe.png
Normal file
Before Width: | Height: | Size: 7 KiB After Width: | Height: | Size: 7 KiB |
BIN
assets/furniture/bedroom/metal/window_ac.png
Normal file
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
BIN
assets/furniture/bedroom/mini_ac.png
Normal file
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
BIN
assets/furniture/bedroom/mouse.png
Normal file
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
BIN
assets/furniture/bedroom/nightlight.png
Normal file
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
BIN
assets/furniture/bedroom/nightstand.png
Normal file
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.4 KiB |
BIN
assets/furniture/bedroom/pc.png
Normal file
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
BIN
assets/furniture/bedroom/phone.png
Normal file
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
BIN
assets/furniture/bedroom/receiver.png
Normal file
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
BIN
assets/furniture/bedroom/tv.png
Normal file
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.9 KiB |
BIN
assets/furniture/bedroom/tv_stand.png
Normal file
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 4.7 KiB |
BIN
assets/furniture/bedroom/wardrobe.png
Normal file
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
BIN
assets/furniture/bedroom/window.png
Normal file
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 9.4 KiB |
BIN
assets/furniture/bedroom/window_ac.png
Normal file
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
BIN
assets/music/Galactic_Rap.ogg
Normal file
BIN
assets/music/Galactic_Rap_Destroyed.ogg
Normal file
BIN
assets/music/Galactic_Rap_Speedup.ogg
Normal file
BIN
assets/music/Mesmerizing_Galaxy_Broken.ogg
Normal file
BIN
assets/music/Mesmerizing_Galaxy_Loop.ogg
Normal file
BIN
assets/sounds/bzz.ogg
Normal file
81
build.rs
|
@ -17,6 +17,9 @@ struct Assets {
|
||||||
/// of the constant storing their png.
|
/// of the constant storing their png.
|
||||||
assets: BTreeMap<String, String>,
|
assets: BTreeMap<String, String>,
|
||||||
|
|
||||||
|
/// Sound assets.
|
||||||
|
sound_assets: BTreeMap<String, String>,
|
||||||
|
|
||||||
/// Asset groups contained within this asset group, mapping their name to the assets
|
/// Asset groups contained within this asset group, mapping their name to the assets
|
||||||
/// that group contains.
|
/// that group contains.
|
||||||
groups: BTreeMap<String, Box<Assets>>
|
groups: BTreeMap<String, Box<Assets>>
|
||||||
|
@ -37,15 +40,19 @@ impl AssetsWriter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_png<P: AsRef<Path>>(
|
fn asset_name<P: AsRef<Path>>(canonical_path: P) -> String {
|
||||||
|
let mut hasher = DefaultHasher::new();
|
||||||
|
canonical_path.as_ref().hash(&mut hasher);
|
||||||
|
let hash = hasher.finish();
|
||||||
|
format!("ASSET_{hash:X}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_png<P: AsRef<Path> + Copy>(
|
||||||
&mut self,
|
&mut self,
|
||||||
canonical_path: P,
|
canonical_path: P,
|
||||||
png: tiny_skia::Pixmap
|
png: tiny_skia::Pixmap
|
||||||
) -> String {
|
) -> String {
|
||||||
let mut hasher = DefaultHasher::new();
|
let const_name = Self::asset_name(canonical_path);
|
||||||
canonical_path.as_ref().hash(&mut hasher);
|
|
||||||
let hash = hasher.finish();
|
|
||||||
let const_name = format!("ASSET_{hash:X}");
|
|
||||||
|
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
let out_dir: PathBuf = out_dir.into();
|
let out_dir: PathBuf = out_dir.into();
|
||||||
|
@ -62,6 +69,18 @@ impl AssetsWriter {
|
||||||
const_name
|
const_name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_ogg<P: AsRef<Path> + Copy>(&mut self, canonical_path: P) -> String {
|
||||||
|
let const_name = Self::asset_name(canonical_path);
|
||||||
|
writeln!(self.file, "// {}", canonical_path.as_ref().display()).unwrap();
|
||||||
|
writeln!(
|
||||||
|
self.file,
|
||||||
|
"const {const_name}: &[u8] = include_bytes!(\"{}\");",
|
||||||
|
canonical_path.as_ref().display()
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
const_name
|
||||||
|
}
|
||||||
|
|
||||||
fn finish(mut self) {
|
fn finish(mut self) {
|
||||||
fn write_assets_struct(
|
fn write_assets_struct(
|
||||||
file: &mut File,
|
file: &mut File,
|
||||||
|
@ -85,6 +104,13 @@ impl AssetsWriter {
|
||||||
asset_name.to_snake_case()
|
asset_name.to_snake_case()
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
for asset_name in root.sound_assets.keys() {
|
||||||
|
writeln!(
|
||||||
|
file,
|
||||||
|
"{indent}\tpub {}: comfy::Sound,",
|
||||||
|
asset_name.to_snake_case()
|
||||||
|
)?;
|
||||||
|
}
|
||||||
for group_name in root.groups.keys() {
|
for group_name in root.groups.keys() {
|
||||||
writeln!(
|
writeln!(
|
||||||
file,
|
file,
|
||||||
|
@ -101,6 +127,9 @@ impl AssetsWriter {
|
||||||
for asset_const_name in root.assets.values() {
|
for asset_const_name in root.assets.values() {
|
||||||
writeln!(file, "{indent}\t\tc.load_texture_from_bytes({asset_const_name:?}, {asset_const_name});")?;
|
writeln!(file, "{indent}\t\tc.load_texture_from_bytes({asset_const_name:?}, {asset_const_name});")?;
|
||||||
}
|
}
|
||||||
|
for asset_const_name in root.sound_assets.values() {
|
||||||
|
writeln!(file, "{indent}\t\tcomfy::load_sound_from_bytes({asset_const_name:?}, {asset_const_name}, Default::default());")?;
|
||||||
|
}
|
||||||
for group_name in root.groups.keys() {
|
for group_name in root.groups.keys() {
|
||||||
writeln!(file, "{indent}\t\t{group_name}::Assets::load(c);")?;
|
writeln!(file, "{indent}\t\t{group_name}::Assets::load(c);")?;
|
||||||
}
|
}
|
||||||
|
@ -118,6 +147,13 @@ impl AssetsWriter {
|
||||||
asset_name.to_snake_case()
|
asset_name.to_snake_case()
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
for (asset_name, asset_const_name) in &root.sound_assets {
|
||||||
|
writeln!(
|
||||||
|
file,
|
||||||
|
"{indent}\t{}: comfy::sound_id({asset_const_name:?}),",
|
||||||
|
asset_name.to_snake_case()
|
||||||
|
)?;
|
||||||
|
}
|
||||||
for group_name in root.groups.keys() {
|
for group_name in root.groups.keys() {
|
||||||
writeln!(
|
writeln!(
|
||||||
file,
|
file,
|
||||||
|
@ -170,6 +206,8 @@ fn process_dir<P: AsRef<Path> + Copy>(
|
||||||
groups.pop();
|
groups.pop();
|
||||||
} else if path.extension().map(|ext| ext == "svg").unwrap_or(false) {
|
} else if path.extension().map(|ext| ext == "svg").unwrap_or(false) {
|
||||||
process_svg(&path, dir, writer, groups);
|
process_svg(&path, dir, writer, groups);
|
||||||
|
} else if path.extension().map(|ext| ext == "ogg").unwrap_or(false) {
|
||||||
|
process_ogg(&path, dir, writer, groups);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,7 +234,8 @@ fn process_svg<P: AsRef<Path> + Copy, Q: AsRef<Path>>(
|
||||||
resvg::render(&tree, transform, &mut pixmap.as_mut());
|
resvg::render(&tree, transform, &mut pixmap.as_mut());
|
||||||
|
|
||||||
let const_name = writer.add_png(
|
let const_name = writer.add_png(
|
||||||
file.as_ref()
|
&file
|
||||||
|
.as_ref()
|
||||||
.canonicalize()
|
.canonicalize()
|
||||||
.expect("Failed to canonicalize"),
|
.expect("Failed to canonicalize"),
|
||||||
pixmap
|
pixmap
|
||||||
|
@ -218,3 +257,33 @@ fn process_svg<P: AsRef<Path> + Copy, Q: AsRef<Path>>(
|
||||||
const_name
|
const_name
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_ogg<P: AsRef<Path> + Copy, Q: AsRef<Path>>(
|
||||||
|
file: P,
|
||||||
|
dir: Q,
|
||||||
|
writer: &mut AssetsWriter,
|
||||||
|
groups: &[String]
|
||||||
|
) {
|
||||||
|
let const_name = writer.add_ogg(
|
||||||
|
&file
|
||||||
|
.as_ref()
|
||||||
|
.canonicalize()
|
||||||
|
.expect("Failed to canonicalize")
|
||||||
|
);
|
||||||
|
let mut group = &mut writer.root;
|
||||||
|
for group_name in groups {
|
||||||
|
if !group.groups.contains_key(group_name) {
|
||||||
|
group.groups.insert(group_name.to_owned(), Box::default());
|
||||||
|
}
|
||||||
|
group = group.groups.get_mut(group_name).unwrap();
|
||||||
|
}
|
||||||
|
group.sound_assets.insert(
|
||||||
|
file.as_ref()
|
||||||
|
.file_stem()
|
||||||
|
.expect("File doesn't have a stem")
|
||||||
|
.to_str()
|
||||||
|
.expect("Non-UTF8 file names aren't allowed")
|
||||||
|
.into(),
|
||||||
|
const_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -21,63 +21,68 @@ pub struct HouseState {
|
||||||
human_layer: bool //Human, magnetic, electric
|
human_layer: bool //Human, magnetic, electric
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn setup(state: &mut crate::State, ctx: &mut EngineContext<'_>) {
|
impl HouseState {
|
||||||
let house = {
|
pub fn generate_new_house(ctx: &mut EngineContext<'_>) -> Self {
|
||||||
let room_count = random_i32(2, MAX_ROOMS) as usize;
|
let room_count = random_i32(2, MAX_ROOMS) as usize;
|
||||||
|
|
||||||
let mut rooms = Vec::new();
|
let mut rooms = Vec::new();
|
||||||
for _ in 0..room_count {
|
for _ in 0 .. room_count {
|
||||||
rooms.push(Room::new(ctx));
|
rooms.push(Room::new(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
let player = Player::new(rooms.first().unwrap());
|
let player = Player::new(rooms.first().unwrap());
|
||||||
HouseState { current_room_id: 0, room_count,
|
HouseState {
|
||||||
|
current_room_id: 0,
|
||||||
|
room_count,
|
||||||
rooms,
|
rooms,
|
||||||
player,
|
player,
|
||||||
human_layer: false
|
human_layer: false
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
state.house = Some(house);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(state: &crate::State, _ctx: &comfy::EngineContext<'_>) {
|
pub fn draw(state: &crate::State, _ctx: &comfy::EngineContext<'_>) {
|
||||||
if let Some(house) = &state.house {
|
let Some(house) = state.house() else {
|
||||||
//Draw House
|
error!("How can I render a house when I'm not in one?!?");
|
||||||
house
|
return;
|
||||||
.rooms.get(house.current_room_id).unwrap().draw(house.human_layer, house.player.can_see_metal(0.1));
|
};
|
||||||
|
|
||||||
//Draw Grid
|
//Draw House
|
||||||
//state.house.grid.draw();
|
house
|
||||||
|
.rooms
|
||||||
|
.get(house.current_room_id)
|
||||||
|
.unwrap()
|
||||||
|
.draw(house.human_layer, house.player.can_see_metal(0.1));
|
||||||
|
|
||||||
//Draw Player
|
//Draw Grid
|
||||||
house.player.draw();
|
//state.house.grid.draw();
|
||||||
}
|
|
||||||
|
//Draw Player
|
||||||
|
house.player.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(state: &mut crate::State, ctx: &mut comfy::EngineContext<'_>) {
|
pub fn update(state: &mut crate::State, ctx: &mut comfy::EngineContext<'_>) {
|
||||||
if let Some(house) = &mut state.house {
|
let house = state.house_mut(ctx);
|
||||||
let current_room = house.rooms.get(house.current_room_id).unwrap();
|
let current_room = house.rooms.get(house.current_room_id).unwrap();
|
||||||
house.player.update(¤t_room.grid);
|
house.player.update(¤t_room.grid);
|
||||||
|
|
||||||
if house.player.is_moving_to_right_room(current_room) {
|
if house.player.is_moving_to_right_room(current_room) {
|
||||||
if house.current_room_id < (house.room_count - 1) {
|
if house.current_room_id < (house.room_count - 1) {
|
||||||
house.current_room_id +=1;
|
house.current_room_id += 1;
|
||||||
|
|
||||||
let current_room = house.rooms.get(house.current_room_id).unwrap();
|
let current_room = house.rooms.get(house.current_room_id).unwrap();
|
||||||
house.player.reset_on_room(current_room, true);
|
house.player.reset_on_room(current_room, true);
|
||||||
} else {
|
} else {
|
||||||
house.player.reset_on_room(current_room, false);
|
house.player.reset_on_room(current_room, false);
|
||||||
}
|
}
|
||||||
} else if house.player.is_moving_to_left_room(current_room) {
|
} else if house.player.is_moving_to_left_room(current_room) {
|
||||||
if house.current_room_id > 0 {
|
if house.current_room_id > 0 {
|
||||||
house.current_room_id -=1;
|
house.current_room_id -= 1;
|
||||||
|
|
||||||
let current_room = house.rooms.get(house.current_room_id).unwrap();
|
let current_room = house.rooms.get(house.current_room_id).unwrap();
|
||||||
house.player.reset_on_room(current_room, false);
|
house.player.reset_on_room(current_room, false);
|
||||||
} else {
|
} else {
|
||||||
house.player.reset_on_room(current_room, true);
|
house.player.reset_on_room(current_room, true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,13 +70,24 @@ impl Player {
|
||||||
} else {
|
} else {
|
||||||
let mut current_index = 0;
|
let mut current_index = 0;
|
||||||
for (i, node_pos) in room.grid.nodes.iter().enumerate().rev() {
|
for (i, node_pos) in room.grid.nodes.iter().enumerate().rev() {
|
||||||
if in_node_range(&vec2((room.size.0 as f32 / 2.0 + 0.5) * SCALE, self.position.y), node_pos, 1.0) {
|
if in_node_range(
|
||||||
|
&vec2((room.size.0 as f32 / 2.0 + 0.5) * SCALE, self.position.y),
|
||||||
|
node_pos,
|
||||||
|
1.0
|
||||||
|
) {
|
||||||
current_index = i;
|
current_index = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let connction_index = room.grid.connections.iter().enumerate().find(|(_i, (_node_1, node_2))| *node_2 == current_index).map(|(i, _)| i).unwrap();
|
let connction_index = room
|
||||||
|
.grid
|
||||||
|
.connections
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find(|(_i, (_node_1, node_2))| *node_2 == current_index)
|
||||||
|
.map(|(i, _)| i)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
self.connection = connction_index;
|
self.connection = connction_index;
|
||||||
self.next_connections = vec![connction_index - 1];
|
self.next_connections = vec![connction_index - 1];
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ struct Tile {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Room {
|
pub struct Room {
|
||||||
room_type: RoomType,
|
_room_type: RoomType,
|
||||||
pub size: (u8, u8), //(width, height)
|
pub size: (u8, u8), //(width, height)
|
||||||
pub grid: Grid,
|
pub grid: Grid,
|
||||||
furnitures: Vec<Tile>
|
furnitures: Vec<Tile>
|
||||||
|
@ -54,7 +54,7 @@ impl Room {
|
||||||
let furnitures = Self::random_room_furniture(&room_type, size, ctx);
|
let furnitures = Self::random_room_furniture(&room_type, size, ctx);
|
||||||
|
|
||||||
Room {
|
Room {
|
||||||
room_type,
|
_room_type: room_type,
|
||||||
size,
|
size,
|
||||||
grid: Self::create_grid(size.0, size.1),
|
grid: Self::create_grid(size.0, size.1),
|
||||||
furnitures
|
furnitures
|
||||||
|
@ -80,23 +80,29 @@ impl Room {
|
||||||
let mut furnitures = Vec::new();
|
let mut furnitures = Vec::new();
|
||||||
|
|
||||||
let mut empty_spots: IndexSet<u8> = (0 .. width).collect();
|
let mut empty_spots: IndexSet<u8> = (0 .. width).collect();
|
||||||
let mut assets_used: HashSet<&'static str> = HashSet::new();
|
|
||||||
|
|
||||||
fn random_empty_spot(empty_spots: &mut IndexSet<u8>) -> Option<u8> {
|
fn random_empty_spot<T>(empty_spots: &mut IndexSet<T>) -> Option<T> {
|
||||||
if empty_spots.is_empty() {
|
if empty_spots.is_empty() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let random_idx = usize::gen_range(0, empty_spots.len());
|
let random_idx = usize::gen_range(0, empty_spots.len());
|
||||||
empty_spots.swap_remove_index(random_idx)
|
empty_spots.swap_remove_index(random_idx)
|
||||||
}
|
}
|
||||||
|
fn random_appliance<T>(empty_spots: &mut Vec<T>) -> Option<T> {
|
||||||
|
if empty_spots.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let random_idx = usize::gen_range(0, empty_spots.len());
|
||||||
|
Some(empty_spots.swap_remove(random_idx))
|
||||||
|
}
|
||||||
|
|
||||||
const SIDEBOARD_HEIGHT: f32 = 0.1;
|
const SIDEBOARD_HEIGHT: f32 = 0.1;
|
||||||
const STOVE_HEIGHT: f32 = 0.025;
|
const STOVE_HEIGHT: f32 = 0.025;
|
||||||
const SINK_HEIGHT: f32 = 0.5;
|
const SINK_HEIGHT: f32 = 0.5;
|
||||||
|
|
||||||
#[allow(clippy::single_match, unreachable_patterns)] // we'll add more stuff later
|
#[allow(clippy::single_match)] // we'll add more stuff later
|
||||||
match room_type {
|
match room_type {
|
||||||
_ => {
|
RoomType::Kitchen => {
|
||||||
// in a kitchen, we always add a fridge
|
// in a kitchen, we always add a fridge
|
||||||
let fridge_pos = u8::gen_range(0, 2) * (width - 1);
|
let fridge_pos = u8::gen_range(0, 2) * (width - 1);
|
||||||
empty_spots.swap_remove(&fridge_pos);
|
empty_spots.swap_remove(&fridge_pos);
|
||||||
|
@ -159,12 +165,21 @@ impl Room {
|
||||||
z: 1
|
z: 1
|
||||||
});
|
});
|
||||||
|
|
||||||
// let's add half of the remaining positions as drawers
|
// the current list of empty spots is the same list we can use to place
|
||||||
|
// on-the-counter appliances later
|
||||||
|
let mut empty_spots_clone = empty_spots.clone();
|
||||||
|
|
||||||
|
// build a list of the remaining kitchen appliances. we only want them
|
||||||
|
// included once, most kitchens don't contain two washing machines etc
|
||||||
|
let mut remaining_appliances: IndexSet<&'static str> =
|
||||||
|
["dishwasher", "dryer", "minifridge", "washing_machine"]
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// let's add at most half of the remaining positions as big appliances
|
||||||
for _ in 0 .. empty_spots.len() / 2 {
|
for _ in 0 .. empty_spots.len() / 2 {
|
||||||
let asset = match u8::gen_range(0, 2) {
|
let Some(asset) = random_empty_spot(&mut remaining_appliances) else {
|
||||||
0 => "drawer",
|
break;
|
||||||
1 => "drawer_cupboard",
|
|
||||||
_ => unreachable!()
|
|
||||||
};
|
};
|
||||||
let Some(spot) = random_empty_spot(&mut empty_spots) else {
|
let Some(spot) = random_empty_spot(&mut empty_spots) else {
|
||||||
error!("WTF I shouldn't've used more than half of the available spots");
|
error!("WTF I shouldn't've used more than half of the available spots");
|
||||||
|
@ -183,6 +198,60 @@ impl Room {
|
||||||
z: 1
|
z: 1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// and fill the remainder with drawers
|
||||||
|
while !empty_spots.is_empty() {
|
||||||
|
let asset = match u8::gen_range(0, 2) {
|
||||||
|
0 => "drawer",
|
||||||
|
1 => "drawer_cupboard",
|
||||||
|
_ => unreachable!()
|
||||||
|
};
|
||||||
|
let Some(spot) = random_empty_spot(&mut empty_spots) else {
|
||||||
|
error!("WTF I should still have spots available");
|
||||||
|
return furnitures;
|
||||||
|
};
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(spot as f32, 0.0),
|
||||||
|
size: vec2(1.0, 1.0),
|
||||||
|
f: Furniture::new("kitchen", asset, ctx),
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(spot as f32, 1.0),
|
||||||
|
size: vec2(1.0, SIDEBOARD_HEIGHT),
|
||||||
|
f: Furniture::new("kitchen", "sideboard_1", ctx),
|
||||||
|
z: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// build a list of on-the-counter kitchen appliances. we only want them
|
||||||
|
// included once, most kitchens don't contain two toasters etc
|
||||||
|
let mut remaining_appliances: Vec<(&'static str, f32, f32)> = [
|
||||||
|
("blender", 0.3, 0.45),
|
||||||
|
("kettle", 0.3, 0.4),
|
||||||
|
("toaster", 0.5, 0.25)
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// and then we fill like half the counter with appliances
|
||||||
|
for _ in 0 .. empty_spots_clone.len() / 2 {
|
||||||
|
let Some((asset, asset_w, asset_h)) =
|
||||||
|
random_appliance(&mut remaining_appliances)
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
let Some(spot) = random_empty_spot(&mut empty_spots_clone) else {
|
||||||
|
error!("WTF I shouldn't've used more than half of the available spots");
|
||||||
|
return furnitures;
|
||||||
|
};
|
||||||
|
furnitures.push(Tile {
|
||||||
|
pos: vec2(spot as f32 + 0.5, 1.0 + SIDEBOARD_HEIGHT),
|
||||||
|
size: vec2(asset_w, asset_h),
|
||||||
|
f: Furniture::new("kitchen", asset, ctx),
|
||||||
|
z: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => {}
|
_ => {}
|
||||||
|
@ -241,7 +310,6 @@ impl Room {
|
||||||
Grid::new(nodes, connections)
|
Grid::new(nodes, connections)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Human, magnetic, electric
|
|
||||||
pub fn draw(&self, human_layer: bool, magnet_layer: bool) {
|
pub fn draw(&self, human_layer: bool, magnet_layer: bool) {
|
||||||
let (width, height) = self.size;
|
let (width, height) = self.size;
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
|
use comfy::IVec2;
|
||||||
|
|
||||||
pub mod house;
|
pub mod house;
|
||||||
pub mod overworld;
|
pub mod overworld;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub enum Activity {
|
pub enum Activity {
|
||||||
|
House(IVec2),
|
||||||
#[default]
|
#[default]
|
||||||
House,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
Overworld
|
Overworld
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
|
use crate::{activities::Activity, game::ZLayer, State};
|
||||||
use comfy::{
|
use comfy::{
|
||||||
draw_rect_outline, draw_sprite, main_camera_mut, EngineContext, IVec2, Vec2, RED,
|
draw_circle, draw_rect_outline, draw_sprite, error, info, is_key_down,
|
||||||
WHITE
|
main_camera_mut, EngineContext, IVec2, KeyCode, Vec2, RED, WHITE
|
||||||
};
|
};
|
||||||
|
use std::time::Instant;
|
||||||
use crate::game::ZLayer;
|
use worldgen::MovementCost;
|
||||||
|
|
||||||
pub mod worldgen;
|
pub mod worldgen;
|
||||||
|
|
||||||
|
@ -21,11 +22,172 @@ pub fn draw(state: &crate::State, _engine: &comfy::EngineContext<'_>) {
|
||||||
draw_rect_outline(coords.as_vec2(), Vec2::ONE, 0.1, RED, 10);
|
draw_rect_outline(coords.as_vec2(), Vec2::ONE, 0.1, RED, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
draw_circle(state.ghost.overworld_pos, 0.5, RED, ZLayer::Ghost.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(state: &mut crate::State, _engine: &mut EngineContext<'_>) {
|
fn update_move_player(state: &mut State, ctx: &mut EngineContext<'_>) {
|
||||||
let mut camera = main_camera_mut();
|
let now = Instant::now();
|
||||||
camera.center = Vec2::ZERO;
|
|
||||||
camera.zoom = 30.0;
|
// Are there any pending position updates? If so, we ignore all user input and execute
|
||||||
state.overworld.get_or_generate_tile(IVec2::ZERO);
|
// the pending updates.
|
||||||
|
if state.ghost.overworld_movement_pending != Vec2::ZERO {
|
||||||
|
info!(
|
||||||
|
"Pending Movement: {:?}",
|
||||||
|
state.ghost.overworld_movement_pending
|
||||||
|
);
|
||||||
|
state.ghost.update_overworld_pos(now);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we check for user inputs, and update the pending movement accordingly.
|
||||||
|
let tile_pos = IVec2 {
|
||||||
|
x: state.ghost.overworld_pos.x.round() as _,
|
||||||
|
y: state.ghost.overworld_pos.y.round() as _
|
||||||
|
};
|
||||||
|
let Some(tile) = state.overworld.get_tile(tile_pos).copied() else {
|
||||||
|
error!("How can we be standing inside a non-generated tile?");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut requested_pos = None;
|
||||||
|
let mut requested_pos_diff = None;
|
||||||
|
let mut requested_cost_curr = None;
|
||||||
|
let mut requested_cost_new = None;
|
||||||
|
|
||||||
|
if is_key_down(KeyCode::Up) {
|
||||||
|
let diff = IVec2 { x: 0, y: 1 };
|
||||||
|
let new_pos = tile_pos + diff;
|
||||||
|
let new_tile = state.overworld.get_or_generate_tile(new_pos);
|
||||||
|
if new_tile.can_stand_inside() {
|
||||||
|
requested_pos = Some(new_pos);
|
||||||
|
requested_pos_diff = Some(diff);
|
||||||
|
requested_cost_curr = Some(tile.movement_cost_up());
|
||||||
|
requested_cost_new = Some(new_tile.movement_cost_down());
|
||||||
|
} else {
|
||||||
|
info!("Rejecting movement - cannot stand in the requested tile.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_key_down(KeyCode::Down) {
|
||||||
|
let diff = IVec2 { x: 0, y: -1 };
|
||||||
|
let new_pos = tile_pos + diff;
|
||||||
|
let new_tile = state.overworld.get_or_generate_tile(new_pos);
|
||||||
|
if new_tile.can_stand_inside() {
|
||||||
|
requested_pos = Some(new_pos);
|
||||||
|
requested_pos_diff = Some(diff);
|
||||||
|
requested_cost_curr = Some(tile.movement_cost_down());
|
||||||
|
requested_cost_new = Some(new_tile.movement_cost_up());
|
||||||
|
} else {
|
||||||
|
info!("Rejecting movement - cannot stand in the requested tile.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_key_down(KeyCode::Left) {
|
||||||
|
let diff = IVec2 { x: -1, y: 0 };
|
||||||
|
let new_pos = tile_pos + diff;
|
||||||
|
let new_tile = state.overworld.get_or_generate_tile(new_pos);
|
||||||
|
if new_tile.can_stand_inside() {
|
||||||
|
requested_pos = Some(new_pos);
|
||||||
|
requested_pos_diff = Some(diff);
|
||||||
|
requested_cost_curr = Some(tile.movement_cost_left());
|
||||||
|
requested_cost_new = Some(new_tile.movement_cost_right());
|
||||||
|
} else {
|
||||||
|
info!("Rejecting movement - cannot stand in the requested tile.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_key_down(KeyCode::Right) {
|
||||||
|
let diff = IVec2 { x: 1, y: 0 };
|
||||||
|
let new_pos = tile_pos + diff;
|
||||||
|
let new_tile = state.overworld.get_or_generate_tile(new_pos);
|
||||||
|
if new_tile.can_stand_inside() {
|
||||||
|
requested_pos = Some(new_pos);
|
||||||
|
requested_pos_diff = Some(diff);
|
||||||
|
requested_cost_curr = Some(tile.movement_cost_right());
|
||||||
|
requested_cost_new = Some(new_tile.movement_cost_left());
|
||||||
|
} else {
|
||||||
|
info!("Rejecting movement - cannot stand in the requested tile.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only continue if some movement was requested
|
||||||
|
let Some(_requested_pos) = requested_pos else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(requested_pos_diff) = requested_pos_diff else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(requested_cost_curr) = requested_cost_curr else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(requested_cost_new) = requested_cost_new else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
state.ghost.overworld_movement_speed = match (requested_cost_curr, requested_cost_new)
|
||||||
|
{
|
||||||
|
// movement in this direction not possible
|
||||||
|
(MovementCost::Infinite, _) | (_, MovementCost::Infinite) => {
|
||||||
|
info!("Rejecting movement - movement cost is infinite");
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
|
||||||
|
// we are walking on a path
|
||||||
|
(MovementCost::Path, MovementCost::Path) => 10.0,
|
||||||
|
|
||||||
|
// we are walking across an obstacle
|
||||||
|
(MovementCost::Obstacle, _) | (_, MovementCost::Obstacle) => 1.0,
|
||||||
|
|
||||||
|
// we are walking on grass
|
||||||
|
_ => 5.0
|
||||||
|
};
|
||||||
|
state.ghost.overworld_movement_pending = Vec2 {
|
||||||
|
x: requested_pos_diff.x as _,
|
||||||
|
y: requested_pos_diff.y as _
|
||||||
|
};
|
||||||
|
state.ghost.overworld_pos_last_update = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(state: &mut State, ctx: &mut EngineContext<'_>) {
|
||||||
|
let mut camera = main_camera_mut();
|
||||||
|
camera.center = state.ghost.overworld_pos;
|
||||||
|
camera.zoom = 30.0;
|
||||||
|
|
||||||
|
// move player
|
||||||
|
update_move_player(state, ctx);
|
||||||
|
|
||||||
|
//move into house if player stepp at door
|
||||||
|
{
|
||||||
|
let tile_pos = IVec2 {
|
||||||
|
x: state.ghost.overworld_pos.x.round() as _,
|
||||||
|
y: state.ghost.overworld_pos.y.round() as _
|
||||||
|
};
|
||||||
|
let current_tile = state.overworld.get_or_generate_tile(tile_pos);
|
||||||
|
if current_tile.can_enter_house() {
|
||||||
|
info!("enter house at {tile_pos}");
|
||||||
|
state.activity = Activity::House(tile_pos);
|
||||||
|
state.house_mut(ctx); // gen new house
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate more chunks if needed
|
||||||
|
{
|
||||||
|
let half_viewport = (camera.world_viewport() * 0.5 + 3.0).as_ivec2();
|
||||||
|
let rounded_ghost_pos = IVec2 {
|
||||||
|
x: state.ghost.overworld_pos.x.round() as _,
|
||||||
|
y: state.ghost.overworld_pos.y.round() as _
|
||||||
|
};
|
||||||
|
state.overworld.get_or_generate_tile(
|
||||||
|
rounded_ghost_pos + IVec2::new(half_viewport.x, half_viewport.y)
|
||||||
|
);
|
||||||
|
state.overworld.get_or_generate_tile(
|
||||||
|
rounded_ghost_pos + IVec2::new(half_viewport.x, -half_viewport.y)
|
||||||
|
);
|
||||||
|
state.overworld.get_or_generate_tile(
|
||||||
|
rounded_ghost_pos + IVec2::new(-half_viewport.x, half_viewport.y)
|
||||||
|
);
|
||||||
|
state.overworld.get_or_generate_tile(
|
||||||
|
rounded_ghost_pos + IVec2::new(-half_viewport.x, -half_viewport.y)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
|
|
||||||
use crate::assets::ASSETS;
|
use crate::assets::ASSETS;
|
||||||
use comfy::{IVec2, TextureHandle, UVec2};
|
use comfy::{info, IVec2, TextureHandle, UVec2};
|
||||||
use log::info;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
pub enum MovementCost {
|
pub enum MovementCost {
|
||||||
|
@ -117,28 +116,61 @@ impl Tile {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn can_stand_inside(&self) -> bool {
|
pub fn can_stand_inside(&self) -> bool {
|
||||||
unimplemented!()
|
#[allow(clippy::match_like_matches_macro)] // I believe this is better readable
|
||||||
|
match self {
|
||||||
|
Self::House { door: false, .. } => false,
|
||||||
|
_ => true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn movement_cost_left(&self) -> MovementCost {
|
pub fn movement_cost_left(&self) -> MovementCost {
|
||||||
unimplemented!()
|
match self {
|
||||||
|
Self::Grass => MovementCost::Default,
|
||||||
|
Self::Path { left: true, .. } => MovementCost::Path,
|
||||||
|
Self::Path { left: false, .. } => MovementCost::Default,
|
||||||
|
Self::Fence { left: false, .. } => MovementCost::Obstacle,
|
||||||
|
_ => MovementCost::Infinite
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn movement_cost_right(&self) -> MovementCost {
|
pub fn movement_cost_right(&self) -> MovementCost {
|
||||||
unimplemented!()
|
match self {
|
||||||
|
Self::Grass => MovementCost::Default,
|
||||||
|
Self::Path { right: true, .. } => MovementCost::Path,
|
||||||
|
Self::Path { right: false, .. } => MovementCost::Default,
|
||||||
|
Self::Fence { right: false, .. } => MovementCost::Obstacle,
|
||||||
|
_ => MovementCost::Infinite
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn movement_cost_up(&self) -> MovementCost {
|
pub fn movement_cost_up(&self) -> MovementCost {
|
||||||
unimplemented!()
|
match self {
|
||||||
|
Self::Grass => MovementCost::Default,
|
||||||
|
Self::Path { top: true, .. } => MovementCost::Path,
|
||||||
|
Self::Path { top: false, .. } => MovementCost::Default,
|
||||||
|
Self::Fence { top: false, .. } => MovementCost::Obstacle,
|
||||||
|
_ => MovementCost::Infinite
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn movement_cost_down(&self) -> MovementCost {
|
pub fn movement_cost_down(&self) -> MovementCost {
|
||||||
unimplemented!()
|
match self {
|
||||||
|
Self::Grass => MovementCost::Default,
|
||||||
|
Self::Path { bottom: true, .. } => MovementCost::Path,
|
||||||
|
Self::Path { bottom: false, .. } => MovementCost::Default,
|
||||||
|
Self::Fence { bottom: false, .. } => MovementCost::Obstacle,
|
||||||
|
Self::House { door: true, .. } => MovementCost::Path,
|
||||||
|
_ => MovementCost::Infinite
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pub fn can_enter_house(&self) -> bool {
|
pub fn can_enter_house(&self) -> bool {
|
||||||
unimplemented!()
|
#[allow(clippy::match_like_matches_macro)] // I believe this is better readable
|
||||||
|
match self {
|
||||||
|
Self::House { door: true, .. } => true,
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The size of a chunk (both width and height). This value squared gives the amount of
|
/// The size of a chunk (both width and height). This value squared gives the amount of
|
||||||
/// tiles in the chunk.
|
/// tiles in the chunk.
|
||||||
const CHUNK_SIZE: u32 = 100;
|
const CHUNK_SIZE: u32 = 50;
|
||||||
|
|
||||||
/// Chunks
|
/// Chunks
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -286,7 +318,10 @@ impl Chunk {
|
||||||
fence_vert,
|
fence_vert,
|
||||||
grass,
|
grass,
|
||||||
house(ASSETS.overworld.house_bottom_left),
|
house(ASSETS.overworld.house_bottom_left),
|
||||||
house(ASSETS.overworld.house_bottom_door),
|
Tile::House {
|
||||||
|
door: true,
|
||||||
|
texture: ASSETS.overworld.house_bottom_door
|
||||||
|
},
|
||||||
house(ASSETS.overworld.house_bottom_window),
|
house(ASSETS.overworld.house_bottom_window),
|
||||||
house(ASSETS.overworld.house_bottom_window),
|
house(ASSETS.overworld.house_bottom_window),
|
||||||
house(ASSETS.overworld.house_bottom_right),
|
house(ASSETS.overworld.house_bottom_right),
|
||||||
|
@ -403,7 +438,15 @@ fn world_to_chunk_and_local_coords(world_coords: IVec2) -> (IVec2, UVec2) {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Overworld {
|
impl Overworld {
|
||||||
fn get_tile(&self, world_coords: IVec2) -> Option<&Tile> {
|
/// Return a [`Tile`] at the given world coordinates, or `None` if that tile has not
|
||||||
|
/// been generated yet. Uses engine/world cordinates.
|
||||||
|
pub fn get_tile(&self, world_coords: IVec2) -> Option<&Tile> {
|
||||||
|
let mut coords = world_coords;
|
||||||
|
coords.y *= -1;
|
||||||
|
self.get_tile_private(coords)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_tile_private(&self, world_coords: IVec2) -> Option<&Tile> {
|
||||||
let (chunk_coords, local_chunk_coords) =
|
let (chunk_coords, local_chunk_coords) =
|
||||||
world_to_chunk_and_local_coords(world_coords);
|
world_to_chunk_and_local_coords(world_coords);
|
||||||
|
|
||||||
|
@ -411,15 +454,15 @@ impl Overworld {
|
||||||
chunk.get_tile(local_chunk_coords)
|
chunk.get_tile(local_chunk_coords)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a [`Tile`] at the given world coordinates, or `None` if that tile has not
|
/// Return a [`Tile`] at the given world coordinates. Generates tiles if necessary.
|
||||||
/// been generated yet. use engine/world cordinates.
|
/// Uses engine/world cordinates.
|
||||||
pub fn get_or_generate_tile(&mut self, world_coords: IVec2) -> &Tile {
|
pub fn get_or_generate_tile(&mut self, world_coords: IVec2) -> &Tile {
|
||||||
let mut coords = world_coords;
|
let mut coords = world_coords;
|
||||||
coords.y *= -1;
|
coords.y *= -1;
|
||||||
self.get_or_generate_tiles_private(coords)
|
self.get_or_generate_tile_private(coords)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_or_generate_tiles_private(&mut self, world_coords: IVec2) -> &Tile {
|
fn get_or_generate_tile_private(&mut self, world_coords: IVec2) -> &Tile {
|
||||||
let (chunk_coords, local_chunk_coords) =
|
let (chunk_coords, local_chunk_coords) =
|
||||||
world_to_chunk_and_local_coords(world_coords);
|
world_to_chunk_and_local_coords(world_coords);
|
||||||
|
|
||||||
|
@ -430,21 +473,21 @@ impl Overworld {
|
||||||
chunk.get_tile(local_chunk_coords).unwrap()
|
chunk.get_tile(local_chunk_coords).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all generated tiles and its engine/world cordinates. using a [`Tile`] at the given world coordinates, or `None` if that tile has not
|
/// Iterate over all generated tiles in all generated chunks and their engine/world
|
||||||
/// been generated yet.
|
/// cordinates.
|
||||||
pub fn iter_tiles(&self) -> impl Iterator<Item = (IVec2, &Tile)> {
|
pub fn iter_tiles(&self) -> impl Iterator<Item = (IVec2, &Tile)> {
|
||||||
self.iter_tilese_private().map(|(coords, tile)| {
|
self.iter_tiles_private().map(|(coords, tile)| {
|
||||||
let mut w_coords = coords;
|
let mut w_coords = coords;
|
||||||
w_coords.y *= -1;
|
w_coords.y *= -1;
|
||||||
(w_coords, tile)
|
(w_coords, tile)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// iterate over all tiles and its global coords
|
/// Iterate over all generated tiles in all generated chunks.
|
||||||
fn iter_tilese_private(&self) -> impl Iterator<Item = (IVec2, &Tile)> {
|
fn iter_tiles_private(&self) -> impl Iterator<Item = (IVec2, &Tile)> {
|
||||||
self.chunks.iter().flat_map(|(chunk_coords, chunk)| {
|
self.chunks.iter().flat_map(|(chunk_coords, chunk)| {
|
||||||
chunk.iter_tiles().map(|(local_coords, tile)| {
|
chunk.iter_tiles().map(|(local_coords, tile)| {
|
||||||
// never fail because chunksize fits alswas into i32
|
// never fail because chunksize fits always into i32
|
||||||
let local_coords: IVec2 = local_coords.try_into().unwrap();
|
let local_coords: IVec2 = local_coords.try_into().unwrap();
|
||||||
(local_coords + (*chunk_coords * CHUNK_SIZE as i32), tile)
|
(local_coords + (*chunk_coords * CHUNK_SIZE as i32), tile)
|
||||||
})
|
})
|
||||||
|
|
61
src/game.rs
|
@ -3,22 +3,64 @@ use crate::{
|
||||||
assets::Assets,
|
assets::Assets,
|
||||||
State
|
State
|
||||||
};
|
};
|
||||||
use comfy::EngineContext;
|
use comfy::{EngineContext, Vec2};
|
||||||
use std::ops::Sub;
|
use std::{ops::Sub, time::Instant};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Ghost {
|
pub struct Ghost {
|
||||||
/// current electric charge of the Ghost
|
/// Current electric charge of the Ghost.
|
||||||
pub charge: f32,
|
pub charge: f32,
|
||||||
/// max electric charge of the Ghost
|
/// Max electric charge of the Ghost.
|
||||||
pub max_charge: f32
|
pub max_charge: f32,
|
||||||
|
|
||||||
|
/// The position of the ghost in the overworld. Expressed in tile coordinates, but
|
||||||
|
/// as a float as a ghost takes more than one tick to move.
|
||||||
|
pub overworld_pos: Vec2,
|
||||||
|
/// Pending movement of the ghost in the overworld.
|
||||||
|
pub overworld_movement_pending: Vec2,
|
||||||
|
/// The current movement speed of the ghost in tiles/sec.
|
||||||
|
pub overworld_movement_speed: f32,
|
||||||
|
/// The timestamp of the last overworld position update.
|
||||||
|
pub overworld_pos_last_update: Instant
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ghost {
|
||||||
|
pub fn update_overworld_pos(&mut self, now: Instant) {
|
||||||
|
// This calculation is extremely simplistic. It will not work properly if both
|
||||||
|
// x and y of movement_pending are non-zero. But we only move left,right,up,down
|
||||||
|
// so that should never happen!
|
||||||
|
let secs = now
|
||||||
|
.duration_since(self.overworld_pos_last_update)
|
||||||
|
.as_secs_f32();
|
||||||
|
let mut movement = self.overworld_movement_pending.signum()
|
||||||
|
* self.overworld_movement_speed
|
||||||
|
* secs;
|
||||||
|
|
||||||
|
// limit the movement to the remaining movement
|
||||||
|
if self.overworld_movement_pending.x.abs() < movement.x.abs() {
|
||||||
|
movement.x = self.overworld_movement_pending.x;
|
||||||
|
}
|
||||||
|
if self.overworld_movement_pending.y.abs() < movement.y.abs() {
|
||||||
|
movement.y = self.overworld_movement_pending.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute the movement
|
||||||
|
self.overworld_pos += movement;
|
||||||
|
self.overworld_movement_pending -= movement;
|
||||||
|
self.overworld_pos_last_update = now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Ghost {
|
impl Default for Ghost {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
charge: 1000.0,
|
charge: 1000.0,
|
||||||
max_charge: 1000.0
|
max_charge: 1000.0,
|
||||||
|
|
||||||
|
overworld_pos: Vec2::ZERO,
|
||||||
|
overworld_movement_pending: Vec2::ZERO,
|
||||||
|
overworld_movement_speed: 0.0,
|
||||||
|
overworld_pos_last_update: Instant::now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,19 +92,20 @@ impl Sub<i32> for ZLayer {
|
||||||
pub fn setup(state: &mut State, ctx: &mut EngineContext<'_>) {
|
pub fn setup(state: &mut State, ctx: &mut EngineContext<'_>) {
|
||||||
Assets::load(ctx);
|
Assets::load(ctx);
|
||||||
|
|
||||||
house::setup(state, ctx);
|
//house::setup(state, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(state: &mut State, engine: &mut EngineContext<'_>) {
|
pub fn update(state: &mut State, engine: &mut EngineContext<'_>) {
|
||||||
|
state.score += engine.delta * 10.0;
|
||||||
match state.activity {
|
match state.activity {
|
||||||
Activity::House => house::update(state, engine),
|
Activity::House(_) => house::update(state, engine),
|
||||||
Activity::Overworld => overworld::update(state, engine)
|
Activity::Overworld => overworld::update(state, engine)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(state: &State, engine: &EngineContext<'_>) {
|
pub fn draw(state: &State, engine: &EngineContext<'_>) {
|
||||||
match state.activity {
|
match state.activity {
|
||||||
Activity::House => house::draw(state, engine),
|
Activity::House(_) => house::draw(state, engine),
|
||||||
Activity::Overworld => overworld::draw(state, engine)
|
Activity::Overworld => overworld::draw(state, engine)
|
||||||
}
|
}
|
||||||
crate::ui::draw(state, engine);
|
crate::ui::draw(state, engine);
|
||||||
|
|
40
src/main.rs
|
@ -15,8 +15,8 @@ use self::{
|
||||||
game::Ghost
|
game::Ghost
|
||||||
};
|
};
|
||||||
use comfy::{
|
use comfy::{
|
||||||
init_game_config, pollster, run_comfy_main_async, EngineContext, EngineState,
|
etagere::euclid::default, init_game_config, pollster, run_comfy_main_async,
|
||||||
GameConfig, GameLoop
|
DevConfig, EngineContext, EngineState, GameConfig, GameLoop, HashMap, IVec2
|
||||||
};
|
};
|
||||||
|
|
||||||
const GAME_NAME: &str = "Powercreep";
|
const GAME_NAME: &str = "Powercreep";
|
||||||
|
@ -27,7 +27,27 @@ struct State {
|
||||||
activity: Activity,
|
activity: Activity,
|
||||||
ghost: Ghost,
|
ghost: Ghost,
|
||||||
overworld: Overworld,
|
overworld: Overworld,
|
||||||
house: Option<HouseState>
|
houses: HashMap<IVec2, HouseState>,
|
||||||
|
score: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn get_house_pos(&self) -> Option<IVec2> {
|
||||||
|
match self.activity {
|
||||||
|
Activity::House(pos) => Some(pos),
|
||||||
|
_ => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn house(&self) -> Option<&HouseState> {
|
||||||
|
self.houses.get(&self.get_house_pos()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn house_mut(&mut self, ctx: &mut EngineContext<'_>) -> &mut HouseState {
|
||||||
|
self.houses
|
||||||
|
.entry(self.get_house_pos().unwrap())
|
||||||
|
.or_insert_with(|| HouseState::generate_new_house(ctx))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GameLoop for State {
|
impl GameLoop for State {
|
||||||
|
@ -37,17 +57,29 @@ impl GameLoop for State {
|
||||||
|
|
||||||
fn update(&mut self, ctx: &mut EngineContext<'_>) {
|
fn update(&mut self, ctx: &mut EngineContext<'_>) {
|
||||||
if !self.setup_called {
|
if !self.setup_called {
|
||||||
|
game::setup(self, ctx);
|
||||||
game::setup(self, ctx);
|
game::setup(self, ctx);
|
||||||
self.setup_called = true;
|
self.setup_called = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
game::update(self, ctx);
|
game::update(self, ctx);
|
||||||
game::draw(self, ctx);
|
game::draw(self, ctx);
|
||||||
|
game::update(self, ctx);
|
||||||
|
game::draw(self, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn config(config: GameConfig) -> GameConfig {
|
fn config(config: GameConfig) -> GameConfig {
|
||||||
config
|
let dev = DevConfig {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
show_fps: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
GameConfig {
|
||||||
|
tonemapping_enabled: true,
|
||||||
|
dev,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run() {
|
async fn run() {
|
||||||
|
|
26
src/ui.rs
|
@ -1,10 +1,11 @@
|
||||||
use crate::State;
|
use crate::State;
|
||||||
use comfy::{
|
use comfy::{
|
||||||
draw_rect, draw_rect_outline, screen_height, screen_to_world, screen_width,
|
draw_rect, draw_rect_outline, egui, screen_height, screen_to_world, screen_width,
|
||||||
EngineContext, Vec2, BLUE, RED
|
EngineContext, Vec2, BLUE, RED, WHITE
|
||||||
};
|
};
|
||||||
|
use egui::widget_text::RichText;
|
||||||
|
|
||||||
pub fn draw(state: &State, _engine: &EngineContext<'_>) {
|
pub fn draw_batterie(state: &State, _engine: &EngineContext<'_>) {
|
||||||
// seperate fill state into smaller section for better readability
|
// seperate fill state into smaller section for better readability
|
||||||
let section_count = 5;
|
let section_count = 5;
|
||||||
let mut start_positon = screen_to_world(Vec2::new(screen_width(), screen_height()));
|
let mut start_positon = screen_to_world(Vec2::new(screen_width(), screen_height()));
|
||||||
|
@ -29,3 +30,22 @@ pub fn draw(state: &State, _engine: &EngineContext<'_>) {
|
||||||
draw_rect_outline(position, section_size, 0.1, RED, 100);
|
draw_rect_outline(position, section_size, 0.1, RED, 100);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn draw_highscore(state: &State, _engine: &EngineContext<'_>) {
|
||||||
|
egui::Area::new("score")
|
||||||
|
.anchor(egui::Align2::RIGHT_TOP, egui::vec2(0.0, 0.0))
|
||||||
|
.show(egui(), |ui| {
|
||||||
|
ui.label(
|
||||||
|
RichText::new(format!("{:.0}", state.score))
|
||||||
|
.color(WHITE)
|
||||||
|
.monospace()
|
||||||
|
.size(16.0)
|
||||||
|
.strong()
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn draw(state: &State, engine: &EngineContext<'_>) {
|
||||||
|
draw_batterie(state, engine);
|
||||||
|
draw_highscore(state, engine);
|
||||||
|
}
|
||||||
|
|