diff --git a/Cargo.lock b/Cargo.lock index 385f69d304..bfa7f2362b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2399,6 +2399,15 @@ dependencies = [ "serde", ] +[[package]] +name = "inline_tweak" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7033e97b20277cc0d043226d1940fa7719ff08d2305d1fc7421e53066d00eb4b" +dependencies = [ + "lazy_static", +] + [[package]] name = "inotify" version = "0.7.1" @@ -5886,6 +5895,7 @@ dependencies = [ "fxhash", "hashbrown", "image", + "inline_tweak", "itertools 0.10.0", "lazy_static", "lz-fear", diff --git a/assets/voxygen/voxel/sprite/iron_bar/iron_bar-0.vox b/assets/voxygen/voxel/sprite/iron_bar/iron_bar-0.vox new file mode 100644 index 0000000000..10217dfe14 --- /dev/null +++ b/assets/voxygen/voxel/sprite/iron_bar/iron_bar-0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fb7fb1a4fc5fb7748aa3aeac00ece2850c7cf18ff13faa6a92a2d697a591306 +size 1316 diff --git a/assets/voxygen/voxel/sprite/iron_bar/iron_bar-1.vox b/assets/voxygen/voxel/sprite/iron_bar/iron_bar-1.vox new file mode 100644 index 0000000000..47e6282414 --- /dev/null +++ b/assets/voxygen/voxel/sprite/iron_bar/iron_bar-1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d85019bf3c6e1b962733a84da76123be9aa8def3bac6e0e2b532bd1059539d78 +size 1492 diff --git a/assets/voxygen/voxel/sprite/tombstones/tombstone-0.vox b/assets/voxygen/voxel/sprite/tombstones/tombstone-0.vox new file mode 100644 index 0000000000..6dfc58dcc0 --- /dev/null +++ b/assets/voxygen/voxel/sprite/tombstones/tombstone-0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c2063c0cddc10eaefc0ff0be9fab0b9b3d9b4bbf9cec2bb585bdce227948fba8 +size 22220 diff --git a/assets/voxygen/voxel/sprite/tombstones/tombstone-1.vox b/assets/voxygen/voxel/sprite/tombstones/tombstone-1.vox new file mode 100644 index 0000000000..4550d22d25 --- /dev/null +++ b/assets/voxygen/voxel/sprite/tombstones/tombstone-1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8cdc73d044ef46d872dc8d2ed838b20bf7778228832430c2097c4a446c6e3358 +size 21939 diff --git a/assets/voxygen/voxel/sprite/tombstones/tombstone-2.vox b/assets/voxygen/voxel/sprite/tombstones/tombstone-2.vox new file mode 100644 index 0000000000..a92a0b72d8 --- /dev/null +++ b/assets/voxygen/voxel/sprite/tombstones/tombstone-2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c561715afe72c257878589e8eca1f5b55125f6b8a24751ab219620523ca51127 +size 22044 diff --git a/assets/voxygen/voxel/sprite/tombstones/tombstone-3.vox b/assets/voxygen/voxel/sprite/tombstones/tombstone-3.vox new file mode 100644 index 0000000000..8e175dddf8 --- /dev/null +++ b/assets/voxygen/voxel/sprite/tombstones/tombstone-3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b034e211d025f9032e762a698b5c171d0f91d5b9712e00f3b342e4ab1f28ece3 +size 22875 diff --git a/assets/voxygen/voxel/sprite_manifest.ron b/assets/voxygen/voxel/sprite_manifest.ron index 4e2b11a7cd..8483e84592 100644 --- a/assets/voxygen/voxel/sprite_manifest.ron +++ b/assets/voxygen/voxel/sprite_manifest.ron @@ -2907,4 +2907,52 @@ CookingPot: Some(( ], wind_sway: 0.0, )), -) +// Iron Bar +IronBar: Some(( + variations: [ + ( + model: "voxygen.voxel.sprite.iron_bar.iron_bar-0", + offset: (-1.5, -1.5, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ], + wind_sway: 0.0, +)), +// Iron Bar Cross +IronBarCross: Some(( + variations: [ + ( + model: "voxygen.voxel.sprite.iron_bar.iron_bar-1", + offset: (-5.5, -1.5, -5.5), + lod_axes: (0.0, 0.0, 0.0), + ), + ], + wind_sway: 0.0, +)), +// Tombstones +Tombstones: Some(( + variations: [ + ( + model: "voxygen.voxel.sprite.tombstones.tombstone-0", + offset: (-5.5, -2.0, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.tombstones.tombstone-1", + offset: (-4.5, -1.0, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.tombstones.tombstone-2", + offset: (-4.5, -1.0, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ( + model: "voxygen.voxel.sprite.tombstones.tombstone-3", + offset: (-7.5, -3.0, 0.0), + lod_axes: (0.0, 0.0, 0.0), + ), + ], + wind_sway: 0.0, +)), +) \ No newline at end of file diff --git a/common/src/terrain/sprite.rs b/common/src/terrain/sprite.rs index e967185ba6..12616b720f 100644 --- a/common/src/terrain/sprite.rs +++ b/common/src/terrain/sprite.rs @@ -148,6 +148,9 @@ make_case_elim!( Cauldron = 0x79, Anvil = 0x7A, CookingPot = 0x7B, + IronBar = 0x7C, + IronBarCross = 0x7D, + Tombstones = 0x7E, } ); @@ -209,7 +212,10 @@ impl SpriteKind { | SpriteKind::Window2 | SpriteKind::Window3 | SpriteKind::Window4 - | SpriteKind::DropGate => 1.0, + | SpriteKind::DropGate + | SpriteKind::IronBar + | SpriteKind::IronBarCross + | SpriteKind::Tombstones => 1.0, // TODO: Figure out if this should be solid or not. SpriteKind::Shelf => 1.0, SpriteKind::Lantern => 0.9, @@ -324,6 +330,8 @@ impl SpriteKind { | SpriteKind::Cauldron | SpriteKind::Anvil | SpriteKind::CookingPot + | SpriteKind::IronBarCross + | SpriteKind::Tombstones ) } } diff --git a/world/Cargo.toml b/world/Cargo.toml index c96c114004..0cc4024237 100644 --- a/world/Cargo.toml +++ b/world/Cargo.toml @@ -36,7 +36,7 @@ rayon = "1.5" serde = { version = "1.0.110", features = ["derive"] } ron = { version = "0.6", default-features = false } assets_manager = {version = "0.4.3", features = ["ron"]} -#inline_tweak = "1.0.2" +inline_tweak = "1.0.2" # compression benchmarks lz-fear = { version = "0.1.1", optional = true } diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index 88ff15c4c2..a4a74cf05c 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -722,6 +722,7 @@ impl Site { for plot in plots_to_render { let (prim_tree, fills) = match &self.plots[plot].kind { PlotKind::Hut(hut) => hut.render_collect(self), + PlotKind::Cemetary(cemetary) => cemetary.render_collect(self), PlotKind::House(house) => house.render_collect(self), PlotKind::Castle(castle) => castle.render_collect(self), _ => continue, diff --git a/world/src/site2/planning.rs b/world/src/site2/planning.rs index 7dd34e7adb..fee863fd54 100644 --- a/world/src/site2/planning.rs +++ b/world/src/site2/planning.rs @@ -1,8 +1,8 @@ -use common::lottery::Lottery; use super::{ + structure::{Cemetary, Hut, Structure}, *, - structure::{Structure, Hut}, }; +use common::lottery::Lottery; // All are weights, must be positive, 1.0 is default. pub struct Values { @@ -23,11 +23,14 @@ impl Site { match *Lottery::from_slice(&mut [ (10.0, 0), // Huts ]) - .choose_seeded(rng.gen()) + .choose_seeded(rng.gen()) { 0 => { - Hut::choose_location((), land, self, rng) - .map(|hut| hut.generate(land, self, rng)); + Cemetary::choose_location((), land, self, rng) + .map(|cemetary| cemetary.generate(land, self, rng)); + }, + 1 => { + Hut::choose_location((), land, self, rng).map(|hut| hut.generate(land, self, rng)); }, _ => unreachable!(), } diff --git a/world/src/site2/plot.rs b/world/src/site2/plot.rs index 2c8a6e352b..304b5182c1 100644 --- a/world/src/site2/plot.rs +++ b/world/src/site2/plot.rs @@ -2,7 +2,7 @@ mod castle; mod house; pub use self::{castle::Castle, house::House}; -pub use super::structure::Hut; +pub use super::structure::{Cemetary, Hut}; use super::*; use crate::util::DHashSet; @@ -32,4 +32,5 @@ pub enum PlotKind { Plaza, Castle(Castle), Road(Path>), + Cemetary(Cemetary), } diff --git a/world/src/site2/structure/cemetary.rs b/world/src/site2/structure/cemetary.rs new file mode 100644 index 0000000000..c81cf40d4e --- /dev/null +++ b/world/src/site2/structure/cemetary.rs @@ -0,0 +1,316 @@ +use super::*; +use vek::*; + +pub struct Cemetary { + root: Vec2, + tile_aabr: Aabr, + bounds: Aabr, + alt: i32, + height: i32, + door_dir: Vec2, +} + +impl Structure for Cemetary { + type Config = (); + + fn choose_location( + cfg: Self::Config, + land: &Land, + site: &Site, + rng: &mut R, + ) -> Option { + let (tile_aabr, root) = site.tiles.find_near(Vec2::zero(), |tile, _| { + if rng.gen_range(0..16) == 0 { + site.tiles.grow_aabr(tile, 15..20, (2, 2), 2).ok() + } else { + None + } + })?; + let center = (tile_aabr.min + (tile_aabr.max - 1)) / 2; + + Some(Self { + root, + tile_aabr, + bounds: Aabr { + min: site.tile_wpos(tile_aabr.min), + max: site.tile_wpos(tile_aabr.max), + }, + alt: land.get_alt_approx(site.tile_center_wpos(center)) as i32, + height: 4, + door_dir: match rng.gen_range(0..4) { + 0 => Vec2::unit_x(), + 1 => -Vec2::unit_x(), + 2 => Vec2::unit_y(), + 3 => -Vec2::unit_y(), + _ => unreachable!(), + }, + }) + } + + fn generate(self, land: &Land, site: &mut Site, rng: &mut R) { + let aabr = self.tile_aabr; + + let plot = site.create_plot(Plot { + root_tile: self.root, + tiles: aabr_tiles(aabr).collect(), + seed: rng.gen(), + kind: PlotKind::Cemetary(self), + }); + + site.blit_aabr(aabr, Tile { + kind: TileKind::Building, + plot: Some(plot), + }); + } +} + +impl Render for Cemetary { + fn render Id, G: FnMut(Id, Fill)>( + &self, + site: &Site, + mut prim: F, + mut fill: G, + ) { + let mut rng = thread_rng(); + let mid = self.bounds.min + (self.bounds.max - self.bounds.min) / 2; + let roof = 5; + let wall_block = Fill::Block(Block::air(SpriteKind::IronBar)); + let lamp = Fill::Block(Block::air(SpriteKind::WallLampSmall)); + let floor_sprite = Fill::Block(match rng.gen_bool(0.1) { + true => Block::air(match rng.gen_range(0..5) { + 0 => SpriteKind::ShortGrass, + 1 => SpriteKind::MediumGrass, + _ => SpriteKind::LargeGrass, + }), + false => Block::empty(), + }); + let structural_wood = Fill::Block(Block::new(BlockKind::Rock, Rgb::new(80, 80, 80))); + let path = Fill::Block(Block::new( + BlockKind::Rock, + Rgb::new(54, 34, 29) + rng.gen_range(0..10), + )); + + // Walls + let inner = prim(Primitive::Aabb(Aabb { + min: (self.bounds.min + 1).with_z(self.alt), + max: self.bounds.max.with_z(self.alt + roof), + })); + let outer = prim(Primitive::Aabb(Aabb { + min: self.bounds.min.with_z(self.alt), + max: (self.bounds.max + 1).with_z(self.alt + roof), + })); + fill(outer, wall_block); + + let walls = prim(Primitive::Xor(outer, inner)); + + // lol + + // Windows x axis + { + let mut windows = prim(Primitive::Empty); + for y in self.tile_aabr.min.y..self.tile_aabr.max.y + 1 { + let window = prim(Primitive::Aabb(Aabb { + min: (site.tile_wpos(Vec2::new(self.tile_aabr.min.x, y)) + Vec2::unit_x() * 1) + .with_z(self.alt + 3 + 1), + max: (site.tile_wpos(Vec2::new(self.tile_aabr.max.x + 1, y + 1)) + - Vec2::unit_x() * 1) + .with_z(self.alt + 3 + 1 + 1), + })); + windows = prim(Primitive::Or(windows, window)); + } + fill( + prim(Primitive::And(walls, windows)), + Fill::Block(Block::air(SpriteKind::IronBarCross).with_ori(0).unwrap()), + ); + } + // Windows y axis + { + let mut windows = prim(Primitive::Empty); + for x in self.tile_aabr.min.x..self.tile_aabr.max.x + 1 { + let window = prim(Primitive::Aabb(Aabb { + min: (site.tile_wpos(Vec2::new(x, self.tile_aabr.min.y)) + Vec2::unit_y() * 1) + .with_z(self.alt + 1 + 3), + max: (site.tile_wpos(Vec2::new(x + 1, self.tile_aabr.max.y))) + .with_z(self.alt + 1 + 3 + 1), + })); + windows = prim(Primitive::Or(windows, window)); + } + fill( + prim(Primitive::And(walls, windows)), + Fill::Block(Block::air(SpriteKind::IronBarCross).with_ori(2).unwrap()), + ); + } + + // wall pillars + let mut pillars_y = prim(Primitive::Empty); + for x in self.tile_aabr.min.x..self.tile_aabr.max.x + 2 { + let pillar = prim(Primitive::Aabb(Aabb { + min: site + .tile_wpos(Vec2::new(x, self.tile_aabr.min.y)) + .with_z(self.alt), + max: (site.tile_wpos(Vec2::new(x, self.tile_aabr.max.y + 1)) + Vec2::unit_x()) + .with_z(self.alt + roof), + })); + pillars_y = prim(Primitive::Or(pillars_y, pillar)); + } + let mut pillars_x = prim(Primitive::Empty); + for y in self.tile_aabr.min.y..self.tile_aabr.max.y + 2 { + let pillar = prim(Primitive::Aabb(Aabb { + min: site + .tile_wpos(Vec2::new(self.tile_aabr.min.x, y)) + .with_z(self.alt), + max: (site.tile_wpos(Vec2::new(self.tile_aabr.max.x + 1, y)) + Vec2::unit_y()) + .with_z(self.alt + roof), + })); + pillars_x = prim(Primitive::Or(pillars_x, pillar)); + } + let pillars = prim(Primitive::And(pillars_x, pillars_y)); + fill(pillars, structural_wood); + fill(inner, Fill::Block(Block::empty())); + + // Floor + let floor = prim(Primitive::Aabb(Aabb { + min: (self.bounds.min).with_z(self.alt), + max: (self.bounds.max + 1).with_z(self.alt + 1), + })); + fill( + floor, + Fill::Block(Block::new( + BlockKind::Grass, + Rgb::new(48, 79, 30) + rng.gen_range(0..5), + )), + ); + + let slice = prim(Primitive::Aabb(Aabb { + min: self.bounds.min.with_z(self.alt), + max: (self.bounds.max + 1).with_z(self.alt + 2), + })); + fill(prim(Primitive::AndNot(slice, floor)), structural_wood); + + // Grass + let grass = Aabb { + min: (self.bounds.min + 1).with_z(self.alt + 1), + max: self.bounds.max.with_z(self.alt + 2), + }; + + for x in grass.min.x..grass.max.x { + for y in grass.min.y..grass.max.y { + let grass_sprite = Fill::Block(match rng.gen_bool(0.1) { + true => Block::air(match rng.gen_range(0..5) { + 0 => SpriteKind::LargeGrass, + 1 => SpriteKind::MediumGrass, + _ => SpriteKind::ShortGrass, + }), + false => Block::empty(), + }); + let grass_block = prim(Primitive::Aabb(Aabb { + min: Vec2::new(x, y).with_z(self.alt + 1), + max: Vec2::new(x + 1, y + 1).with_z(self.alt + 2), + })); + fill(grass_block, grass_sprite); + } + } + + // Tombstones + + let dalle_block = Fill::Block(Block::new(BlockKind::Rock, Rgb::new(40, 40, 40))); + + for x in grass.min.x..grass.max.x { + for y in grass.min.y..grass.max.y { + let tomb = prim(Primitive::Aabb(Aabb { + min: Vec2::new(x, y).with_z(self.alt + 1), + max: Vec2::new(x + 1, y + 1).with_z(self.alt + 2), + })); + let dalle = prim(Primitive::Aabb(Aabb { + min: Vec2::new(x, y).with_z(self.alt + 0), + max: Vec2::new(x + 1, y + 1).with_z(self.alt + 1), + })); + if x % 4 == 0 && y % 5 == 1 { + let tomb_sprite = Fill::Block(Block::air(SpriteKind::Tombstones)); + fill(tomb, tomb_sprite); + } + if x % 4 == 0 && (y % 5 == 0 || y % 5 == 1) { + fill(dalle, dalle_block); + } + } + } + + // Paths + for x in grass.min.x..grass.max.x { + for y in grass.min.y..grass.max.y { + let block = prim(Primitive::Aabb(Aabb { + min: Vec2::new(x, y).with_z(self.alt), + max: Vec2::new(x + 1, y + 1).with_z(self.alt + 1), + })); + if (y % 5 == 3 || y % 5 == 4) && rng.gen_bool(0.95) { + fill(block, path); + } + } + } + + /*let path_x = prim(Primitive::Aabb(Aabb { + min: Vec3::new(mid.x - 2, grass.min.y, self.alt), + max: Vec3::new(mid.x + 3, grass.max.y, self.alt + 1), + }));*/ + for x in grass.min.x..grass.max.x { + for y in grass.min.y..grass.max.y { + let block = prim(Primitive::Aabb(Aabb { + min: Vec2::new(x, y).with_z(self.alt), + max: Vec2::new(x + 1, y + 1).with_z(self.alt + 1), + })); + if x > mid.x - 2 && x < mid.x + 2 && rng.gen_bool(0.95) { + fill(block, path); + } + } + } + //fill(path_x, path); + + /*let test1 = prim(Primitive::Cylinder(Aabb { + min: Vec3::new(mid.x - inline_tweak::tweak!(5), self.bounds.min.y - inline_tweak::tweak!(5), self.alt), + max: Vec3::new(mid.x + inline_tweak::tweak!(5), self.bounds.min.y + inline_tweak::tweak!(5), self.alt + 3), + })); + let test2 = prim(Primitive::Cylinder(Aabb { + min: Vec3::new(mid.x - 3, mid.y - 4, self.alt), + max: Vec3::new(mid.x + 3, mid.y + 4, self.alt), + })); + let xor = prim(Primitive::Xor(test1, test2)); + + let rotation = Mat3::new( + 1, 0, 0, + 0, 0, -1, + 0, 1, 0 + ); + let rotated = prim(Primitive::Rotate(test1, rotation)); + let translated = prim(Primitive::Offset(rotated, Vec3::new(0, 5, 0))); + fill(translated, structural_wood);*/ + + let high = prim(Primitive::Pyramid { + aabb: Aabb { + min: Vec2::new(mid.x - 3, self.bounds.min.y).with_z(self.alt + 6), + max: Vec2::new(mid.x + 3, self.bounds.min.y + 1).with_z(self.alt + 9), + }, + inset: Vec2::broadcast(5), + }); + let door = prim(Primitive::Aabb(Aabb { + min: Vec2::new(mid.x - 3, self.bounds.min.y).with_z(self.alt + 1), + max: Vec2::new(mid.x + 3, self.bounds.min.y + 1).with_z(self.alt + 6), + })); + let door_empty = prim(Primitive::Aabb(Aabb { + min: Vec2::new(mid.x - 2, self.bounds.min.y).with_z(self.alt + 1), + max: Vec2::new(mid.x + 2, self.bounds.min.y + 1).with_z(self.alt + 6), + })); + let lamp_1 = prim(Primitive::Aabb(Aabb { + min: Vec2::new(mid.x - 3, self.bounds.min.y - 1).with_z(self.alt + 3), + max: Vec2::new(mid.x - 2, self.bounds.min.y).with_z(self.alt + 4), + })); + let lamp_2 = prim(Primitive::Aabb(Aabb { + min: Vec2::new(mid.x + 2, self.bounds.min.y - 1).with_z(self.alt + 3), + max: Vec2::new(mid.x + 3, self.bounds.min.y).with_z(self.alt + 4), + })); + fill(door, structural_wood); + fill(door_empty, Fill::Block(Block::empty())); + fill(high, structural_wood); + fill(lamp_1, lamp); + fill(lamp_2, lamp); + } +} diff --git a/world/src/site2/structure/mod.rs b/world/src/site2/structure/mod.rs index d17e28b55e..52e5f9a426 100644 --- a/world/src/site2/structure/mod.rs +++ b/world/src/site2/structure/mod.rs @@ -1,8 +1,7 @@ +mod cemetary; mod hut; -pub use self::{ - hut::Hut, -}; +pub use self::{cemetary::Cemetary, hut::Hut}; use super::*; @@ -10,7 +9,12 @@ pub trait Structure: Sized { type Config; /// Attempt to choose a location to place this plot in the given site. - fn choose_location(cfg: Self::Config, land: &Land, site: &Site, rng: &mut R) -> Option; + fn choose_location( + cfg: Self::Config, + land: &Land, + site: &Site, + rng: &mut R, + ) -> Option; /// Generate the plot with the given location information on the given site fn generate(self, land: &Land, site: &mut Site, rng: &mut R);