diff --git a/Cargo.toml b/Cargo.toml index 41d8a76f9b..98990c57d5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ opt-level = 2 overflow-checks = true debug-assertions = true panic = "abort" -debug = true +debug = false codegen-units = 8 lto = false incremental = true diff --git a/assets/voxygen/background/bg_10.png b/assets/voxygen/background/bg_10.png new file mode 100644 index 0000000000..8d27d84d83 --- /dev/null +++ b/assets/voxygen/background/bg_10.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5d34cd8aa7655efae1ab92b86dfa6916560eeed22d60e7ce45de6ff4d77e4340 +size 1175394 diff --git a/assets/voxygen/background/bg_11.png b/assets/voxygen/background/bg_11.png new file mode 100644 index 0000000000..3aa49f8c2d --- /dev/null +++ b/assets/voxygen/background/bg_11.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cc0e614d52708cba30d71f658f0c290395371d4333f2f6a866c207a8f471878 +size 1301002 diff --git a/assets/voxygen/voxel/sprite/cabbage/cabbage-0.vox b/assets/voxygen/voxel/sprite/cabbage/cabbage-0.vox index fa2b836395..44e668cd82 100644 --- a/assets/voxygen/voxel/sprite/cabbage/cabbage-0.vox +++ b/assets/voxygen/voxel/sprite/cabbage/cabbage-0.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc86c7aec7a72bbde175d6663e0d308cdc823d5eeabd84a06438a27ac4ba10ec -size 2060 +oid sha256:d791e42efa4d87cffa51e09bfe308576390ae1ce392e2c2ec971ce41a0ee35b5 +size 2064 diff --git a/assets/voxygen/voxel/sprite/cabbage/cabbage-1.vox b/assets/voxygen/voxel/sprite/cabbage/cabbage-1.vox index e9687ef70e..34e4d5cb5e 100644 --- a/assets/voxygen/voxel/sprite/cabbage/cabbage-1.vox +++ b/assets/voxygen/voxel/sprite/cabbage/cabbage-1.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bac99ca3cfc05aa66cb7999d5e3af6467f3e0a41ba8bab4b339e37db01a3ff72 -size 2060 +oid sha256:eaa8cf37b170c93a1c6ecf5a71376ed041d5e1a58faedcb96ee3f128aa5fa2d4 +size 2064 diff --git a/assets/voxygen/voxel/sprite/cabbage/cabbage-2.vox b/assets/voxygen/voxel/sprite/cabbage/cabbage-2.vox index 0326cba8e8..da5cdbed44 100644 --- a/assets/voxygen/voxel/sprite/cabbage/cabbage-2.vox +++ b/assets/voxygen/voxel/sprite/cabbage/cabbage-2.vox @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:89df0256d7b39a9b0a9b6349b9c8fa3885bab3ce3e14dc90f8a6a69e23d27e5c -size 2060 +oid sha256:9bc7359384c83b0e71f67df185e0c86594375335cf24845469f3f873985c9147 +size 2064 diff --git a/assets/voxygen/voxel/sprite/carrot/0.vox b/assets/voxygen/voxel/sprite/carrot/0.vox new file mode 100644 index 0000000000..0a31db6a43 --- /dev/null +++ b/assets/voxygen/voxel/sprite/carrot/0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bdccd8d4e34a2bc43ef75d406b01806b757a2602ee300d58c82ea2818ebb45b8 +size 1716 diff --git a/assets/voxygen/voxel/sprite/carrot/1.vox b/assets/voxygen/voxel/sprite/carrot/1.vox new file mode 100644 index 0000000000..a34498bf76 --- /dev/null +++ b/assets/voxygen/voxel/sprite/carrot/1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbe059eaf30ea72eb10981c8f6f52b9604c2fbf161240e298240c580d5c6cf81 +size 1560 diff --git a/assets/voxygen/voxel/sprite/carrot/2.vox b/assets/voxygen/voxel/sprite/carrot/2.vox new file mode 100644 index 0000000000..fa50b10e15 --- /dev/null +++ b/assets/voxygen/voxel/sprite/carrot/2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b29b616c5557510e857735d694e05af85668f0e8b44b9096ad0a1d1a1d7d03f1 +size 1664 diff --git a/assets/voxygen/voxel/sprite/carrot/3.vox b/assets/voxygen/voxel/sprite/carrot/3.vox new file mode 100644 index 0000000000..91f533832a --- /dev/null +++ b/assets/voxygen/voxel/sprite/carrot/3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:14e7624d5fc4d068f3886a00a389e6fd25f9b2c027bb3b51a9d577f404dc51b0 +size 1656 diff --git a/assets/voxygen/voxel/sprite/carrot/4.vox b/assets/voxygen/voxel/sprite/carrot/4.vox new file mode 100644 index 0000000000..f7de3722ac --- /dev/null +++ b/assets/voxygen/voxel/sprite/carrot/4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0d9442ed362367c675d33552b5fbe0157c69ce900860ce5667414ad05de19b8 +size 1672 diff --git a/assets/voxygen/voxel/sprite/carrot/5.vox b/assets/voxygen/voxel/sprite/carrot/5.vox new file mode 100644 index 0000000000..ceb03ecdbc --- /dev/null +++ b/assets/voxygen/voxel/sprite/carrot/5.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f97c8894e1c607dd9d71967e33fa5221be743e21aef0fae5587c0011de290035 +size 1632 diff --git a/assets/voxygen/voxel/sprite/flax/flax-0.vox b/assets/voxygen/voxel/sprite/flax/flax-0.vox new file mode 100644 index 0000000000..b98bde1434 --- /dev/null +++ b/assets/voxygen/voxel/sprite/flax/flax-0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bb094f06bb530d44dab7581539de04bb011208f4883e40999be6445538cb6e5a +size 1664 diff --git a/assets/voxygen/voxel/sprite/flax/flax-1.vox b/assets/voxygen/voxel/sprite/flax/flax-1.vox new file mode 100644 index 0000000000..d0c80d4766 --- /dev/null +++ b/assets/voxygen/voxel/sprite/flax/flax-1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d7ee17a1f40778a071929be44f7d63e449fcb7dc35ac3fa5aa20bf38e9252c3 +size 1768 diff --git a/assets/voxygen/voxel/sprite/flax/flax-2.vox b/assets/voxygen/voxel/sprite/flax/flax-2.vox new file mode 100644 index 0000000000..af5b27d1a7 --- /dev/null +++ b/assets/voxygen/voxel/sprite/flax/flax-2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:632b581575bae82cf5b9a106bd11a98fbed8c7b85b85fc652a689a890a2d985b +size 1800 diff --git a/assets/voxygen/voxel/sprite/flax/flax-3.vox b/assets/voxygen/voxel/sprite/flax/flax-3.vox new file mode 100644 index 0000000000..9db44fb2cd --- /dev/null +++ b/assets/voxygen/voxel/sprite/flax/flax-3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5a979f7d9b0d8047a6720ab17bf35b005c14190ccc4a35435d56e77a3e5ebb8f +size 1472 diff --git a/assets/voxygen/voxel/sprite/flax/flax-4.vox b/assets/voxygen/voxel/sprite/flax/flax-4.vox new file mode 100644 index 0000000000..93e28fb98a --- /dev/null +++ b/assets/voxygen/voxel/sprite/flax/flax-4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9f33665ea85935b94de2db220c2921ad7525b681ac5513daa5030e2b3b96930e +size 1632 diff --git a/assets/voxygen/voxel/sprite/flax/flax-5.vox b/assets/voxygen/voxel/sprite/flax/flax-5.vox new file mode 100644 index 0000000000..9396ef40d1 --- /dev/null +++ b/assets/voxygen/voxel/sprite/flax/flax-5.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ceab01a1f453292838c38d4c1cd26c0ec94842edf01461cd6fe4f080d788688 +size 1380 diff --git a/assets/voxygen/voxel/sprite/radish/0.vox b/assets/voxygen/voxel/sprite/radish/0.vox new file mode 100644 index 0000000000..2dbf98d2d8 --- /dev/null +++ b/assets/voxygen/voxel/sprite/radish/0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acf2166f24ef4931837e10ca8b8ff71b1963480db475f98227cf13053b08d8e6 +size 1640 diff --git a/assets/voxygen/voxel/sprite/radish/1.vox b/assets/voxygen/voxel/sprite/radish/1.vox new file mode 100644 index 0000000000..745b8f6df4 --- /dev/null +++ b/assets/voxygen/voxel/sprite/radish/1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25f1d72e72380947c51610d83c693a0fcbd985ff0f65dd11065d95d01f0b66e1 +size 1604 diff --git a/assets/voxygen/voxel/sprite/radish/2.vox b/assets/voxygen/voxel/sprite/radish/2.vox new file mode 100644 index 0000000000..4560be2fdb --- /dev/null +++ b/assets/voxygen/voxel/sprite/radish/2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b872912c813034d2d398ec2aa2d9f9a410a8f28766b2070a6307d6bef495d8f5 +size 1452 diff --git a/assets/voxygen/voxel/sprite/radish/3.vox b/assets/voxygen/voxel/sprite/radish/3.vox new file mode 100644 index 0000000000..01957a6b9c --- /dev/null +++ b/assets/voxygen/voxel/sprite/radish/3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e6d4fbed96dc161c5fbd3de4cb8e62e0b6b79b8430f0c01b2d6bd48ba77b213 +size 1488 diff --git a/assets/voxygen/voxel/sprite/radish/4.vox b/assets/voxygen/voxel/sprite/radish/4.vox new file mode 100644 index 0000000000..d5d4efb5aa --- /dev/null +++ b/assets/voxygen/voxel/sprite/radish/4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:21d9b7c04d63493858ebdc0162a00633927c71f79fce5df0aa06fffbd5f7a477 +size 1520 diff --git a/assets/voxygen/voxel/sprite/tomato/0.vox b/assets/voxygen/voxel/sprite/tomato/0.vox new file mode 100644 index 0000000000..c018365cbb --- /dev/null +++ b/assets/voxygen/voxel/sprite/tomato/0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d0d0296db4368f79f0231f5cfd5d2b89cf06c4820196bf5a5a3b2a4f2430bb23 +size 2520 diff --git a/assets/voxygen/voxel/sprite/tomato/1.vox b/assets/voxygen/voxel/sprite/tomato/1.vox new file mode 100644 index 0000000000..f44563c1d3 --- /dev/null +++ b/assets/voxygen/voxel/sprite/tomato/1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0b588c45d77f3712195f992f7e979724aa196ea0648a07c002998d2f1b8435b +size 2616 diff --git a/assets/voxygen/voxel/sprite/tomato/2.vox b/assets/voxygen/voxel/sprite/tomato/2.vox new file mode 100644 index 0000000000..99127bfa46 --- /dev/null +++ b/assets/voxygen/voxel/sprite/tomato/2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f1c035ba21740573df46c3fa0a49aaf00b82758279f716c32ea5763df0a3a8b3 +size 2500 diff --git a/assets/voxygen/voxel/sprite/tomato/3.vox b/assets/voxygen/voxel/sprite/tomato/3.vox new file mode 100644 index 0000000000..149bce29f6 --- /dev/null +++ b/assets/voxygen/voxel/sprite/tomato/3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:019abd752ceba6bb30cefebbb55a9c4742f114cb7c155f93642253cb71ee2532 +size 2444 diff --git a/assets/voxygen/voxel/sprite/tomato/4.vox b/assets/voxygen/voxel/sprite/tomato/4.vox new file mode 100644 index 0000000000..f909913d61 --- /dev/null +++ b/assets/voxygen/voxel/sprite/tomato/4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4fc2ca75eb934c5684e8911b2b4fb4ee3f22b9ae2cdfec780f5f2e4025ceaa3 +size 2456 diff --git a/assets/voxygen/voxel/sprite/turnip/turnip-0.vox b/assets/voxygen/voxel/sprite/turnip/turnip-0.vox new file mode 100644 index 0000000000..fc55170674 --- /dev/null +++ b/assets/voxygen/voxel/sprite/turnip/turnip-0.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:08fb3876bbc499ab64f62776fcfbdab9709ed5ac90c5c94d283a5c6defe7a0fd +size 1360 diff --git a/assets/voxygen/voxel/sprite/turnip/turnip-1.vox b/assets/voxygen/voxel/sprite/turnip/turnip-1.vox new file mode 100644 index 0000000000..9ab7b388d6 --- /dev/null +++ b/assets/voxygen/voxel/sprite/turnip/turnip-1.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:34d59017041f4f340b57d67b0929482bf170d996404f15dbb323f65e72c6b3f6 +size 1528 diff --git a/assets/voxygen/voxel/sprite/turnip/turnip-2.vox b/assets/voxygen/voxel/sprite/turnip/turnip-2.vox new file mode 100644 index 0000000000..d3b58dbed2 --- /dev/null +++ b/assets/voxygen/voxel/sprite/turnip/turnip-2.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c86e20979271f63338dc4ac7afecb79f9461297d460731f145f6b35d149eefa +size 1328 diff --git a/assets/voxygen/voxel/sprite/turnip/turnip-3.vox b/assets/voxygen/voxel/sprite/turnip/turnip-3.vox new file mode 100644 index 0000000000..e34eb2b716 --- /dev/null +++ b/assets/voxygen/voxel/sprite/turnip/turnip-3.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ea31d0b8739c93d07566f6c01f782aacd7873ff41407f2bd9880776dca7bcb48 +size 1480 diff --git a/assets/voxygen/voxel/sprite/turnip/turnip-4.vox b/assets/voxygen/voxel/sprite/turnip/turnip-4.vox new file mode 100644 index 0000000000..a7b8e8ae2d --- /dev/null +++ b/assets/voxygen/voxel/sprite/turnip/turnip-4.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1844a7c3c35b1e0b3ad6914eee0193b2f7aca0a1ff745b408b9ea89809bc9c09 +size 1336 diff --git a/assets/voxygen/voxel/sprite/turnip/turnip-5.vox b/assets/voxygen/voxel/sprite/turnip/turnip-5.vox new file mode 100644 index 0000000000..bc49b4c6e3 --- /dev/null +++ b/assets/voxygen/voxel/sprite/turnip/turnip-5.vox @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b18479edcd9b10aa4ba7c7bdc1f1456ea09236aee2f499a5f86cf7adb72214d8 +size 1312 diff --git a/common/src/astar.rs b/common/src/astar.rs index 3468c687ac..3ad4912733 100644 --- a/common/src/astar.rs +++ b/common/src/astar.rs @@ -139,9 +139,7 @@ impl Astar { } } - pub fn get_cheapest_cost(&self) -> Option { - self.cheapest_cost - } + pub fn get_cheapest_cost(&self) -> Option { self.cheapest_cost } fn reconstruct_path_to(&mut self, end: S) -> Path { let mut path = vec![end.clone()]; diff --git a/common/src/generation.rs b/common/src/generation.rs index 2e2035922d..383b23cfa2 100644 --- a/common/src/generation.rs +++ b/common/src/generation.rs @@ -47,7 +47,5 @@ pub struct ChunkSupplement { } impl ChunkSupplement { - pub fn add_entity(&mut self, entity: EntityInfo) { - self.entities.push(entity); - } + pub fn add_entity(&mut self, entity: EntityInfo) { self.entities.push(entity); } } diff --git a/common/src/lib.rs b/common/src/lib.rs index c9c27fc778..899578a760 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -28,13 +28,13 @@ pub mod region; pub mod spiral; pub mod state; pub mod states; +pub mod store; pub mod sync; pub mod sys; pub mod terrain; pub mod util; pub mod vol; pub mod volumes; -pub mod store; /// The networking module containing high-level wrappers of `TcpListener` and /// `TcpStream` (`PostOffice` and `PostBox` respectively) and data types used by diff --git a/common/src/store.rs b/common/src/store.rs index 5f81925c30..febad3be2d 100644 --- a/common/src/store.rs +++ b/common/src/store.rs @@ -26,9 +26,7 @@ impl fmt::Debug for Id { } } impl hash::Hash for Id { - fn hash(&self, h: &mut H) { - self.0.hash(h); - } + fn hash(&self, h: &mut H) { self.0.hash(h); } } pub struct Store { @@ -53,8 +51,7 @@ impl Store { pub fn iter_mut(&mut self) -> impl Iterator { self.items.iter_mut() } pub fn iter_ids(&self) -> impl Iterator, &T)> { - self - .items + self.items .iter() .enumerate() .map(|(i, item)| (Id(i, PhantomData), item)) diff --git a/common/src/terrain/block.rs b/common/src/terrain/block.rs index 4a373efe39..7e3c63937c 100644 --- a/common/src/terrain/block.rs +++ b/common/src/terrain/block.rs @@ -45,6 +45,10 @@ pub enum BlockKind { WheatYellow, WheatGreen, Cabbage, + Flax, + Carrot, + Tomato, + Radish, Coconut, } @@ -91,7 +95,12 @@ impl BlockKind { BlockKind::Corn => true, BlockKind::WheatYellow => true, BlockKind::WheatGreen => true, - BlockKind::Cabbage => true, + BlockKind::Cabbage => false, + BlockKind::Pumpkin => false, + BlockKind::Flax => true, + BlockKind::Carrot => true, + BlockKind::Tomato => false, + BlockKind::Radish => true, BlockKind::Coconut => true, _ => false, } @@ -142,6 +151,10 @@ impl BlockKind { BlockKind::WheatYellow => false, BlockKind::WheatGreen => false, BlockKind::Cabbage => false, + BlockKind::Flax => false, + BlockKind::Carrot => false, + BlockKind::Tomato => false, + BlockKind::Radish => false, BlockKind::Coconut => false, _ => true, } @@ -171,7 +184,7 @@ impl BlockKind { BlockKind::Mushroom => false, BlockKind::Liana => false, BlockKind::Chest => true, - BlockKind::Pumpkin => true, + BlockKind::Pumpkin => false, BlockKind::Welwitch => false, BlockKind::LingonBerry => false, BlockKind::LeafyPlant => false, @@ -183,6 +196,10 @@ impl BlockKind { BlockKind::WheatYellow => false, BlockKind::WheatGreen => false, BlockKind::Cabbage => false, + BlockKind::Flax => false, + BlockKind::Carrot => false, + BlockKind::Tomato => true, + BlockKind::Radish => false, BlockKind::Coconut => false, _ => true, } @@ -205,7 +222,6 @@ impl BlockKind { BlockKind::Velorite => true, BlockKind::VeloriteFrag => true, BlockKind::Chest => true, - BlockKind::Pumpkin => true, BlockKind::Coconut => true, _ => false, } diff --git a/voxygen/src/menu/main/ui.rs b/voxygen/src/menu/main/ui.rs index e841d304bf..10a9bc2117 100644 --- a/voxygen/src/menu/main/ui.rs +++ b/voxygen/src/menu/main/ui.rs @@ -177,6 +177,8 @@ impl MainMenuUi { "voxygen.background.bg_7", "voxygen.background.bg_8", "voxygen.background.bg_9", + "voxygen.background.bg_10", + "voxygen.background.bg_11", ]; let mut rng = thread_rng(); diff --git a/voxygen/src/mesh/segment.rs b/voxygen/src/mesh/segment.rs index bf02458f28..2d9a4b36a1 100644 --- a/voxygen/src/mesh/segment.rs +++ b/voxygen/src/mesh/segment.rs @@ -88,7 +88,9 @@ impl Meshable for Segment { SpriteVertex::new( origin, norm, - linear_to_srgb(srgb_to_linear(col) * light.min(ao.powf(0.5) * 0.75 + 0.25)), + linear_to_srgb( + srgb_to_linear(col) * light.min(ao.powf(0.5) * 0.75 + 0.25), + ), ) }, &{ diff --git a/voxygen/src/mesh/terrain.rs b/voxygen/src/mesh/terrain.rs index d3fe6dabb2..82a301040d 100644 --- a/voxygen/src/mesh/terrain.rs +++ b/voxygen/src/mesh/terrain.rs @@ -197,11 +197,13 @@ fn calc_light + ReadVol + Debug>( None } else { let pos = wpos - outer.min; - Some(light_map - .get(lm_idx(pos.x, pos.y, pos.z)) - .filter(|l| **l != OPAQUE && **l != UNKNOWN) - .map(|l| *l as f32 / SUNLIGHT as f32) - .unwrap_or(0.0)) + Some( + light_map + .get(lm_idx(pos.x, pos.y, pos.z)) + .filter(|l| **l != OPAQUE && **l != UNKNOWN) + .map(|l| *l as f32 / SUNLIGHT as f32) + .unwrap_or(0.0), + ) } } } diff --git a/voxygen/src/mesh/vol.rs b/voxygen/src/mesh/vol.rs index ae693a9bb9..90f8b11ee0 100644 --- a/voxygen/src/mesh/vol.rs +++ b/voxygen/src/mesh/vol.rs @@ -45,11 +45,12 @@ fn get_ao_quad( for x in 0..2 { for y in 0..2 { let dark_pos = shift + offs[0] * x + offs[1] * y + 1; - if let Some(dark) = unsafe { darknesses - .get_unchecked(dark_pos.z as usize) - .get_unchecked(dark_pos.y as usize) - .get_unchecked(dark_pos.x as usize) } - { + if let Some(dark) = unsafe { + darknesses + .get_unchecked(dark_pos.z as usize) + .get_unchecked(dark_pos.y as usize) + .get_unchecked(dark_pos.x as usize) + } { darkness += dark; total += 1.0; } diff --git a/voxygen/src/scene/mod.rs b/voxygen/src/scene/mod.rs index 4689ea6fc2..5a0a15e4e9 100644 --- a/voxygen/src/scene/mod.rs +++ b/voxygen/src/scene/mod.rs @@ -240,7 +240,10 @@ impl Scene { ); // Tick camera for interpolation. - self.camera.update(scene_data.state.get_time(), scene_data.state.get_delta_time()); + self.camera.update( + scene_data.state.get_time(), + scene_data.state.get_delta_time(), + ); // Compute camera matrices. self.camera.compute_dependents(&*scene_data.state.terrain()); diff --git a/voxygen/src/scene/terrain.rs b/voxygen/src/scene/terrain.rs index a620e5b44f..c3cea733cc 100644 --- a/voxygen/src/scene/terrain.rs +++ b/voxygen/src/scene/terrain.rs @@ -190,6 +190,22 @@ fn sprite_config_for(kind: BlockKind) -> Option { variations: 3, wind_sway: 0.0, }), + BlockKind::Flax => Some(SpriteConfig { + variations: 6, + wind_sway: 0.4, + }), + BlockKind::Carrot => Some(SpriteConfig { + variations: 6, + wind_sway: 0.1, + }), + BlockKind::Tomato => Some(SpriteConfig { + variations: 5, + wind_sway: 0.0, + }), + BlockKind::Radish => Some(SpriteConfig { + variations: 5, + wind_sway: 0.1, + }), BlockKind::Coconut => Some(SpriteConfig { variations: 1, wind_sway: 0.0, @@ -1301,6 +1317,147 @@ impl Terrain { Vec3::new(-6.0, -6.0, 0.0), ), ), + // Flax + ( + (BlockKind::Flax, 0), + make_model( + "voxygen.voxel.sprite.flax.flax-0", + Vec3::new(-6.0, -6.0, 0.0), + ), + ), + ( + (BlockKind::Flax, 1), + make_model( + "voxygen.voxel.sprite.flax.flax-1", + Vec3::new(-6.0, -6.0, 0.0), + ), + ), + ( + (BlockKind::Flax, 2), + make_model( + "voxygen.voxel.sprite.flax.flax-2", + Vec3::new(-6.0, -6.0, 0.0), + ), + ), + ( + (BlockKind::Flax, 3), + make_model( + "voxygen.voxel.sprite.flax.flax-3", + Vec3::new(-6.0, -6.0, 0.0), + ), + ), + ( + (BlockKind::Flax, 4), + make_model( + "voxygen.voxel.sprite.flax.flax-4", + Vec3::new(-6.0, -6.0, 0.0), + ), + ), + ( + (BlockKind::Flax, 5), + make_model( + "voxygen.voxel.sprite.flax.flax-5", + Vec3::new(-6.0, -6.0, 0.0), + ), + ), + // Carrot + ( + (BlockKind::Carrot, 0), + make_model( + "voxygen.voxel.sprite.carrot.0", + Vec3::new(-5.5, -5.5, -0.25), + ), + ), + ( + (BlockKind::Carrot, 1), + make_model( + "voxygen.voxel.sprite.carrot.1", + Vec3::new(-5.5, -5.5, -0.25), + ), + ), + ( + (BlockKind::Carrot, 2), + make_model( + "voxygen.voxel.sprite.carrot.2", + Vec3::new(-5.5, -5.5, -0.25), + ), + ), + ( + (BlockKind::Carrot, 3), + make_model( + "voxygen.voxel.sprite.carrot.3", + Vec3::new(-5.5, -5.5, -0.25), + ), + ), + ( + (BlockKind::Carrot, 4), + make_model( + "voxygen.voxel.sprite.carrot.4", + Vec3::new(-5.5, -5.5, -0.25), + ), + ), + ( + (BlockKind::Carrot, 5), + make_model( + "voxygen.voxel.sprite.carrot.5", + Vec3::new(-5.5, -5.5, -0.25), + ), + ), + ( + (BlockKind::Tomato, 0), + make_model("voxygen.voxel.sprite.tomato.0", Vec3::new(-5.5, -5.5, 0.0)), + ), + ( + (BlockKind::Tomato, 1), + make_model("voxygen.voxel.sprite.tomato.1", Vec3::new(-5.5, -5.5, 0.0)), + ), + ( + (BlockKind::Tomato, 2), + make_model("voxygen.voxel.sprite.tomato.2", Vec3::new(-5.5, -5.5, 0.0)), + ), + ( + (BlockKind::Tomato, 3), + make_model("voxygen.voxel.sprite.tomato.3", Vec3::new(-5.5, -5.5, 0.0)), + ), + ( + (BlockKind::Tomato, 4), + make_model("voxygen.voxel.sprite.tomato.4", Vec3::new(-5.5, -5.5, 0.0)), + ), + ( + (BlockKind::Radish, 0), + make_model( + "voxygen.voxel.sprite.radish.0", + Vec3::new(-5.5, -5.5, -0.25), + ), + ), + ( + (BlockKind::Radish, 1), + make_model( + "voxygen.voxel.sprite.radish.1", + Vec3::new(-5.5, -5.5, -0.25), + ), + ), + ( + (BlockKind::Radish, 2), + make_model( + "voxygen.voxel.sprite.radish.2", + Vec3::new(-5.5, -5.5, -0.25), + ), + ), + ( + (BlockKind::Radish, 3), + make_model( + "voxygen.voxel.sprite.radish.3", + Vec3::new(-5.5, -5.5, -0.25), + ), + ), + ( + (BlockKind::Radish, 4), + make_model( + "voxygen.voxel.sprite.radish.4", + Vec3::new(-5.5, -5.5, -0.25), + ), + ), // Coconut ( (BlockKind::Coconut, 0), diff --git a/world/examples/water.rs b/world/examples/water.rs index 40f80a39a3..0011211c9a 100644 --- a/world/examples/water.rs +++ b/world/examples/water.rs @@ -150,10 +150,14 @@ fn main() { } if win.get_mouse_down(minifb::MouseButton::Left) { if let Some((mx, my)) = win.get_mouse_pos(minifb::MouseMode::Clamp) { - let chunk_pos = (Vec2::::from(focus) + (Vec2::new(mx as f64, my as f64) * scale)) + let chunk_pos = (Vec2::::from(focus) + + (Vec2::new(mx as f64, my as f64) * scale)) .map(|e| e as i32); let block_pos = chunk_pos.map2(TerrainChunkSize::RECT_SIZE, |e, f| e * f as i32); - println!("Block: ({}, {}), Chunk: ({}, {})", block_pos.x, block_pos.y, chunk_pos.x, chunk_pos.y); + println!( + "Block: ({}, {}), Chunk: ({}, {})", + block_pos.x, block_pos.y, chunk_pos.x, chunk_pos.y + ); if let Some(chunk) = sampler.get(chunk_pos) { //println!("Chunk info: {:#?}", chunk); if let Some(id) = &chunk.place { diff --git a/world/src/block/mod.rs b/world/src/block/mod.rs index c52953dd02..ebbfe5ecc5 100644 --- a/world/src/block/mod.rs +++ b/world/src/block/mod.rs @@ -66,7 +66,11 @@ impl<'a> BlockGen<'a> { // Conservative range of radius: [8, 47] let radius = RandomField::new(seed + 2).get(cliff_pos3d) % 48 + 8; - if cliff_sample.water_dist.map(|d| d > radius as f32).unwrap_or(true) { + if cliff_sample + .water_dist + .map(|d| d > radius as f32) + .unwrap_or(true) + { max_height.max( if cliff_pos.map(|e| e as f32).distance_squared(wpos) < (radius as f32 + tolerance).powf(2.0) diff --git a/world/src/civ/econ.rs b/world/src/civ/econ.rs index a0d3390aea..97181d94c7 100644 --- a/world/src/civ/econ.rs +++ b/world/src/civ/econ.rs @@ -1,5 +1,5 @@ -use rand::prelude::*; use super::GenCtx; +use rand::prelude::*; pub struct SellOrder { pub quantity: f32, @@ -42,25 +42,27 @@ impl Belief { pub fn buy_units<'a>( ctx: &mut GenCtx, - sellers: impl Iterator, + sellers: impl Iterator, max_quantity: f32, max_price: f32, max_spend: f32, ) -> (f32, f32) { - let mut sell_orders = sellers - .filter(|so| so.quantity > 0.0) - .collect::>(); + let mut sell_orders = sellers.filter(|so| so.quantity > 0.0).collect::>(); // Sort sell orders by price, cheapest first - sell_orders.sort_by(|a, b| a.price.partial_cmp(&b.price).unwrap_or_else(|| panic!("{} and {}", a.price, b.price))); + sell_orders.sort_by(|a, b| { + a.price + .partial_cmp(&b.price) + .unwrap_or_else(|| panic!("{} and {}", a.price, b.price)) + }); let mut quantity = 0.0; let mut spent = 0.0; for order in sell_orders { - if - quantity >= max_quantity || // We've purchased enough + if quantity >= max_quantity || // We've purchased enough spent >= max_spend || // We've spent enough - order.price > max_price // Price is too high + order.price > max_price + // Price is too high { break; } else { diff --git a/world/src/civ/mod.rs b/world/src/civ/mod.rs index bc881b6dae..36b315c676 100644 --- a/world/src/civ/mod.rs +++ b/world/src/civ/mod.rs @@ -1,27 +1,23 @@ mod econ; -use std::{ - ops::Range, - hash::Hash, - fmt, +use crate::{ + sim::{SimChunk, WorldSim}, + site::{Dungeon, Settlement, Site as WorldSite}, + util::{attempt, seed_expan}, }; -use hashbrown::{HashMap, HashSet}; -use vek::*; -use rand::prelude::*; -use rand_chacha::ChaChaRng; use common::{ + astar::Astar, + path::Path, + spiral::Spiral2d, + store::{Id, Store}, terrain::TerrainChunkSize, vol::RectVolSize, - store::{Id, Store}, - path::Path, - astar::Astar, - spiral::Spiral2d, -}; -use crate::{ - sim::{WorldSim, SimChunk}, - site::{Site as WorldSite, Settlement, Dungeon}, - util::{seed_expan, attempt}, }; +use hashbrown::{HashMap, HashSet}; +use rand::prelude::*; +use rand_chacha::ChaChaRng; +use std::{fmt, hash::Hash, ops::Range}; +use vek::*; const CARDINALS: [Vec2; 4] = [ Vec2::new(1, 0), @@ -107,13 +103,15 @@ impl Civs { // Temporary! for track in this.tracks.iter() { for loc in track.path.iter() { - ctx.sim.get_mut(*loc).unwrap().place = Some(this.civs.iter().next().unwrap().homeland); + ctx.sim.get_mut(*loc).unwrap().place = + Some(this.civs.iter().next().unwrap().homeland); } } // Flatten ground around sites for site in this.sites.iter() { - if let SiteKind::Settlement = &site.kind {} else { + if let SiteKind::Settlement = &site.kind { + } else { continue; } @@ -124,9 +122,16 @@ impl Civs { let flatten_radius = 10.0; if let Some(center_alt) = ctx.sim.get_alt_approx(wpos) { for offs in Spiral2d::new().take(radius.pow(2) as usize) { - let center_alt = center_alt + if offs.magnitude_squared() <= 6i32.pow(2) { 16.0 } else { 0.0 }; // Raise the town centre up a little + let center_alt = center_alt + + if offs.magnitude_squared() <= 6i32.pow(2) { + 16.0 + } else { + 0.0 + }; // Raise the town centre up a little let pos = site.center + offs; - let factor = (1.0 - (site.center - pos).map(|e| e as f32).magnitude() / flatten_radius) * 1.15; + let factor = (1.0 + - (site.center - pos).map(|e| e as f32).magnitude() / flatten_radius) + * 1.15; ctx.sim .get_mut(pos) // Don't disrupt chunks that are near water @@ -147,12 +152,20 @@ impl Civs { let wpos = site.center * Vec2::from(TerrainChunkSize::RECT_SIZE).map(|e: u32| e as i32); let world_site = match &site.kind { - SiteKind::Settlement => WorldSite::from(Settlement::generate(wpos, Some(ctx.sim), ctx.rng)), - SiteKind::Dungeon => WorldSite::from(Dungeon::generate(wpos, Some(ctx.sim), ctx.rng)), + SiteKind::Settlement => { + WorldSite::from(Settlement::generate(wpos, Some(ctx.sim), ctx.rng)) + }, + SiteKind::Dungeon => { + WorldSite::from(Dungeon::generate(wpos, Some(ctx.sim), ctx.rng)) + }, }; - let radius_chunks = (world_site.radius() / TerrainChunkSize::RECT_SIZE.x as f32).ceil() as usize; - for pos in Spiral2d::new().map(|offs| site.center + offs).take((radius_chunks * 2).pow(2)) { + let radius_chunks = + (world_site.radius() / TerrainChunkSize::RECT_SIZE.x as f32).ceil() as usize; + for pos in Spiral2d::new() + .map(|offs| site.center + offs) + .take((radius_chunks * 2).pow(2)) + { ctx.sim .get_mut(pos) .map(|chunk| chunk.sites.push(world_site.clone())); @@ -167,9 +180,7 @@ impl Civs { pub fn place(&self, id: Id) -> &Place { self.places.get(id) } - pub fn sites(&self) -> impl Iterator + '_ { - self.sites.iter() - } + pub fn sites(&self) -> impl Iterator + '_ { self.sites.iter() } fn display_info(&self) { for (id, civ) in self.civs.iter_ids() { @@ -189,24 +200,39 @@ impl Civs { self.track_map .get(&a) .and_then(|dests| dests.get(&b)) - .or_else(|| self.track_map - .get(&b) - .and_then(|dests| dests.get(&a))) + .or_else(|| self.track_map.get(&b).and_then(|dests| dests.get(&a))) .copied() } /// Return an iterator over a site's neighbors - fn neighbors(&self, site: Id) -> impl Iterator> + '_ { - let to = self.track_map.get(&site).map(|dests| dests.keys()).into_iter().flatten(); - let fro = self.track_map.iter().filter(move |(_, dests)| dests.contains_key(&site)).map(|(p, _)| p); + fn neighbors(&self, site: Id) -> impl Iterator> + '_ { + let to = self + .track_map + .get(&site) + .map(|dests| dests.keys()) + .into_iter() + .flatten(); + let fro = self + .track_map + .iter() + .filter(move |(_, dests)| dests.contains_key(&site)) + .map(|(p, _)| p); to.chain(fro).filter(move |p| **p != site).copied() } /// Find the cheapest route between two places fn route_between(&self, a: Id, b: Id) -> Option<(Path>, f32)> { - let heuristic = move |p: &Id| (self.sites.get(*p).center.distance_squared(self.sites.get(b).center) as f32).sqrt(); + let heuristic = move |p: &Id| { + (self + .sites + .get(*p) + .center + .distance_squared(self.sites.get(b).center) as f32) + .sqrt() + }; let neighbors = |p: &Id| self.neighbors(*p); - let transition = |a: &Id, b: &Id| self.tracks.get(self.track_between(*a, *b).unwrap()).cost; + let transition = + |a: &Id, b: &Id| self.tracks.get(self.track_between(*a, *b).unwrap()).cost; let satisfied = |p: &Id| *p == b; let mut astar = Astar::new(100, a, heuristic); astar @@ -248,7 +274,12 @@ impl Civs { Some(civ) } - fn establish_place(&mut self, ctx: &mut GenCtx, loc: Vec2, area: Range) -> Option> { + fn establish_place( + &mut self, + ctx: &mut GenCtx, + loc: Vec2, + area: Range, + ) -> Option> { let mut dead = HashSet::new(); let mut alive = HashSet::new(); alive.insert(loc); @@ -258,7 +289,13 @@ impl Civs { for dir in CARDINALS.iter() { if site_in_dir(&ctx.sim, cloc, *dir) { let rloc = cloc + *dir; - if !dead.contains(&rloc) && ctx.sim.get(rloc).map(|c| c.place.is_none()).unwrap_or(false) { + if !dead.contains(&rloc) + && ctx + .sim + .get(rloc) + .map(|c| c.place.is_none()) + .unwrap_or(false) + { alive.insert(rloc); } } @@ -291,7 +328,12 @@ impl Civs { Some(place) } - fn establish_site(&mut self, ctx: &mut GenCtx, loc: Vec2, site_fn: impl FnOnce(Id) -> Site) -> Option> { + fn establish_site( + &mut self, + ctx: &mut GenCtx, + loc: Vec2, + site_fn: impl FnOnce(Id) -> Site, + ) -> Option> { const SITE_AREA: Range = 64..256; let place = match ctx.sim.get(loc).and_then(|site| site.place) { @@ -303,7 +345,8 @@ impl Civs { // Find neighbors const MAX_NEIGHBOR_DISTANCE: f32 = 250.0; - let mut nearby = self.sites + let mut nearby = self + .sites .iter_ids() .map(|(id, p)| (id, (p.center.distance_squared(loc) as f32).sqrt())) .filter(|(p, dist)| *dist < MAX_NEIGHBOR_DISTANCE) @@ -320,10 +363,7 @@ impl Civs { .filter(|(_, route_cost)| *route_cost < cost * 3.0) .is_none() { - let track = self.tracks.insert(Track { - cost, - path, - }); + let track = self.tracks.insert(Track { cost, path }); self.track_map .entry(site) .or_default() @@ -342,15 +382,17 @@ impl Civs { // Trade stocks // let mut stocks = TRADE_STOCKS; - // stocks.shuffle(ctx.rng); // Give each stock a chance to be traded first - // for stock in stocks.iter().copied() { + // stocks.shuffle(ctx.rng); // Give each stock a chance to be traded + // first for stock in stocks.iter().copied() { // let mut sell_orders = self.sites // .iter_ids() // .map(|(id, site)| (id, { // econ::SellOrder { - // quantity: site.export_targets[stock].max(0.0).min(site.stocks[stock]), - // price: site.trade_states[stock].sell_belief.choose_price(ctx) * 1.25, // Trade cost - // q_sold: 0.0, + // quantity: + // site.export_targets[stock].max(0.0).min(site.stocks[stock]), + // price: + // site.trade_states[stock].sell_belief.choose_price(ctx) * 1.25, // + // Trade cost q_sold: 0.0, // } // })) // .filter(|(_, order)| order.quantity > 0.0) @@ -364,17 +406,17 @@ impl Civs { // let (max_spend, max_price, max_import) = { // let site = self.sites.get(site); // let budget = site.coin * 0.5; - // let total_value = site.values.iter().map(|(_, v)| (*v).unwrap_or(0.0)).sum::(); - // ( - // 100000.0,//(site.values[stock].unwrap_or(0.1) / total_value * budget).min(budget), - // site.trade_states[stock].buy_belief.price, - // -site.export_targets[stock].min(0.0), - // ) + // let total_value = site.values.iter().map(|(_, v)| + // (*v).unwrap_or(0.0)).sum::(); ( + // 100000.0,//(site.values[stock].unwrap_or(0.1) / + // total_value * budget).min(budget), + // site.trade_states[stock].buy_belief.price, + // -site.export_targets[stock].min(0.0), ) // }; // let (quantity, spent) = econ::buy_units(ctx, sell_orders // .iter_mut() - // .filter(|(id, _)| site != *id && self.track_between(site, *id).is_some()) - // .map(|(_, order)| order), + // .filter(|(id, _)| site != *id && self.track_between(site, + // *id).is_some()) .map(|(_, order)| order), // max_import, // 1000000.0, // Max price TODO // max_spend, @@ -384,9 +426,9 @@ impl Civs { // if quantity > 0.0 { // site.stocks[stock] += quantity; // site.last_exports[stock] = -quantity; - // site.trade_states[stock].buy_belief.update_buyer(years, spent / quantity); - // println!("Belief: {:?}", site.trade_states[stock].buy_belief); - // } + // site.trade_states[stock].buy_belief.update_buyer(years, + // spent / quantity); println!("Belief: {:?}", + // site.trade_states[stock].buy_belief); } // } // for (site, order) in sell_orders { @@ -395,22 +437,31 @@ impl Civs { // if order.q_sold > 0.0 { // site.stocks[stock] -= order.q_sold; // site.last_exports[stock] = order.q_sold; - // site.trade_states[stock].sell_belief.update_seller(order.q_sold / order.quantity); - // } + // + // site.trade_states[stock].sell_belief.update_seller(order.q_sold / + // order.quantity); } // } // } } } /// Attempt to find a path between two locations -fn find_path(ctx: &mut GenCtx, a: Vec2, b: Vec2) -> Option<(Path>, f32)> { +fn find_path( + ctx: &mut GenCtx, + a: Vec2, + b: Vec2, +) -> Option<(Path>, f32)> { let sim = &ctx.sim; let heuristic = move |l: &Vec2| (l.distance_squared(b) as f32).sqrt(); let neighbors = |l: &Vec2| { let l = *l; - DIAGONALS.iter().filter(move |dir| walk_in_dir(sim, l, **dir).is_some()).map(move |dir| l + *dir) + DIAGONALS + .iter() + .filter(move |dir| walk_in_dir(sim, l, **dir).is_some()) + .map(move |dir| l + *dir) }; - let transition = |a: &Vec2, b: &Vec2| 1.0 + walk_in_dir(sim, *a, *b - *a).unwrap_or(10000.0); + let transition = + |a: &Vec2, b: &Vec2| 1.0 + walk_in_dir(sim, *a, *b - *a).unwrap_or(10000.0); let satisfied = |l: &Vec2| *l == b; let mut astar = Astar::new(20000, a, heuristic); astar @@ -419,11 +470,10 @@ fn find_path(ctx: &mut GenCtx, a: Vec2, b: Vec2) -> Option<( .and_then(|path| astar.get_cheapest_cost().map(|cost| (path, cost))) } -/// Return true if travel between a location and a chunk next to it is permitted (TODO: by whom?) +/// Return true if travel between a location and a chunk next to it is permitted +/// (TODO: by whom?) fn walk_in_dir(sim: &WorldSim, a: Vec2, dir: Vec2) -> Option { - if loc_suitable_for_walking(sim, a) && - loc_suitable_for_walking(sim, a + dir) - { + if loc_suitable_for_walking(sim, a) && loc_suitable_for_walking(sim, a + dir) { let a_alt = sim.get(a)?.alt; let b_alt = sim.get(a + dir)?.alt; Some((b_alt - a_alt).abs() / 2.5) @@ -441,18 +491,22 @@ fn loc_suitable_for_walking(sim: &WorldSim, loc: Vec2) -> bool { } } -/// Return true if a site could be constructed between a location and a chunk next to it is permitted (TODO: by whom?) +/// Return true if a site could be constructed between a location and a chunk +/// next to it is permitted (TODO: by whom?) fn site_in_dir(sim: &WorldSim, a: Vec2, dir: Vec2) -> bool { - loc_suitable_for_site(sim, a) && - loc_suitable_for_site(sim, a + dir) + loc_suitable_for_site(sim, a) && loc_suitable_for_site(sim, a + dir) } -/// Return true if a position is suitable for site construction (TODO: criteria?) +/// Return true if a position is suitable for site construction (TODO: +/// criteria?) fn loc_suitable_for_site(sim: &WorldSim, loc: Vec2) -> bool { if let Some(chunk) = sim.get(loc) { - !chunk.river.is_ocean() && - !chunk.river.is_lake() && - sim.get_gradient_approx(loc).map(|grad| grad < 1.0).unwrap_or(false) + !chunk.river.is_ocean() + && !chunk.river.is_lake() + && sim + .get_gradient_approx(loc) + .map(|grad| grad < 1.0) + .unwrap_or(false) } else { false } @@ -464,10 +518,15 @@ fn find_site_loc(ctx: &mut GenCtx, near: Option<(Vec2, f32)>) -> let mut loc = None; for _ in 0..MAX_ATTEMPTS { let test_loc = loc.unwrap_or_else(|| match near { - Some((origin, dist)) => origin + (Vec2::new( - ctx.rng.gen_range(-1.0, 1.0), - ctx.rng.gen_range(-1.0, 1.0), - ).try_normalized().unwrap_or(Vec2::zero()) * ctx.rng.gen::() * dist).map(|e| e as i32), + Some((origin, dist)) => { + origin + + (Vec2::new(ctx.rng.gen_range(-1.0, 1.0), ctx.rng.gen_range(-1.0, 1.0)) + .try_normalized() + .unwrap_or(Vec2::zero()) + * ctx.rng.gen::() + * dist) + .map(|e| e as i32) + }, None => Vec2::new( ctx.rng.gen_range(0, ctx.sim.get_size().x as i32), ctx.rng.gen_range(0, ctx.sim.get_size().y as i32), @@ -478,9 +537,14 @@ fn find_site_loc(ctx: &mut GenCtx, near: Option<(Vec2, f32)>) -> return Some(test_loc); } - loc = ctx.sim.get(test_loc).and_then(|c| Some(c.downhill?.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| { - e / (sz as i32) - }))); + loc = ctx.sim.get(test_loc).and_then(|c| { + Some( + c.downhill? + .map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| { + e / (sz as i32) + }), + ) + }); } None } @@ -508,23 +572,36 @@ pub struct NaturalResources { impl NaturalResources { fn include_chunk(&mut self, ctx: &mut GenCtx, loc: Vec2) { - let chunk = if let Some(chunk) = ctx.sim.get(loc) { chunk } else { return }; + let chunk = if let Some(chunk) = ctx.sim.get(loc) { + chunk + } else { + return; + }; self.wood += chunk.tree_density; self.rock += chunk.rockiness; self.river += if chunk.river.is_river() { 5.0 } else { 0.0 }; - self.farmland += if - chunk.humidity > 0.35 && - chunk.temp > -0.3 && chunk.temp < 0.75 && - chunk.chaos < 0.5 && - ctx.sim.get_gradient_approx(loc).map(|grad| grad < 0.7).unwrap_or(false) - { 1.0 } else { 0.0 }; + self.farmland += if chunk.humidity > 0.35 + && chunk.temp > -0.3 + && chunk.temp < 0.75 + && chunk.chaos < 0.5 + && ctx + .sim + .get_gradient_approx(loc) + .map(|grad| grad < 0.7) + .unwrap_or(false) + { + 1.0 + } else { + 0.0 + }; } } pub struct Track { - /// Cost of using this track relative to other paths. This cost is an arbitrary unit and - /// doesn't make sense unless compared to other track costs. + /// Cost of using this track relative to other paths. This cost is an + /// arbitrary unit and doesn't make sense unless compared to other track + /// costs. cost: f32, path: Path>, } @@ -570,7 +647,14 @@ impl fmt::Display for Site { } writeln!(f, "Values")?; for stock in TRADE_STOCKS.iter() { - writeln!(f, "- {}: {}", stock, self.values[*stock].map(|x| x.to_string()).unwrap_or_else(|| "N/A".to_string()))?; + writeln!( + f, + "- {}: {}", + stock, + self.values[*stock] + .map(|x| x.to_string()) + .unwrap_or_else(|| "N/A".to_string()) + )?; } writeln!(f, "Laborers")?; for (labor, n) in self.labors.iter() { @@ -619,8 +703,8 @@ impl Site { (Some(HUNTER), vec![(GAME, 4.0)]), (Some(FARMER), vec![(WHEAT, 4.0)]), ] - .into_iter() - .collect::>>(); + .into_iter() + .collect::>>(); // Per labourer, per year let production = Stocks::from_list(&[ @@ -634,7 +718,11 @@ impl Site { let mut demand = Stocks::from_default(0.0); for (labor, orders) in &orders { - let scale = if let Some(labor) = labor { self.labors[*labor] } else { 1.0 } * self.population; + let scale = if let Some(labor) = labor { + self.labors[*labor] + } else { + 1.0 + } * self.population; for (stock, amount) in orders { demand[*stock] += *amount * scale; } @@ -647,20 +735,27 @@ impl Site { let last_exports = &self.last_exports; let stocks = &self.stocks; - self.surplus = demand.clone().map(|stock, tgt| { - supply[stock] + stocks[stock] - demand[stock] - last_exports[stock] - }); + self.surplus = demand + .clone() + .map(|stock, tgt| supply[stock] + stocks[stock] - demand[stock] - last_exports[stock]); // Update values according to the surplus of each stock let values = &mut self.values; self.surplus.iter().for_each(|(stock, surplus)| { let val = 3.5f32.powf(1.0 - *surplus / demand[stock]); - values[stock] = if val > 0.001 && val < 1000.0 { Some(val) } else { None }; + values[stock] = if val > 0.001 && val < 1000.0 { + Some(val) + } else { + None + }; }); // Update export targets based on relative values - let value_avg = - values.iter().map(|(_, v)| (*v).unwrap_or(0.0)).sum::().max(0.01) + let value_avg = values + .iter() + .map(|(_, v)| (*v).unwrap_or(0.0)) + .sum::() + .max(0.01) / values.iter().filter(|(_, v)| v.is_some()).count() as f32; let export_targets = &mut self.export_targets; let last_exports = &self.last_exports; @@ -668,7 +763,7 @@ impl Site { self.values.iter().for_each(|(stock, value)| { let rvalue = (*value).map(|v| v - value_avg).unwrap_or(0.0); //let factor = if export_targets[stock] > 0.0 { 1.0 / rvalue } else { rvalue }; - export_targets[stock] = last_exports[stock] - rvalue * 0.1;// + (trade_states[stock].sell_belief.price - trade_states[stock].buy_belief.price) * 0.025; + export_targets[stock] = last_exports[stock] - rvalue * 0.1; // + (trade_states[stock].sell_belief.price - trade_states[stock].buy_belief.price) * 0.025; }); let population = self.population; @@ -680,17 +775,24 @@ impl Site { let labor_ratio_sum = labor_ratios.iter().map(|(_, r)| *r).sum::().max(0.01); production.iter().for_each(|(labor, _)| { let smooth = 0.8; - self.labors[labor] = smooth * self.labors[labor] + (1.0 - smooth) * (labor_ratios[labor].max(labor_ratio_sum / 1000.0) / labor_ratio_sum); + self.labors[labor] = smooth * self.labors[labor] + + (1.0 - smooth) + * (labor_ratios[labor].max(labor_ratio_sum / 1000.0) / labor_ratio_sum); }); // Production let stocks_before = self.stocks.clone(); for (labor, orders) in orders.iter() { - let scale = if let Some(labor) = labor { self.labors[*labor] } else { 1.0 } * population; + let scale = if let Some(labor) = labor { + self.labors[*labor] + } else { + 1.0 + } * population; - // For each order, we try to find the minimum satisfaction rate - this limits how much - // we can produce! For example, if we need 0.25 fish and 0.75 oats to make 1 unit of - // food, but only 0.5 units of oats are available then we only need to consume 2/3rds + // For each order, we try to find the minimum satisfaction rate - this limits + // how much we can produce! For example, if we need 0.25 fish and + // 0.75 oats to make 1 unit of food, but only 0.5 units of oats are + // available then we only need to consume 2/3rds // of other ingredients and leave the rest in stock // In effect, this is the productivity let productivity = orders @@ -703,7 +805,9 @@ impl Site { satisfaction }) .min_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap_or_else(|| panic!("Industry {:?} requires at least one input order", labor)); + .unwrap_or_else(|| { + panic!("Industry {:?} requires at least one input order", labor) + }); for (stock, amount) in orders { // What quantity is this order requesting? @@ -733,7 +837,11 @@ impl Site { // Births/deaths const NATURAL_BIRTH_RATE: f32 = 0.15; const DEATH_RATE: f32 = 0.05; - let birth_rate = if self.surplus[FOOD] > 0.0 { NATURAL_BIRTH_RATE } else { 0.0 }; + let birth_rate = if self.surplus[FOOD] > 0.0 { + NATURAL_BIRTH_RATE + } else { + 0.0 + }; self.population += years * self.population * (birth_rate - DEATH_RATE); } } @@ -757,13 +865,7 @@ const LOGS: Stock = "logs"; const WOOD: Stock = "wood"; const ROCK: Stock = "rock"; const STONE: Stock = "stone"; -const TRADE_STOCKS: [Stock; 5] = [ - FLOUR, - MEAT, - FOOD, - WOOD, - STONE, -]; +const TRADE_STOCKS: [Stock; 5] = [FLOUR, MEAT, FOOD, WOOD, STONE]; #[derive(Debug, Clone)] struct TradeState { @@ -795,8 +897,10 @@ pub struct MapVec { } impl MapVec { - pub fn from_list<'a>(i: impl IntoIterator) -> Self - where K: 'a, T: 'a + pub fn from_list<'a>(i: impl IntoIterator) -> Self + where + K: 'a, + T: 'a, { Self { entries: i.into_iter().cloned().collect(), @@ -813,39 +917,37 @@ impl MapVec { pub fn get_mut(&mut self, entry: K) -> &mut T { let default = &self.default; - self - .entries - .entry(entry) - .or_insert_with(|| default.clone()) + self.entries.entry(entry).or_insert_with(|| default.clone()) } - pub fn get(&self, entry: K) -> &T { - self.entries.get(&entry).unwrap_or(&self.default) - } + pub fn get(&self, entry: K) -> &T { self.entries.get(&entry).unwrap_or(&self.default) } pub fn map(mut self, mut f: impl FnMut(K, T) -> U) -> MapVec { MapVec { - entries: self.entries.into_iter().map(|(s, v)| (s.clone(), f(s, v))).collect(), + entries: self + .entries + .into_iter() + .map(|(s, v)| (s.clone(), f(s, v))) + .collect(), default: U::default(), } } - pub fn iter(&self) -> impl Iterator + '_ { + pub fn iter(&self) -> impl Iterator + '_ { self.entries.iter().map(|(s, v)| (*s, v)) } - pub fn iter_mut(&mut self) -> impl Iterator + '_ { + pub fn iter_mut(&mut self) -> impl Iterator + '_ { self.entries.iter_mut().map(|(s, v)| (*s, v)) } } impl std::ops::Index for MapVec { type Output = T; + fn index(&self, entry: K) -> &Self::Output { self.get(entry) } } impl std::ops::IndexMut for MapVec { fn index_mut(&mut self, entry: K) -> &mut Self::Output { self.get_mut(entry) } } - - diff --git a/world/src/column/mod.rs b/world/src/column/mod.rs index 3377bc6ca5..94e993b9aa 100644 --- a/world/src/column/mod.rs +++ b/world/src/column/mod.rs @@ -1,10 +1,7 @@ use crate::{ all::ForestKind, block::StructureMeta, - sim::{ - local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, RiverKind, SimChunk, - WorldSim, - }, + sim::{local_cells, uniform_idx_as_vec2, vec2_as_uniform_idx, RiverKind, SimChunk, WorldSim}, util::{RandomPerm, Sampler, UnitChooser}, CONFIG, }; @@ -598,239 +595,270 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { river_chunk, max_border_river, max_border_river_dist, - )) = max_river + )) = + max_river { // This is flowing into a lake, or a lake, or is at least a non-ocean tile. // // If we are <= water_alt, we are in the lake; otherwise, we are flowing into // it. - let (in_water, water_dist, new_alt, new_water_alt, riverless_alt, warp_factor) = max_border_river - .river_kind - .and_then(|river_kind| { - if let RiverKind::River { cross_section } = river_kind { - if max_border_river_dist.map(|(_, dist, _, _)| dist) != Some(Vec2::zero()) { - return None; + let (in_water, water_dist, new_alt, new_water_alt, riverless_alt, warp_factor) = + max_border_river + .river_kind + .and_then(|river_kind| { + if let RiverKind::River { cross_section } = river_kind { + if max_border_river_dist.map(|(_, dist, _, _)| dist) + != Some(Vec2::zero()) + { + return None; + } + let ( + _, + _, + river_width, + (river_t, (river_pos, _), downhill_river_chunk), + ) = max_border_river_dist.unwrap(); + let river_alt = Lerp::lerp( + river_chunk.alt.max(river_chunk.water_alt), + downhill_river_chunk.alt.max(downhill_river_chunk.water_alt), + river_t as f32, + ); + let new_alt = river_alt - river_gouge; + let river_dist = wposf.distance(river_pos); + let river_height_factor = river_dist / (river_width * 0.5); + + let valley_alt = Lerp::lerp( + new_alt - cross_section.y.max(1.0), + new_alt - 1.0, + (river_height_factor * river_height_factor) as f32, + ); + + Some(( + true, + Some((river_dist - river_width * 0.5) as f32), + valley_alt, + new_alt, + river_alt, + 0.0, + )) + } else { + None } - let (_, _, river_width, (river_t, (river_pos, _), downhill_river_chunk)) = - max_border_river_dist.unwrap(); - let river_alt = Lerp::lerp( - river_chunk.alt.max(river_chunk.water_alt), - downhill_river_chunk.alt.max(downhill_river_chunk.water_alt), - river_t as f32, - ); - let new_alt = river_alt - river_gouge; - let river_dist = wposf.distance(river_pos); - let river_height_factor = river_dist / (river_width * 0.5); - - let valley_alt = Lerp::lerp( - new_alt - cross_section.y.max(1.0), - new_alt - 1.0, - (river_height_factor * river_height_factor) as f32, - ); - - Some(( - true, - Some((river_dist - river_width * 0.5) as f32), - valley_alt, - new_alt, - river_alt, - 0.0, - )) - } else { - None - } - }) - .unwrap_or_else(|| { - max_border_river - .river_kind - .and_then(|river_kind| { - match river_kind { - RiverKind::Ocean => { - let ( - _, - dist, - river_width, - (river_t, (river_pos, _), downhill_river_chunk), - ) = if let Some(dist) = max_border_river_dist { - dist - } else { - log::error!( - "Ocean: {:?} Here: {:?}, Ocean: {:?}", - max_border_river, - chunk_pos, - max_border_river_pos - ); - panic!( - "Oceans should definitely have a downhill! ...Right?" - ); - }; - let lake_water_alt = Lerp::lerp( - river_chunk.alt.max(river_chunk.water_alt), - downhill_river_chunk - .alt - .max(downhill_river_chunk.water_alt), - river_t as f32, - ); - - if dist == Vec2::zero() { - let river_dist = wposf.distance(river_pos); - let _river_height_factor = river_dist / (river_width * 0.5); - return Some(( - true, - Some((river_dist - river_width * 0.5) as f32), - alt_for_river.min(lake_water_alt - 1.0 - river_gouge), - lake_water_alt - river_gouge, - alt_for_river.max(lake_water_alt), - 0.0, - )); - } - - Some(( - river_scale_factor <= 1.0, - Some((wposf.distance(river_pos) - river_width * 0.5) as f32), - alt_for_river, - downhill_water_alt, - alt_for_river, - river_scale_factor as f32, - )) - }, - RiverKind::Lake { .. } => { - let lake_dist = (max_border_river_pos.map(|e| e as f64) - * neighbor_coef) - .distance(wposf); - let downhill_river_chunk = max_border_river_pos; - let lake_id_dist = downhill_river_chunk - chunk_pos; - let in_bounds = lake_id_dist.x >= -1 - && lake_id_dist.y >= -1 - && lake_id_dist.x <= 1 - && lake_id_dist.y <= 1; - let in_bounds = - in_bounds && (lake_id_dist.x >= 0 && lake_id_dist.y >= 0); - let (_, dist, _, (river_t, _, downhill_river_chunk)) = - if let Some(dist) = max_border_river_dist { + }) + .unwrap_or_else(|| { + max_border_river + .river_kind + .and_then(|river_kind| { + match river_kind { + RiverKind::Ocean => { + let ( + _, + dist, + river_width, + (river_t, (river_pos, _), downhill_river_chunk), + ) = if let Some(dist) = max_border_river_dist { dist } else { - if lake_dist - <= TerrainChunkSize::RECT_SIZE.x as f64 * 1.0 - || in_bounds - { - let gouge_factor = 0.0; - return Some(( - in_bounds - || downhill_water_alt + log::error!( + "Ocean: {:?} Here: {:?}, Ocean: {:?}", + max_border_river, + chunk_pos, + max_border_river_pos + ); + panic!( + "Oceans should definitely have a downhill! \ + ...Right?" + ); + }; + let lake_water_alt = Lerp::lerp( + river_chunk.alt.max(river_chunk.water_alt), + downhill_river_chunk + .alt + .max(downhill_river_chunk.water_alt), + river_t as f32, + ); + + if dist == Vec2::zero() { + let river_dist = wposf.distance(river_pos); + let _river_height_factor = + river_dist / (river_width * 0.5); + return Some(( + true, + Some((river_dist - river_width * 0.5) as f32), + alt_for_river + .min(lake_water_alt - 1.0 - river_gouge), + lake_water_alt - river_gouge, + alt_for_river.max(lake_water_alt), + 0.0, + )); + } + + Some(( + river_scale_factor <= 1.0, + Some( + (wposf.distance(river_pos) - river_width * 0.5) + as f32, + ), + alt_for_river, + downhill_water_alt, + alt_for_river, + river_scale_factor as f32, + )) + }, + RiverKind::Lake { .. } => { + let lake_dist = (max_border_river_pos.map(|e| e as f64) + * neighbor_coef) + .distance(wposf); + let downhill_river_chunk = max_border_river_pos; + let lake_id_dist = downhill_river_chunk - chunk_pos; + let in_bounds = lake_id_dist.x >= -1 + && lake_id_dist.y >= -1 + && lake_id_dist.x <= 1 + && lake_id_dist.y <= 1; + let in_bounds = in_bounds + && (lake_id_dist.x >= 0 && lake_id_dist.y >= 0); + let (_, dist, _, (river_t, _, downhill_river_chunk)) = + if let Some(dist) = max_border_river_dist { + dist + } else { + if lake_dist + <= TerrainChunkSize::RECT_SIZE.x as f64 * 1.0 + || in_bounds + { + let gouge_factor = 0.0; + return Some(( + in_bounds + || downhill_water_alt + .max(river_chunk.water_alt) + > alt_for_river, + Some(lake_dist as f32), + alt_for_river, + (downhill_water_alt .max(river_chunk.water_alt) - > alt_for_river, + - river_gouge), + alt_for_river, + river_scale_factor as f32 + * (1.0 - gouge_factor), + )); + } else { + return Some(( + false, + Some(lake_dist as f32), + alt_for_river, + downhill_water_alt, + alt_for_river, + river_scale_factor as f32, + )); + } + }; + + let lake_dist = dist.y; + let lake_water_alt = Lerp::lerp( + river_chunk.alt.max(river_chunk.water_alt), + downhill_river_chunk + .alt + .max(downhill_river_chunk.water_alt), + river_t as f32, + ); + if dist == Vec2::zero() { + return Some(( + true, + Some(lake_dist as f32), + alt_for_river + .min(lake_water_alt - 1.0 - river_gouge), + lake_water_alt - river_gouge, + alt_for_river.max(lake_water_alt), + 0.0, + )); + } + if lake_dist <= TerrainChunkSize::RECT_SIZE.x as f64 * 1.0 + || in_bounds + { + let gouge_factor = if in_bounds && lake_dist <= 1.0 { + 1.0 + } else { + 0.0 + }; + let in_bounds_ = lake_dist + <= TerrainChunkSize::RECT_SIZE.x as f64 * 0.5; + if gouge_factor == 1.0 { + return Some(( + true, Some(lake_dist as f32), + alt.min(lake_water_alt - 1.0 - river_gouge), + downhill_water_alt.max(lake_water_alt) + - river_gouge, + alt.max(lake_water_alt), + 0.0, + )); + } else { + return Some(( + true, + None, alt_for_river, - (downhill_water_alt.max(river_chunk.water_alt) - - river_gouge), + if in_bounds_ { + downhill_water_alt.max(lake_water_alt) + } else { + downhill_water_alt + } - river_gouge, alt_for_river, river_scale_factor as f32 * (1.0 - gouge_factor), )); - } else { - return Some(( - false, - Some(lake_dist as f32), - alt_for_river, - downhill_water_alt, - alt_for_river, - river_scale_factor as f32, - )); } - }; - - let lake_dist = dist.y; - let lake_water_alt = Lerp::lerp( - river_chunk.alt.max(river_chunk.water_alt), - downhill_river_chunk - .alt - .max(downhill_river_chunk.water_alt), - river_t as f32, - ); - if dist == Vec2::zero() { - return Some(( - true, - Some(lake_dist as f32), - alt_for_river.min(lake_water_alt - 1.0 - river_gouge), - lake_water_alt - river_gouge, - alt_for_river.max(lake_water_alt), - 0.0, - )); - } - if lake_dist <= TerrainChunkSize::RECT_SIZE.x as f64 * 1.0 - || in_bounds - { - let gouge_factor = if in_bounds && lake_dist <= 1.0 { - 1.0 - } else { - 0.0 - }; - let in_bounds_ = - lake_dist <= TerrainChunkSize::RECT_SIZE.x as f64 * 0.5; - if gouge_factor == 1.0 { - return Some(( - true, - Some(lake_dist as f32), - alt.min(lake_water_alt - 1.0 - river_gouge), - downhill_water_alt.max(lake_water_alt) - - river_gouge, - alt.max(lake_water_alt), - 0.0, - )); - } else { - return Some(( - true, - None, - alt_for_river, - if in_bounds_ { - downhill_water_alt.max(lake_water_alt) - } else { - downhill_water_alt - } - river_gouge, - alt_for_river, - river_scale_factor as f32 * (1.0 - gouge_factor), - )); } - } - Some(( - river_scale_factor <= 1.0, - Some(lake_dist as f32), - alt_for_river, - downhill_water_alt, - alt_for_river, - river_scale_factor as f32, - )) - }, - RiverKind::River { .. } => { - let (_, _, river_width, (_, (river_pos, _), _)) = - max_border_river_dist.unwrap(); - let river_dist = wposf.distance(river_pos); + Some(( + river_scale_factor <= 1.0, + Some(lake_dist as f32), + alt_for_river, + downhill_water_alt, + alt_for_river, + river_scale_factor as f32, + )) + }, + RiverKind::River { .. } => { + let (_, _, river_width, (_, (river_pos, _), _)) = + max_border_river_dist.unwrap(); + let river_dist = wposf.distance(river_pos); - // FIXME: Make water altitude accurate. - Some(( - river_scale_factor <= 1.0, - Some((river_dist - river_width * 0.5) as f32), - alt_for_river, - downhill_water_alt, - alt_for_river, - river_scale_factor as f32, - )) - }, - } - }) - .unwrap_or(( - false, - None, - alt_for_river, - downhill_water_alt, - alt_for_river, - river_scale_factor as f32, - )) - }); - (in_water, water_dist, new_alt, new_water_alt, riverless_alt, warp_factor) + // FIXME: Make water altitude accurate. + Some(( + river_scale_factor <= 1.0, + Some((river_dist - river_width * 0.5) as f32), + alt_for_river, + downhill_water_alt, + alt_for_river, + river_scale_factor as f32, + )) + }, + } + }) + .unwrap_or(( + false, + None, + alt_for_river, + downhill_water_alt, + alt_for_river, + river_scale_factor as f32, + )) + }); + ( + in_water, + water_dist, + new_alt, + new_water_alt, + riverless_alt, + warp_factor, + ) } else { - (false, None, alt_for_river, downhill_water_alt, alt_for_river, 1.0) + ( + false, + None, + alt_for_river, + downhill_water_alt, + alt_for_river, + 1.0, + ) }; let warp_factor = warp_factor * chunk_warp_factor; // NOTE: To disable warp, uncomment this line. @@ -1106,8 +1134,13 @@ impl<'a> Sampler<'a> for ColumnGen<'a> { ), sub_surface_color, // No growing directly on bedrock. - // And, no growing on sites that don't want them TODO: More precise than this when we apply trees as a post-processing layer - tree_density: if sim_chunk.sites.iter().all(|site| site.spawn_rules(wpos).trees) { + // And, no growing on sites that don't want them TODO: More precise than this when we + // apply trees as a post-processing layer + tree_density: if sim_chunk + .sites + .iter() + .all(|site| site.spawn_rules(wpos).trees) + { Lerp::lerp(0.0, tree_density, alt.sub(2.0).sub(basement).mul(0.5)) } else { 0.0 diff --git a/world/src/lib.rs b/world/src/lib.rs index 50137592a6..4775b1162b 100644 --- a/world/src/lib.rs +++ b/world/src/lib.rs @@ -4,12 +4,12 @@ mod all; mod block; +pub mod civ; mod column; pub mod config; pub mod sim; pub mod site; pub mod util; -pub mod civ; // Reexports pub use crate::config::CONFIG; @@ -72,17 +72,20 @@ impl World { let chunk_wpos2d = Vec2::from(chunk_pos) * TerrainChunkSize::RECT_SIZE.map(|e| e as i32); let grid_border = 4; - let zcache_grid = - Grid::populate_from(TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2, |offs| { - sampler.get_z_cache(chunk_wpos2d - grid_border + offs) - }); + let zcache_grid = Grid::populate_from( + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) + grid_border * 2, + |offs| sampler.get_z_cache(chunk_wpos2d - grid_border + offs), + ); let air = Block::empty(); - let stone = Block::new(BlockKind::Dense, zcache_grid - .get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2) - .and_then(|zcache| zcache.as_ref()) - .map(|zcache| zcache.sample.stone_col) - .unwrap_or(Rgb::new(125, 120, 130))); + let stone = Block::new( + BlockKind::Dense, + zcache_grid + .get(grid_border + TerrainChunkSize::RECT_SIZE.map(|e| e as i32) / 2) + .and_then(|zcache| zcache.as_ref()) + .map(|zcache| zcache.sample.stone_col) + .unwrap_or(Rgb::new(125, 120, 130)), + ); let water = Block::new(BlockKind::Water, Rgb::new(60, 90, 190)); let _chunk_size2d = TerrainChunkSize::RECT_SIZE; diff --git a/world/src/sim/erosion.rs b/world/src/sim/erosion.rs index 41db868ebe..27d4f05014 100644 --- a/world/src/sim/erosion.rs +++ b/world/src/sim/erosion.rs @@ -218,9 +218,7 @@ impl RiverData { pub fn near_river(&self) -> bool { self.is_river() || self.neighbor_rivers.len() > 0 } - pub fn near_water(&self) -> bool { - self.near_river() || self.is_lake() || self.is_ocean() - } + pub fn near_water(&self) -> bool { self.near_river() || self.is_lake() || self.is_ocean() } } /// Draw rivers and assign them heights, widths, and velocities. Take some diff --git a/world/src/sim/mod.rs b/world/src/sim/mod.rs index 9198d48bcb..894dd46b14 100644 --- a/world/src/sim/mod.rs +++ b/world/src/sim/mod.rs @@ -24,17 +24,17 @@ pub use self::{ use crate::{ all::ForestKind, block::BlockGen, + civ::Place, column::ColumnGen, site::{Settlement, Site}, - civ::Place, util::{seed_expan, FastNoise, RandomField, Sampler, StructureGen2d}, CONFIG, }; use common::{ assets, + store::Id, terrain::{BiomeKind, TerrainChunkSize}, vol::RectVolSize, - store::Id, }; use hashbrown::HashMap; use noise::{ @@ -1305,9 +1305,7 @@ impl WorldSim { this } - pub fn get_size(&self) -> Vec2 { - WORLD_SIZE.map(|e| e as u32) - } + pub fn get_size(&self) -> Vec2 { WORLD_SIZE.map(|e| e as u32) } /// Draw a map of the world based on chunk information. Returns a buffer of /// u32s. @@ -1558,9 +1556,11 @@ impl WorldSim { pub fn get_gradient_approx(&self, chunk_pos: Vec2) -> Option { let a = self.get(chunk_pos)?; if let Some(downhill) = a.downhill { - let b = self.get(downhill.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| { - e / (sz as i32) - }))?; + let b = self.get( + downhill.map2(Vec2::from(TerrainChunkSize::RECT_SIZE), |e, sz: u32| { + e / (sz as i32) + }), + )?; Some((a.alt - b.alt).abs() / TerrainChunkSize::RECT_SIZE.x as f32) } else { Some(0.0) @@ -1868,7 +1868,8 @@ impl SimChunk { ) }; - //let cliff = gen_ctx.cliff_nz.get((wposf.div(2048.0)).into_array()) as f32 + chaos * 0.2; + //let cliff = gen_ctx.cliff_nz.get((wposf.div(2048.0)).into_array()) as f32 + + // chaos * 0.2; let cliff = 0.0; // Disable cliffs // Logistic regression. Make sure x ∈ (0, 1). @@ -2033,7 +2034,9 @@ impl SimChunk { } } - pub fn is_underwater(&self) -> bool { self.water_alt > self.alt || self.river.river_kind.is_some() } + pub fn is_underwater(&self) -> bool { + self.water_alt > self.alt || self.river.river_kind.is_some() + } pub fn get_base_z(&self) -> f32 { self.alt - self.chaos * 50.0 - 16.0 } diff --git a/world/src/site/dungeon/mod.rs b/world/src/site/dungeon/mod.rs index 99cefc0ea9..cb1f76ede8 100644 --- a/world/src/site/dungeon/mod.rs +++ b/world/src/site/dungeon/mod.rs @@ -1,19 +1,19 @@ +use super::SpawnRules; use crate::{ column::ColumnSample, sim::{SimChunk, WorldSim}, - util::{attempt, Grid, RandomField, Sampler, StructureGen2d}, site::BlockMask, + util::{attempt, Grid, RandomField, Sampler, StructureGen2d}, }; -use super::SpawnRules; use common::{ astar::Astar, + comp::Alignment, + generation::{ChunkSupplement, EntityInfo, EntityKind}, path::Path, spiral::Spiral2d, - terrain::{Block, BlockKind, TerrainChunkSize}, - vol::{BaseVol, RectSizedVol, RectVolSize, ReadVol, WriteVol, Vox}, store::{Id, Store}, - generation::{ChunkSupplement, EntityInfo}, - comp::{Alignment}, + terrain::{Block, BlockKind, TerrainChunkSize}, + vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol}, }; use hashbrown::{HashMap, HashSet}; use rand::prelude::*; @@ -22,16 +22,13 @@ use vek::*; impl WorldSim { fn can_host_dungeon(&self, pos: Vec2) -> bool { - self - .get(pos) - .map(|chunk| { - !chunk.near_cliffs && !chunk.river.is_river() && !chunk.river.is_lake() - }) - .unwrap_or(false) - && self - .get_gradient_approx(pos) - .map(|grad| grad > 0.25 && grad < 1.5) + self.get(pos) + .map(|chunk| !chunk.near_cliffs && !chunk.river.is_river() && !chunk.river.is_lake()) .unwrap_or(false) + && self + .get_gradient_approx(pos) + .map(|grad| grad > 0.25 && grad < 1.5) + .unwrap_or(false) } } @@ -52,7 +49,11 @@ impl Dungeon { let mut ctx = GenCtx { sim, rng }; let mut this = Self { origin: wpos, - alt: ctx.sim.and_then(|sim| sim.get_alt_approx(wpos)).unwrap_or(0.0) as i32 + 6, + alt: ctx + .sim + .and_then(|sim| sim.get_alt_approx(wpos)) + .unwrap_or(0.0) as i32 + + 6, noise: RandomField::new(ctx.rng.gen()), floors: (0..6) .scan(Vec2::zero(), |stair_tile, level| { @@ -196,23 +197,25 @@ pub struct Floor { const FLOOR_SIZE: Vec2 = Vec2::new(18, 18); impl Floor { - pub fn generate(ctx: &mut GenCtx, stair_tile: Vec2, level: i32) -> (Self, Vec2) { - let new_stair_tile = std::iter::from_fn(|| Some(FLOOR_SIZE.map(|sz| ctx.rng.gen_range(-sz / 2 + 1, sz / 2)))) - .filter(|pos| *pos != stair_tile) - .take(8) - .max_by_key(|pos| (*pos - stair_tile).map(|e| e.abs()).sum()) - .unwrap(); + pub fn generate( + ctx: &mut GenCtx, + stair_tile: Vec2, + level: i32, + ) -> (Self, Vec2) { + let new_stair_tile = std::iter::from_fn(|| { + Some(FLOOR_SIZE.map(|sz| ctx.rng.gen_range(-sz / 2 + 1, sz / 2))) + }) + .filter(|pos| *pos != stair_tile) + .take(8) + .max_by_key(|pos| (*pos - stair_tile).map(|e| e.abs()).sum()) + .unwrap(); let tile_offset = -FLOOR_SIZE / 2; let mut this = Floor { tile_offset, tiles: Grid::new(FLOOR_SIZE, Tile::Solid), rooms: Store::default(), - solid_depth: if level == 0 { - 80 - } else { - 13 * 2 - }, + solid_depth: if level == 0 { 80 } else { 13 * 2 }, hollow_depth: 13, stair_tile: new_stair_tile - tile_offset, }; @@ -224,10 +227,16 @@ impl Floor { this.create_route(ctx, a.center(), b.center(), true); } } - this.create_route(ctx, stair_tile - tile_offset, new_stair_tile - tile_offset, false); + this.create_route( + ctx, + stair_tile - tile_offset, + new_stair_tile - tile_offset, + false, + ); this.tiles.set(stair_tile - tile_offset, Tile::UpStair); - this.tiles.set(new_stair_tile - tile_offset, Tile::DownStair); + this.tiles + .set(new_stair_tile - tile_offset, Tile::DownStair); (this, new_stair_tile) } @@ -238,15 +247,16 @@ impl Floor { for _ in 0..n { let area = match attempt(30, || { let sz = Vec2::::zero().map(|_| ctx.rng.gen_range(dim_limits.0, dim_limits.1)); - let pos = FLOOR_SIZE.map2(sz, |floor_sz, room_sz| ctx.rng.gen_range(0, floor_sz + 1 - room_sz)); + let pos = FLOOR_SIZE.map2(sz, |floor_sz, room_sz| { + ctx.rng.gen_range(0, floor_sz + 1 - room_sz) + }); let area = Rect::from((pos, Extent2::from(sz))); let area_border = Rect::from((pos - 1, Extent2::from(sz) + 2)); // The room, but with some personal space // Ensure no overlap - if self.rooms - .iter() - .any(|r| r.area.collides_with_rect(area_border) || r.area.contains_point(self.stair_tile)) - { + if self.rooms.iter().any(|r| { + r.area.collides_with_rect(area_border) || r.area.contains_point(self.stair_tile) + }) { return None; } @@ -265,18 +275,27 @@ impl Floor { for x in 0..area.extent().w { for y in 0..area.extent().h { - self.tiles.set(area.position() + Vec2::new(x, y), Tile::Room(room)); + self.tiles + .set(area.position() + Vec2::new(x, y), Tile::Room(room)); } } } } - fn create_route(&mut self, ctx: &mut GenCtx, a: Vec2, b: Vec2, optimise_longest: bool) { + fn create_route( + &mut self, + ctx: &mut GenCtx, + a: Vec2, + b: Vec2, + optimise_longest: bool, + ) { let sim = &ctx.sim; - let heuristic = move |l: &Vec2| if optimise_longest { - (l.distance_squared(b) as f32).sqrt() - } else { - 100.0 - (l.distance_squared(b) as f32).sqrt() + let heuristic = move |l: &Vec2| { + if optimise_longest { + (l.distance_squared(b) as f32).sqrt() + } else { + 100.0 - (l.distance_squared(b) as f32).sqrt() + } }; let neighbors = |l: &Vec2| { let l = *l; @@ -294,7 +313,13 @@ impl Floor { let satisfied = |l: &Vec2| *l == b; let mut astar = Astar::new(20000, a, heuristic); let path = astar - .poll(FLOOR_SIZE.product() as usize + 1, heuristic, neighbors, transition, satisfied) + .poll( + FLOOR_SIZE.product() as usize + 1, + heuristic, + neighbors, + transition, + satisfied, + ) .into_path() .expect("No route between locations - this shouldn't be able to happen"); @@ -305,11 +330,19 @@ impl Floor { } } - pub fn apply_supplement(&self, area: Aabr, origin: Vec3, supplement: &mut ChunkSupplement) { - let align = |e: i32| e.div_euclid(TILE_SIZE) + if e.rem_euclid(TILE_SIZE) > TILE_SIZE / 2 { - 1 - } else { - 0 + pub fn apply_supplement( + &self, + area: Aabr, + origin: Vec3, + supplement: &mut ChunkSupplement, + ) { + let align = |e: i32| { + e.div_euclid(TILE_SIZE) + + if e.rem_euclid(TILE_SIZE) > TILE_SIZE / 2 { + 1 + } else { + 0 + } }; let aligned_area = Aabr { min: area.min.map(align) + self.tile_offset, @@ -321,10 +354,16 @@ impl Floor { let tile_pos = Vec2::new(x, y); if let Some(Tile::Room(room)) = self.tiles.get(tile_pos) { let room = &self.rooms[*room]; - if room.enemies && tile_pos.x % 4 == 0 && tile_pos.y % 4 == 0 { // Bad - let entity = EntityInfo::at((origin + Vec3::from(self.tile_offset + tile_pos) * TILE_SIZE + TILE_SIZE / 2).map(|e| e as f32)) - .into_giant() - .with_alignment(Alignment::Enemy); + if room.enemies && tile_pos.x % 4 == 0 && tile_pos.y % 4 == 0 { + // Bad + let entity = EntityInfo::at( + (origin + + Vec3::from(self.tile_offset + tile_pos) * TILE_SIZE + + TILE_SIZE / 2) + .map(|e| e as f32), + ) + .into_giant() + .with_alignment(Alignment::Enemy); supplement.add_entity(entity); } } @@ -332,22 +371,26 @@ impl Floor { } } - pub fn total_depth(&self) -> i32 { - self.solid_depth + self.hollow_depth - } + pub fn total_depth(&self) -> i32 { self.solid_depth + self.hollow_depth } pub fn nearest_wall(&self, rpos: Vec2) -> Option> { let tile_pos = rpos.map(|e| e.div_euclid(TILE_SIZE)); let tile_center = tile_pos * TILE_SIZE + TILE_SIZE / 2; - DIRS - .iter() + DIRS.iter() .map(|dir| tile_pos + *dir) - .filter(|other_tile_pos| self.tiles.get(*other_tile_pos).filter(|tile| tile.is_passable()).is_none()) - .map(|other_tile_pos| rpos.clamped( - other_tile_pos * TILE_SIZE, - (other_tile_pos + 1) * TILE_SIZE - 1, - )) + .filter(|other_tile_pos| { + self.tiles + .get(*other_tile_pos) + .filter(|tile| tile.is_passable()) + .is_none() + }) + .map(|other_tile_pos| { + rpos.clamped( + other_tile_pos * TILE_SIZE, + (other_tile_pos + 1) * TILE_SIZE - 1, + ) + }) .min_by_key(|nearest| rpos.distance_squared(*nearest)) } @@ -365,7 +408,11 @@ impl Floor { if (pos.xy().magnitude_squared() as f32) < inner_radius.powf(2.0) { stone } else if (pos.xy().magnitude_squared() as f32) < radius.powf(2.0) { - if ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch + pos.z as f32).rem_euclid(stretch) < 1.5 { + if ((pos.x as f32).atan2(pos.y as f32) / (f32::consts::PI * 2.0) * stretch + + pos.z as f32) + .rem_euclid(stretch) + < 1.5 + { stone } else { empty @@ -376,38 +423,54 @@ impl Floor { }; let wall_thickness = 3.0; - let dist_to_wall = self.nearest_wall(rpos).map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()).unwrap_or(TILE_SIZE as f32); - let tunnel_dist = 1.0 - (dist_to_wall.powf(2.0) - wall_thickness).max(0.0).sqrt() / TILE_SIZE as f32; + let dist_to_wall = self + .nearest_wall(rpos) + .map(|nearest| (nearest.distance_squared(rpos) as f32).sqrt()) + .unwrap_or(TILE_SIZE as f32); + let tunnel_dist = + 1.0 - (dist_to_wall.powf(2.0) - wall_thickness).max(0.0).sqrt() / TILE_SIZE as f32; - move |z| { - match self.tiles.get(tile_pos) { - Some(Tile::Solid) => BlockMask::nothing(), - Some(Tile::Tunnel) => { - if (z as f32) < 8.0 - 8.0 * tunnel_dist.powf(4.0) { - empty - } else { - BlockMask::nothing() - } - }, - Some(Tile::Room(_)) | Some(Tile::DownStair) if dist_to_wall < wall_thickness || z as f32 >= self.hollow_depth as f32 - 13.0 * tunnel_dist.powf(4.0) => BlockMask::nothing(), - Some(Tile::Room(room)) => { - let room = &self.rooms[*room]; - if z == 0 && RandomField::new(room.seed).chance(Vec3::from(pos), room.loot_density) { - BlockMask::new(Block::new(BlockKind::Chest, Rgb::white()), 1) - } else { - empty - } - }, - Some(Tile::DownStair) => make_staircase(Vec3::new(rtile_pos.x, rtile_pos.y, z), 0.0, 0.5, 9.0).resolve_with(empty), - Some(Tile::UpStair) => { - let mut block = make_staircase(Vec3::new(rtile_pos.x, rtile_pos.y, z), TILE_SIZE as f32 / 2.0, 0.5, 9.0); - if z < self.hollow_depth { - block = block.resolve_with(empty); - } - block - }, - None => BlockMask::nothing(), - } + move |z| match self.tiles.get(tile_pos) { + Some(Tile::Solid) => BlockMask::nothing(), + Some(Tile::Tunnel) => { + if (z as f32) < 8.0 - 8.0 * tunnel_dist.powf(4.0) { + empty + } else { + BlockMask::nothing() + } + }, + Some(Tile::Room(_)) | Some(Tile::DownStair) + if dist_to_wall < wall_thickness + || z as f32 >= self.hollow_depth as f32 - 13.0 * tunnel_dist.powf(4.0) => + { + BlockMask::nothing() + }, + Some(Tile::Room(room)) => { + let room = &self.rooms[*room]; + if z == 0 && RandomField::new(room.seed).chance(Vec3::from(pos), room.loot_density) + { + BlockMask::new(Block::new(BlockKind::Chest, Rgb::white()), 1) + } else { + empty + } + }, + Some(Tile::DownStair) => { + make_staircase(Vec3::new(rtile_pos.x, rtile_pos.y, z), 0.0, 0.5, 9.0) + .resolve_with(empty) + }, + Some(Tile::UpStair) => { + let mut block = make_staircase( + Vec3::new(rtile_pos.x, rtile_pos.y, z), + TILE_SIZE as f32 / 2.0, + 0.5, + 9.0, + ); + if z < self.hollow_depth { + block = block.resolve_with(empty); + } + block + }, + None => BlockMask::nothing(), } } } diff --git a/world/src/site/mod.rs b/world/src/site/mod.rs index baebf2814a..44775325ba 100644 --- a/world/src/site/mod.rs +++ b/world/src/site/mod.rs @@ -1,18 +1,17 @@ -mod settlement; mod dungeon; +mod settlement; // Reexports -pub use self::settlement::Settlement; -pub use self::dungeon::Dungeon; +pub use self::{dungeon::Dungeon, settlement::Settlement}; use crate::{ column::ColumnSample, util::{Grid, Sampler}, }; use common::{ - terrain::Block, - vol::{Vox, BaseVol, RectSizedVol, ReadVol, WriteVol}, generation::ChunkSupplement, + terrain::Block, + vol::{BaseVol, ReadVol, RectSizedVol, Vox, WriteVol}, }; use std::{fmt, sync::Arc}; use vek::*; @@ -24,9 +23,7 @@ pub struct BlockMask { } impl BlockMask { - pub fn new(block: Block, priority: i32) -> Self { - Self { block, priority } - } + pub fn new(block: Block, priority: i32) -> Self { Self { block, priority } } pub fn nothing() -> Self { Self { @@ -62,11 +59,7 @@ pub struct SpawnRules { } impl Default for SpawnRules { - fn default() -> Self { - Self { - trees: true, - } - } + fn default() -> Self { Self { trees: true } } } #[derive(Clone)] @@ -109,7 +102,9 @@ impl Site { supplement: &mut ChunkSupplement, ) { match self { - Site::Settlement(settlement) => settlement.apply_supplement(wpos2d, get_column, supplement), + Site::Settlement(settlement) => { + settlement.apply_supplement(wpos2d, get_column, supplement) + }, Site::Dungeon(dungeon) => dungeon.apply_supplement(wpos2d, get_column, supplement), } } diff --git a/world/src/site/settlement/building/archetype/house.rs b/world/src/site/settlement/building/archetype/house.rs index ae05edac7e..96c3279bc8 100644 --- a/world/src/site/settlement/building/archetype/house.rs +++ b/world/src/site/settlement/building/archetype/house.rs @@ -1,17 +1,14 @@ -use vek::*; -use rand::prelude::*; +use super::{super::skeleton::*, Archetype}; +use crate::{ + site::BlockMask, + util::{RandomField, Sampler}, +}; use common::{ terrain::{Block, BlockKind}, vol::Vox, }; -use crate::{ - util::{RandomField, Sampler}, - site::BlockMask, -}; -use super::{ - Archetype, - super::skeleton::*, -}; +use rand::prelude::*; +use vek::*; const COLOR_THEMES: [Rgb; 11] = [ Rgb::new(0x1D, 0x4D, 0x45), @@ -53,8 +50,21 @@ enum StoreyFill { } impl StoreyFill { - fn has_lower(&self) -> bool { if let StoreyFill::All = self { true } else { false } } - fn has_upper(&self) -> bool { if let StoreyFill::None = self { false } else { true } } + fn has_lower(&self) -> bool { + if let StoreyFill::All = self { + true + } else { + false + } + } + + fn has_upper(&self) -> bool { + if let StoreyFill::None = self { + false + } else { + true + } + } } pub struct Attr { @@ -116,16 +126,21 @@ impl Archetype for House { .iter() .map(|flip| (0..branches_per_side).map(move |i| (i, *flip))) .flatten() - .filter_map(|(i, flip)| if rng.gen() { - Some((i as i32 * len / (branches_per_side - 1).max(1) as i32, Branch { - len: rng.gen_range(8, 16) * flip, - attr: Attr::generate(rng, locus), - locus: (6 + rng.gen_range(0, 3)).min(locus), - border: 4, - children: Vec::new(), - })) - } else { - None + .filter_map(|(i, flip)| { + if rng.gen() { + Some(( + i as i32 * len / (branches_per_side - 1).max(1) as i32, + Branch { + len: rng.gen_range(8, 16) * flip, + attr: Attr::generate(rng, locus), + locus: (6 + rng.gen_range(0, 3)).min(locus), + border: 4, + children: Vec::new(), + }, + )) + } else { + None + } }) .collect(), }, @@ -155,8 +170,17 @@ impl Archetype for House { let profile = Vec2::new(bound_offset.x, z); let make_block = |r, g, b| { - let nz = self.noise.get(Vec3::new(center_offset.x, center_offset.y, z * 8)); - BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(r, g, b).map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8))), 2) + let nz = self + .noise + .get(Vec3::new(center_offset.x, center_offset.y, z * 8)); + BlockMask::new( + Block::new( + BlockKind::Normal, + Rgb::new(r, g, b) + .map(|e: u8| e.saturating_add((nz & 0x0F) as u8).saturating_sub(8)), + ), + 2, + ) }; let facade_layer = 3; @@ -168,7 +192,8 @@ impl Archetype for House { let log = make_block(60, 45, 30); let floor = make_block(100, 75, 50); let wall = make_block(200, 180, 150).with_priority(facade_layer); - let roof = make_block(self.roof_color.r, self.roof_color.g, self.roof_color.b).with_priority(facade_layer); + let roof = make_block(self.roof_color.r, self.roof_color.g, self.roof_color.b) + .with_priority(facade_layer); let empty = BlockMask::nothing(); let internal = BlockMask::new(Block::empty(), structural_layer); let fire = BlockMask::new(Block::new(BlockKind::Ember, Rgb::white()), foundation_layer); @@ -176,13 +201,19 @@ impl Archetype for House { let ceil_height = 6; let lower_width = branch.locus - 1; let upper_width = branch.locus; - let width = if profile.y >= ceil_height { upper_width } else { lower_width }; + let width = if profile.y >= ceil_height { + upper_width + } else { + lower_width + }; let foundation_height = 0 - (dist - width - 1).max(0); let roof_top = 8 + width; if let Pillar::Chimney(chimney_top) = branch.attr.pillar { // Chimney shaft - if center_offset.map(|e| e.abs()).reduce_max() == 0 && profile.y >= foundation_height + 1 { + if center_offset.map(|e| e.abs()).reduce_max() == 0 + && profile.y >= foundation_height + 1 + { return if profile.y == foundation_height + 1 { fire } else { @@ -193,7 +224,10 @@ impl Archetype for House { // Chimney if center_offset.map(|e| e.abs()).reduce_max() <= 1 && profile.y < chimney_top { // Fireplace - if center_offset.product() == 0 && profile.y > foundation_height + 1 && profile.y <= foundation_height + 3 { + if center_offset.product() == 0 + && profile.y > foundation_height + 1 + && profile.y <= foundation_height + 3 + { return internal; } else { return foundation; @@ -201,16 +235,20 @@ impl Archetype for House { } } - if profile.y <= foundation_height && dist < width + 3 { // Foundations + if profile.y <= foundation_height && dist < width + 3 { + // Foundations if branch.attr.storey_fill.has_lower() { - if dist == width - 1 { // Floor lining + if dist == width - 1 { + // Floor lining return log.with_priority(floor_layer); - } else if dist < width - 1 && profile.y == foundation_height { // Floor + } else if dist < width - 1 && profile.y == foundation_height { + // Floor return floor.with_priority(floor_layer); } } - if dist < width && profile.y < foundation_height && profile.y >= foundation_height - 3 { // Basement + if dist < width && profile.y < foundation_height && profile.y >= foundation_height - 3 { + // Basement return internal; } else { return foundation.with_priority(1); @@ -218,102 +256,122 @@ impl Archetype for House { } // Roofs and walls - let do_roof_wall = |profile: Vec2, width, dist, bound_offset: Vec2, roof_top, mansard| { - // Roof + let do_roof_wall = + |profile: Vec2, width, dist, bound_offset: Vec2, roof_top, mansard| { + // Roof - let (roof_profile, roof_dist) = match &branch.attr.roof_style { - RoofStyle::Hip => (Vec2::new(dist, profile.y), dist), - RoofStyle::Gable => (profile, dist), - RoofStyle::Rounded => { - let circular_dist = (bound_offset.map(|e| e.pow(4) as f32).sum().powf(0.25) - 0.5).ceil() as i32; - (Vec2::new(circular_dist, profile.y), circular_dist) - }, - }; + let (roof_profile, roof_dist) = match &branch.attr.roof_style { + RoofStyle::Hip => (Vec2::new(dist, profile.y), dist), + RoofStyle::Gable => (profile, dist), + RoofStyle::Rounded => { + let circular_dist = (bound_offset.map(|e| e.pow(4) as f32).sum().powf(0.25) + - 0.5) + .ceil() as i32; + (Vec2::new(circular_dist, profile.y), circular_dist) + }, + }; - let roof_level = roof_top - roof_profile.x.max(mansard); + let roof_level = roof_top - roof_profile.x.max(mansard); - if profile.y > roof_level { - return None; - } - - // Roof - if profile.y == roof_level && roof_dist <= width + 2 { - let is_ribbing = ((profile.y - ceil_height) % 3 == 0 && self.roof_ribbing) - || (bound_offset.x == bound_offset.y && self.roof_ribbing_diagonal); - if (roof_profile.x == 0 && mansard == 0) || roof_dist == width + 2 || is_ribbing { // Eaves - return Some(log); - } else { - return Some(roof); + if profile.y > roof_level { + return None; } - } - // Wall - - if dist == width && profile.y < roof_level { - if bound_offset.x == bound_offset.y || profile.y == ceil_height { // Support beams - return Some(log); - } else if !branch.attr.storey_fill.has_lower() && profile.y < ceil_height { - return Some(empty); - } else if !branch.attr.storey_fill.has_upper() { - return Some(empty); - } else { - let frame_bounds = if profile.y >= ceil_height { - Aabr { - min: Vec2::new(-1, ceil_height + 2), - max: Vec2::new(1, ceil_height + 5), - } - } else { - Aabr { - min: Vec2::new(2, foundation_height + 2), - max: Vec2::new(width - 2, ceil_height - 2), - } - }; - let window_bounds = Aabr { - min: (frame_bounds.min + 1).map2(frame_bounds.center(), |a, b| a.min(b)), - max: (frame_bounds.max - 1).map2(frame_bounds.center(), |a, b| a.max(b)), - }; - - // Window - if (frame_bounds.size() + 1).reduce_min() > 2 { // Window frame is large enough for a window - let surface_pos = Vec2::new(bound_offset.x, profile.y); - if window_bounds.contains_point(surface_pos) { - return Some(internal); - } else if frame_bounds.contains_point(surface_pos) { - return Some(log.with_priority(3)); - }; - } - - // Wall - return Some(if branch.attr.central_supports && profile.x == 0 { // Support beams - log.with_priority(structural_layer) - } else { - wall - }); - } - } - - if dist < width { // Internals - if profile.y == ceil_height { - if profile.x == 0 {// Rafters + // Roof + if profile.y == roof_level && roof_dist <= width + 2 { + let is_ribbing = ((profile.y - ceil_height) % 3 == 0 && self.roof_ribbing) + || (bound_offset.x == bound_offset.y && self.roof_ribbing_diagonal); + if (roof_profile.x == 0 && mansard == 0) || roof_dist == width + 2 || is_ribbing + { + // Eaves return Some(log); - } else if branch.attr.storey_fill.has_upper() { // Ceiling - return Some(floor); + } else { + return Some(roof); } - } else if (!branch.attr.storey_fill.has_lower() && profile.y < ceil_height) - || (!branch.attr.storey_fill.has_upper() && profile.y >= ceil_height) - { - return Some(empty); - } else { - return Some(internal); } - } - None - }; + // Wall + + if dist == width && profile.y < roof_level { + if bound_offset.x == bound_offset.y || profile.y == ceil_height { + // Support beams + return Some(log); + } else if !branch.attr.storey_fill.has_lower() && profile.y < ceil_height { + return Some(empty); + } else if !branch.attr.storey_fill.has_upper() { + return Some(empty); + } else { + let frame_bounds = if profile.y >= ceil_height { + Aabr { + min: Vec2::new(-1, ceil_height + 2), + max: Vec2::new(1, ceil_height + 5), + } + } else { + Aabr { + min: Vec2::new(2, foundation_height + 2), + max: Vec2::new(width - 2, ceil_height - 2), + } + }; + let window_bounds = Aabr { + min: (frame_bounds.min + 1) + .map2(frame_bounds.center(), |a, b| a.min(b)), + max: (frame_bounds.max - 1) + .map2(frame_bounds.center(), |a, b| a.max(b)), + }; + + // Window + if (frame_bounds.size() + 1).reduce_min() > 2 { + // Window frame is large enough for a window + let surface_pos = Vec2::new(bound_offset.x, profile.y); + if window_bounds.contains_point(surface_pos) { + return Some(internal); + } else if frame_bounds.contains_point(surface_pos) { + return Some(log.with_priority(3)); + }; + } + + // Wall + return Some(if branch.attr.central_supports && profile.x == 0 { + // Support beams + log.with_priority(structural_layer) + } else { + wall + }); + } + } + + if dist < width { + // Internals + if profile.y == ceil_height { + if profile.x == 0 { + // Rafters + return Some(log); + } else if branch.attr.storey_fill.has_upper() { + // Ceiling + return Some(floor); + } + } else if (!branch.attr.storey_fill.has_lower() && profile.y < ceil_height) + || (!branch.attr.storey_fill.has_upper() && profile.y >= ceil_height) + { + return Some(empty); + } else { + return Some(internal); + } + } + + None + }; let mut cblock = empty; - if let Some(block) = do_roof_wall(profile, width, dist, bound_offset, roof_top, branch.attr.mansard) { + if let Some(block) = do_roof_wall( + profile, + width, + dist, + bound_offset, + roof_top, + branch.attr.mansard, + ) { cblock = cblock.resolve_with(block); } @@ -321,8 +379,15 @@ impl Archetype for House { let profile = Vec2::new(center_offset.x.abs(), profile.y); let dist = center_offset.map(|e| e.abs()).reduce_max(); - if let Some(block) = do_roof_wall(profile, 4, dist, center_offset.map(|e| e.abs()), tower_top, branch.attr.mansard) { - cblock = cblock.resolve_with(block); + if let Some(block) = do_roof_wall( + profile, + 4, + dist, + center_offset.map(|e| e.abs()), + tower_top, + branch.attr.mansard, + ) { + cblock = cblock.resolve_with(block); } } diff --git a/world/src/site/settlement/building/archetype/keep.rs b/world/src/site/settlement/building/archetype/keep.rs index af0e95314a..8758c67bae 100644 --- a/world/src/site/settlement/building/archetype/keep.rs +++ b/world/src/site/settlement/building/archetype/keep.rs @@ -1,14 +1,11 @@ -use vek::*; -use rand::prelude::*; +use super::{super::skeleton::*, Archetype}; +use crate::site::BlockMask; use common::{ terrain::{Block, BlockKind}, vol::Vox, }; -use super::{ - Archetype, - super::skeleton::*, -}; -use crate::site::BlockMask; +use rand::prelude::*; +use vek::*; pub struct Keep; @@ -26,13 +23,18 @@ impl Archetype for Keep { locus: 5 + rng.gen_range(0, 5), border: 3, children: (0..rng.gen_range(0, 4)) - .map(|_| (rng.gen_range(-5, len + 5).clamped(0, len.max(1) - 1), Branch { - len: rng.gen_range(5, 12) * if rng.gen() { 1 } else { -1 }, - attr: Self::Attr::default(), - locus: 5 + rng.gen_range(0, 3), - border: 3, - children: Vec::new(), - })) + .map(|_| { + ( + rng.gen_range(-5, len + 5).clamped(0, len.max(1) - 1), + Branch { + len: rng.gen_range(5, 12) * if rng.gen() { 1 } else { -1 }, + attr: Self::Attr::default(), + locus: 5 + rng.gen_range(0, 3), + border: 3, + children: Vec::new(), + }, + ) + }) .collect(), }, }; @@ -50,9 +52,8 @@ impl Archetype for Keep { ) -> BlockMask { let profile = Vec2::new(bound_offset.x, z); - let make_block = |r, g, b| { - BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(r, g, b)), 2) - }; + let make_block = + |r, g, b| BlockMask::new(Block::new(BlockKind::Normal, Rgb::new(r, g, b)), 2); let foundation = make_block(100, 100, 100); let log = make_block(60, 45, 30); @@ -65,7 +66,8 @@ impl Archetype for Keep { let roof_height = 12 + width; let ceil_height = 16; - if profile.y <= 1 - (dist - width - 1).max(0) && dist < width + 3 { // Foundations + if profile.y <= 1 - (dist - width - 1).max(0) && dist < width + 3 { + // Foundations foundation } else if profile.y == ceil_height && dist < rampart_width { roof diff --git a/world/src/site/settlement/building/archetype/mod.rs b/world/src/site/settlement/building/archetype/mod.rs index d029a9c819..a40b87035c 100644 --- a/world/src/site/settlement/building/archetype/mod.rs +++ b/world/src/site/settlement/building/archetype/mod.rs @@ -1,15 +1,17 @@ pub mod house; pub mod keep; -use vek::*; -use rand::prelude::*; use super::skeleton::*; use crate::site::BlockMask; +use rand::prelude::*; +use vek::*; pub trait Archetype { type Attr; - fn generate(rng: &mut R) -> (Self, Skeleton) where Self: Sized; + fn generate(rng: &mut R) -> (Self, Skeleton) + where + Self: Sized; fn draw( &self, dist: i32, diff --git a/world/src/site/settlement/building/mod.rs b/world/src/site/settlement/building/mod.rs index 3ddc7c3f04..9b44c5316a 100644 --- a/world/src/site/settlement/building/mod.rs +++ b/world/src/site/settlement/building/mod.rs @@ -1,13 +1,13 @@ -mod skeleton; mod archetype; +mod skeleton; // Reexports pub use self::archetype::Archetype; -use vek::*; -use rand::prelude::*; use self::skeleton::*; use common::terrain::Block; +use rand::prelude::*; +use vek::*; pub type HouseBuilding = Building; @@ -19,7 +19,8 @@ pub struct Building { impl Building { pub fn generate(rng: &mut impl Rng, origin: Vec3) -> Self - where A: Sized + where + A: Sized, { let len = rng.gen_range(-8, 12).max(0); let (archetype, skel) = A::generate(rng); @@ -48,8 +49,11 @@ impl Building { pub fn sample(&self, pos: Vec3) -> Option { let rpos = pos - self.origin; - self.skel.sample_closest(rpos.into(), |dist, bound_offset, center_offset, branch| { - self.archetype.draw(dist, bound_offset, center_offset, rpos.z, branch) - }).finish() + self.skel + .sample_closest(rpos.into(), |dist, bound_offset, center_offset, branch| { + self.archetype + .draw(dist, bound_offset, center_offset, rpos.z, branch) + }) + .finish() } } diff --git a/world/src/site/settlement/building/skeleton.rs b/world/src/site/settlement/building/skeleton.rs index 7b30f95ecc..ac2cc0404b 100644 --- a/world/src/site/settlement/building/skeleton.rs +++ b/world/src/site/settlement/building/skeleton.rs @@ -1,5 +1,5 @@ -use vek::*; use crate::site::BlockMask; +use vek::*; #[derive(Copy, Clone, PartialEq, Eq)] pub enum Ori { @@ -32,7 +32,14 @@ pub struct Branch { } impl Branch { - fn for_each<'a>(&'a self, node: Vec2, ori: Ori, is_child: bool, parent_locus: i32, f: &mut impl FnMut(Vec2, Ori, &'a Branch, bool, i32)) { + fn for_each<'a>( + &'a self, + node: Vec2, + ori: Ori, + is_child: bool, + parent_locus: i32, + f: &mut impl FnMut(Vec2, Ori, &'a Branch, bool, i32), + ) { f(node, ori, self, is_child, parent_locus); for (offset, child) in &self.children { child.for_each(node + ori.dir() * *offset, ori.flip(), true, self.locus, f); @@ -48,7 +55,8 @@ pub struct Skeleton { impl Skeleton { pub fn for_each<'a>(&'a self, mut f: impl FnMut(Vec2, Ori, &'a Branch, bool, i32)) { - self.root.for_each(self.ori.dir() * self.offset, self.ori, false, 0, &mut f); + self.root + .for_each(self.ori.dir() * self.offset, self.ori, false, 0, &mut f); } pub fn bounds(&self) -> Aabr { @@ -64,24 +72,35 @@ impl Skeleton { bounds } - pub fn sample_closest(&self, pos: Vec2, mut f: impl FnMut(i32, Vec2, Vec2, &Branch) -> BlockMask) -> BlockMask { + pub fn sample_closest( + &self, + pos: Vec2, + mut f: impl FnMut(i32, Vec2, Vec2, &Branch) -> BlockMask, + ) -> BlockMask { let mut min = None::<(_, BlockMask)>; self.for_each(|node, ori, branch, is_child, parent_locus| { let node2 = node + ori.dir() * branch.len; - let node = node + if is_child { ori.dir() * branch.len.signum() * (branch.locus - parent_locus).clamped(0, branch.len.abs()) } else { Vec2::zero() }; - let bounds = Aabr::new_empty(node) - .expanded_to_contain_point(node2); + let node = node + + if is_child { + ori.dir() + * branch.len.signum() + * (branch.locus - parent_locus).clamped(0, branch.len.abs()) + } else { + Vec2::zero() + }; + let bounds = Aabr::new_empty(node).expanded_to_contain_point(node2); let bound_offset = if ori == Ori::East { Vec2::new( node.y - pos.y, - pos.x - pos.x.clamped(bounds.min.x, bounds.max.x) + pos.x - pos.x.clamped(bounds.min.x, bounds.max.x), ) } else { Vec2::new( node.x - pos.x, - pos.y - pos.y.clamped(bounds.min.y, bounds.max.y) + pos.y - pos.y.clamped(bounds.min.y, bounds.max.y), ) - }.map(|e| e.abs()); + } + .map(|e| e.abs()); let center_offset = if ori == Ori::East { Vec2::new(pos.y - bounds.center().y, pos.x - bounds.center().x) } else { @@ -89,10 +108,13 @@ impl Skeleton { }; let dist = bound_offset.reduce_max(); let dist_locus = dist - branch.locus; - if !is_child || match ori { - Ori::East => (pos.x - node.x) * branch.len.signum() >= 0, - Ori::North => (pos.y - node.y) * branch.len.signum() >= 0, - } || true { + if !is_child + || match ori { + Ori::East => (pos.x - node.x) * branch.len.signum() >= 0, + Ori::North => (pos.y - node.y) * branch.len.signum() >= 0, + } + || true + { let new_bm = f(dist, bound_offset, center_offset, branch); min = min .map(|(_, bm)| (dist_locus, bm.resolve_with(new_bm))) diff --git a/world/src/site/settlement/mod.rs b/world/src/site/settlement/mod.rs index 789ebd062d..b89757cc8a 100644 --- a/world/src/site/settlement/mod.rs +++ b/world/src/site/settlement/mod.rs @@ -1,20 +1,20 @@ mod building; +use self::building::HouseBuilding; +use super::SpawnRules; use crate::{ column::ColumnSample, sim::{SimChunk, WorldSim}, util::{Grid, RandomField, Sampler, StructureGen2d}, }; -use self::building::HouseBuilding; -use super::SpawnRules; use common::{ astar::Astar, + generation::ChunkSupplement, path::Path, spiral::Spiral2d, - terrain::{Block, BlockKind, TerrainChunkSize}, - vol::{BaseVol, RectSizedVol, RectVolSize, ReadVol, WriteVol, Vox}, store::{Id, Store}, - generation::ChunkSupplement, + terrain::{Block, BlockKind, TerrainChunkSize}, + vol::{BaseVol, ReadVol, RectSizedVol, RectVolSize, Vox, WriteVol}, }; use hashbrown::{HashMap, HashSet}; use rand::prelude::*; @@ -74,16 +74,13 @@ pub fn center_of(p: [Vec2; 3]) -> Vec2 { impl WorldSim { fn can_host_settlement(&self, pos: Vec2) -> bool { - self - .get(pos) - .map(|chunk| { - !chunk.near_cliffs && !chunk.river.is_river() && !chunk.river.is_lake() - }) - .unwrap_or(false) - && self - .get_gradient_approx(pos) - .map(|grad| grad < 0.75) + self.get(pos) + .map(|chunk| !chunk.near_cliffs && !chunk.river.is_river() && !chunk.river.is_lake()) .unwrap_or(false) + && self + .get_gradient_approx(pos) + .map(|grad| grad < 0.75) + .unwrap_or(false) } } @@ -172,7 +169,8 @@ impl Settlement { let cpos = wpos.map(|e| e.div_euclid(TerrainChunkSize::RECT_SIZE.x as i32)); !sim.can_host_settlement(cpos) }) - || rng.gen_range(0, 16) == 0 // Randomly consider some tiles inaccessible + || rng.gen_range(0, 16) == 0 + // Randomly consider some tiles inaccessible { self.land.set(tile, hazard); } @@ -328,35 +326,49 @@ impl Settlement { return; }; - for tile in Spiral2d::new().map(|offs| town_center + offs).take(16usize.pow(2)) { + for tile in Spiral2d::new() + .map(|offs| town_center + offs) + .take(16usize.pow(2)) + { // This is a stupid way to decide how to place buildings for _ in 0..ctx.rng.gen_range(2, 5) { for _ in 0..25 { let house_pos = tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 / 2) - + Vec2::::zero().map(|_| ctx.rng.gen_range(-(AREA_SIZE as i32) / 2, AREA_SIZE as i32 / 2)); + + Vec2::::zero().map(|_| { + ctx.rng + .gen_range(-(AREA_SIZE as i32) / 2, AREA_SIZE as i32 / 2) + }); let tile_pos = house_pos.map(|e| e.div_euclid(AREA_SIZE as i32)); if !matches!(self.land.plot_at(tile_pos), Some(Plot::Town)) - || self.land.tile_at(tile_pos).map(|t| t.contains(WayKind::Path)).unwrap_or(true) + || self + .land + .tile_at(tile_pos) + .map(|t| t.contains(WayKind::Path)) + .unwrap_or(true) { continue; } let structure = Structure { - kind: StructureKind::House(HouseBuilding::generate(ctx.rng, Vec3::new( - house_pos.x, - house_pos.y, - ctx.sim - .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) - .unwrap_or(0.0) - .ceil() as i32, - ))), + kind: StructureKind::House(HouseBuilding::generate( + ctx.rng, + Vec3::new( + house_pos.x, + house_pos.y, + ctx.sim + .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) + .unwrap_or(0.0) + .ceil() as i32, + ), + )), }; let bounds = structure.bounds_2d(); // Check for collision with other structures - if self.structures + if self + .structures .iter() .any(|s| s.bounds_2d().collides_with_aabr(bounds)) { @@ -385,12 +397,13 @@ impl Settlement { // Farmhouses // for _ in 0..ctx.rng.gen_range(1, 3) { - // let house_pos = base_tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 / 2) - // + Vec2::new(ctx.rng.gen_range(-16, 16), ctx.rng.gen_range(-16, 16)); + // let house_pos = base_tile.map(|e| e * AREA_SIZE as i32 + AREA_SIZE as i32 + // / 2) + Vec2::new(ctx.rng.gen_range(-16, 16), + // ctx.rng.gen_range(-16, 16)); // self.structures.push(Structure { - // kind: StructureKind::House(HouseBuilding::generate(ctx.rng, Vec3::new( - // house_pos.x, + // kind: StructureKind::House(HouseBuilding::generate(ctx.rng, + // Vec3::new( house_pos.x, // house_pos.y, // ctx.sim // .and_then(|sim| sim.get_alt_approx(self.origin + house_pos)) @@ -421,11 +434,15 @@ impl Settlement { let field = self.land.new_plot(Plot::Field { farm, seed: rng.gen(), - crop: match rng.gen_range(0, 5) { + crop: match rng.gen_range(0, 8) { 0 => Crop::Corn, 1 => Crop::Wheat, 2 => Crop::Cabbage, 3 => Crop::Pumpkin, + 4 => Crop::Flax, + 5 => Crop::Carrot, + 6 => Crop::Tomato, + 7 => Crop::Radish, _ => Crop::Sunflower, }, }); @@ -447,7 +464,8 @@ impl Settlement { pub fn spawn_rules(&self, wpos: Vec2) -> SpawnRules { SpawnRules { - trees: self.land + trees: self + .land .get_at_block(wpos - self.origin) .plot .map(|p| if let Plot::Hazard = p { true } else { false }) @@ -484,19 +502,28 @@ impl Settlement { let noisy_color = |col: Rgb, factor: u32| { let nz = self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, surface_z)); - col.map(|e| (e as u32 + nz % (factor * 2)).saturating_sub(factor).min(255) as u8) + col.map(|e| { + (e as u32 + nz % (factor * 2)) + .saturating_sub(factor) + .min(255) as u8 + }) }; // Paths if let Some((WayKind::Path, dist, nearest)) = sample.way { let inset = -1; - // Try to use the column at the centre of the path for sampling to make them flatter - let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos)).unwrap_or(col_sample); + // Try to use the column at the centre of the path for sampling to make them + // flatter + let col = get_column(offs + (nearest.floor().map(|e| e as i32) - rpos)) + .unwrap_or(col_sample); let (bridge_offset, depth) = if let Some(water_dist) = col.water_dist { ( ((water_dist.max(0.0) * 0.2).min(f32::consts::PI).cos() + 1.0) * 5.0, - ((1.0 - ((water_dist + 2.0) * 0.3).min(0.0).cos().abs()) * (col.riverless_alt + 5.0 - col.alt).max(0.0) * 1.75 + 3.0) as i32, + ((1.0 - ((water_dist + 2.0) * 0.3).min(0.0).cos().abs()) + * (col.riverless_alt + 5.0 - col.alt).max(0.0) + * 1.75 + + 3.0) as i32, ) } else { (0.0, 3) @@ -528,10 +555,15 @@ impl Settlement { Some(Plot::Dirt) => Some(Rgb::new(90, 70, 50)), Some(Plot::Grass) => Some(Rgb::new(100, 200, 0)), Some(Plot::Water) => Some(Rgb::new(100, 150, 250)), - Some(Plot::Town) => Some(Rgb::new(150, 110, 60) - .map2(Rgb::iota(), |e: u8, i: i32| e - .saturating_add((self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 16) as u8) - .saturating_sub(8))), + Some(Plot::Town) => { + Some(Rgb::new(150, 110, 60).map2(Rgb::iota(), |e: u8, i: i32| { + e.saturating_add( + (self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, i * 5)) % 16) + as u8, + ) + .saturating_sub(8) + })) + }, Some(Plot::Field { seed, crop, .. }) => { let furrow_dirs = [ Vec2::new(1, 0), @@ -542,37 +574,60 @@ impl Settlement { let furrow_dir = furrow_dirs[*seed as usize % furrow_dirs.len()]; let in_furrow = (wpos2d * furrow_dir).sum().rem_euclid(5) < 2; - let roll = |seed, n| self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, seed * 5)) % n; + let roll = |seed, n| { + self.noise.get(Vec3::new(wpos2d.x, wpos2d.y, seed * 5)) % n + }; - let dirt = Rgb::new(80, 55, 35).map(|e| e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) % 32) as u8); - let mound = Rgb::new(70, 80, 30).map(|e| e + roll(0, 8) as u8).map(|e| e + (self.noise.get(Vec3::broadcast((seed % 4096 + 1) as i32)) % 32) as u8); + let dirt = Rgb::new(80, 55, 35).map(|e| { + e + (self.noise.get(Vec3::broadcast((seed % 4096 + 0) as i32)) % 32) + as u8 + }); + let mound = + Rgb::new(70, 80, 30).map(|e| e + roll(0, 8) as u8).map(|e| { + e + (self.noise.get(Vec3::broadcast((seed % 4096 + 1) as i32)) + % 32) as u8 + }); if in_furrow { if roll(0, 5) == 0 { surface_block = match crop { Crop::Corn => Some(BlockKind::Corn), - Crop::Wheat if roll(1, 2) == 0 => Some(BlockKind::WheatYellow), + Crop::Wheat if roll(1, 2) == 0 => { + Some(BlockKind::WheatYellow) + }, Crop::Wheat => Some(BlockKind::WheatGreen), - Crop::Cabbage if roll(2, 2) == 0 => Some(BlockKind::Cabbage), - Crop::Pumpkin if roll(3, 2) == 0 => Some(BlockKind::Pumpkin), + Crop::Cabbage if roll(2, 2) == 0 => { + Some(BlockKind::Cabbage) + }, + Crop::Pumpkin if roll(3, 2) == 0 => { + Some(BlockKind::Pumpkin) + }, + Crop::Flax if roll(4, 100) < 80 => Some(BlockKind::Flax), + Crop::Carrot if roll(5, 100) < 80 => { + Some(BlockKind::Carrot) + }, + Crop::Tomato if roll(6, 100) < 80 => { + Some(BlockKind::Tomato) + }, + Crop::Radish if roll(7, 100) < 80 => { + Some(BlockKind::Radish) + }, Crop::Sunflower => Some(BlockKind::Sunflower), _ => None, } - .map(|kind| Block::new(kind, Rgb::white())); + .map(|kind| Block::new(kind, Rgb::white())); } } else { if roll(0, 30) == 0 { - surface_block = Some(Block::new(BlockKind::ShortGrass, Rgb::white())); + surface_block = + Some(Block::new(BlockKind::ShortGrass, Rgb::white())); } else if roll(0, 30) == 0 { - surface_block = Some(Block::new(BlockKind::MediumGrass, Rgb::white())); + surface_block = + Some(Block::new(BlockKind::MediumGrass, Rgb::white())); } } - Some(if in_furrow { - dirt - } else { - mound - }) + Some(if in_furrow { dirt } else { mound }) }, _ => None, }; @@ -589,7 +644,10 @@ impl Settlement { vol.set(pos, Block::empty()); } } else { - vol.set(pos, Block::new(BlockKind::Normal, noisy_color(color, 4))); + vol.set( + pos, + Block::new(BlockKind::Normal, noisy_color(color, 4)), + ); } } } @@ -613,9 +671,7 @@ impl Settlement { } as i32; for z in z_offset..12 { - if dist / WayKind::Wall.width() - < ((1.0 - z as f32 / 12.0) * 2.0).min(1.0) - { + if dist / WayKind::Wall.width() < ((1.0 - z as f32 / 12.0) * 2.0).min(1.0) { vol.set( Vec3::new(offs.x, offs.y, surface_z + z), Block::new(BlockKind::Normal, color), @@ -655,13 +711,16 @@ impl Settlement { for x in bounds.min.x..bounds.max.x + 1 { for y in bounds.min.y..bounds.max.y + 1 { - let col = if let Some(col) = get_column(self.origin + Vec2::new(x, y) - wpos2d) { + let col = if let Some(col) = + get_column(self.origin + Vec2::new(x, y) - wpos2d) + { col } else { continue; }; - for z in bounds.min.z.min(col.alt.floor() as i32 - 1)..bounds.max.z + 1 { + for z in bounds.min.z.min(col.alt.floor() as i32 - 1)..bounds.max.z + 1 + { let rpos = Vec3::new(x, y, z); let wpos = Vec3::from(self.origin) + rpos; let coffs = wpos - Vec3::from(wpos2d); @@ -705,10 +764,12 @@ impl Settlement { Some(Plot::Dirt) => return Some(Rgb::new(90, 70, 50)), Some(Plot::Grass) => return Some(Rgb::new(100, 200, 0)), Some(Plot::Water) => return Some(Rgb::new(100, 150, 250)), - Some(Plot::Town) => return Some(Rgb::new(150, 110, 60) - .map2(Rgb::iota(), |e: u8, i: i32| e - .saturating_add((self.noise.get(Vec3::new(pos.x, pos.y, i * 5)) % 16) as u8) - .saturating_sub(8))), + Some(Plot::Town) => { + return Some(Rgb::new(150, 110, 60).map2(Rgb::iota(), |e: u8, i: i32| { + e.saturating_add((self.noise.get(Vec3::new(pos.x, pos.y, i * 5)) % 16) as u8) + .saturating_sub(8) + })); + }, Some(Plot::Field { seed, .. }) => { let furrow_dirs = [ Vec2::new(1, 0), @@ -741,6 +802,10 @@ pub enum Crop { Wheat, Cabbage, Pumpkin, + Flax, + Carrot, + Tomato, + Radish, Sunflower, } @@ -751,7 +816,11 @@ pub enum Plot { Grass, Water, Town, - Field { farm: Id, seed: u32, crop: Crop }, + Field { + farm: Id, + seed: u32, + crop: Crop, + }, } const CARDINALS: [Vec2; 4] = [ @@ -855,7 +924,8 @@ impl Land { let proj_point = line.projected_point(pos.map(|e| e as f32)); let dist = proj_point.distance(pos.map(|e| e as f32)); if dist < way.width() { - sample.way = sample.way + sample.way = sample + .way .filter(|(_, d, _)| *d < dist) .or(Some((way, dist, proj_point))); }