feat: partially implement Stronghold
also fixed some bugs
This commit is contained in:
parent
f36baa6f44
commit
a44a5babe3
128
Cargo.lock
generated
128
Cargo.lock
generated
@ -125,13 +125,12 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-compression"
|
name = "async-compression"
|
||||||
version = "0.4.36"
|
version = "0.4.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37"
|
checksum = "d10e4f991a553474232bc0a31799f6d24b034a84c0971d80d2e2f78b2e576e40"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"compression-codecs",
|
"compression-codecs",
|
||||||
"compression-core",
|
"compression-core",
|
||||||
"futures-core",
|
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
@ -210,9 +209,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base16ct"
|
name = "base16ct"
|
||||||
version = "0.3.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8b59d472eab27ade8d770dcb11da7201c11234bef9f82ce7aa517be028d462b"
|
checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
@ -228,9 +227,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64ct"
|
name = "base64ct"
|
||||||
version = "1.8.1"
|
version = "1.8.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a"
|
checksum = "7d809780667f4410e7c41b07f52439b94d2bdf8528eeedc287fa38d3b7f95d82"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
@ -282,9 +281,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.51"
|
version = "1.2.52"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7a0aeaff4ff1a90589618835a598e545176939b97874f7abc7851caa0618f203"
|
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"find-msvc-tools",
|
"find-msvc-tools",
|
||||||
"shlex",
|
"shlex",
|
||||||
@ -401,9 +400,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "compression-codecs"
|
name = "compression-codecs"
|
||||||
version = "0.4.35"
|
version = "0.4.36"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2"
|
checksum = "00828ba6fd27b45a448e57dbfe84f1029d4c9f26b368157e9a448a5f49a2ec2a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"compression-core",
|
"compression-core",
|
||||||
"flate2",
|
"flate2",
|
||||||
@ -463,9 +462,9 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "const-oid"
|
name = "const-oid"
|
||||||
version = "0.10.1"
|
version = "0.10.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e"
|
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "convert_case"
|
name = "convert_case"
|
||||||
@ -625,12 +624,13 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossfire"
|
name = "crossfire"
|
||||||
version = "2.1.9"
|
version = "2.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f19636ec7667a64122ad23414cd773dcdd32532a7df7a4f68ad16bba0db5f02"
|
checksum = "fd901251b9b46c1752c85edfee0aee718c03a85a065f4126d32e5d6d419edf48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossbeam-queue",
|
"crossbeam-queue",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
|
"enum_dispatch",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
]
|
]
|
||||||
@ -706,9 +706,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-common"
|
name = "crypto-common"
|
||||||
version = "0.2.0-rc.8"
|
version = "0.2.0-rc.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6165b8029cdc3e765b74d3548f85999ee799d5124877ce45c2c85ca78e4d4aa"
|
checksum = "41b8986f836d4aeb30ccf4c9d3bd562fd716074cfd7fc4a2948359fbd21ed809"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hybrid-array",
|
"hybrid-array",
|
||||||
]
|
]
|
||||||
@ -755,7 +755,7 @@ version = "0.8.0-rc.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653"
|
checksum = "02c1d73e9668ea6b6a28172aa55f3ebec38507131ce179051c8033b5c6037653"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-oid 0.10.1",
|
"const-oid 0.10.2",
|
||||||
"pem-rfc7468 1.0.0",
|
"pem-rfc7468 1.0.0",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
@ -810,8 +810,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "ebf9423bafb058e4142194330c52273c343f8a5beb7176d052f0e73b17dd35b9"
|
checksum = "ebf9423bafb058e4142194330c52273c343f8a5beb7176d052f0e73b17dd35b9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer 0.11.0",
|
"block-buffer 0.11.0",
|
||||||
"const-oid 0.10.1",
|
"const-oid 0.10.2",
|
||||||
"crypto-common 0.2.0-rc.8",
|
"crypto-common 0.2.0-rc.9",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -945,9 +945,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.6"
|
version = "0.1.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "645cbb3a84e60b7531617d5ae4e57f7e27308f6445f5abf653209ea76dec8dff"
|
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flate2"
|
name = "flate2"
|
||||||
@ -1129,9 +1129,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.12"
|
version = "0.4.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
|
checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -1447,9 +1447,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.12.1"
|
version = "2.13.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
@ -1498,9 +1498,9 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jiff"
|
name = "jiff"
|
||||||
version = "0.2.17"
|
version = "0.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a87d9b8105c23642f50cbbae03d1f75d8422c5cb98ce7ee9271f7ff7505be6b8"
|
checksum = "e67e8da4c49d6d9909fe03361f9b620f58898859f5c7aded68351e85e71ecf50"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jiff-static",
|
"jiff-static",
|
||||||
"log",
|
"log",
|
||||||
@ -1511,9 +1511,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jiff-static"
|
name = "jiff-static"
|
||||||
version = "0.2.17"
|
version = "0.2.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b787bebb543f8969132630c51fd0afab173a86c6abae56ff3b9e5e3e3f9f6e58"
|
checksum = "e0c84ee7f197eca9a86c6fd6cb771e55eb991632f15f2bc3ca6ec838929e6e78"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1538,9 +1538,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.179"
|
version = "0.2.180"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5a2d376baa530d1238d133232d15e239abad80d05838b4b59354e5268af431f"
|
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libloading"
|
name = "libloading"
|
||||||
@ -1593,9 +1593,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru"
|
name = "lru"
|
||||||
version = "0.16.2"
|
version = "0.16.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "96051b46fc183dc9cd4a223960ef37b9af631b55191852a8274bfef064cda20f"
|
checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
]
|
]
|
||||||
@ -2050,9 +2050,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.104"
|
version = "1.0.105"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9695f8df41bb4f3d222c95a67532365f569318332d03d5f3f67f37b20e6ebdf0"
|
checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
@ -2307,9 +2307,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.42"
|
version = "1.0.43"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
]
|
]
|
||||||
@ -2322,8 +2322,8 @@ checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand"
|
name = "rand"
|
||||||
version = "0.10.0-rc.5"
|
version = "0.10.0-rc.6"
|
||||||
source = "git+https://github.com/rust-random/rand#75fe38fff59e5abb16ed5a146d61960cad27dc0f"
|
source = "git+https://github.com/rust-random/rand#da2211b80e7217403b2bd8305a98ae7bc61c1146"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chacha20",
|
"chacha20",
|
||||||
"getrandom 0.4.0-rc.0",
|
"getrandom 0.4.0-rc.0",
|
||||||
@ -2433,7 +2433,7 @@ version = "0.10.0-rc.10"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e499c52862d75a86c0024cc99dcb6d7127d15af3beae7b03573d62fab7ade08a"
|
checksum = "e499c52862d75a86c0024cc99dcb6d7127d15af3beae7b03573d62fab7ade08a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const-oid 0.10.1",
|
"const-oid 0.10.2",
|
||||||
"crypto-bigint 0.7.0-rc.10",
|
"crypto-bigint 0.7.0-rc.10",
|
||||||
"crypto-primes",
|
"crypto-primes",
|
||||||
"digest 0.11.0-rc.5",
|
"digest 0.11.0-rc.5",
|
||||||
@ -2477,9 +2477,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.35"
|
version = "0.23.36"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
|
checksum = "c665f33d38cea657d9614f766881e4d510e0eda4239891eea56b4cadcf01801b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@ -2607,9 +2607,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.148"
|
version = "1.0.149"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3084b546a1dd6289475996f182a22aba973866ea8e8b02c51d9f46b1336a22da"
|
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
@ -2640,11 +2640,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serdect"
|
name = "serdect"
|
||||||
version = "0.4.1"
|
version = "0.4.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d3ef0e35b322ddfaecbc60f34ab448e157e48531288ee49fafbb053696b8ffe2"
|
checksum = "9af4a3e75ebd5599b30d4de5768e00b5095d518a79fefc3ecbaf77e665d1ec06"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base16ct 0.3.0",
|
"base16ct 1.0.0",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -2848,9 +2848,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.112"
|
version = "2.0.114"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "21f182278bf2d2bcb3c88b1b08a37df029d71ce3d3ae26168e3c653b213b99d4"
|
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3029,9 +3029,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-stream"
|
name = "tokio-stream"
|
||||||
version = "0.1.17"
|
version = "0.1.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047"
|
checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
@ -3040,9 +3040,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.17"
|
version = "0.7.18"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594"
|
checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
@ -3054,9 +3054,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.9.10+spec-1.1.0"
|
version = "0.9.11+spec-1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48"
|
checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde_core",
|
"serde_core",
|
||||||
@ -3297,9 +3297,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.7"
|
version = "2.5.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b"
|
checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"form_urlencoded",
|
"form_urlencoded",
|
||||||
"idna",
|
"idna",
|
||||||
@ -3683,18 +3683,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.8.31"
|
version = "0.8.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
|
checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zerocopy-derive",
|
"zerocopy-derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy-derive"
|
name = "zerocopy-derive"
|
||||||
version = "0.8.31"
|
version = "0.8.33"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
|
checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3763,6 +3763,6 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.8"
|
version = "1.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "317f17ff091ac4515f17cc7a190d2769a8c9a96d227de5d64b500b01cda8f2cd"
|
checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8"
|
||||||
|
|||||||
10
Cargo.toml
10
Cargo.toml
@ -50,7 +50,7 @@ serde_json = "1.0"
|
|||||||
tempfile = { version = "3.24.0", default-features = false }
|
tempfile = { version = "3.24.0", default-features = false }
|
||||||
proc-macro-error2 = "2"
|
proc-macro-error2 = "2"
|
||||||
aes = "0.8"
|
aes = "0.8"
|
||||||
async-compression = "0.4.36"
|
async-compression = "0.4.37"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
bitflags = "2.10.0"
|
bitflags = "2.10.0"
|
||||||
cesu8 = "1.1"
|
cesu8 = "1.1"
|
||||||
@ -60,7 +60,7 @@ console-subscriber = { version = "0.5.0", default-features = false }
|
|||||||
crc-fast = "1.9.0"
|
crc-fast = "1.9.0"
|
||||||
criterion = { version = "0.8", default-features = false }
|
criterion = { version = "0.8", default-features = false }
|
||||||
crossbeam-utils = "0.8.21"
|
crossbeam-utils = "0.8.21"
|
||||||
crossfire = "2.1.9"
|
crossfire = "2.1.10"
|
||||||
dashmap = "6.1"
|
dashmap = "6.1"
|
||||||
ecdsa = "0.16.9"
|
ecdsa = "0.16.9"
|
||||||
enum_dispatch = "0.3.13"
|
enum_dispatch = "0.3.13"
|
||||||
@ -68,10 +68,10 @@ env_logger = "0.11.8"
|
|||||||
flate2 = "1.1.5"
|
flate2 = "1.1.5"
|
||||||
heck = "0.5"
|
heck = "0.5"
|
||||||
hmac = "=0.13.0-rc.3"
|
hmac = "=0.13.0-rc.3"
|
||||||
indexmap = "2.12"
|
indexmap = "2.13"
|
||||||
itertools = "0.14.0"
|
itertools = "0.14.0"
|
||||||
libloading = "0.8"
|
libloading = "0.8"
|
||||||
lru = "0.16.2"
|
lru = "0.16.3"
|
||||||
lz4-java-wrc = "0.2.0"
|
lz4-java-wrc = "0.2.0"
|
||||||
md5 = "0.8"
|
md5 = "0.8"
|
||||||
num-bigint = "0.4"
|
num-bigint = "0.4"
|
||||||
@ -107,6 +107,6 @@ take_mut = "0.2.2"
|
|||||||
temp-dir = "0.1.16"
|
temp-dir = "0.1.16"
|
||||||
thread_local = "1.1.9"
|
thread_local = "1.1.9"
|
||||||
time = "0.3"
|
time = "0.3"
|
||||||
tokio-util = "0.7.17"
|
tokio-util = "0.7.18"
|
||||||
toml = "0.9"
|
toml = "0.9"
|
||||||
ureq = "3.1.4"
|
ureq = "3.1.4"
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# Pumpkin
|
# Pumpkin
|
||||||
|
|
||||||

|

|
||||||
[](https://discord.gg/wT8XjrjKkf)
|
[](https://discord.gg/wT8XjrjKkf)
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||

|

|
||||||
|
|||||||
@ -22,7 +22,7 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"installation": {
|
"installation": {
|
||||||
"script": "#!\/bin\/ash\r\nset -eux\r\n\r\napk add --no-cache musl-dev git\r\nmkdir -p \/mnt\/server\r\n\r\nREPO_URL=\"${GIT_URL:-https:\/\/github.com\/Snowiiii\/Pumpkin.git}\"\r\n\r\nif [ -n \"$GIT_BRANCH\" ]; then\r\n git clone --depth 1 --single-branch --branch \"$GIT_BRANCH\" \"$REPO_URL\"\r\nelse\r\n git clone --depth 1 --single-branch \"$REPO_URL\"\r\nfi\r\n\r\nif [ -n \"$GIT_COMMIT\" ]; then\r\n git checkout \"$GIT_COMMIT\"\r\nfi\r\n\r\ncd Pumpkin\r\n\r\n#export RUSTFLAGS=\"-C target-feature=-crt-static -C target-cpu=native\"\r\nexport CARGO_BUILD_JOBS=${CARGO_BUILD_JOBS:-default}\r\n\r\nif [[ \"$BUILD_RELEASE\" == \"1\" || \"$BUILD_RELEASE\" == \"true\" ]]; then\r\n cargo build --release\r\n strip target\/release\/pumpkin\r\n cp target\/release\/pumpkin \/mnt\/server\/pumpkin\r\nelse\r\n cargo build\r\n cp target\/debug\/pumpkin \/mnt\/server\/pumpkin\r\nfi",
|
"script": "#!\/bin\/ash\r\nset -eux\r\n\r\napk add --no-cache musl-dev git\r\nmkdir -p \/mnt\/server\r\n\r\nREPO_URL=\"${GIT_URL:-https:\/\/github.com\/Pumpkin-MC\/Pumpkin.git}\"\r\n\r\nif [ -n \"$GIT_BRANCH\" ]; then\r\n git clone --depth 1 --single-branch --branch \"$GIT_BRANCH\" \"$REPO_URL\"\r\nelse\r\n git clone --depth 1 --single-branch \"$REPO_URL\"\r\nfi\r\n\r\nif [ -n \"$GIT_COMMIT\" ]; then\r\n git checkout \"$GIT_COMMIT\"\r\nfi\r\n\r\ncd Pumpkin\r\n\r\n#export RUSTFLAGS=\"-C target-feature=-crt-static -C target-cpu=native\"\r\nexport CARGO_BUILD_JOBS=${CARGO_BUILD_JOBS:-default}\r\n\r\nif [[ \"$BUILD_RELEASE\" == \"1\" || \"$BUILD_RELEASE\" == \"true\" ]]; then\r\n cargo build --release\r\n strip target\/release\/pumpkin\r\n cp target\/release\/pumpkin \/mnt\/server\/pumpkin\r\nelse\r\n cargo build\r\n cp target\/debug\/pumpkin \/mnt\/server\/pumpkin\r\nfi",
|
||||||
"container": "docker.io\/rust:1-alpine3.21",
|
"container": "docker.io\/rust:1-alpine3.21",
|
||||||
"entrypoint": "ash"
|
"entrypoint": "ash"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,8 @@ pub use difficulty::Difficulty;
|
|||||||
pub use gamemode::GameMode;
|
pub use gamemode::GameMode;
|
||||||
pub use permission::PermissionLvl;
|
pub use permission::PermissionLvl;
|
||||||
|
|
||||||
|
use crate::math::vector3::Axis;
|
||||||
|
|
||||||
pub mod biome;
|
pub mod biome;
|
||||||
pub mod difficulty;
|
pub mod difficulty;
|
||||||
pub mod gamemode;
|
pub mod gamemode;
|
||||||
@ -83,6 +85,26 @@ pub enum ProfileAction {
|
|||||||
UsingBannedSkin,
|
UsingBannedSkin,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum BlockDirection {
|
||||||
|
Down = 0,
|
||||||
|
Up,
|
||||||
|
North,
|
||||||
|
South,
|
||||||
|
West,
|
||||||
|
East,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BlockDirection {
|
||||||
|
pub fn get_axis(&self) -> Axis {
|
||||||
|
match self {
|
||||||
|
BlockDirection::Up | BlockDirection::Down => Axis::Y,
|
||||||
|
BlockDirection::North | BlockDirection::South => Axis::Z,
|
||||||
|
BlockDirection::East | BlockDirection::West => Axis::X,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Takes a mutable reference of an index and returns a mutable "slice" where we can mutate both at
|
/// Takes a mutable reference of an index and returns a mutable "slice" where we can mutate both at
|
||||||
/// the same time
|
/// the same time
|
||||||
pub struct MutableSplitSlice<'a, T> {
|
pub struct MutableSplitSlice<'a, T> {
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
use crate::math::{
|
use crate::{
|
||||||
position::BlockPos,
|
BlockDirection,
|
||||||
vector3::{Axis, Vector3},
|
math::{
|
||||||
|
position::BlockPos,
|
||||||
|
vector3::{Axis, Vector3},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug)]
|
||||||
@ -40,6 +43,56 @@ impl BlockBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn rotated(
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
offset_x: i32,
|
||||||
|
offset_y: i32,
|
||||||
|
offset_z: i32,
|
||||||
|
size_x: i32,
|
||||||
|
size_y: i32,
|
||||||
|
size_z: i32,
|
||||||
|
facing: &BlockDirection,
|
||||||
|
) -> Self {
|
||||||
|
match facing {
|
||||||
|
BlockDirection::North => BlockBox::new(
|
||||||
|
x + offset_x,
|
||||||
|
y + offset_y,
|
||||||
|
z - size_z + 1 + offset_z,
|
||||||
|
x + size_x - 1 + offset_x,
|
||||||
|
y + size_y - 1 + offset_y,
|
||||||
|
z + offset_z,
|
||||||
|
),
|
||||||
|
BlockDirection::West => BlockBox::new(
|
||||||
|
x - size_z + 1 + offset_z,
|
||||||
|
y + offset_y,
|
||||||
|
z + offset_x,
|
||||||
|
x + offset_z,
|
||||||
|
y + size_y - 1 + offset_y,
|
||||||
|
z + size_x - 1 + offset_x,
|
||||||
|
),
|
||||||
|
BlockDirection::East => BlockBox::new(
|
||||||
|
x + offset_z,
|
||||||
|
y + offset_y,
|
||||||
|
z + offset_x,
|
||||||
|
x + size_z - 1 + offset_z,
|
||||||
|
y + size_y - 1 + offset_y,
|
||||||
|
z + size_x - 1 + offset_x,
|
||||||
|
),
|
||||||
|
// Default / South
|
||||||
|
_ => BlockBox::new(
|
||||||
|
x + offset_x,
|
||||||
|
y + offset_y,
|
||||||
|
z + offset_z,
|
||||||
|
x + size_x - 1 + offset_x,
|
||||||
|
y + size_y - 1 + offset_y,
|
||||||
|
z + size_z - 1 + offset_z,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from_pos(pos: BlockPos) -> Self {
|
pub fn from_pos(pos: BlockPos) -> Self {
|
||||||
Self {
|
Self {
|
||||||
min: pos.0,
|
min: pos.0,
|
||||||
@ -47,10 +100,70 @@ impl BlockBox {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn move_pos(&mut self, dx: i32, dy: i32, dz: i32) {
|
||||||
|
self.min.x += dx;
|
||||||
|
self.min.y += dy;
|
||||||
|
self.min.z += dz;
|
||||||
|
self.max.x += dx;
|
||||||
|
self.max.y += dy;
|
||||||
|
self.max.z += dz;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains_pos(&self, pos: &Vector3<i32>) -> bool {
|
||||||
|
self.contains(pos.x, pos.y, pos.z)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, x: i32, y: i32, z: i32) -> bool {
|
||||||
|
x >= self.min.x
|
||||||
|
&& x <= self.max.x
|
||||||
|
&& y >= self.min.y
|
||||||
|
&& y <= self.max.y
|
||||||
|
&& z >= self.min.z
|
||||||
|
&& z <= self.max.z
|
||||||
|
}
|
||||||
|
|
||||||
pub fn intersects(&self, other: &Self) -> bool {
|
pub fn intersects(&self, other: &Self) -> bool {
|
||||||
self.min.x < other.max.x
|
self.min.x < other.max.x
|
||||||
&& self.max.x > other.min.x
|
&& self.max.x > other.min.x
|
||||||
&& self.min.y < other.max.y
|
&& self.min.y < other.max.y
|
||||||
&& self.max.y > other.min.y
|
&& self.max.y > other.min.y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn intersects_xz(&self, other: &Self) -> bool {
|
||||||
|
self.max.x >= other.min.x
|
||||||
|
&& self.min.x <= other.max.x
|
||||||
|
&& self.max.z >= other.min.z
|
||||||
|
&& self.min.z <= other.max.z
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn intersects_raw_xz(&self, min_x: i32, min_z: i32, max_x: i32, max_z: i32) -> bool {
|
||||||
|
self.max.x >= min_x && self.min.x <= max_x && self.max.z >= min_z && self.min.z <= max_z
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_block_count_y(&self) -> i32 {
|
||||||
|
self.max.y - self.min.y + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encompass(&mut self, other: &BlockBox) {
|
||||||
|
self.min.x = self.min.x.min(other.min.x);
|
||||||
|
self.min.y = self.min.y.min(other.min.y);
|
||||||
|
self.min.z = self.min.z.min(other.min.z);
|
||||||
|
self.max.x = self.max.x.max(other.max.x);
|
||||||
|
self.max.y = self.max.y.max(other.max.y);
|
||||||
|
self.max.z = self.max.z.max(other.max.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Static helper to find the box covering a collection of boxes
|
||||||
|
pub fn encompass_all<I>(boxes: I) -> Option<BlockBox>
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = BlockBox>,
|
||||||
|
{
|
||||||
|
let mut iter = boxes.into_iter();
|
||||||
|
let mut result = iter.next()?; // Return None if empty
|
||||||
|
|
||||||
|
for b in iter {
|
||||||
|
result.encompass(&b);
|
||||||
|
}
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,10 +62,10 @@ macro_rules! population_seed_fn {
|
|||||||
|
|
||||||
// TODO: Write unit test for this
|
// TODO: Write unit test for this
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_decorator_seed(population_seed: u64, index: usize, step: usize) -> u64 {
|
pub fn get_decorator_seed(population_seed: u64, index: u64, step: u64) -> u64 {
|
||||||
population_seed
|
population_seed
|
||||||
.wrapping_add(index as u64)
|
.wrapping_add(index)
|
||||||
.wrapping_add(10_000u64.wrapping_mul(step as u64))
|
.wrapping_add(10_000u64.wrapping_mul(step))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|||||||
@ -66,12 +66,9 @@ mod test {
|
|||||||
use crate::{
|
use crate::{
|
||||||
GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoChunk,
|
GENERATION_SETTINGS, GeneratorSetting, GlobalRandomConfig, ProtoChunk,
|
||||||
chunk::palette::BIOME_NETWORK_MAX_BITS,
|
chunk::palette::BIOME_NETWORK_MAX_BITS,
|
||||||
generation::{
|
generation::noise::router::{
|
||||||
noise::router::{
|
multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions},
|
||||||
multi_noise_sampler::{MultiNoiseSampler, MultiNoiseSamplerBuilderOptions},
|
proto_noise_router::ProtoNoiseRouters,
|
||||||
proto_noise_router::ProtoNoiseRouters,
|
|
||||||
},
|
|
||||||
proto_chunk::TerrainCache,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -109,7 +106,7 @@ mod test {
|
|||||||
let surface_settings = GENERATION_SETTINGS
|
let surface_settings = GENERATION_SETTINGS
|
||||||
.get(&GeneratorSetting::Overworld)
|
.get(&GeneratorSetting::Overworld)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let _terrain_cache = TerrainCache::from_random(&random_config);
|
//let _terrain_cache = TerrainCache::from_random(&random_config);
|
||||||
let default_block = surface_settings.default_block.get_state();
|
let default_block = surface_settings.default_block.get_state();
|
||||||
|
|
||||||
for data in expected_data.into_iter() {
|
for data in expected_data.into_iter() {
|
||||||
|
|||||||
@ -621,6 +621,8 @@ impl StagedChunkEnum {
|
|||||||
}
|
}
|
||||||
const fn get_direct_dependencies(self) -> &'static [StagedChunkEnum] {
|
const fn get_direct_dependencies(self) -> &'static [StagedChunkEnum] {
|
||||||
match self {
|
match self {
|
||||||
|
// In vanilla StructureStart is first, but since it needs the biome in Vanilla it gets computed in StructureStart and
|
||||||
|
// the Biome Step, this should be more efficient
|
||||||
Self::Biomes => &[Self::Empty],
|
Self::Biomes => &[Self::Empty],
|
||||||
Self::StructureStart => &[Self::Biomes],
|
Self::StructureStart => &[Self::Biomes],
|
||||||
Self::StructureReferences => &[Self::StructureStart],
|
Self::StructureReferences => &[Self::StructureStart],
|
||||||
@ -774,8 +776,14 @@ impl Chunk {
|
|||||||
Proto(chunk) => chunk,
|
Proto(chunk) => chunk,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn get_proto_chunk(&self) -> &ProtoChunk {
|
||||||
|
match self {
|
||||||
|
Chunk::Level(_) => panic!("chunk isn't a ProtoChunk"),
|
||||||
|
Proto(chunk) => chunk,
|
||||||
|
}
|
||||||
|
}
|
||||||
fn upgrade_to_level_chunk(&mut self, dimension: &Dimension) {
|
fn upgrade_to_level_chunk(&mut self, dimension: &Dimension) {
|
||||||
let proto_chunk = self.get_proto_chunk_mut();
|
let proto_chunk = self.get_proto_chunk();
|
||||||
|
|
||||||
let total_sections = dimension.height as usize / 16;
|
let total_sections = dimension.height as usize / 16;
|
||||||
let mut sections = ChunkSections::new(
|
let mut sections = ChunkSections::new(
|
||||||
@ -853,7 +861,7 @@ impl Chunk {
|
|||||||
|
|
||||||
struct Cache {
|
struct Cache {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
z: i32,
|
||||||
size: i32,
|
size: i32,
|
||||||
pub chunks: Vec<Chunk>,
|
pub chunks: Vec<Chunk>,
|
||||||
}
|
}
|
||||||
@ -903,6 +911,33 @@ impl BlockAccessor for Cache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GenerationCache for Cache {
|
impl GenerationCache for Cache {
|
||||||
|
fn get_chunk_mut(&mut self, chunk_x: i32, chunk_z: i32) -> Option<&mut ProtoChunk> {
|
||||||
|
let dx = chunk_x - self.x;
|
||||||
|
let dz = chunk_z - self.z;
|
||||||
|
|
||||||
|
if dx >= 0 && dx < self.size && dz >= 0 && dz < self.size {
|
||||||
|
Some(self.chunks[(dx * self.size + dz) as usize].get_proto_chunk_mut())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_chunk(&self, chunk_x: i32, chunk_z: i32) -> Option<&ProtoChunk> {
|
||||||
|
let dx = chunk_x - self.x;
|
||||||
|
let dz = chunk_z - self.z;
|
||||||
|
|
||||||
|
if dx >= 0 && dx < self.size && dz >= 0 && dz < self.size {
|
||||||
|
Some(self.chunks[(dx * self.size + dz) as usize].get_proto_chunk())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_center_chunk(&self) -> &ProtoChunk {
|
||||||
|
let mid = ((self.size * self.size) >> 1) as usize;
|
||||||
|
self.chunks[mid].get_proto_chunk()
|
||||||
|
}
|
||||||
|
|
||||||
fn get_center_chunk_mut(&mut self) -> &mut ProtoChunk {
|
fn get_center_chunk_mut(&mut self) -> &mut ProtoChunk {
|
||||||
let mid = ((self.size * self.size) >> 1) as usize;
|
let mid = ((self.size * self.size) >> 1) as usize;
|
||||||
self.chunks[mid].get_proto_chunk_mut()
|
self.chunks[mid].get_proto_chunk_mut()
|
||||||
@ -941,7 +976,7 @@ impl GenerationCache for Cache {
|
|||||||
|
|
||||||
fn get_block_state(&self, pos: &Vector3<i32>) -> RawBlockState {
|
fn get_block_state(&self, pos: &Vector3<i32>) -> RawBlockState {
|
||||||
let dx = (pos.x >> 4) - self.x;
|
let dx = (pos.x >> 4) - self.x;
|
||||||
let dz = (pos.z >> 4) - self.y;
|
let dz = (pos.z >> 4) - self.z;
|
||||||
// debug_assert!(dx < self.size && dz < self.size);
|
// debug_assert!(dx < self.size && dz < self.size);
|
||||||
// debug_assert!(dx >= 0 && dz >= 0);
|
// debug_assert!(dx >= 0 && dz >= 0);
|
||||||
if !(dx < self.size && dz < self.size && dx >= 0 && dz >= 0) {
|
if !(dx < self.size && dz < self.size && dx >= 0 && dz >= 0) {
|
||||||
@ -949,7 +984,7 @@ impl GenerationCache for Cache {
|
|||||||
log::error!(
|
log::error!(
|
||||||
"illegal get_block_state {pos:?} cache pos ({}, {}) size {}",
|
"illegal get_block_state {pos:?} cache pos ({}, {}) size {}",
|
||||||
self.x,
|
self.x,
|
||||||
self.y,
|
self.z,
|
||||||
self.size
|
self.size
|
||||||
);
|
);
|
||||||
return RawBlockState::AIR;
|
return RawBlockState::AIR;
|
||||||
@ -969,7 +1004,7 @@ impl GenerationCache for Cache {
|
|||||||
}
|
}
|
||||||
fn set_block_state(&mut self, pos: &Vector3<i32>, block_state: &BlockState) {
|
fn set_block_state(&mut self, pos: &Vector3<i32>, block_state: &BlockState) {
|
||||||
let dx = (pos.x >> 4) - self.x;
|
let dx = (pos.x >> 4) - self.x;
|
||||||
let dz = (pos.z >> 4) - self.y;
|
let dz = (pos.z >> 4) - self.z;
|
||||||
// debug_assert!(dx < self.size && dz < self.size);
|
// debug_assert!(dx < self.size && dz < self.size);
|
||||||
// debug_assert!(dx >= 0 && dz >= 0);
|
// debug_assert!(dx >= 0 && dz >= 0);
|
||||||
if !(dx < self.size && dz < self.size && dx >= 0 && dz >= 0) {
|
if !(dx < self.size && dz < self.size && dx >= 0 && dz >= 0) {
|
||||||
@ -977,7 +1012,7 @@ impl GenerationCache for Cache {
|
|||||||
log::error!(
|
log::error!(
|
||||||
"illegal set_block_state {pos:?} cache pos ({}, {}) size {}",
|
"illegal set_block_state {pos:?} cache pos ({}, {}) size {}",
|
||||||
self.x,
|
self.x,
|
||||||
self.y,
|
self.z,
|
||||||
self.size
|
self.size
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -1013,7 +1048,7 @@ impl GenerationCache for Cache {
|
|||||||
|
|
||||||
fn top_motion_blocking_block_height_exclusive(&self, x: i32, z: i32) -> i32 {
|
fn top_motion_blocking_block_height_exclusive(&self, x: i32, z: i32) -> i32 {
|
||||||
let dx = (x >> 4) - self.x;
|
let dx = (x >> 4) - self.x;
|
||||||
let dy = (z >> 4) - self.y;
|
let dy = (z >> 4) - self.z;
|
||||||
debug_assert!(dx < self.size && dy < self.size);
|
debug_assert!(dx < self.size && dy < self.size);
|
||||||
debug_assert!(dx >= 0 && dy >= 0);
|
debug_assert!(dx >= 0 && dy >= 0);
|
||||||
match &self.chunks[(dx * self.size + dy) as usize] {
|
match &self.chunks[(dx * self.size + dy) as usize] {
|
||||||
@ -1032,7 +1067,7 @@ impl GenerationCache for Cache {
|
|||||||
|
|
||||||
fn top_motion_blocking_block_no_leaves_height_exclusive(&self, x: i32, z: i32) -> i32 {
|
fn top_motion_blocking_block_no_leaves_height_exclusive(&self, x: i32, z: i32) -> i32 {
|
||||||
let dx = (x >> 4) - self.x;
|
let dx = (x >> 4) - self.x;
|
||||||
let dy = (z >> 4) - self.y;
|
let dy = (z >> 4) - self.z;
|
||||||
debug_assert!(dx < self.size && dy < self.size);
|
debug_assert!(dx < self.size && dy < self.size);
|
||||||
debug_assert!(dx >= 0 && dy >= 0);
|
debug_assert!(dx >= 0 && dy >= 0);
|
||||||
match &self.chunks[(dx * self.size + dy) as usize] {
|
match &self.chunks[(dx * self.size + dy) as usize] {
|
||||||
@ -1051,7 +1086,7 @@ impl GenerationCache for Cache {
|
|||||||
|
|
||||||
fn top_block_height_exclusive(&self, x: i32, z: i32) -> i32 {
|
fn top_block_height_exclusive(&self, x: i32, z: i32) -> i32 {
|
||||||
let dx = (x >> 4) - self.x;
|
let dx = (x >> 4) - self.x;
|
||||||
let dy = (z >> 4) - self.y;
|
let dy = (z >> 4) - self.z;
|
||||||
debug_assert!(dx < self.size && dy < self.size);
|
debug_assert!(dx < self.size && dy < self.size);
|
||||||
debug_assert!(dx >= 0 && dy >= 0);
|
debug_assert!(dx >= 0 && dy >= 0);
|
||||||
match &self.chunks[(dx * self.size + dy) as usize] {
|
match &self.chunks[(dx * self.size + dy) as usize] {
|
||||||
@ -1070,7 +1105,7 @@ impl GenerationCache for Cache {
|
|||||||
|
|
||||||
fn ocean_floor_height_exclusive(&self, x: i32, z: i32) -> i32 {
|
fn ocean_floor_height_exclusive(&self, x: i32, z: i32) -> i32 {
|
||||||
let dx = (x >> 4) - self.x;
|
let dx = (x >> 4) - self.x;
|
||||||
let dy = (z >> 4) - self.y;
|
let dy = (z >> 4) - self.z;
|
||||||
debug_assert!(dx < self.size && dy < self.size);
|
debug_assert!(dx < self.size && dy < self.size);
|
||||||
debug_assert!(dx >= 0 && dy >= 0);
|
debug_assert!(dx >= 0 && dy >= 0);
|
||||||
match &self.chunks[(dx * self.size + dy) as usize] {
|
match &self.chunks[(dx * self.size + dy) as usize] {
|
||||||
@ -1083,7 +1118,7 @@ impl GenerationCache for Cache {
|
|||||||
|
|
||||||
fn get_biome_for_terrain_gen(&self, x: i32, y: i32, z: i32) -> &'static Biome {
|
fn get_biome_for_terrain_gen(&self, x: i32, y: i32, z: i32) -> &'static Biome {
|
||||||
let dx = (x >> 4) - self.x;
|
let dx = (x >> 4) - self.x;
|
||||||
let dy = (z >> 4) - self.y;
|
let dy = (z >> 4) - self.z;
|
||||||
debug_assert!(dx < self.size && dy < self.size);
|
debug_assert!(dx < self.size && dy < self.size);
|
||||||
debug_assert!(dx >= 0 && dy >= 0);
|
debug_assert!(dx >= 0 && dy >= 0);
|
||||||
match &self.chunks[(dx * self.size + dy) as usize] {
|
match &self.chunks[(dx * self.size + dy) as usize] {
|
||||||
@ -1109,10 +1144,10 @@ impl GenerationCache for Cache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Cache {
|
impl Cache {
|
||||||
fn new(x: i32, y: i32, size: i32) -> Cache {
|
fn new(x: i32, z: i32, size: i32) -> Cache {
|
||||||
Cache {
|
Cache {
|
||||||
x,
|
x,
|
||||||
y,
|
z,
|
||||||
size,
|
size,
|
||||||
chunks: Vec::with_capacity((size * size) as usize),
|
chunks: Vec::with_capacity((size * size) as usize),
|
||||||
}
|
}
|
||||||
@ -1133,10 +1168,8 @@ impl Cache {
|
|||||||
Empty => panic!("empty stage"),
|
Empty => panic!("empty stage"),
|
||||||
StagedChunkEnum::StructureStart => self.chunks[mid]
|
StagedChunkEnum::StructureStart => self.chunks[mid]
|
||||||
.get_proto_chunk_mut()
|
.get_proto_chunk_mut()
|
||||||
.set_structure_starts(random_config),
|
.set_structure_starts(random_config, settings),
|
||||||
StagedChunkEnum::StructureReferences => self.chunks[mid]
|
StagedChunkEnum::StructureReferences => ProtoChunk::set_structure_references(self),
|
||||||
.get_proto_chunk_mut()
|
|
||||||
.set_structure_references(),
|
|
||||||
Biomes => self.chunks[mid]
|
Biomes => self.chunks[mid]
|
||||||
.get_proto_chunk_mut()
|
.get_proto_chunk_mut()
|
||||||
.step_to_biomes(dimension, noise_router),
|
.step_to_biomes(dimension, noise_router),
|
||||||
@ -1155,8 +1188,9 @@ impl Cache {
|
|||||||
ProtoChunk::generate_features_and_structure(self, block_registry, random_config)
|
ProtoChunk::generate_features_and_structure(self, block_registry, random_config)
|
||||||
}
|
}
|
||||||
Full => {
|
Full => {
|
||||||
debug_assert_eq!(self.chunks[mid].get_proto_chunk_mut().stage, Features);
|
let chunk = self.chunks[mid].get_proto_chunk_mut();
|
||||||
self.chunks[mid].get_proto_chunk_mut().stage = Full;
|
debug_assert_eq!(chunk.stage, Features);
|
||||||
|
chunk.stage = Full;
|
||||||
self.chunks[mid].upgrade_to_level_chunk(&dimension);
|
self.chunks[mid].upgrade_to_level_chunk(&dimension);
|
||||||
}
|
}
|
||||||
StagedChunkEnum::None => {}
|
StagedChunkEnum::None => {}
|
||||||
@ -1532,9 +1566,9 @@ impl GenerationSchedule {
|
|||||||
let dependency = stage.get_direct_dependencies();
|
let dependency = stage.get_direct_dependencies();
|
||||||
let radius = stage.get_direct_radius();
|
let radius = stage.get_direct_radius();
|
||||||
for dx in -radius..=radius {
|
for dx in -radius..=radius {
|
||||||
for dy in -radius..=radius {
|
for dz in -radius..=radius {
|
||||||
let new_pos = pos.add_raw(dx, dy);
|
let new_pos = pos.add_raw(dx, dz);
|
||||||
let req_stage = dependency[dx.abs().max(dy.abs()) as usize];
|
let req_stage = dependency[dx.abs().max(dz.abs()) as usize];
|
||||||
if new_pos == pos {
|
if new_pos == pos {
|
||||||
// TODO
|
// TODO
|
||||||
holder.occupied_by = self
|
holder.occupied_by = self
|
||||||
@ -1883,7 +1917,7 @@ impl GenerationSchedule {
|
|||||||
let mut dx = 0;
|
let mut dx = 0;
|
||||||
let mut dy = 0;
|
let mut dy = 0;
|
||||||
for chunk in data.chunks {
|
for chunk in data.chunks {
|
||||||
let new_pos = ChunkPos::new(data.x + dx, data.y + dy);
|
let new_pos = ChunkPos::new(data.x + dx, data.z + dy);
|
||||||
match chunk {
|
match chunk {
|
||||||
Chunk::Level(chunk) => {
|
Chunk::Level(chunk) => {
|
||||||
let mut holder = self.chunk_map.remove(&new_pos).unwrap();
|
let mut holder = self.chunk_map.remove(&new_pos).unwrap();
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
use core::f32;
|
use core::f32;
|
||||||
use std::collections::HashSet;
|
|
||||||
|
|
||||||
use pumpkin_data::{BlockDirection, BlockState};
|
use pumpkin_data::{BlockDirection, BlockState};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
@ -84,7 +83,8 @@ impl OreFeature {
|
|||||||
vertical_size: i32,
|
vertical_size: i32,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let mut placed_blocks_count = 0;
|
let mut placed_blocks_count = 0;
|
||||||
let mut bit_set = HashSet::new();
|
let bitset_size = (horizontal_size * vertical_size * horizontal_size) as usize;
|
||||||
|
let mut bit_set = vec![false; bitset_size];
|
||||||
let mut mutable_pos = BlockPos::ZERO;
|
let mut mutable_pos = BlockPos::ZERO;
|
||||||
let j = self.size;
|
let j = self.size;
|
||||||
let mut ds = vec![0.0; (j * 4) as usize];
|
let mut ds = vec![0.0; (j * 4) as usize];
|
||||||
@ -151,6 +151,8 @@ impl OreFeature {
|
|||||||
if u_val * u_val + w_val * w_val >= 1.0 {
|
if u_val * u_val + w_val * w_val >= 1.0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let base_idx = (t_val - x_bound) + (v_val - y_bound) * horizontal_size;
|
||||||
|
|
||||||
for aa_val in p_bound..=s_bound {
|
for aa_val in p_bound..=s_bound {
|
||||||
let ab_val = (aa_val as f64 + 0.5 - h_val) / d_val;
|
let ab_val = (aa_val as f64 + 0.5 - h_val) / d_val;
|
||||||
if u_val * u_val + w_val * w_val + ab_val * ab_val >= 1.0 {
|
if u_val * u_val + w_val * w_val + ab_val * ab_val >= 1.0 {
|
||||||
@ -160,14 +162,13 @@ impl OreFeature {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ac = (t_val - x_bound)
|
let ac = (base_idx + (aa_val - z_bound) * horizontal_size * vertical_size)
|
||||||
+ (v_val - y_bound) * horizontal_size
|
as usize;
|
||||||
+ (aa_val - z_bound) * horizontal_size * vertical_size;
|
|
||||||
|
|
||||||
if bit_set.contains(&ac) {
|
if bit_set[ac] {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bit_set.insert(ac);
|
bit_set[ac] = true;
|
||||||
|
|
||||||
mutable_pos.0.x = t_val;
|
mutable_pos.0.x = t_val;
|
||||||
mutable_pos.0.y = v_val;
|
mutable_pos.0.y = v_val;
|
||||||
@ -177,12 +178,13 @@ impl OreFeature {
|
|||||||
// continue;
|
// continue;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
let ad = t_val;
|
// let ad = t_val;
|
||||||
let ae = v_val;
|
// let ae = v_val;
|
||||||
let af = aa_val;
|
// let af = aa_val;
|
||||||
|
// TODO: using a section would be faster
|
||||||
|
|
||||||
let block_state =
|
let pos_vec = Vector3::new(t_val, v_val, aa_val);
|
||||||
GenerationCache::get_block_state(chunk, &Vector3::new(ad, ae, af));
|
let block_state = GenerationCache::get_block_state(chunk, &pos_vec);
|
||||||
|
|
||||||
for target in &self.targets {
|
for target in &self.targets {
|
||||||
if self.should_place(
|
if self.should_place(
|
||||||
@ -192,10 +194,7 @@ impl OreFeature {
|
|||||||
target,
|
target,
|
||||||
&mut mutable_pos,
|
&mut mutable_pos,
|
||||||
) {
|
) {
|
||||||
chunk.set_block_state(
|
chunk.set_block_state(&pos_vec, target.state.get_state());
|
||||||
&Vector3::new(ad, ae, af),
|
|
||||||
target.state.get_state(),
|
|
||||||
);
|
|
||||||
placed_blocks_count += 1;
|
placed_blocks_count += 1;
|
||||||
break; // Equivalent to 'continue block11;'
|
break; // Equivalent to 'continue block11;'
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::pin::Pin;
|
use std::pin::Pin;
|
||||||
|
|
||||||
use pumpkin_data::dimension::Dimension;
|
use pumpkin_data::dimension::Dimension;
|
||||||
@ -7,7 +7,7 @@ use pumpkin_data::tag;
|
|||||||
use pumpkin_data::{
|
use pumpkin_data::{
|
||||||
Block, BlockState, block_properties::blocks_movement, chunk::Biome, tag::Taggable,
|
Block, BlockState, block_properties::blocks_movement, chunk::Biome, tag::Taggable,
|
||||||
};
|
};
|
||||||
use pumpkin_util::math::block_box::BlockBox;
|
use pumpkin_util::random::{RandomImpl, get_carver_seed};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
HeightMap,
|
HeightMap,
|
||||||
math::{position::BlockPos, vector3::Vector3},
|
math::{position::BlockPos, vector3::Vector3},
|
||||||
@ -37,7 +37,7 @@ use crate::generation::noise::perlin::DoublePerlinNoiseSampler;
|
|||||||
use crate::generation::noise::router::surface_height_sampler::SurfaceHeightSamplerBuilderOptions;
|
use crate::generation::noise::router::surface_height_sampler::SurfaceHeightSamplerBuilderOptions;
|
||||||
use crate::generation::structure::placement::StructurePlacementCalculator;
|
use crate::generation::structure::placement::StructurePlacementCalculator;
|
||||||
use crate::generation::structure::structures::StructureInstance;
|
use crate::generation::structure::structures::StructureInstance;
|
||||||
use crate::generation::structure::{STRUCTURE_SETS, STRUCTURES, Structure};
|
use crate::generation::structure::{STRUCTURE_SETS, STRUCTURES, StructureKeys, WeightedEntry};
|
||||||
use crate::{
|
use crate::{
|
||||||
BlockStateId, ProtoNoiseRouters,
|
BlockStateId, ProtoNoiseRouters,
|
||||||
biome::{BiomeSupplier, MultiNoiseBiomeSupplier, end::TheEndBiomeSupplier},
|
biome::{BiomeSupplier, MultiNoiseBiomeSupplier, end::TheEndBiomeSupplier},
|
||||||
@ -49,6 +49,11 @@ use crate::{
|
|||||||
|
|
||||||
pub trait GenerationCache: HeightLimitView + BlockAccessor {
|
pub trait GenerationCache: HeightLimitView + BlockAccessor {
|
||||||
fn get_center_chunk_mut(&mut self) -> &mut ProtoChunk;
|
fn get_center_chunk_mut(&mut self) -> &mut ProtoChunk;
|
||||||
|
fn get_center_chunk(&self) -> &ProtoChunk;
|
||||||
|
|
||||||
|
fn get_chunk_mut(&mut self, chunk_x: i32, chunk_z: i32) -> Option<&mut ProtoChunk>;
|
||||||
|
fn get_chunk(&self, chunk_x: i32, chunk_z: i32) -> Option<&ProtoChunk>;
|
||||||
|
|
||||||
fn get_block_state(&self, pos: &Vector3<i32>) -> RawBlockState;
|
fn get_block_state(&self, pos: &Vector3<i32>) -> RawBlockState;
|
||||||
fn get_fluid_and_fluid_state(&self, position: &Vector3<i32>) -> (Fluid, FluidState);
|
fn get_fluid_and_fluid_state(&self, position: &Vector3<i32>) -> (Fluid, FluidState);
|
||||||
fn set_block_state(&mut self, pos: &Vector3<i32>, block_state: &BlockState);
|
fn set_block_state(&mut self, pos: &Vector3<i32>, block_state: &BlockState);
|
||||||
@ -134,8 +139,7 @@ pub struct ProtoChunk {
|
|||||||
flat_ocean_floor_height_map: Box<[i16]>,
|
flat_ocean_floor_height_map: Box<[i16]>,
|
||||||
pub flat_motion_blocking_height_map: Box<[i16]>,
|
pub flat_motion_blocking_height_map: Box<[i16]>,
|
||||||
pub flat_motion_blocking_no_leaves_height_map: Box<[i16]>,
|
pub flat_motion_blocking_no_leaves_height_map: Box<[i16]>,
|
||||||
// may want to use chunk status
|
structure_starts: HashMap<StructureKeys, StructureInstance>,
|
||||||
structure_starts: HashMap<Structure, StructureInstance>,
|
|
||||||
|
|
||||||
// Height of the chunk for indexing
|
// Height of the chunk for indexing
|
||||||
height: u16,
|
height: u16,
|
||||||
@ -463,7 +467,8 @@ impl ProtoChunk {
|
|||||||
let blocks_movement = blocks_movement(block_state, block);
|
let blocks_movement = blocks_movement(block_state, block);
|
||||||
if blocks_movement {
|
if blocks_movement {
|
||||||
self.maybe_update_ocean_floor_height_map(local_x, pos.y, local_z);
|
self.maybe_update_ocean_floor_height_map(local_x, pos.y, local_z);
|
||||||
} else if blocks_movement || block_state.is_liquid() {
|
}
|
||||||
|
if blocks_movement || block_state.is_liquid() {
|
||||||
self.maybe_update_motion_blocking_height_map(local_x, pos.y, local_z);
|
self.maybe_update_motion_blocking_height_map(local_x, pos.y, local_z);
|
||||||
if !block.has_tag(&tag::Block::MINECRAFT_LEAVES) {
|
if !block.has_tag(&tag::Block::MINECRAFT_LEAVES) {
|
||||||
{
|
{
|
||||||
@ -634,8 +639,6 @@ impl ProtoChunk {
|
|||||||
dimension,
|
dimension,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
//dbg!("Populating biome: {:?} -> {:?}", biome_pos, biome);
|
|
||||||
|
|
||||||
let index = self.local_biome_pos_to_biome_index(
|
let index = self.local_biome_pos_to_biome_index(
|
||||||
x,
|
x,
|
||||||
start_biome_y + y - biome_coords::from_block(min_y as i32),
|
start_biome_y + y - biome_coords::from_block(min_y as i32),
|
||||||
@ -906,96 +909,221 @@ impl ProtoChunk {
|
|||||||
block_registry: &dyn BlockRegistryExt,
|
block_registry: &dyn BlockRegistryExt,
|
||||||
random_config: &GlobalRandomConfig,
|
random_config: &GlobalRandomConfig,
|
||||||
) {
|
) {
|
||||||
let chunk = cache.get_center_chunk_mut();
|
let (center_x, center_z, min_y, height, biomes_in_chunk) = {
|
||||||
debug_assert_eq!(chunk.stage, StagedChunkEnum::Surface);
|
let chunk = cache.get_center_chunk();
|
||||||
let min_y = chunk.bottom_y();
|
let mut biomes = HashSet::new();
|
||||||
let height = chunk.height();
|
// bad
|
||||||
|
for biome in chunk.flat_biome_map.clone() {
|
||||||
|
biomes.insert(biome);
|
||||||
|
}
|
||||||
|
|
||||||
let bottom_section = section_coords::block_to_section(min_y) as i32;
|
(chunk.x, chunk.z, chunk.bottom_y(), chunk.height(), biomes)
|
||||||
let block_pos = BlockPos(Vector3::new(
|
};
|
||||||
section_coords::section_to_block(chunk.x),
|
|
||||||
bottom_section,
|
|
||||||
section_coords::section_to_block(chunk.z),
|
|
||||||
));
|
|
||||||
|
|
||||||
|
let block_pos = BlockPos::new(
|
||||||
|
section_coords::section_to_block(center_x),
|
||||||
|
section_coords::block_to_section(min_y) as i32,
|
||||||
|
section_coords::section_to_block(center_z),
|
||||||
|
);
|
||||||
let population_seed =
|
let population_seed =
|
||||||
Xoroshiro::get_population_seed(random_config.seed, block_pos.0.x, block_pos.0.z);
|
Xoroshiro::get_population_seed(random_config.seed, block_pos.0.x, block_pos.0.z);
|
||||||
|
|
||||||
let _chunk_box = chunk.get_block_box_for_chunk();
|
const MAX_STEPS: usize = 11;
|
||||||
for (_structure_config, instance) in chunk.structure_starts.clone() {
|
|
||||||
dbg!("generating");
|
for step in 0..MAX_STEPS {
|
||||||
match instance {
|
let decorator_seed = get_decorator_seed(population_seed, 0, step as u64);
|
||||||
StructureInstance::Start(pos, _stype) => {
|
|
||||||
// Use the collector directly to place blocks
|
|
||||||
pos.collector
|
|
||||||
.generate_in_chunk(chunk, random_config.seed as i64);
|
|
||||||
}
|
|
||||||
StructureInstance::Reference(_start_block_pos) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: This needs to be different depending on what biomes are in the chunk -> affects the
|
|
||||||
// random
|
|
||||||
for (name, feature) in PLACED_FEATURES.iter() {
|
|
||||||
// TODO: Properly set index and step
|
|
||||||
let decorator_seed = get_decorator_seed(population_seed, 0, 0);
|
|
||||||
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(decorator_seed));
|
let mut random = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(decorator_seed));
|
||||||
feature.generate(
|
|
||||||
cache,
|
|
||||||
block_registry,
|
|
||||||
min_y,
|
|
||||||
height,
|
|
||||||
name,
|
|
||||||
&mut random,
|
|
||||||
block_pos,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let chunk = cache.get_center_chunk_mut();
|
|
||||||
chunk.stage = StagedChunkEnum::Features;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_structure_starts(&mut self, random_config: &GlobalRandomConfig) {
|
let mut tasks = Vec::new();
|
||||||
for (_set_name, set) in STRUCTURE_SETS.iter() {
|
|
||||||
let calculator = StructurePlacementCalculator {
|
|
||||||
seed: random_config.seed as i64,
|
|
||||||
};
|
|
||||||
|
|
||||||
if !set.placement.should_generate(calculator, self.x, self.z) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if set.structures.len() == 1
|
|
||||||
&& let Some(structure_entry) = set.structures.first()
|
|
||||||
{
|
{
|
||||||
let position = structure_entry.structure.try_generate(
|
let center_chunk = cache.get_center_chunk();
|
||||||
random_config.seed as i64,
|
for (id, instance) in ¢er_chunk.structure_starts {
|
||||||
self.x,
|
// Check if this structure belongs to the current step
|
||||||
self.z,
|
if let Some(s) = STRUCTURES.get(id)
|
||||||
self,
|
&& s.step.ordinal() != step
|
||||||
);
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(pos) = position
|
match instance {
|
||||||
&& let Some(structure_config) = STRUCTURES.get(&structure_entry.structure)
|
StructureInstance::Start(pos) => {
|
||||||
{
|
tasks.push(pos.collector.clone());
|
||||||
self.structure_starts.insert(
|
}
|
||||||
structure_config.clone(),
|
StructureInstance::Reference(origin_pos) => {
|
||||||
StructureInstance::Start(pos, structure_entry.structure.clone()),
|
let origin_chunk_x = origin_pos.0.x >> 4;
|
||||||
|
let origin_chunk_z = origin_pos.0.z >> 4;
|
||||||
|
|
||||||
|
if let Some(neighbor) = cache.get_chunk(origin_chunk_x, origin_chunk_z)
|
||||||
|
&& let Some(StructureInstance::Start(pos)) =
|
||||||
|
neighbor.structure_starts.get(id)
|
||||||
|
{
|
||||||
|
tasks.push(pos.collector.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let chunk = cache.get_center_chunk_mut();
|
||||||
|
for collector_arc in tasks {
|
||||||
|
let mut collector = collector_arc.lock().unwrap();
|
||||||
|
collector.generate_in_chunk(chunk, &mut random, random_config.seed as i64);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut features_to_run = HashSet::new();
|
||||||
|
for biome in &biomes_in_chunk {
|
||||||
|
if let Some(features_at_step) = biome.features.get(step) {
|
||||||
|
for feature_id in *features_at_step {
|
||||||
|
features_to_run
|
||||||
|
.insert(feature_id.strip_prefix("minecraft:").unwrap_or(feature_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// let mut sorted_features: Vec<_> = features_to_run.into_iter().collect();
|
||||||
|
// sorted_features.sort_by_key(|f| f.registry_index());
|
||||||
|
|
||||||
|
for (p, feature_id) in features_to_run.into_iter().enumerate() {
|
||||||
|
if let Some(feature) = PLACED_FEATURES.get(feature_id) {
|
||||||
|
let decorator_seed = get_decorator_seed(population_seed, p as u64, step as u64);
|
||||||
|
let mut random =
|
||||||
|
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(decorator_seed));
|
||||||
|
|
||||||
|
feature.generate(
|
||||||
|
cache,
|
||||||
|
block_registry,
|
||||||
|
min_y,
|
||||||
|
height,
|
||||||
|
feature_id,
|
||||||
|
&mut random,
|
||||||
|
block_pos,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO add weight
|
}
|
||||||
|
|
||||||
|
cache.get_center_chunk_mut().stage = StagedChunkEnum::Features;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_structure_starts(
|
||||||
|
&mut self,
|
||||||
|
random_config: &GlobalRandomConfig,
|
||||||
|
settings: &GenerationSettings,
|
||||||
|
) {
|
||||||
|
let seed = random_config.seed;
|
||||||
|
let calculator = StructurePlacementCalculator::new(seed as i64);
|
||||||
|
|
||||||
|
for (_set_name, set) in STRUCTURE_SETS.iter() {
|
||||||
|
if !set.placement.should_generate(&calculator, self.x, self.z) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if set.structures.len() == 1 {
|
||||||
|
if let Some(entry) = set.structures.first() {
|
||||||
|
self.try_set_structure_start(settings.sea_level, entry, random_config);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut candidates = set.structures.clone();
|
||||||
|
let mut random: RandomGenerator =
|
||||||
|
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(seed));
|
||||||
|
let carver_seed = get_carver_seed(&mut random, seed, self.x, self.z);
|
||||||
|
let mut random: RandomGenerator =
|
||||||
|
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed));
|
||||||
|
|
||||||
|
let mut total_weight: u32 = candidates.iter().map(|e| e.weight).sum();
|
||||||
|
|
||||||
|
while !candidates.is_empty() {
|
||||||
|
let mut roll = random.next_bounded_i32(total_weight as i32);
|
||||||
|
let mut selected_idx = 0;
|
||||||
|
|
||||||
|
for (i, entry) in candidates.iter().enumerate() {
|
||||||
|
roll -= entry.weight as i32;
|
||||||
|
if roll < 0 {
|
||||||
|
selected_idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let selected_entry = &candidates[selected_idx];
|
||||||
|
|
||||||
|
if self.try_set_structure_start(settings.sea_level, selected_entry, random_config) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let failed_entry = candidates.remove(selected_idx);
|
||||||
|
total_weight -= failed_entry.weight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.stage = StagedChunkEnum::StructureStart;
|
self.stage = StagedChunkEnum::StructureStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_structure_references(&mut self) {
|
fn try_set_structure_start(
|
||||||
self.stage = StagedChunkEnum::StructureReferences;
|
&mut self,
|
||||||
|
sea_level: i32,
|
||||||
|
entry: &WeightedEntry,
|
||||||
|
random_config: &GlobalRandomConfig,
|
||||||
|
) -> bool {
|
||||||
|
if let Some(structure) = STRUCTURES.get(&entry.structure) {
|
||||||
|
let position =
|
||||||
|
entry
|
||||||
|
.structure
|
||||||
|
.try_generate(structure, random_config.seed as i64, self, sea_level);
|
||||||
|
|
||||||
|
if let Some(pos) = position {
|
||||||
|
self.structure_starts
|
||||||
|
.insert(entry.structure.clone(), StructureInstance::Start(pos));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_block_box_for_chunk(&self) -> BlockBox {
|
pub fn set_structure_references<T: GenerationCache>(cache: &mut T) {
|
||||||
let x = start_block_x(self.x);
|
let (center_x, center_z) = {
|
||||||
let z = start_block_z(self.z);
|
let chunk = cache.get_center_chunk();
|
||||||
let bottom = self.bottom_y() as i32 + 1;
|
(chunk.x, chunk.z)
|
||||||
let top = self.top_y() as i32;
|
};
|
||||||
BlockBox::new(x, bottom, z, x + 15, top, z + 15)
|
|
||||||
|
let start_block_x = chunk_pos::start_block_x(center_x);
|
||||||
|
let start_block_z = chunk_pos::start_block_z(center_z);
|
||||||
|
let end_block_x = start_block_x + 15;
|
||||||
|
let end_block_z = start_block_z + 15;
|
||||||
|
|
||||||
|
let radius = 8;
|
||||||
|
let mut references_to_add = Vec::new();
|
||||||
|
|
||||||
|
for x in (center_x - radius)..=(center_x + radius) {
|
||||||
|
for z in (center_z - radius)..=(center_z + radius) {
|
||||||
|
if let Some(neighbor_chunk) = cache.get_chunk(x, z) {
|
||||||
|
for (structure_key, instance) in &neighbor_chunk.structure_starts {
|
||||||
|
if let StructureInstance::Start(start_data) = instance {
|
||||||
|
let bbox = start_data.get_bounding_box();
|
||||||
|
if bbox.intersects_raw_xz(
|
||||||
|
start_block_x,
|
||||||
|
start_block_z,
|
||||||
|
end_block_x,
|
||||||
|
end_block_z,
|
||||||
|
) {
|
||||||
|
references_to_add
|
||||||
|
.push((structure_key.clone(), start_data.start_pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let center_chunk = cache.get_center_chunk_mut();
|
||||||
|
for (key, pos) in references_to_add {
|
||||||
|
center_chunk
|
||||||
|
.structure_starts
|
||||||
|
.entry(key)
|
||||||
|
.or_insert(StructureInstance::Reference(pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
center_chunk.stage = StagedChunkEnum::StructureReferences;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_cell_x(&self, horizontal_cell_block_count: u8) -> i32 {
|
fn start_cell_x(&self, horizontal_cell_block_count: u8) -> i32 {
|
||||||
|
|||||||
@ -11,14 +11,17 @@ use crate::{
|
|||||||
structure::{
|
structure::{
|
||||||
placement::StructurePlacement,
|
placement::StructurePlacement,
|
||||||
structures::{
|
structures::{
|
||||||
StructureGenerator, StructurePosition, buried_treasure::BuriedTreasureGenerator,
|
StructureGenerator, StructureGeneratorContext, StructurePosition,
|
||||||
nether_fortress::NetherFortressGenerator, swamp_hut::SwampHutGenerator,
|
buried_treasure::BuriedTreasureGenerator, create_chunk_random,
|
||||||
|
stronghold::StrongholdGenerator, swamp_hut::SwampHutGenerator,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub mod piece;
|
||||||
pub mod placement;
|
pub mod placement;
|
||||||
|
pub mod shiftable_piece;
|
||||||
pub mod structures;
|
pub mod structures;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@ -27,49 +30,49 @@ pub struct StructureSet {
|
|||||||
pub structures: Vec<WeightedEntry>,
|
pub structures: Vec<WeightedEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize, Clone)]
|
||||||
pub struct WeightedEntry {
|
pub struct WeightedEntry {
|
||||||
pub structure: StructureKeys,
|
pub structure: StructureKeys,
|
||||||
pub weight: u32,
|
pub weight: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Clone)]
|
// #[derive(Deserialize)]
|
||||||
pub enum StructureType {
|
// pub enum StructureType {
|
||||||
#[serde(rename = "minecraft:buried_treasure")]
|
// #[serde(rename = "minecraft:buried_treasure")]
|
||||||
BuriedTreasure(BuriedTreasureGenerator),
|
// BuriedTreasure(BuriedTreasureGenerator),
|
||||||
#[serde(rename = "minecraft:desert_pyramid")]
|
// #[serde(rename = "minecraft:desert_pyramid")]
|
||||||
DesertPyramid,
|
// DesertPyramid,
|
||||||
#[serde(rename = "minecraft:end_city")]
|
// #[serde(rename = "minecraft:end_city")]
|
||||||
EndCity,
|
// EndCity,
|
||||||
#[serde(rename = "minecraft:fortress")]
|
// #[serde(rename = "minecraft:fortress")]
|
||||||
NetherFortress(NetherFortressGenerator),
|
// NetherFortress(NetherFortressGenerator),
|
||||||
#[serde(rename = "minecraft:igloo")]
|
// #[serde(rename = "minecraft:igloo")]
|
||||||
Igloo,
|
// Igloo,
|
||||||
#[serde(rename = "minecraft:jigsaw")]
|
// #[serde(rename = "minecraft:jigsaw")]
|
||||||
Jigsaw,
|
// Jigsaw,
|
||||||
#[serde(rename = "minecraft:jungle_temple")]
|
// #[serde(rename = "minecraft:jungle_temple")]
|
||||||
JungleTemple,
|
// JungleTemple,
|
||||||
#[serde(rename = "minecraft:mineshaft")]
|
// #[serde(rename = "minecraft:mineshaft")]
|
||||||
Mineshaft,
|
// Mineshaft,
|
||||||
#[serde(rename = "minecraft:nether_fossil")]
|
// #[serde(rename = "minecraft:nether_fossil")]
|
||||||
NetherFossil,
|
// NetherFossil,
|
||||||
#[serde(rename = "minecraft:ocean_monument")]
|
// #[serde(rename = "minecraft:ocean_monument")]
|
||||||
OceanMonument,
|
// OceanMonument,
|
||||||
#[serde(rename = "minecraft:ocean_ruin")]
|
// #[serde(rename = "minecraft:ocean_ruin")]
|
||||||
OceanRuin,
|
// OceanRuin,
|
||||||
#[serde(rename = "minecraft:ruined_portal")]
|
// #[serde(rename = "minecraft:ruined_portal")]
|
||||||
RuinedPortal,
|
// RuinedPortal,
|
||||||
#[serde(rename = "minecraft:shipwreck")]
|
// #[serde(rename = "minecraft:shipwreck")]
|
||||||
Shipwreck,
|
// Shipwreck,
|
||||||
#[serde(rename = "minecraft:stronghold")]
|
// #[serde(rename = "minecraft:stronghold")]
|
||||||
Stronghold,
|
// Stronghold,
|
||||||
#[serde(rename = "minecraft:swamp_hut")]
|
// #[serde(rename = "minecraft:swamp_hut")]
|
||||||
SwampHut(SwampHutGenerator),
|
// SwampHut(SwampHutGenerator),
|
||||||
#[serde(rename = "minecraft:woodland_mansion")]
|
// #[serde(rename = "minecraft:woodland_mansion")]
|
||||||
WoodlandMansion,
|
// WoodlandMansion,
|
||||||
}
|
// }
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, PartialEq, Eq, Hash)]
|
#[derive(Deserialize, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub enum StructureKeys {
|
pub enum StructureKeys {
|
||||||
#[serde(rename = "minecraft:pillager_outpost")]
|
#[serde(rename = "minecraft:pillager_outpost")]
|
||||||
PillagerOutpost,
|
PillagerOutpost,
|
||||||
@ -147,44 +150,49 @@ pub struct EmptyStruct {}
|
|||||||
impl StructureKeys {
|
impl StructureKeys {
|
||||||
pub fn try_generate(
|
pub fn try_generate(
|
||||||
&self,
|
&self,
|
||||||
|
structure: &Structure,
|
||||||
seed: i64,
|
seed: i64,
|
||||||
chunk_x: i32,
|
chunk: &ProtoChunk,
|
||||||
chunk_z: i32,
|
sea_level: i32,
|
||||||
chunk_ref_for_biomes: &ProtoChunk,
|
|
||||||
) -> Option<StructurePosition> {
|
) -> Option<StructurePosition> {
|
||||||
|
let random = create_chunk_random(seed, chunk.x, chunk.z);
|
||||||
|
let context = StructureGeneratorContext {
|
||||||
|
seed,
|
||||||
|
chunk_x: chunk.x,
|
||||||
|
chunk_z: chunk.z,
|
||||||
|
random,
|
||||||
|
sea_level,
|
||||||
|
min_y: chunk.bottom_y() as i32,
|
||||||
|
};
|
||||||
let structure_pos = match self {
|
let structure_pos = match self {
|
||||||
StructureKeys::BuriedTreasure => BuriedTreasureGenerator::get_structure_position(
|
StructureKeys::BuriedTreasure => {
|
||||||
&BuriedTreasureGenerator,
|
BuriedTreasureGenerator::get_structure_position(&BuriedTreasureGenerator, context)
|
||||||
seed,
|
}
|
||||||
chunk_x,
|
// StructureKeys::Fortress => {
|
||||||
chunk_z,
|
// NetherFortressGenerator::get_structure_position(&NetherFortressGenerator, context)
|
||||||
),
|
// }
|
||||||
StructureKeys::Fortress => NetherFortressGenerator::get_structure_position(
|
StructureKeys::SwampHut => {
|
||||||
&NetherFortressGenerator,
|
SwampHutGenerator::get_structure_position(&SwampHutGenerator, context)
|
||||||
seed,
|
}
|
||||||
chunk_x,
|
StructureKeys::Stronghold => {
|
||||||
chunk_z,
|
StrongholdGenerator::get_structure_position(&StrongholdGenerator, context)
|
||||||
),
|
}
|
||||||
StructureKeys::SwampHut => SwampHutGenerator::get_structure_position(
|
// StructureKeys::DesertPyramid => DesertTempleGenerator::get_structure_position(
|
||||||
&SwampHutGenerator,
|
// &DesertTempleGenerator,
|
||||||
seed,
|
// context
|
||||||
chunk_x,
|
// ),
|
||||||
chunk_z,
|
|
||||||
),
|
|
||||||
// TODO
|
// TODO
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(pos) = structure_pos
|
if let Some(pos) = structure_pos {
|
||||||
&& let Some(structure_data) = STRUCTURES.get(self)
|
|
||||||
{
|
|
||||||
// Get the biome at the structure's starting position
|
// Get the biome at the structure's starting position
|
||||||
let current_biome = chunk_ref_for_biomes.get_biome(
|
let current_biome = chunk.get_biome(
|
||||||
biome_coords::from_block(pos.start_pos.0.x),
|
biome_coords::from_block(pos.start_pos.0.x),
|
||||||
biome_coords::from_block(pos.start_pos.0.y),
|
biome_coords::from_block(pos.start_pos.0.y),
|
||||||
biome_coords::from_block(pos.start_pos.0.z),
|
biome_coords::from_block(pos.start_pos.0.z),
|
||||||
);
|
);
|
||||||
let biomes = Biome::get_tag_values(&structure_data.biomes).unwrap();
|
let biomes = Biome::get_tag_values(&structure.biomes).unwrap();
|
||||||
// Check if the biome is allowed for this structure
|
// Check if the biome is allowed for this structure
|
||||||
if biomes.contains(¤t_biome.registry_id) {
|
if biomes.contains(¤t_biome.registry_id) {
|
||||||
return Some(pos);
|
return Some(pos);
|
||||||
@ -195,17 +203,32 @@ impl StructureKeys {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_structure_pieces(
|
|
||||||
structure_pos: &StructurePosition,
|
|
||||||
chunk: &mut ProtoChunk,
|
|
||||||
seed: i64,
|
|
||||||
) {
|
|
||||||
structure_pos.collector.generate_in_chunk(chunk, seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Deserialize, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
pub struct Structure {
|
pub struct Structure {
|
||||||
biomes: String,
|
pub biomes: String,
|
||||||
|
pub step: GenerationStep,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum GenerationStep {
|
||||||
|
RawGeneration,
|
||||||
|
Lakes,
|
||||||
|
LocalModifications,
|
||||||
|
UndergroundStructures,
|
||||||
|
SurfaceStructures,
|
||||||
|
Strongholds,
|
||||||
|
UndergroundOres,
|
||||||
|
UndergroundDecoration,
|
||||||
|
FluidSprings,
|
||||||
|
VegetalDecoration,
|
||||||
|
TopLayerModification,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenerationStep {
|
||||||
|
pub fn ordinal(&self) -> usize {
|
||||||
|
*self as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static STRUCTURES: LazyLock<HashMap<StructureKeys, Structure>> = LazyLock::new(
|
pub static STRUCTURES: LazyLock<HashMap<StructureKeys, Structure>> = LazyLock::new(
|
||||||
|
|||||||
70
pumpkin-world/src/generation/structure/piece.rs
Normal file
70
pumpkin-world/src/generation/structure/piece.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum StructurePieceType {
|
||||||
|
// Mineshaft
|
||||||
|
MineshaftCorridor,
|
||||||
|
MineshaftCrossing,
|
||||||
|
MineshaftRoom,
|
||||||
|
MineshaftStairs,
|
||||||
|
|
||||||
|
// Nether Fortress
|
||||||
|
NetherFortressBridgeCrossing,
|
||||||
|
NetherFortressBridgeEnd,
|
||||||
|
NetherFortressBridge,
|
||||||
|
NetherFortressCorridorStairs,
|
||||||
|
NetherFortressCorridorBalcony,
|
||||||
|
NetherFortressCorridorExit,
|
||||||
|
NetherFortressCorridorCrossing,
|
||||||
|
NetherFortressCorridorLeftTurn,
|
||||||
|
NetherFortressSmallCorridor,
|
||||||
|
NetherFortressCorridorRightTurn,
|
||||||
|
NetherFortressCorridorNetherWartsRoom,
|
||||||
|
NetherFortressBridgePlatform,
|
||||||
|
NetherFortressBridgeSmallCrossing,
|
||||||
|
NetherFortressBridgeStairs,
|
||||||
|
NetherFortressStart,
|
||||||
|
|
||||||
|
// Stronghold
|
||||||
|
StrongholdChestCorridor,
|
||||||
|
StrongholdSmallCorridor,
|
||||||
|
StrongholdFiveWayCrossing,
|
||||||
|
StrongholdLeftTurn,
|
||||||
|
StrongholdLibrary,
|
||||||
|
StrongholdPortalRoom,
|
||||||
|
StrongholdPrisonHall,
|
||||||
|
StrongholdRightTurn,
|
||||||
|
StrongholdSquareRoom,
|
||||||
|
StrongholdSpiralStaircase,
|
||||||
|
StrongholdStart,
|
||||||
|
StrongholdCorridor,
|
||||||
|
StrongholdStairs,
|
||||||
|
|
||||||
|
// Overworld/General
|
||||||
|
JungleTemple,
|
||||||
|
OceanTemple,
|
||||||
|
Igloo,
|
||||||
|
RuinedPortal,
|
||||||
|
SwampHut,
|
||||||
|
DesertTemple,
|
||||||
|
|
||||||
|
// Ocean Monument
|
||||||
|
OceanMonumentBase,
|
||||||
|
OceanMonumentCoreRoom,
|
||||||
|
OceanMonumentDoubleXRoom,
|
||||||
|
OceanMonumentDoubleXYRoom,
|
||||||
|
OceanMonumentDoubleYRoom,
|
||||||
|
OceanMonumentDoubleYZRoom,
|
||||||
|
OceanMonumentDoubleZRoom,
|
||||||
|
OceanMonumentEntryRoom,
|
||||||
|
OceanMonumentPenthouse,
|
||||||
|
OceanMonumentSimpleRoom,
|
||||||
|
OceanMonumentSimpleTopRoom,
|
||||||
|
OceanMonumentWingRoom,
|
||||||
|
|
||||||
|
// End / Other
|
||||||
|
EndCity,
|
||||||
|
WoodlandMansion,
|
||||||
|
BuriedTreasure,
|
||||||
|
Shipwreck,
|
||||||
|
NetherFossil,
|
||||||
|
Jigsaw,
|
||||||
|
}
|
||||||
@ -19,12 +19,12 @@ pub struct StructurePlacement {
|
|||||||
impl StructurePlacement {
|
impl StructurePlacement {
|
||||||
pub fn should_generate(
|
pub fn should_generate(
|
||||||
&self,
|
&self,
|
||||||
calculator: StructurePlacementCalculator,
|
calculator: &StructurePlacementCalculator,
|
||||||
chunk_x: i32,
|
chunk_x: i32,
|
||||||
chunk_z: i32,
|
chunk_z: i32,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.r#type
|
self.r#type
|
||||||
.is_start_chunk(&calculator, chunk_x, chunk_z, self.salt)
|
.is_start_chunk(calculator, chunk_x, chunk_z, self.salt)
|
||||||
&& self.apply_frequency_reduction(calculator.seed, chunk_x, chunk_z)
|
&& self.apply_frequency_reduction(calculator.seed, chunk_x, chunk_z)
|
||||||
// TODO: add exclusion_zone, only used for pillager_outposts
|
// TODO: add exclusion_zone, only used for pillager_outposts
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ pub enum StructurePlacementType {
|
|||||||
#[serde(rename = "minecraft:random_spread")]
|
#[serde(rename = "minecraft:random_spread")]
|
||||||
RandomSpread(RandomSpreadStructurePlacement),
|
RandomSpread(RandomSpreadStructurePlacement),
|
||||||
#[serde(rename = "minecraft:concentric_rings")]
|
#[serde(rename = "minecraft:concentric_rings")]
|
||||||
ConcentricRings,
|
ConcentricRings(ConcentricRingsStructurePlacement),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructurePlacementType {
|
impl StructurePlacementType {
|
||||||
@ -115,7 +115,10 @@ impl StructurePlacementType {
|
|||||||
StructurePlacementType::RandomSpread(placement) => {
|
StructurePlacementType::RandomSpread(placement) => {
|
||||||
placement.is_start_chunk(calculator, chunk_x, chunk_z, salt)
|
placement.is_start_chunk(calculator, chunk_x, chunk_z, salt)
|
||||||
}
|
}
|
||||||
StructurePlacementType::ConcentricRings => false, // TODO, This is needed for Stronghold, since it is placed in rings
|
// This is needed for Stronghold, since it is placed in rings
|
||||||
|
StructurePlacementType::ConcentricRings(placement) => {
|
||||||
|
placement.is_start_chunk(calculator, chunk_x, chunk_z, salt)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,6 +130,45 @@ pub struct RandomSpreadStructurePlacement {
|
|||||||
spread_type: Option<SpreadType>,
|
spread_type: Option<SpreadType>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct ConcentricRingsStructurePlacement {
|
||||||
|
spread: i32,
|
||||||
|
distance: i32,
|
||||||
|
count: i32,
|
||||||
|
preferred_biomes: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConcentricRingsStructurePlacement {
|
||||||
|
pub fn is_start_chunk(
|
||||||
|
&self,
|
||||||
|
_calculator: &StructurePlacementCalculator,
|
||||||
|
chunk_x: i32,
|
||||||
|
chunk_z: i32,
|
||||||
|
salt: u32,
|
||||||
|
) -> bool {
|
||||||
|
// TODO: this is entirely wrong, but the original logic is too complex for now
|
||||||
|
let x = (chunk_x << 4) as f64;
|
||||||
|
let z = (chunk_z << 4) as f64;
|
||||||
|
let distance_sq = x * x + z * z;
|
||||||
|
let distance = distance_sq.sqrt();
|
||||||
|
|
||||||
|
let ring_width = 1000.0;
|
||||||
|
let ring_gap = 2000.0;
|
||||||
|
|
||||||
|
let in_ring = (distance % (ring_width + ring_gap)) < ring_width;
|
||||||
|
|
||||||
|
if !in_ring {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let seed = (chunk_x as i64).wrapping_mul(341873128712)
|
||||||
|
^ (chunk_z as i64).wrapping_mul(132897987541)
|
||||||
|
^ (salt as i64);
|
||||||
|
|
||||||
|
(seed.abs() % 400) == 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum SpreadType {
|
pub enum SpreadType {
|
||||||
@ -174,6 +216,12 @@ pub struct StructurePlacementCalculator {
|
|||||||
pub seed: i64,
|
pub seed: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl StructurePlacementCalculator {
|
||||||
|
pub fn new(seed: i64) -> Self {
|
||||||
|
Self { seed }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use pumpkin_util::random::{
|
use pumpkin_util::random::{
|
||||||
|
|||||||
@ -0,0 +1,116 @@
|
|||||||
|
use pumpkin_util::{
|
||||||
|
HeightMap,
|
||||||
|
math::{block_box::BlockBox, vector3::Axis},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{piece::StructurePieceType, structures::StructurePiece},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const DEFAULT_H_POS: i32 = -1;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ShiftableStructurePiece {
|
||||||
|
pub piece: StructurePiece,
|
||||||
|
h_pos: i32,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShiftableStructurePiece {
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn new(
|
||||||
|
r#type: StructurePieceType,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
depth: i32,
|
||||||
|
axis: Axis,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
piece: StructurePiece::new(
|
||||||
|
r#type,
|
||||||
|
BlockBox::create_box(x, y, z, axis, width, height, depth),
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
h_pos: DEFAULT_H_POS,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn adjust_to_average_height(&mut self, chunk: &ProtoChunk) -> bool {
|
||||||
|
let bounding_box = self.piece.bounding_box;
|
||||||
|
if self.h_pos >= 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut sum_y = 0;
|
||||||
|
let mut count = 0;
|
||||||
|
|
||||||
|
for z in bounding_box.min.z..=bounding_box.max.z {
|
||||||
|
for x in bounding_box.min.x..=bounding_box.max.x {
|
||||||
|
let y = chunk.get_top_y(&HeightMap::OceanFloorWg, x, z);
|
||||||
|
sum_y += y;
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if count == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.h_pos = sum_y / count;
|
||||||
|
|
||||||
|
let current_min_y = bounding_box.min.y;
|
||||||
|
let offset = self.h_pos - current_min_y;
|
||||||
|
|
||||||
|
self.piece.bounding_box.min.y += offset;
|
||||||
|
self.piece.bounding_box.max.y += offset;
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn adjust_to_min_height(&mut self, chunk: &ProtoChunk, y_offset: i32) -> bool {
|
||||||
|
let bounding_box = self.piece.bounding_box;
|
||||||
|
|
||||||
|
if self.h_pos >= 0 {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut min_y = i32::MAX;
|
||||||
|
let mut found_any = false;
|
||||||
|
|
||||||
|
// Iterate over the X/Z footprint of the bounding box
|
||||||
|
for z in bounding_box.min.z..=bounding_box.max.z {
|
||||||
|
for x in bounding_box.min.x..=bounding_box.max.x {
|
||||||
|
// Vanilla uses MOTION_BLOCKING_NO_LEAVES.
|
||||||
|
// In Pumpkin/Standard generation, OceanFloorWg is usually the closest equivalent for terrain height.
|
||||||
|
let y = chunk.get_top_y(&HeightMap::OceanFloorWg, x, z);
|
||||||
|
|
||||||
|
if y < min_y {
|
||||||
|
min_y = y;
|
||||||
|
}
|
||||||
|
found_any = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should only return false if the bounding box has no area (width or depth is 0)
|
||||||
|
if !found_any {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.h_pos = min_y;
|
||||||
|
|
||||||
|
// Calculate shift: (Target Height - Current Height) + Offset
|
||||||
|
let current_y = bounding_box.min.y;
|
||||||
|
let shift_y = (self.h_pos - current_y) + y_offset;
|
||||||
|
|
||||||
|
self.piece.bounding_box.min.y += shift_y;
|
||||||
|
self.piece.bounding_box.max.y += shift_y;
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,10 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pumpkin_data::{Block, BlockDirection};
|
use pumpkin_data::{Block, BlockDirection};
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{
|
||||||
HeightMap,
|
HeightMap,
|
||||||
math::{block_box::BlockBox, position::BlockPos},
|
math::{block_box::BlockBox, position::BlockPos},
|
||||||
|
random::RandomGenerator,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
@ -9,59 +12,61 @@ use crate::{
|
|||||||
ProtoChunk,
|
ProtoChunk,
|
||||||
generation::{
|
generation::{
|
||||||
positions::chunk_pos::{get_center_x, get_center_z},
|
positions::chunk_pos::{get_center_x, get_center_z},
|
||||||
structure::structures::{
|
structure::{
|
||||||
StructureGenerator, StructurePiece, StructurePiecesCollector, StructurePosition,
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructureGenerator, StructureGeneratorContext, StructurePiece, StructurePieceBase,
|
||||||
|
StructurePiecesCollector, StructurePosition,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize, Clone)]
|
#[derive(Deserialize)]
|
||||||
pub struct BuriedTreasureGenerator;
|
pub struct BuriedTreasureGenerator;
|
||||||
|
|
||||||
impl StructureGenerator for BuriedTreasureGenerator {
|
impl StructureGenerator for BuriedTreasureGenerator {
|
||||||
fn get_structure_position(
|
fn get_structure_position(
|
||||||
&self,
|
&self,
|
||||||
_seed: i64,
|
context: StructureGeneratorContext,
|
||||||
chunk_x: i32,
|
|
||||||
chunk_z: i32,
|
|
||||||
) -> Option<StructurePosition> {
|
) -> Option<StructurePosition> {
|
||||||
let x = get_center_x(chunk_x);
|
let x = get_center_x(context.chunk_x);
|
||||||
let z = get_center_z(chunk_z);
|
let z = get_center_z(context.chunk_z);
|
||||||
|
|
||||||
let bounding_box = BlockBox::new(x, -64, z, x, 320, z);
|
let bounding_box = BlockBox::new(x, -64, z, x, 320, z);
|
||||||
|
|
||||||
let mut collector = StructurePiecesCollector::default();
|
let mut collector = StructurePiecesCollector::default();
|
||||||
|
|
||||||
collector.add_piece(Box::new(BuriedTreasurePiece {
|
collector.add_piece(Box::new(BuriedTreasurePiece {
|
||||||
target_x: x,
|
piece: StructurePiece::new(StructurePieceType::BuriedTreasure, bounding_box, 0),
|
||||||
target_z: z,
|
|
||||||
bounding_box,
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Some(StructurePosition {
|
Some(StructurePosition {
|
||||||
start_pos: BlockPos::new(x, 90, z),
|
start_pos: BlockPos::new(x, 90, z),
|
||||||
collector: collector.into(),
|
collector: Arc::new(collector.into()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The Logic for actually placing the chest
|
#[derive(Clone)]
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct BuriedTreasurePiece {
|
pub struct BuriedTreasurePiece {
|
||||||
target_x: i32,
|
piece: StructurePiece,
|
||||||
target_z: i32,
|
|
||||||
bounding_box: BlockBox,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructurePiece for BuriedTreasurePiece {
|
impl StructurePieceBase for BuriedTreasurePiece {
|
||||||
fn bounding_box(&self) -> &BlockBox {
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
&self.bounding_box
|
Box::new((*self).clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn place(&self, chunk: &mut ProtoChunk, _seed: i64) {
|
fn place(&mut self, chunk: &mut ProtoChunk, _random: &mut RandomGenerator, _seed: i64) {
|
||||||
let y = chunk.get_top_y(&HeightMap::OceanFloorWg, self.target_x, self.target_z);
|
let boundingbox = self.bounding_box();
|
||||||
|
let y = chunk.get_top_y(
|
||||||
|
&HeightMap::OceanFloorWg,
|
||||||
|
boundingbox.min.x,
|
||||||
|
boundingbox.min.z,
|
||||||
|
);
|
||||||
|
|
||||||
let mut pos = BlockPos::new(self.target_x, y, self.target_z);
|
let mut pos = BlockPos::new(boundingbox.min.x, y, boundingbox.min.z);
|
||||||
let bottom_y = chunk.bottom_y() as i32;
|
let bottom_y = chunk.bottom_y() as i32;
|
||||||
|
|
||||||
for _ in (bottom_y..=y).rev() {
|
for _ in (bottom_y..=y).rev() {
|
||||||
@ -111,6 +116,14 @@ impl StructurePiece for BuriedTreasurePiece {
|
|||||||
pos = pos.down();
|
pos = pos.down();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BuriedTreasurePiece {
|
impl BuriedTreasurePiece {
|
||||||
|
|||||||
@ -1,175 +1,542 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use pumpkin_data::Block;
|
||||||
use pumpkin_data::BlockState;
|
use pumpkin_data::BlockState;
|
||||||
use pumpkin_util::math::{block_box::BlockBox, position::BlockPos, vector3::Vector3};
|
use pumpkin_util::HeightMap;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::{block_box::BlockBox, position::BlockPos, vector3::Vector3},
|
||||||
|
random::{RandomGenerator, RandomImpl, get_carver_seed, xoroshiro128::Xoroshiro},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ProtoChunk,
|
ProtoChunk,
|
||||||
generation::{height_limit::HeightLimitView, structure::StructureKeys},
|
generation::{
|
||||||
|
height_limit::HeightLimitView,
|
||||||
|
positions::chunk_pos::{start_block_x, start_block_z},
|
||||||
|
structure::piece::StructurePieceType,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod buried_treasure;
|
pub mod buried_treasure;
|
||||||
pub mod nether_fortress;
|
pub mod nether_fortress;
|
||||||
|
pub mod stronghold;
|
||||||
pub mod swamp_hut;
|
pub mod swamp_hut;
|
||||||
|
|
||||||
#[expect(clippy::too_many_arguments)]
|
pub trait BlockRandomizer {
|
||||||
pub fn fill_with_outline(
|
fn get_block(&self, rng: &mut RandomGenerator, is_border: bool) -> &BlockState;
|
||||||
min_x: i32,
|
|
||||||
min_y: i32,
|
|
||||||
min_z: i32,
|
|
||||||
max_x: i32,
|
|
||||||
max_y: i32,
|
|
||||||
max_z: i32,
|
|
||||||
outline: &BlockState,
|
|
||||||
inside: &BlockState,
|
|
||||||
chunk: &mut ProtoChunk,
|
|
||||||
) {
|
|
||||||
let chunk_x = chunk.x;
|
|
||||||
let chunk_z = chunk.z;
|
|
||||||
|
|
||||||
let chunk_min_x = chunk_x * 16;
|
|
||||||
let chunk_max_x = chunk_min_x + 15;
|
|
||||||
let chunk_min_z = chunk_z * 16;
|
|
||||||
let chunk_max_z = chunk_min_z + 15;
|
|
||||||
|
|
||||||
let start_x = min_x.max(chunk_min_x);
|
|
||||||
let end_x = max_x.min(chunk_max_x);
|
|
||||||
let start_z = min_z.max(chunk_min_z);
|
|
||||||
let end_z = max_z.min(chunk_max_z);
|
|
||||||
|
|
||||||
if start_x > end_x || start_z > end_z {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for y in min_y..=max_y {
|
|
||||||
for x in start_x..=end_x {
|
|
||||||
for z in start_z..=end_z {
|
|
||||||
let is_border = x == min_x
|
|
||||||
|| x == max_x
|
|
||||||
|| y == min_y
|
|
||||||
|| y == max_y
|
|
||||||
|| z == min_z
|
|
||||||
|| z == max_z;
|
|
||||||
|
|
||||||
let block = if is_border { outline } else { inside };
|
|
||||||
chunk.set_block_state(&Vector3::new(x, y, z), block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fills a solid cuboid.
|
|
||||||
#[expect(clippy::too_many_arguments)]
|
|
||||||
pub fn fill(
|
|
||||||
min_x: i32,
|
|
||||||
min_y: i32,
|
|
||||||
min_z: i32,
|
|
||||||
max_x: i32,
|
|
||||||
max_y: i32,
|
|
||||||
max_z: i32,
|
|
||||||
state: &BlockState,
|
|
||||||
chunk: &mut ProtoChunk,
|
|
||||||
) {
|
|
||||||
// Basic bounds check to avoid iterating 1000s of blocks outside the chunk
|
|
||||||
let chunk_x = chunk.x;
|
|
||||||
let chunk_z = chunk.z;
|
|
||||||
let chunk_min_x = chunk_x * 16;
|
|
||||||
let chunk_max_x = chunk_min_x + 15;
|
|
||||||
let chunk_min_z = chunk_z * 16;
|
|
||||||
let chunk_max_z = chunk_min_z + 15;
|
|
||||||
|
|
||||||
let start_x = min_x.max(chunk_min_x);
|
|
||||||
let end_x = max_x.min(chunk_max_x);
|
|
||||||
let start_z = min_z.max(chunk_min_z);
|
|
||||||
let end_z = max_z.min(chunk_max_z);
|
|
||||||
|
|
||||||
if start_x > end_x || start_z > end_z {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for y in min_y..=max_y {
|
|
||||||
for x in start_x..=end_x {
|
|
||||||
for z in start_z..=end_z {
|
|
||||||
chunk.set_block_state(&Vector3::new(x, y, z), state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fills downwards from a point until the bottom of the world.
|
|
||||||
pub fn fill_downwards(x: i32, y: i32, z: i32, state: &BlockState, chunk: &mut ProtoChunk) {
|
|
||||||
let start_y = y;
|
|
||||||
let end_y = chunk.bottom_y() as i32; // e.g. -64
|
|
||||||
|
|
||||||
for current_y in (end_y..=start_y).rev() {
|
|
||||||
chunk.set_block_state(&Vector3::new(x, current_y, z), state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a single component of a structure (e.g., a room, a bridge).
|
/// Represents a single component of a structure (e.g., a room, a bridge).
|
||||||
pub trait StructurePiece: Send + Sync {
|
pub trait StructurePieceBase: Send + Sync {
|
||||||
/// The bounding box of this specific piece.
|
fn get_structure_piece(&self) -> &StructurePiece;
|
||||||
fn bounding_box(&self) -> &BlockBox;
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece;
|
||||||
|
|
||||||
|
fn bounding_box(&self) -> BlockBox {
|
||||||
|
self.get_structure_piece().bounding_box
|
||||||
|
}
|
||||||
|
|
||||||
|
fn translate(&mut self, x: i32, y: i32, z: i32) {
|
||||||
|
self.get_structure_piece_mut().translate(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase>;
|
||||||
|
|
||||||
/// Places the blocks for this piece into the chunk.
|
/// Places the blocks for this piece into the chunk.
|
||||||
fn place(&self, chunk: &mut ProtoChunk, seed: i64);
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, seed: i64);
|
||||||
|
|
||||||
|
fn fill_openings(
|
||||||
|
&self,
|
||||||
|
_start: &StructurePiece,
|
||||||
|
_random: &mut RandomGenerator,
|
||||||
|
_collector: &mut StructurePiecesCollector,
|
||||||
|
_pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct StructurePiece {
|
||||||
|
pub r#type: StructurePieceType,
|
||||||
|
pub bounding_box: BlockBox,
|
||||||
|
pub facing: Option<BlockDirection>,
|
||||||
|
pub chain_length: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePiece {
|
||||||
|
pub fn new(r#type: StructurePieceType, bounding_box: BlockBox, chain_length: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
r#type,
|
||||||
|
bounding_box,
|
||||||
|
facing: None,
|
||||||
|
chain_length,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_facing(&mut self, facing: Option<BlockDirection>) {
|
||||||
|
self.facing = facing;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offset_pos(&self, x: i32, y: i32, z: i32) -> Vector3<i32> {
|
||||||
|
Vector3::new(
|
||||||
|
self.apply_x_transform(x, z),
|
||||||
|
self.apply_y_transform(y),
|
||||||
|
self.apply_z_transform(x, z),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_x_transform(&self, x: i32, z: i32) -> i32 {
|
||||||
|
match self.facing {
|
||||||
|
None => x,
|
||||||
|
Some(BlockDirection::North) | Some(BlockDirection::South) => {
|
||||||
|
self.bounding_box.min.x + x
|
||||||
|
}
|
||||||
|
Some(BlockDirection::West) => self.bounding_box.max.x - z,
|
||||||
|
Some(BlockDirection::East) => self.bounding_box.min.x + z,
|
||||||
|
_ => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_y_transform(&self, y: i32) -> i32 {
|
||||||
|
match self.facing {
|
||||||
|
None => y,
|
||||||
|
Some(_) => y + self.bounding_box.min.y,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn apply_z_transform(&self, x: i32, z: i32) -> i32 {
|
||||||
|
match self.facing {
|
||||||
|
None => z,
|
||||||
|
Some(BlockDirection::North) => self.bounding_box.max.z - z,
|
||||||
|
Some(BlockDirection::South) => self.bounding_box.min.z + z,
|
||||||
|
Some(BlockDirection::West) | Some(BlockDirection::East) => self.bounding_box.min.z + x,
|
||||||
|
_ => z,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn fill_outline_random(
|
||||||
|
&self,
|
||||||
|
min_x: i32,
|
||||||
|
min_y: i32,
|
||||||
|
min_z: i32,
|
||||||
|
max_x: i32,
|
||||||
|
max_y: i32,
|
||||||
|
max_z: i32,
|
||||||
|
randomizer: &impl BlockRandomizer,
|
||||||
|
chunk: &mut ProtoChunk,
|
||||||
|
cant_replace_air: bool,
|
||||||
|
rng: &mut RandomGenerator,
|
||||||
|
box_limit: &BlockBox,
|
||||||
|
) {
|
||||||
|
for y in min_y..=max_y {
|
||||||
|
for x in min_x..=max_x {
|
||||||
|
for z in min_z..=max_z {
|
||||||
|
if cant_replace_air && self.get_block_at(chunk, x, y, z, box_limit).is_air() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let is_border = x == min_x
|
||||||
|
|| x == max_x
|
||||||
|
|| y == min_y
|
||||||
|
|| y == max_y
|
||||||
|
|| z == min_z
|
||||||
|
|| z == max_z;
|
||||||
|
let state = randomizer.get_block(rng, is_border);
|
||||||
|
self.add_block(chunk, state, x, y, z, box_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn fill_with_outline(
|
||||||
|
&self,
|
||||||
|
chunk: &mut ProtoChunk,
|
||||||
|
box_limit: &BlockBox,
|
||||||
|
cant_replace_air: bool,
|
||||||
|
min_x: i32,
|
||||||
|
min_y: i32,
|
||||||
|
min_z: i32,
|
||||||
|
max_x: i32,
|
||||||
|
max_y: i32,
|
||||||
|
max_z: i32,
|
||||||
|
outline: &BlockState,
|
||||||
|
inside: &BlockState,
|
||||||
|
) {
|
||||||
|
for y in min_y..=max_y {
|
||||||
|
for x in min_x..=max_x {
|
||||||
|
for z in min_z..=max_z {
|
||||||
|
if cant_replace_air && self.get_block_at(chunk, x, y, z, box_limit).is_air() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let is_border = x == min_x
|
||||||
|
|| x == max_x
|
||||||
|
|| y == min_y
|
||||||
|
|| y == max_y
|
||||||
|
|| z == min_z
|
||||||
|
|| z == max_z;
|
||||||
|
|
||||||
|
let block = if is_border { outline } else { inside };
|
||||||
|
self.add_block(chunk, block, x, y, z, box_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn fill_with_outline_under_sea_level(
|
||||||
|
&self,
|
||||||
|
chunk: &mut ProtoChunk,
|
||||||
|
box_limit: &BlockBox,
|
||||||
|
rng: &mut RandomGenerator,
|
||||||
|
block_chance: f32,
|
||||||
|
min_x: i32,
|
||||||
|
min_y: i32,
|
||||||
|
min_z: i32,
|
||||||
|
max_x: i32,
|
||||||
|
max_y: i32,
|
||||||
|
max_z: i32,
|
||||||
|
outline: &BlockState,
|
||||||
|
inside: &BlockState,
|
||||||
|
cant_replace_air: bool,
|
||||||
|
stay_below_sea_level: bool,
|
||||||
|
) {
|
||||||
|
for y in min_y..=max_y {
|
||||||
|
for x in min_x..=max_x {
|
||||||
|
for z in min_z..=max_z {
|
||||||
|
// 1. Random Threshold Check
|
||||||
|
if rng.next_f32() > block_chance {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Air Replacement Check
|
||||||
|
if cant_replace_air && self.get_block_at(chunk, x, y, z, box_limit).is_air() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if stay_below_sea_level && !self.is_under_sea_level(chunk, x, y, z, box_limit) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_border = x == min_x
|
||||||
|
|| x == max_x
|
||||||
|
|| y == min_y
|
||||||
|
|| y == max_y
|
||||||
|
|| z == min_z
|
||||||
|
|| z == max_z;
|
||||||
|
|
||||||
|
let state = if is_border { outline } else { inside };
|
||||||
|
self.add_block(chunk, state, x, y, z, box_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fills a solid cuboid.
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn fill(
|
||||||
|
&self,
|
||||||
|
chunk: &mut ProtoChunk,
|
||||||
|
box_limit: &BlockBox,
|
||||||
|
min_x: i32,
|
||||||
|
min_y: i32,
|
||||||
|
min_z: i32,
|
||||||
|
max_x: i32,
|
||||||
|
max_y: i32,
|
||||||
|
max_z: i32,
|
||||||
|
state: &BlockState,
|
||||||
|
) {
|
||||||
|
for y in min_y..=max_y {
|
||||||
|
for x in min_x..=max_x {
|
||||||
|
for z in min_z..=max_z {
|
||||||
|
self.add_block(chunk, state, x, y, z, box_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fills downwards from a relative point until the bottom of the world.
|
||||||
|
pub fn fill_downwards(
|
||||||
|
&self,
|
||||||
|
chunk: &mut ProtoChunk,
|
||||||
|
state: &BlockState,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
box_limit: &BlockBox,
|
||||||
|
) {
|
||||||
|
// We transform the starting point to world space to get the real Y
|
||||||
|
let world_pos = self.offset_pos(x, y, z);
|
||||||
|
let start_y = world_pos.y;
|
||||||
|
let end_y = chunk.bottom_y() as i32;
|
||||||
|
|
||||||
|
for current_y in (end_y..=start_y).rev() {
|
||||||
|
// We bypass add_block here because add_block transforms X/Z again.
|
||||||
|
// We use the already-transformed world X/Z but iterate Y manually.
|
||||||
|
let current_pos = Vector3::new(world_pos.x, current_y, world_pos.z);
|
||||||
|
|
||||||
|
if box_limit.contains_pos(¤t_pos) {
|
||||||
|
chunk.set_block_state(¤t_pos, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_under_sea_level(
|
||||||
|
&self,
|
||||||
|
chunk: &mut ProtoChunk,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
box_limit: &BlockBox,
|
||||||
|
) -> bool {
|
||||||
|
let block_pos = self.offset_pos(x, y, z);
|
||||||
|
|
||||||
|
if !box_limit.contains_pos(&block_pos) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sea_level_at_pos = chunk.get_top_y(&HeightMap::OceanFloorWg, block_pos.x, block_pos.z);
|
||||||
|
block_pos.y < sea_level_at_pos
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_block_at(
|
||||||
|
&self,
|
||||||
|
chunk: &ProtoChunk,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
box_limit: &BlockBox,
|
||||||
|
) -> &BlockState {
|
||||||
|
let block_pos = self.offset_pos(x, y, z);
|
||||||
|
|
||||||
|
if !box_limit.contains_pos(&block_pos) {
|
||||||
|
log::warn!("Structure out of bounds");
|
||||||
|
return Block::AIR.default_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk.get_block_state(&block_pos).to_state()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_block(
|
||||||
|
&self,
|
||||||
|
world: &mut ProtoChunk,
|
||||||
|
block: &BlockState,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
box_limit: &BlockBox,
|
||||||
|
) {
|
||||||
|
let block_pos = self.offset_pos(x, y, z);
|
||||||
|
|
||||||
|
// Bounds and logic checks
|
||||||
|
if !box_limit.contains_pos(&block_pos) {
|
||||||
|
log::warn!("Structure out of bounds");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// // Apply Mirror and Rotation
|
||||||
|
// if self.mirror != BlockMirror::None {
|
||||||
|
// block = block.mirror(self.mirror);
|
||||||
|
// }
|
||||||
|
// if self.rotation != BlockRotation::None {
|
||||||
|
// block = block.rotate(self.rotation);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// World interaction
|
||||||
|
world.set_block_state(&block_pos, block);
|
||||||
|
|
||||||
|
// let fluid_state = world.get_fluid_state(&block_pos);
|
||||||
|
// if !fluid_state.is_empty() {
|
||||||
|
// world.schedule_fluid_tick(&block_pos, fluid_state.fluid(), 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if block.needs_post_processing() {
|
||||||
|
// world.mark_block_for_post_processing(&block_pos);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_random_horizontal_direction(random: &mut impl RandomImpl) -> BlockDirection {
|
||||||
|
match random.next_bounded_i32(4) {
|
||||||
|
0 => BlockDirection::North,
|
||||||
|
1 => BlockDirection::South,
|
||||||
|
2 => BlockDirection::West,
|
||||||
|
_ => BlockDirection::East,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for StructurePiece {
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, _chunk: &mut ProtoChunk, _random: &mut RandomGenerator, _seed: i64) {}
|
||||||
|
|
||||||
|
fn translate(&mut self, x: i32, y: i32, z: i32) {
|
||||||
|
self.bounding_box.move_pos(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Holds all the pieces that make up a generated structure instance.
|
/// Holds all the pieces that make up a generated structure instance.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct StructurePiecesCollector {
|
pub struct StructurePiecesCollector {
|
||||||
pub pieces: Vec<Box<dyn StructurePiece>>,
|
pub pieces: Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
cached_box: Option<BlockBox>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructurePiecesCollector {
|
impl StructurePiecesCollector {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { pieces: Vec::new() }
|
Self {
|
||||||
|
pieces: Vec::new(),
|
||||||
|
cached_box: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_piece(&mut self, piece: Box<dyn StructurePiece>) {
|
pub fn add_piece(&mut self, piece: Box<dyn StructurePieceBase>) {
|
||||||
self.pieces.push(piece);
|
self.pieces.push(piece);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::borrowed_box)]
|
||||||
|
pub fn get_intersecting(
|
||||||
|
&self,
|
||||||
|
box_to_check: &BlockBox,
|
||||||
|
) -> Option<&Box<dyn StructurePieceBase>> {
|
||||||
|
self.pieces
|
||||||
|
.iter()
|
||||||
|
.find(|&piece| {
|
||||||
|
piece
|
||||||
|
.get_structure_piece()
|
||||||
|
.bounding_box
|
||||||
|
.intersects(box_to_check)
|
||||||
|
})
|
||||||
|
.map(|v| v as _)
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterates over all pieces and generates them if they intersect the current chunk.
|
/// Iterates over all pieces and generates them if they intersect the current chunk.
|
||||||
pub fn generate_in_chunk(&self, chunk: &mut ProtoChunk, seed: i64) {
|
pub fn generate_in_chunk(
|
||||||
let chunk_x = chunk.x;
|
&mut self,
|
||||||
let chunk_z = chunk.z;
|
chunk: &mut ProtoChunk,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
seed: i64,
|
||||||
|
) {
|
||||||
|
let chunk_x = start_block_x(chunk.x);
|
||||||
|
let chunk_z = start_block_z(chunk.z);
|
||||||
let chunk_box = BlockBox::new(
|
let chunk_box = BlockBox::new(
|
||||||
chunk_x * 16,
|
chunk_x,
|
||||||
chunk.bottom_y() as i32,
|
chunk.bottom_y() as i32 + 1,
|
||||||
chunk_z * 16,
|
chunk_z,
|
||||||
(chunk_x * 16) + 15,
|
chunk_x + 15,
|
||||||
chunk.top_y() as i32,
|
chunk.bottom_y() as i32 + chunk.top_y() as i32 - 1,
|
||||||
(chunk_z * 16) + 15,
|
chunk_z + 15,
|
||||||
);
|
);
|
||||||
|
|
||||||
for piece in &self.pieces {
|
for piece in self.pieces.iter_mut() {
|
||||||
if piece.bounding_box().intersects(&chunk_box) {
|
if piece.bounding_box().intersects(&chunk_box) {
|
||||||
piece.place(chunk, seed);
|
piece.place(chunk, random, seed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn shift(&mut self, y_offset: i32) {
|
||||||
|
for piece in self.pieces.iter_mut() {
|
||||||
|
piece.translate(0, y_offset, 0);
|
||||||
|
}
|
||||||
|
self.cached_box = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculates a random vertical position and shifts the structure to fit.
|
||||||
|
/// Matches 'shiftInto(int topY, int bottomY, Random random, int topPenalty)'
|
||||||
|
pub fn shift_into(
|
||||||
|
&mut self,
|
||||||
|
top_y: i32,
|
||||||
|
bottom_y: i32,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
top_penalty: i32,
|
||||||
|
) -> i32 {
|
||||||
|
let i = top_y - top_penalty;
|
||||||
|
let bounding_box = self.get_bounding_box();
|
||||||
|
|
||||||
|
let mut j = (bounding_box.max.y - bounding_box.min.y + 1) + bottom_y + 1;
|
||||||
|
|
||||||
|
if j < i {
|
||||||
|
j += random.next_bounded_i32(i - j);
|
||||||
|
}
|
||||||
|
|
||||||
|
let k = j - bounding_box.max.y;
|
||||||
|
|
||||||
|
self.shift(k);
|
||||||
|
|
||||||
|
k
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bounding_box(&mut self) -> BlockBox {
|
||||||
|
if let Some(bbox) = self.cached_box {
|
||||||
|
return bbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
let bbox = BlockBox::encompass_all(self.pieces.iter().map(|p| p.bounding_box()))
|
||||||
|
.expect("Structure must have at least one piece to calculate a bounding box");
|
||||||
|
|
||||||
|
self.cached_box = Some(bbox);
|
||||||
|
bbox
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.pieces.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.pieces.clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct StructurePosition {
|
pub struct StructurePosition {
|
||||||
pub start_pos: BlockPos,
|
pub start_pos: BlockPos,
|
||||||
pub collector: Arc<StructurePiecesCollector>,
|
pub collector: Arc<Mutex<StructurePiecesCollector>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePosition {
|
||||||
|
pub fn get_bounding_box(&self) -> BlockBox {
|
||||||
|
self.collector.lock().unwrap().get_bounding_box()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait StructureGenerator {
|
pub trait StructureGenerator {
|
||||||
fn get_structure_position(
|
fn get_structure_position(
|
||||||
&self,
|
&self,
|
||||||
seed: i64,
|
context: StructureGeneratorContext,
|
||||||
chunk_x: i32,
|
|
||||||
chunk_z: i32,
|
|
||||||
) -> Option<StructurePosition>;
|
) -> Option<StructurePosition>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct StructureGeneratorContext {
|
||||||
|
pub seed: i64,
|
||||||
|
pub chunk_x: i32,
|
||||||
|
pub chunk_z: i32,
|
||||||
|
pub random: RandomGenerator,
|
||||||
|
pub sea_level: i32,
|
||||||
|
pub min_y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_chunk_random(seed: i64, chunk_x: i32, chunk_z: i32) -> RandomGenerator {
|
||||||
|
let mut random: RandomGenerator = RandomGenerator::Xoroshiro(Xoroshiro::from_seed(seed as u64));
|
||||||
|
let carver_seed = get_carver_seed(&mut random, seed as u64, chunk_x, chunk_z);
|
||||||
|
RandomGenerator::Xoroshiro(Xoroshiro::from_seed(carver_seed))
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum StructureInstance {
|
pub enum StructureInstance {
|
||||||
/// This chunk is the "owner" of the structure.
|
/// This chunk is the "owner" of the structure.
|
||||||
Start(StructurePosition, StructureKeys),
|
Start(StructurePosition),
|
||||||
/// This chunk just contains a piece of a structure starting elsewhere.
|
/// This chunk just contains a piece of a structure starting elsewhere.
|
||||||
/// Stores the BlockPos of the 'Start' so you can look it up.
|
/// Stores the BlockPos of the 'Start' so you can look it up.
|
||||||
Reference(BlockPos),
|
Reference(BlockPos),
|
||||||
|
|||||||
@ -1,191 +1,188 @@
|
|||||||
use pumpkin_data::Block;
|
// use std::sync::Arc;
|
||||||
use pumpkin_util::math::{block_box::BlockBox, position::BlockPos, vector3::Vector3};
|
|
||||||
use serde::Deserialize;
|
|
||||||
|
|
||||||
use crate::{
|
// use pumpkin_data::Block;
|
||||||
ProtoChunk,
|
// use pumpkin_util::{
|
||||||
generation::{
|
// math::{block_box::BlockBox, position::BlockPos, vector3::Vector3},
|
||||||
positions::chunk_pos,
|
// random::RandomGenerator,
|
||||||
structure::structures::{
|
// };
|
||||||
StructureGenerator, StructurePiece, StructurePiecesCollector, StructurePosition, fill,
|
// use serde::Deserialize;
|
||||||
fill_downwards, fill_with_outline,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Deserialize, Clone, Debug)]
|
// use crate::{
|
||||||
pub struct NetherFortressGenerator;
|
// ProtoChunk,
|
||||||
|
// generation::{
|
||||||
|
// positions::chunk_pos,
|
||||||
|
// structure::structures::{
|
||||||
|
// StructureGenerator, StructureGeneratorContext, StructurePiece, StructurePieceBase,
|
||||||
|
// StructurePiecesCollector, StructurePosition,
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// };
|
||||||
|
|
||||||
impl StructureGenerator for NetherFortressGenerator {
|
// #[derive(Deserialize, Clone, Debug)]
|
||||||
fn get_structure_position(
|
// pub struct NetherFortressGenerator;
|
||||||
&self,
|
|
||||||
_seed: i64,
|
|
||||||
chunk_x: i32,
|
|
||||||
chunk_z: i32,
|
|
||||||
) -> Option<StructurePosition> {
|
|
||||||
let start_x = chunk_pos::start_block_x(chunk_x);
|
|
||||||
let start_z = chunk_pos::start_block_z(chunk_z);
|
|
||||||
let start_y = 64;
|
|
||||||
|
|
||||||
let mut collector = StructurePiecesCollector::default();
|
// impl StructureGenerator for NetherFortressGenerator {
|
||||||
|
// fn get_structure_position(
|
||||||
|
// &self,
|
||||||
|
// context: StructureGeneratorContext,
|
||||||
|
// ) -> Option<StructurePosition> {
|
||||||
|
// let start_x = chunk_pos::start_block_x(context.chunk_x);
|
||||||
|
// let start_z = chunk_pos::start_block_z(context.chunk_z);
|
||||||
|
// let start_y = 64;
|
||||||
|
|
||||||
// Add a Bridge Platform piece
|
// let mut collector = StructurePiecesCollector::default();
|
||||||
let box_min = BlockPos::new(start_x, start_y, start_z);
|
|
||||||
let box_max = BlockPos::new(start_x + 8, start_y + 10, start_z + 8);
|
|
||||||
|
|
||||||
collector.add_piece(Box::new(NetherFortressPiece {
|
// // Add a Bridge Platform piece
|
||||||
bounding_box: BlockBox::new(
|
// let box_min = BlockPos::new(start_x, start_y, start_z);
|
||||||
box_min.0.x,
|
// let box_max = BlockPos::new(start_x + 8, start_y + 10, start_z + 8);
|
||||||
box_min.0.y,
|
|
||||||
box_min.0.z,
|
|
||||||
box_max.0.x,
|
|
||||||
box_max.0.y,
|
|
||||||
box_max.0.z,
|
|
||||||
),
|
|
||||||
piece_type: NetherFortressPieceType::BridgePlatform,
|
|
||||||
}));
|
|
||||||
|
|
||||||
Some(StructurePosition {
|
// todo!();
|
||||||
start_pos: box_min,
|
|
||||||
collector: collector.into(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
// Some(StructurePosition {
|
||||||
pub enum NetherFortressPieceType {
|
// start_pos: box_min,
|
||||||
BridgePlatform,
|
// collector: Arc::new(collector.into()),
|
||||||
SmallCorridor,
|
// })
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
// #[derive(Debug, Clone)]
|
||||||
pub struct NetherFortressPiece {
|
// pub enum NetherFortressPieceType {
|
||||||
pub bounding_box: BlockBox,
|
// BridgePlatform,
|
||||||
pub piece_type: NetherFortressPieceType,
|
// SmallCorridor,
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl StructurePiece for NetherFortressPiece {
|
// struct NetherFortressPiece {
|
||||||
fn bounding_box(&self) -> &BlockBox {
|
// piece: StructurePiece,
|
||||||
&self.bounding_box
|
// piece_type: NetherFortressPieceType,
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn place(&self, chunk: &mut ProtoChunk, _seed: i64) {
|
// impl StructurePieceBase for NetherFortressPiece {
|
||||||
let origin_x = self.bounding_box.min.x;
|
// fn place(&mut self, chunk: &mut ProtoChunk, _seed: i64) {
|
||||||
let origin_y = self.bounding_box.min.y;
|
// let bounding_box = self.piece.bounding_box;
|
||||||
let origin_z = self.bounding_box.min.z;
|
// let origin_x = bounding_box.min.x;
|
||||||
|
// let origin_y = bounding_box.min.y;
|
||||||
|
// let origin_z = bounding_box.min.z;
|
||||||
|
|
||||||
let offset = |x, y, z| Vector3::new(origin_x + x, origin_y + y, origin_z + z);
|
// let offset = |x, y, z| Vector3::new(origin_x + x, origin_y + y, origin_z + z);
|
||||||
|
|
||||||
let fill_rel = |min_x, min_y, min_z, max_x, max_y, max_z, block, c: &mut ProtoChunk| {
|
// let fill_rel = |min_x, min_y, min_z, max_x, max_y, max_z, block, c: &mut ProtoChunk| {
|
||||||
fill(
|
// fill(
|
||||||
origin_x + min_x,
|
// origin_x + min_x,
|
||||||
origin_y + min_y,
|
// origin_y + min_y,
|
||||||
origin_z + min_z,
|
// origin_z + min_z,
|
||||||
origin_x + max_x,
|
// origin_x + max_x,
|
||||||
origin_y + max_y,
|
// origin_y + max_y,
|
||||||
origin_z + max_z,
|
// origin_z + max_z,
|
||||||
block,
|
// block,
|
||||||
c,
|
// c,
|
||||||
)
|
// )
|
||||||
};
|
// };
|
||||||
|
|
||||||
let fill_outline_rel =
|
// let fill_outline_rel =
|
||||||
|min_x, min_y, min_z, max_x, max_y, max_z, outline, inside, c: &mut ProtoChunk| {
|
// |min_x, min_y, min_z, max_x, max_y, max_z, outline, inside, c: &mut ProtoChunk| {
|
||||||
fill_with_outline(
|
// fill_with_outline(
|
||||||
origin_x + min_x,
|
// origin_x + min_x,
|
||||||
origin_y + min_y,
|
// origin_y + min_y,
|
||||||
origin_z + min_z,
|
// origin_z + min_z,
|
||||||
origin_x + max_x,
|
// origin_x + max_x,
|
||||||
origin_y + max_y,
|
// origin_y + max_y,
|
||||||
origin_z + max_z,
|
// origin_z + max_z,
|
||||||
outline,
|
// outline,
|
||||||
inside,
|
// inside,
|
||||||
c,
|
// c,
|
||||||
)
|
// )
|
||||||
};
|
// };
|
||||||
|
|
||||||
match self.piece_type {
|
// match self.piece_type {
|
||||||
NetherFortressPieceType::BridgePlatform => {
|
// NetherFortressPieceType::BridgePlatform => {
|
||||||
fill_rel(0, 2, 0, 6, 7, 7, Block::AIR.default_state, chunk);
|
// fill_rel(0, 2, 0, 6, 7, 7, Block::AIR.default_state, chunk);
|
||||||
fill_rel(1, 0, 0, 5, 1, 7, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(1, 0, 0, 5, 1, 7, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(1, 2, 1, 5, 2, 7, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(1, 2, 1, 5, 2, 7, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(1, 3, 2, 5, 3, 7, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(1, 3, 2, 5, 3, 7, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(1, 4, 3, 5, 4, 7, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(1, 4, 3, 5, 4, 7, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(1, 2, 0, 1, 4, 2, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(1, 2, 0, 1, 4, 2, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(5, 2, 0, 5, 4, 2, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(5, 2, 0, 5, 4, 2, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(1, 5, 2, 1, 5, 3, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(1, 5, 2, 1, 5, 3, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(5, 5, 2, 5, 5, 3, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(5, 5, 2, 5, 5, 3, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(0, 5, 3, 0, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(0, 5, 3, 0, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(6, 5, 3, 6, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(6, 5, 3, 6, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(1, 5, 8, 5, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(1, 5, 8, 5, 5, 8, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
|
|
||||||
let fence = &Block::NETHER_BRICK_FENCE.default_state;
|
// let fence = &Block::NETHER_BRICK_FENCE.default_state;
|
||||||
|
|
||||||
chunk.set_block_state(&offset(1, 6, 3), fence);
|
// chunk.set_block_state(&offset(1, 6, 3), fence);
|
||||||
chunk.set_block_state(&offset(5, 6, 3), fence);
|
// chunk.set_block_state(&offset(5, 6, 3), fence);
|
||||||
chunk.set_block_state(&offset(0, 6, 3), fence);
|
// chunk.set_block_state(&offset(0, 6, 3), fence);
|
||||||
chunk.set_block_state(&offset(6, 6, 3), fence);
|
// chunk.set_block_state(&offset(6, 6, 3), fence);
|
||||||
|
|
||||||
fill_outline_rel(0, 6, 4, 0, 6, 7, fence, fence, chunk);
|
// fill_outline_rel(0, 6, 4, 0, 6, 7, fence, fence, chunk);
|
||||||
fill_outline_rel(6, 6, 4, 6, 6, 7, fence, fence, chunk);
|
// fill_outline_rel(6, 6, 4, 6, 6, 7, fence, fence, chunk);
|
||||||
|
|
||||||
chunk.set_block_state(&offset(0, 6, 8), fence);
|
// chunk.set_block_state(&offset(0, 6, 8), fence);
|
||||||
chunk.set_block_state(&offset(6, 6, 8), fence);
|
// chunk.set_block_state(&offset(6, 6, 8), fence);
|
||||||
|
|
||||||
fill_outline_rel(1, 6, 8, 5, 6, 8, fence, fence, chunk);
|
// fill_outline_rel(1, 6, 8, 5, 6, 8, fence, fence, chunk);
|
||||||
|
|
||||||
chunk.set_block_state(&offset(1, 7, 8), fence);
|
// chunk.set_block_state(&offset(1, 7, 8), fence);
|
||||||
fill_outline_rel(2, 7, 8, 4, 7, 8, fence, fence, chunk);
|
// fill_outline_rel(2, 7, 8, 4, 7, 8, fence, fence, chunk);
|
||||||
chunk.set_block_state(&offset(5, 7, 8), fence);
|
// chunk.set_block_state(&offset(5, 7, 8), fence);
|
||||||
|
|
||||||
chunk.set_block_state(&offset(2, 8, 8), fence);
|
// chunk.set_block_state(&offset(2, 8, 8), fence);
|
||||||
chunk.set_block_state(&offset(3, 8, 8), fence);
|
// chunk.set_block_state(&offset(3, 8, 8), fence);
|
||||||
chunk.set_block_state(&offset(4, 8, 8), fence);
|
// chunk.set_block_state(&offset(4, 8, 8), fence);
|
||||||
|
|
||||||
// Blaze Spawner
|
// // Blaze Spawner
|
||||||
chunk.set_block_state(&offset(3, 5, 5), Block::SPAWNER.default_state);
|
// chunk.set_block_state(&offset(3, 5, 5), Block::SPAWNER.default_state);
|
||||||
|
|
||||||
// Pillars down
|
// // Pillars down
|
||||||
for x in 0..6 {
|
// for x in 0..6 {
|
||||||
for z in 0..6 {
|
// for z in 0..6 {
|
||||||
fill_downwards(
|
// fill_downwards(
|
||||||
origin_x + x,
|
// origin_x + x,
|
||||||
origin_y - 1,
|
// origin_y - 1,
|
||||||
origin_z + z,
|
// origin_z + z,
|
||||||
Block::NETHER_BRICKS.default_state,
|
// Block::NETHER_BRICKS.default_state,
|
||||||
chunk,
|
// chunk,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
NetherFortressPieceType::SmallCorridor => {
|
// NetherFortressPieceType::SmallCorridor => {
|
||||||
fill_rel(0, 0, 0, 4, 1, 4, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(0, 0, 0, 4, 1, 4, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(0, 2, 0, 4, 5, 4, Block::AIR.default_state, chunk);
|
// fill_rel(0, 2, 0, 4, 5, 4, Block::AIR.default_state, chunk);
|
||||||
|
|
||||||
let fence = &Block::NETHER_BRICK_FENCE.default_state;
|
// let fence = &Block::NETHER_BRICK_FENCE.default_state;
|
||||||
|
|
||||||
fill_rel(0, 2, 0, 0, 5, 4, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(0, 2, 0, 0, 5, 4, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
fill_rel(4, 2, 0, 4, 5, 4, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(4, 2, 0, 4, 5, 4, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
|
|
||||||
fill_rel(0, 3, 1, 0, 4, 1, fence, chunk);
|
// fill_rel(0, 3, 1, 0, 4, 1, fence, chunk);
|
||||||
fill_rel(0, 3, 3, 0, 4, 3, fence, chunk);
|
// fill_rel(0, 3, 3, 0, 4, 3, fence, chunk);
|
||||||
fill_rel(4, 3, 1, 4, 4, 1, fence, chunk);
|
// fill_rel(4, 3, 1, 4, 4, 1, fence, chunk);
|
||||||
fill_rel(4, 3, 3, 4, 4, 3, fence, chunk);
|
// fill_rel(4, 3, 3, 4, 4, 3, fence, chunk);
|
||||||
|
|
||||||
fill_rel(0, 6, 0, 4, 6, 4, Block::NETHER_BRICKS.default_state, chunk);
|
// fill_rel(0, 6, 0, 4, 6, 4, Block::NETHER_BRICKS.default_state, chunk);
|
||||||
|
|
||||||
for i in 0..=4 {
|
// for i in 0..=4 {
|
||||||
for j in 0..=4 {
|
// for j in 0..=4 {
|
||||||
fill_downwards(
|
// fill_downwards(
|
||||||
origin_x + i,
|
// origin_x + i,
|
||||||
origin_y - 1,
|
// origin_y - 1,
|
||||||
origin_z + j,
|
// origin_z + j,
|
||||||
Block::NETHER_BRICKS.default_state,
|
// Block::NETHER_BRICKS.default_state,
|
||||||
chunk,
|
// chunk,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
|
||||||
|
// fn get_structure_piece(&self) -> &super::StructurePiece {
|
||||||
|
// &self.piece
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fn get_structure_piece_mut(&mut self) -> &mut super::StructurePiece {
|
||||||
|
// &mut self.piece
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|||||||
@ -0,0 +1,145 @@
|
|||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::block_box::BlockBox,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructurePiece, StructurePieceBase, StructurePiecesCollector,
|
||||||
|
stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ChestCorridorPiece {
|
||||||
|
pub piece: StrongholdPiece,
|
||||||
|
pub chest_generated: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ChestCorridorPiece {
|
||||||
|
pub fn create(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
let bounding_box = BlockBox::rotated(x, y, z, -1, -1, 0, 5, 5, 7, &orientation);
|
||||||
|
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdChestCorridor,
|
||||||
|
chain_length,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
piece.entry_door = EntranceType::get_random(random);
|
||||||
|
|
||||||
|
Some(Box::new(Self {
|
||||||
|
piece,
|
||||||
|
chest_generated: false,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for ChestCorridorPiece {
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_openings(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
self.piece
|
||||||
|
.fill_forward_opening(start, collector, random, 1, 1, pieces_to_process, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let randomizer = StoneBrickRandomizer;
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
let p = &self.piece;
|
||||||
|
let inner = &p.piece;
|
||||||
|
|
||||||
|
inner.fill_outline_random(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
6,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
true,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
p.generate_entrance(chunk, &box_limit, self.piece.entry_door, 1, 1, 0);
|
||||||
|
p.generate_entrance(chunk, &box_limit, EntranceType::Opening, 1, 1, 6);
|
||||||
|
|
||||||
|
let stone_bricks = Block::STONE_BRICKS.default_state;
|
||||||
|
inner.fill_with_outline(
|
||||||
|
chunk,
|
||||||
|
&box_limit,
|
||||||
|
false,
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
stone_bricks,
|
||||||
|
stone_bricks,
|
||||||
|
);
|
||||||
|
|
||||||
|
let slab = Block::STONE_BRICK_SLAB.default_state;
|
||||||
|
inner.add_block(chunk, slab, 3, 1, 1, &box_limit);
|
||||||
|
inner.add_block(chunk, slab, 3, 1, 5, &box_limit);
|
||||||
|
inner.add_block(chunk, slab, 3, 2, 2, &box_limit);
|
||||||
|
inner.add_block(chunk, slab, 3, 2, 4, &box_limit);
|
||||||
|
|
||||||
|
for i in 2..=4 {
|
||||||
|
inner.add_block(chunk, slab, 2, 1, i, &box_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if !self.chest_generated {
|
||||||
|
// // Check if the target chest position is within the current chunk being processed
|
||||||
|
// let chest_pos = inner.to_world(3, 2, 3);
|
||||||
|
// if box_limit.contains(&chest_pos) {
|
||||||
|
// self.chest_generated = true;
|
||||||
|
|
||||||
|
// let chest_state = Block::CHEST.default_state;
|
||||||
|
// // Note: In a full implementation, you would use a helper to set the LootTable NBT here
|
||||||
|
// inner.add_block(chunk, &chest_state, 3, 2, 3, &box_limit);
|
||||||
|
|
||||||
|
// // If Pumpkin has a loot provider, you'd hook into it here:
|
||||||
|
// // chunk.set_loot_table(chest_pos, "minecraft:chests/stronghold_corridor");
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,247 @@
|
|||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::block_box::BlockBox,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructurePiece, StructurePieceBase, StructurePiecesCollector,
|
||||||
|
stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct CorridorPiece {
|
||||||
|
piece: StrongholdPiece,
|
||||||
|
left_exit: bool,
|
||||||
|
right_exit: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CorridorPiece {
|
||||||
|
pub fn create(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
let bounding_box = BlockBox::rotated(x, y, z, -1, -1, 0, 5, 5, 7, &orientation);
|
||||||
|
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdCorridor,
|
||||||
|
chain_length,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
piece.entry_door = EntranceType::get_random(random);
|
||||||
|
|
||||||
|
Some(Box::new(Self {
|
||||||
|
piece,
|
||||||
|
left_exit: random.next_bounded_i32(2) == 0,
|
||||||
|
right_exit: random.next_bounded_i32(2) == 0,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for CorridorPiece {
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_openings(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
self.piece
|
||||||
|
.fill_forward_opening(start, collector, random, 1, 1, pieces_to_process, None);
|
||||||
|
|
||||||
|
if self.left_exit {
|
||||||
|
self.piece
|
||||||
|
.fill_nw_opening(start, collector, random, 1, 2, pieces_to_process);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.right_exit {
|
||||||
|
self.piece
|
||||||
|
.fill_se_opening(start, collector, random, 1, 2, pieces_to_process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let randomizer = StoneBrickRandomizer;
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
let p = &self.piece;
|
||||||
|
let inner = &p.piece;
|
||||||
|
let air = Block::AIR.default_state;
|
||||||
|
|
||||||
|
inner.fill_outline_random(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
6,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
true,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
p.generate_entrance(chunk, &box_limit, self.piece.entry_door, 1, 1, 0);
|
||||||
|
p.generate_entrance(chunk, &box_limit, EntranceType::Opening, 1, 1, 6);
|
||||||
|
|
||||||
|
// let torch_east = Block::WALL_TORCH.default_state.with("facing", "east");
|
||||||
|
// let torch_west = Block::WALL_TORCH.default_state.with("facing", "west");
|
||||||
|
|
||||||
|
// inner.add_block_with_random_threshold(chunk, &box_limit, random, 0.1, 1, 2, 1, &torch_east);
|
||||||
|
// inner.add_block_with_random_threshold(chunk, &box_limit, random, 0.1, 3, 2, 1, &torch_west);
|
||||||
|
// inner.add_block_with_random_threshold(chunk, &box_limit, random, 0.1, 1, 2, 5, &torch_east);
|
||||||
|
// inner.add_block_with_random_threshold(chunk, &box_limit, random, 0.1, 3, 2, 5, &torch_west);
|
||||||
|
|
||||||
|
if self.left_exit {
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 0, 1, 2, 0, 3, 4, air, air);
|
||||||
|
}
|
||||||
|
if self.right_exit {
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 4, 1, 2, 4, 3, 4, air, air);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SmallCorridorPiece {
|
||||||
|
pub piece: StrongholdPiece,
|
||||||
|
pub length: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SmallCorridorPiece {
|
||||||
|
pub fn new(chain_length: u32, bbox: BlockBox, orientation: BlockDirection) -> Self {
|
||||||
|
let length = match orientation {
|
||||||
|
BlockDirection::North | BlockDirection::South => bbox.max.z - bbox.min.z + 1,
|
||||||
|
_ => bbox.max.x - bbox.min.x + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdSmallCorridor,
|
||||||
|
chain_length,
|
||||||
|
bbox,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
|
||||||
|
Self { piece, length }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Port of the specialized static 'create' logic
|
||||||
|
pub fn create_box(
|
||||||
|
holder: &mut StructurePiecesCollector,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: &BlockDirection,
|
||||||
|
) -> Option<BlockBox> {
|
||||||
|
// Initial attempt: 5x5x4 box
|
||||||
|
let mut block_box = BlockBox::rotated(x, y, z, -1, -1, 0, 5, 5, 4, orientation);
|
||||||
|
|
||||||
|
// Find if we hit something
|
||||||
|
let intersecting = holder.get_intersecting(&block_box)?;
|
||||||
|
|
||||||
|
// Vanilla Logic: If we hit something at the same height, try to shorten the corridor
|
||||||
|
// so it perfectly abuts the intersecting piece.
|
||||||
|
if intersecting.bounding_box().min.y == block_box.min.y {
|
||||||
|
for j in (1..=2).rev() {
|
||||||
|
// Corresponds to for (int j = 2; j >= 1; --j)
|
||||||
|
block_box = BlockBox::rotated(x, y, z, -1, -1, 0, 5, 5, j, orientation);
|
||||||
|
|
||||||
|
if !intersecting.bounding_box().intersects(&block_box) {
|
||||||
|
// Found a shorter length that doesn't intersect!
|
||||||
|
// Return the next size up (j + 1) to seal the gap.
|
||||||
|
return Some(BlockBox::rotated(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
-1,
|
||||||
|
-1,
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
5,
|
||||||
|
j + 1,
|
||||||
|
orientation,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for SmallCorridorPiece {
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece(&self) -> &crate::generation::structure::structures::StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> &mut crate::generation::structure::structures::StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, _random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
let p = &self.piece.piece;
|
||||||
|
|
||||||
|
let stone = Block::STONE_BRICKS.default_state;
|
||||||
|
let cave_air = Block::CAVE_AIR.default_state;
|
||||||
|
|
||||||
|
for i in 0..self.length {
|
||||||
|
// 1. Floor (Y=0)
|
||||||
|
p.add_block(chunk, stone, 0, 0, i, &box_limit);
|
||||||
|
p.add_block(chunk, stone, 1, 0, i, &box_limit);
|
||||||
|
p.add_block(chunk, stone, 2, 0, i, &box_limit);
|
||||||
|
p.add_block(chunk, stone, 3, 0, i, &box_limit);
|
||||||
|
p.add_block(chunk, stone, 4, 0, i, &box_limit);
|
||||||
|
|
||||||
|
for j in 1..=3 {
|
||||||
|
p.add_block(chunk, stone, 0, j, i, &box_limit); // Left Wall
|
||||||
|
p.add_block(chunk, cave_air, 1, j, i, &box_limit); // Air Gap
|
||||||
|
p.add_block(chunk, cave_air, 2, j, i, &box_limit); // Air Gap
|
||||||
|
p.add_block(chunk, cave_air, 3, j, i, &box_limit); // Air Gap
|
||||||
|
p.add_block(chunk, stone, 4, j, i, &box_limit); // Right Wall
|
||||||
|
}
|
||||||
|
|
||||||
|
p.add_block(chunk, stone, 0, 4, i, &box_limit);
|
||||||
|
p.add_block(chunk, stone, 1, 4, i, &box_limit);
|
||||||
|
p.add_block(chunk, stone, 2, 4, i, &box_limit);
|
||||||
|
p.add_block(chunk, stone, 3, 4, i, &box_limit);
|
||||||
|
p.add_block(chunk, stone, 4, 4, i, &box_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,249 @@
|
|||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::block_box::BlockBox,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructurePiece, StructurePieceBase, StructurePiecesCollector,
|
||||||
|
stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct FiveWayCrossingPiece {
|
||||||
|
pub piece: StrongholdPiece,
|
||||||
|
pub lower_left_exists: bool,
|
||||||
|
pub upper_left_exists: bool,
|
||||||
|
pub lower_right_exists: bool,
|
||||||
|
pub upper_right_exists: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FiveWayCrossingPiece {
|
||||||
|
pub fn create(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
let bounding_box = BlockBox::rotated(x, y, z, -4, -3, 0, 10, 9, 11, &orientation);
|
||||||
|
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdFiveWayCrossing,
|
||||||
|
chain_length,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
piece.entry_door = EntranceType::get_random(random);
|
||||||
|
|
||||||
|
Some(Box::new(Self {
|
||||||
|
piece,
|
||||||
|
lower_left_exists: random.next_bool(),
|
||||||
|
upper_left_exists: random.next_bool(),
|
||||||
|
lower_right_exists: random.next_bool(),
|
||||||
|
upper_right_exists: random.next_bounded_i32(3) > 0,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for FiveWayCrossingPiece {
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_openings(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
let mut i = 3;
|
||||||
|
let mut j = 5;
|
||||||
|
let facing = self.piece.piece.facing.unwrap_or(BlockDirection::North);
|
||||||
|
|
||||||
|
if facing == BlockDirection::West || facing == BlockDirection::North {
|
||||||
|
i = 8 - i;
|
||||||
|
j = 8 - j;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.piece
|
||||||
|
.fill_forward_opening(start, collector, random, 5, 1, pieces_to_process, None);
|
||||||
|
|
||||||
|
if self.lower_left_exists {
|
||||||
|
self.piece
|
||||||
|
.fill_nw_opening(start, collector, random, i, 1, pieces_to_process);
|
||||||
|
}
|
||||||
|
if self.upper_left_exists {
|
||||||
|
self.piece
|
||||||
|
.fill_nw_opening(start, collector, random, j, 7, pieces_to_process);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.lower_right_exists {
|
||||||
|
self.piece
|
||||||
|
.fill_se_opening(start, collector, random, i, 1, pieces_to_process);
|
||||||
|
}
|
||||||
|
if self.upper_right_exists {
|
||||||
|
self.piece
|
||||||
|
.fill_se_opening(start, collector, random, j, 7, pieces_to_process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let randomizer = StoneBrickRandomizer;
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
let p = &self.piece;
|
||||||
|
let inner = &p.piece;
|
||||||
|
let air = Block::AIR.default_state;
|
||||||
|
|
||||||
|
inner.fill_outline_random(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
9,
|
||||||
|
8,
|
||||||
|
10,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
true,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
p.generate_entrance(chunk, &box_limit, self.piece.entry_door, 4, 3, 0);
|
||||||
|
|
||||||
|
if self.lower_left_exists {
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 0, 3, 1, 0, 5, 3, air, air);
|
||||||
|
}
|
||||||
|
if self.lower_right_exists {
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 9, 3, 1, 9, 5, 3, air, air);
|
||||||
|
}
|
||||||
|
if self.upper_left_exists {
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 0, 5, 7, 0, 7, 9, air, air);
|
||||||
|
}
|
||||||
|
if self.upper_right_exists {
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 9, 5, 7, 9, 7, 9, air, air);
|
||||||
|
}
|
||||||
|
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 5, 1, 10, 7, 3, 10, air, air);
|
||||||
|
|
||||||
|
inner.fill_outline_random(
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
8,
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_outline_random(
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
9,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_outline_random(
|
||||||
|
8,
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
8,
|
||||||
|
4,
|
||||||
|
9,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_outline_random(
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
7,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
9,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_outline_random(
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
3,
|
||||||
|
3,
|
||||||
|
6,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
let slab = Block::SMOOTH_STONE_SLAB.default_state;
|
||||||
|
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 1, 3, 4, 3, 3, 4, slab, slab);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 1, 4, 6, 3, 4, 6, slab, slab);
|
||||||
|
|
||||||
|
inner.fill_outline_random(
|
||||||
|
5,
|
||||||
|
1,
|
||||||
|
7,
|
||||||
|
7,
|
||||||
|
1,
|
||||||
|
8,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 5, 1, 9, 7, 1, 9, slab, slab);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 5, 2, 7, 7, 2, 7, slab, slab);
|
||||||
|
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 4, 5, 7, 4, 5, 9, slab, slab);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 8, 5, 7, 8, 5, 9, slab, slab);
|
||||||
|
|
||||||
|
// let double_slab = Block::SMOOTH_STONE_SLAB.default_state.with("type", "double");
|
||||||
|
// inner.fill_with_outline(chunk, &box_limit, false, 5, 5, 7, 7, 5, 9, &double_slab, &double_slab);
|
||||||
|
|
||||||
|
// let torch = Block::WALL_TORCH.default_state.with("facing", "south");
|
||||||
|
// inner.add_block(chunk, &torch, 6, 5, 6, &box_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::block_box::BlockBox,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructurePiece, StructurePieceBase, StructurePiecesCollector,
|
||||||
|
stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LeftTurnPiece {
|
||||||
|
pub piece: StrongholdPiece,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LeftTurnPiece {
|
||||||
|
pub fn create(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
let bounding_box = BlockBox::rotated(x, y, z, -1, -1, 0, 5, 5, 5, &orientation);
|
||||||
|
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdLeftTurn,
|
||||||
|
chain_length,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
piece.entry_door = EntranceType::get_random(random);
|
||||||
|
|
||||||
|
Some(Box::new(Self { piece }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for LeftTurnPiece {
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_openings(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
let facing = self.piece.piece.facing.unwrap_or(BlockDirection::North);
|
||||||
|
|
||||||
|
if facing == BlockDirection::North || facing == BlockDirection::East {
|
||||||
|
self.piece
|
||||||
|
.fill_nw_opening(start, collector, random, 1, 1, pieces_to_process);
|
||||||
|
} else {
|
||||||
|
self.piece
|
||||||
|
.fill_se_opening(start, collector, random, 1, 1, pieces_to_process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let randomizer = StoneBrickRandomizer;
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
let p = &self.piece;
|
||||||
|
let inner = &p.piece;
|
||||||
|
let air = Block::AIR.default_state;
|
||||||
|
|
||||||
|
inner.fill_outline_random(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
true,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
p.generate_entrance(chunk, &box_limit, self.piece.entry_door, 1, 1, 0);
|
||||||
|
|
||||||
|
let direction = inner.facing.unwrap_or(BlockDirection::North);
|
||||||
|
|
||||||
|
if direction == BlockDirection::North || direction == BlockDirection::East {
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 0, 1, 1, 0, 3, 3, air, air);
|
||||||
|
} else {
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 4, 1, 1, 4, 3, 3, air, air);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,233 @@
|
|||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::block_box::BlockBox,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructurePiece, StructurePieceBase, StructurePiecesCollector,
|
||||||
|
stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct LibraryPiece {
|
||||||
|
pub piece: StrongholdPiece,
|
||||||
|
pub tall: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LibraryPiece {
|
||||||
|
pub fn create(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
let mut bounding_box = BlockBox::rotated(x, y, z, -4, -1, 0, 14, 11, 15, &orientation);
|
||||||
|
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
bounding_box = BlockBox::rotated(x, y, z, -4, -1, 0, 14, 6, 15, &orientation);
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdLibrary,
|
||||||
|
chain_length,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
piece.entry_door = EntranceType::get_random(random);
|
||||||
|
|
||||||
|
let tall = bounding_box.get_block_count_y() > 6;
|
||||||
|
|
||||||
|
Some(Box::new(Self { piece, tall }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for LibraryPiece {
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let randomizer = StoneBrickRandomizer;
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
let p = &self.piece;
|
||||||
|
let inner = &p.piece;
|
||||||
|
|
||||||
|
let max_y = if self.tall { 10 } else { 5 }; // Java: i-1 where i is 11 or 6
|
||||||
|
|
||||||
|
// 1. Main Shell
|
||||||
|
inner.fill_outline_random(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
13,
|
||||||
|
max_y,
|
||||||
|
14,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
true,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Entrance
|
||||||
|
p.generate_entrance(chunk, &box_limit, self.piece.entry_door, 4, 1, 0);
|
||||||
|
|
||||||
|
// 3. Cobwebs (Random probability placement)
|
||||||
|
inner.fill_with_outline_under_sea_level(
|
||||||
|
chunk,
|
||||||
|
&box_limit,
|
||||||
|
random,
|
||||||
|
0.07,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
11,
|
||||||
|
4,
|
||||||
|
13,
|
||||||
|
Block::COBWEB.default_state,
|
||||||
|
Block::COBWEB.default_state,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. Bookshelves, Pillars, and Wall Torches
|
||||||
|
let planks = Block::OAK_PLANKS.default_state;
|
||||||
|
let books = Block::BOOKSHELF.default_state;
|
||||||
|
// let torch_e = Block::WALL_TORCH.default_state.with("facing", "east");
|
||||||
|
// let torch_w = Block::WALL_TORCH.default_state.with("facing", "west");
|
||||||
|
|
||||||
|
for l in 1..=13 {
|
||||||
|
if (l - 1) % 4 == 0 {
|
||||||
|
// Pillars
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 1, 1, l, 1, 4, l, planks, planks);
|
||||||
|
inner.fill_with_outline(
|
||||||
|
chunk, &box_limit, false, 12, 1, l, 12, 4, l, planks, planks,
|
||||||
|
);
|
||||||
|
// Torches
|
||||||
|
// inner.add_block(chunk, &torch_e, 2, 3, l, &box_limit);
|
||||||
|
// inner.add_block(chunk, &torch_w, 11, 3, l, &box_limit);
|
||||||
|
|
||||||
|
if self.tall {
|
||||||
|
inner.fill_with_outline(
|
||||||
|
chunk, &box_limit, false, 1, 6, l, 1, 9, l, planks, planks,
|
||||||
|
);
|
||||||
|
inner.fill_with_outline(
|
||||||
|
chunk, &box_limit, false, 12, 6, l, 12, 9, l, planks, planks,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Shelf Walls
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 1, 1, l, 1, 4, l, books, books);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 12, 1, l, 12, 4, l, books, books);
|
||||||
|
if self.tall {
|
||||||
|
inner.fill_with_outline(
|
||||||
|
chunk, &box_limit, false, 1, 6, l, 1, 9, l, books, books,
|
||||||
|
);
|
||||||
|
inner.fill_with_outline(
|
||||||
|
chunk, &box_limit, false, 12, 6, l, 12, 9, l, books, books,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Central Freestanding Shelves
|
||||||
|
for l in (3..12).step_by(2) {
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 3, 1, l, 4, 3, l, books, books);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 6, 1, l, 7, 3, l, books, books);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 9, 1, l, 10, 3, l, books, books);
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.tall {
|
||||||
|
// Upper Walkways
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 1, 5, 1, 3, 5, 13, planks, planks);
|
||||||
|
inner.fill_with_outline(
|
||||||
|
chunk, &box_limit, false, 10, 5, 1, 12, 5, 13, planks, planks,
|
||||||
|
);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 4, 5, 1, 9, 5, 2, planks, planks);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 4, 5, 12, 9, 5, 13, planks, planks);
|
||||||
|
|
||||||
|
// Walkway corners
|
||||||
|
inner.add_block(chunk, planks, 9, 5, 11, &box_limit);
|
||||||
|
inner.add_block(chunk, planks, 8, 5, 11, &box_limit);
|
||||||
|
inner.add_block(chunk, planks, 9, 5, 10, &box_limit);
|
||||||
|
|
||||||
|
// Fences / Railings
|
||||||
|
// let f_ns = Block::OAK_FENCE.default_state.with("north", "true").with("south", "true");
|
||||||
|
// let f_ew = Block::OAK_FENCE.default_state.with("east", "true").with("west", "true");
|
||||||
|
|
||||||
|
// inner.fill_with_outline(chunk, &box_limit, false, 3, 6, 3, 3, 6, 11, &f_ns, &f_ns);
|
||||||
|
// inner.fill_with_outline(chunk, &box_limit, false, 10, 6, 3, 10, 6, 9, &f_ns, &f_ns);
|
||||||
|
// inner.fill_with_outline(chunk, &box_limit, false, 4, 6, 2, 9, 6, 2, &f_ew, &f_ew);
|
||||||
|
// inner.fill_with_outline(chunk, &box_limit, false, 4, 6, 12, 7, 6, 12, &f_ew, &f_ew);
|
||||||
|
|
||||||
|
// // Complex fence junctions (matching the m-loop and specific offsets)
|
||||||
|
// inner.add_block(chunk, &Block::OAK_FENCE.default_state.with("north", "true").with("east", "true"), 3, 6, 2, &box_limit);
|
||||||
|
// inner.add_block(chunk, &Block::OAK_FENCE.default_state.with("south", "true").with("east", "true"), 3, 6, 12, &box_limit);
|
||||||
|
// inner.add_block(chunk, &Block::OAK_FENCE.default_state.with("north", "true").with("west", "true"), 10, 6, 2, &box_limit);
|
||||||
|
// for m in 0..=2 {
|
||||||
|
// inner.add_block(chunk, &Block::OAK_FENCE.default_state.with("south", "true").with("west", "true"), 8 + m, 6, 12 - m, &box_limit);
|
||||||
|
// if m != 2 {
|
||||||
|
// inner.add_block(chunk, &Block::OAK_FENCE.default_state.with("north", "true").with("east", "true"), 8 + m, 6, 11 - m, &box_limit);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Ladder
|
||||||
|
// let ladder = Block::LADDER.default_state.with("facing", "south");
|
||||||
|
// for y in 1..=7 {
|
||||||
|
// inner.add_block(chunk, &ladder, 10, y, 13, &box_limit);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Chandelier (The central structure)
|
||||||
|
// let f_e = Block::OAK_FENCE.default_state.with("east", "true");
|
||||||
|
// let f_w = Block::OAK_FENCE.default_state.with("west", "true");
|
||||||
|
// inner.add_block(chunk, &f_e, 6, 9, 7, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_w, 7, 9, 7, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_e, 6, 8, 7, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_w, 7, 8, 7, &box_limit);
|
||||||
|
|
||||||
|
// let f_full = f_ns.with("west", "true").with("east", "true");
|
||||||
|
// inner.add_block(chunk, &f_full, 6, 7, 7, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_full, 7, 7, 7, &box_limit);
|
||||||
|
|
||||||
|
// Chandelier Torches
|
||||||
|
let t = Block::TORCH.default_state;
|
||||||
|
inner.add_block(chunk, t, 5, 8, 7, &box_limit);
|
||||||
|
inner.add_block(chunk, t, 8, 8, 7, &box_limit);
|
||||||
|
inner.add_block(chunk, t, 6, 8, 6, &box_limit);
|
||||||
|
inner.add_block(chunk, t, 6, 8, 8, &box_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Chests
|
||||||
|
// inner.add_chest(chunk, &box_limit, random, 3, 3, 5, "stronghold_library");
|
||||||
|
// if self.tall {
|
||||||
|
// inner.add_block(chunk, &Block::AIR.default_state, 12, 9, 1, &box_limit); // Clear space above top chest
|
||||||
|
// inner.add_chest(chunk, &box_limit, random, 12, 8, 1, "stronghold_library");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,724 @@
|
|||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
|
use pumpkin_data::{Block, BlockState};
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::{block_box::BlockBox, position::BlockPos},
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
// Removed unused rand imports to avoid confusion with Pumpkin's random
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::{
|
||||||
|
positions::chunk_pos::{get_offset_x, get_offset_z},
|
||||||
|
structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
BlockRandomizer, StructureGenerator, StructureGeneratorContext, StructurePiece,
|
||||||
|
StructurePieceBase, StructurePiecesCollector, StructurePosition,
|
||||||
|
stronghold::{
|
||||||
|
chest_corridor::ChestCorridorPiece,
|
||||||
|
corridor::{CorridorPiece, SmallCorridorPiece},
|
||||||
|
five_way_crossing::FiveWayCrossingPiece,
|
||||||
|
left_turn::LeftTurnPiece,
|
||||||
|
library::LibraryPiece,
|
||||||
|
portal_room::PortalRoomPiece,
|
||||||
|
prison_hall::PrisonHallPiece,
|
||||||
|
right_turn::RightTurnPiece,
|
||||||
|
spiral_staircase::SpiralStaircasePiece,
|
||||||
|
square_room::SquareRoomPiece,
|
||||||
|
stairs::StairsPiece,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Assuming these modules exist as per your import
|
||||||
|
pub mod chest_corridor;
|
||||||
|
pub mod corridor;
|
||||||
|
pub mod five_way_crossing;
|
||||||
|
pub mod left_turn;
|
||||||
|
pub mod library;
|
||||||
|
pub mod portal_room;
|
||||||
|
pub mod prison_hall;
|
||||||
|
pub mod right_turn;
|
||||||
|
pub mod spiral_staircase;
|
||||||
|
pub mod square_room;
|
||||||
|
pub mod stairs;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct StrongholdGenerator;
|
||||||
|
|
||||||
|
impl StructureGenerator for StrongholdGenerator {
|
||||||
|
fn get_structure_position(
|
||||||
|
&self,
|
||||||
|
context: StructureGeneratorContext,
|
||||||
|
) -> Option<StructurePosition> {
|
||||||
|
let mut _i = 0;
|
||||||
|
// The collector must be shared/mutable during generation
|
||||||
|
let mut collector = StructurePiecesCollector::default();
|
||||||
|
let mut random = context.random;
|
||||||
|
|
||||||
|
// Loop until we have a valid stronghold (Vanilla loop logic)
|
||||||
|
loop {
|
||||||
|
collector.clear();
|
||||||
|
_i += 1;
|
||||||
|
|
||||||
|
let start_x = get_offset_x(context.chunk_x, 2);
|
||||||
|
let start_z = get_offset_z(context.chunk_z, 2);
|
||||||
|
|
||||||
|
// Create the Start Piece (SpiralStaircase)
|
||||||
|
// Note: new_start must return a concrete struct that implements StructurePieceBase
|
||||||
|
let start_piece = SpiralStaircasePiece::new_start(&mut random, start_x, start_z);
|
||||||
|
|
||||||
|
// We need to Box the start piece to treat it uniformly in the list
|
||||||
|
let start_box: Box<dyn StructurePieceBase> = Box::new(start_piece.clone());
|
||||||
|
|
||||||
|
let mut pieces_to_process: Vec<Box<dyn StructurePieceBase>> =
|
||||||
|
vec![start_box.clone_box()];
|
||||||
|
|
||||||
|
collector.add_piece(start_box);
|
||||||
|
|
||||||
|
// 1. Initial Fill
|
||||||
|
// We cast strictly to SpiralStaircasePiece here because the start piece is special
|
||||||
|
start_piece.fill_openings(
|
||||||
|
&start_piece.piece.piece,
|
||||||
|
&mut random,
|
||||||
|
&mut collector,
|
||||||
|
&mut pieces_to_process,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. The Growth Loop
|
||||||
|
while !pieces_to_process.is_empty() {
|
||||||
|
// Pick a random piece from the "to process" list
|
||||||
|
let idx = random.next_bounded_i32(pieces_to_process.len() as i32) as usize;
|
||||||
|
let piece = pieces_to_process.remove(idx);
|
||||||
|
|
||||||
|
piece.fill_openings(
|
||||||
|
&start_piece.piece.piece,
|
||||||
|
&mut random,
|
||||||
|
&mut collector,
|
||||||
|
&mut pieces_to_process,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
collector.shift_into(context.sea_level, context.min_y, &mut random, 10);
|
||||||
|
|
||||||
|
let has_portal_room = collector.pieces.iter().any(|p| {
|
||||||
|
p.get_structure_piece().r#type == StructurePieceType::StrongholdPortalRoom
|
||||||
|
});
|
||||||
|
|
||||||
|
if !collector.is_empty() && has_portal_room || collector.pieces.len() >= 100 {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(StructurePosition {
|
||||||
|
start_pos: BlockPos::new(context.chunk_x, 0, context.chunk_z),
|
||||||
|
collector: Arc::new(Mutex::new(collector)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
pub enum EntranceType {
|
||||||
|
Opening = 0,
|
||||||
|
WoodDoor = 1,
|
||||||
|
Grates = 2,
|
||||||
|
IronDoor = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EntranceType {
|
||||||
|
pub fn get_random(random: &mut impl RandomImpl) -> Self {
|
||||||
|
match random.next_bounded_i32(5) {
|
||||||
|
2 => EntranceType::WoodDoor,
|
||||||
|
3 => EntranceType::Grates,
|
||||||
|
4 => EntranceType::IronDoor,
|
||||||
|
_ => EntranceType::Opening,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StoneBrickRandomizer;
|
||||||
|
|
||||||
|
impl BlockRandomizer for StoneBrickRandomizer {
|
||||||
|
fn get_block(&self, rng: &mut RandomGenerator, is_border: bool) -> &BlockState {
|
||||||
|
if !is_border {
|
||||||
|
return Block::AIR.default_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
let roll = rng.next_f32();
|
||||||
|
if roll < 0.2 {
|
||||||
|
Block::CRACKED_STONE_BRICKS.default_state
|
||||||
|
} else if roll < 0.5 {
|
||||||
|
Block::MOSSY_STONE_BRICKS.default_state
|
||||||
|
} else {
|
||||||
|
Block::STONE_BRICKS.default_state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub enum StrongholdPieceType {
|
||||||
|
Corridor,
|
||||||
|
PrisonHall,
|
||||||
|
LeftTurn,
|
||||||
|
RightTurn,
|
||||||
|
SquareRoom,
|
||||||
|
Stairs,
|
||||||
|
SpiralStaircase,
|
||||||
|
FiveWayCrossing,
|
||||||
|
ChestCorridor,
|
||||||
|
Library,
|
||||||
|
PortalRoom,
|
||||||
|
}
|
||||||
|
impl StrongholdPieceType {
|
||||||
|
pub fn as_structure_type(&self) -> StructurePieceType {
|
||||||
|
match self {
|
||||||
|
Self::Corridor => StructurePieceType::StrongholdCorridor,
|
||||||
|
Self::PrisonHall => StructurePieceType::StrongholdPrisonHall,
|
||||||
|
Self::LeftTurn => StructurePieceType::StrongholdLeftTurn,
|
||||||
|
Self::RightTurn => StructurePieceType::StrongholdRightTurn,
|
||||||
|
Self::SquareRoom => StructurePieceType::StrongholdSquareRoom,
|
||||||
|
Self::Stairs => StructurePieceType::StrongholdStairs,
|
||||||
|
Self::SpiralStaircase => StructurePieceType::StrongholdSpiralStaircase,
|
||||||
|
Self::FiveWayCrossing => StructurePieceType::StrongholdFiveWayCrossing,
|
||||||
|
Self::ChestCorridor => StructurePieceType::StrongholdChestCorridor,
|
||||||
|
Self::Library => StructurePieceType::StrongholdLibrary,
|
||||||
|
Self::PortalRoom => StructurePieceType::StrongholdPortalRoom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PieceWeight {
|
||||||
|
pub piece_type: StrongholdPieceType,
|
||||||
|
pub weight: i32,
|
||||||
|
pub limit: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PieceWeight {
|
||||||
|
fn new(piece_type: StrongholdPieceType, weight: i32, limit: i32) -> Self {
|
||||||
|
Self {
|
||||||
|
piece_type,
|
||||||
|
weight,
|
||||||
|
limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn can_generate(&self, _chain_length: u32) -> bool {
|
||||||
|
self.limit == 0 //|| self.generated_count < self.limit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const POSSIBLE_PIECES: &[PieceWeight] = &[
|
||||||
|
PieceWeight {
|
||||||
|
piece_type: StrongholdPieceType::Corridor,
|
||||||
|
weight: 40,
|
||||||
|
limit: 0,
|
||||||
|
},
|
||||||
|
PieceWeight {
|
||||||
|
piece_type: StrongholdPieceType::PrisonHall,
|
||||||
|
weight: 5,
|
||||||
|
limit: 5,
|
||||||
|
},
|
||||||
|
PieceWeight {
|
||||||
|
piece_type: StrongholdPieceType::LeftTurn,
|
||||||
|
weight: 20,
|
||||||
|
limit: 0,
|
||||||
|
},
|
||||||
|
PieceWeight {
|
||||||
|
piece_type: StrongholdPieceType::RightTurn,
|
||||||
|
weight: 20,
|
||||||
|
limit: 0,
|
||||||
|
},
|
||||||
|
PieceWeight {
|
||||||
|
piece_type: StrongholdPieceType::SquareRoom,
|
||||||
|
weight: 10,
|
||||||
|
limit: 6,
|
||||||
|
},
|
||||||
|
PieceWeight {
|
||||||
|
piece_type: StrongholdPieceType::Stairs,
|
||||||
|
weight: 5,
|
||||||
|
limit: 5,
|
||||||
|
},
|
||||||
|
PieceWeight {
|
||||||
|
piece_type: StrongholdPieceType::SpiralStaircase,
|
||||||
|
weight: 5,
|
||||||
|
limit: 5,
|
||||||
|
},
|
||||||
|
PieceWeight {
|
||||||
|
piece_type: StrongholdPieceType::FiveWayCrossing,
|
||||||
|
weight: 5,
|
||||||
|
limit: 4,
|
||||||
|
},
|
||||||
|
PieceWeight {
|
||||||
|
piece_type: StrongholdPieceType::ChestCorridor,
|
||||||
|
weight: 5,
|
||||||
|
limit: 4,
|
||||||
|
},
|
||||||
|
PieceWeight {
|
||||||
|
piece_type: StrongholdPieceType::Library,
|
||||||
|
weight: 10,
|
||||||
|
limit: 2,
|
||||||
|
},
|
||||||
|
PieceWeight {
|
||||||
|
piece_type: StrongholdPieceType::PortalRoom,
|
||||||
|
weight: 20,
|
||||||
|
limit: 1,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Pre-calculate total weight (const loop because iter().sum() isn't always const yet)
|
||||||
|
const TOTAL_WEIGHT: i32 = {
|
||||||
|
let mut sum = 0;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < POSSIBLE_PIECES.len() {
|
||||||
|
sum += POSSIBLE_PIECES[i].weight;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct StrongholdPiece {
|
||||||
|
pub piece: StructurePiece,
|
||||||
|
pub entry_door: EntranceType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StrongholdPiece {
|
||||||
|
pub fn new(r#type: StructurePieceType, chain_length: u32, bbox: BlockBox) -> Self {
|
||||||
|
Self {
|
||||||
|
piece: StructurePiece::new(r#type, bbox, chain_length),
|
||||||
|
entry_door: EntranceType::Opening,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn is_in_bounds(bb: &BlockBox) -> bool {
|
||||||
|
bb.min.y > 10
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_entrance(
|
||||||
|
&self,
|
||||||
|
chunk: &mut ProtoChunk,
|
||||||
|
box_limit: &BlockBox,
|
||||||
|
entrance_type: EntranceType,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
) {
|
||||||
|
match entrance_type {
|
||||||
|
EntranceType::Opening => {
|
||||||
|
let air = Block::AIR.default_state;
|
||||||
|
self.piece.fill_with_outline(
|
||||||
|
chunk,
|
||||||
|
box_limit,
|
||||||
|
false,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
x + 2,
|
||||||
|
y + 2,
|
||||||
|
z,
|
||||||
|
air,
|
||||||
|
air,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
EntranceType::WoodDoor => {
|
||||||
|
let stone = Block::STONE_BRICKS.default_state;
|
||||||
|
let _door_lower = Block::OAK_DOOR.default_state;
|
||||||
|
//let door_upper = Block::OAK_DOOR.default_state.with("half", "upper");
|
||||||
|
|
||||||
|
// Frame
|
||||||
|
self.piece.add_block(chunk, stone, x, y, z, box_limit);
|
||||||
|
self.piece.add_block(chunk, stone, x, y + 1, z, box_limit);
|
||||||
|
self.piece.add_block(chunk, stone, x, y + 2, z, box_limit);
|
||||||
|
self.piece
|
||||||
|
.add_block(chunk, stone, x + 1, y + 2, z, box_limit);
|
||||||
|
self.piece
|
||||||
|
.add_block(chunk, stone, x + 2, y + 2, z, box_limit);
|
||||||
|
self.piece
|
||||||
|
.add_block(chunk, stone, x + 2, y + 1, z, box_limit);
|
||||||
|
self.piece.add_block(chunk, stone, x + 2, y, z, box_limit);
|
||||||
|
|
||||||
|
// Door
|
||||||
|
// self.piece.add_block(chunk, &door_lower, x + 1, y, z, box_limit);
|
||||||
|
// self.piece.add_block(chunk, &door_upper, x + 1, y + 1, z, box_limit);
|
||||||
|
}
|
||||||
|
EntranceType::Grates => {
|
||||||
|
let air = Block::CAVE_AIR.default_state;
|
||||||
|
// let bar_w = Block::IRON_BARS.default_state.with("west", "true");
|
||||||
|
// let bar_e = Block::IRON_BARS.default_state.with("east", "true");
|
||||||
|
// let bar_ew = Block::IRON_BARS.default_state.with("east", "true").with("west", "true");
|
||||||
|
|
||||||
|
self.piece.add_block(chunk, air, x + 1, y, z, box_limit);
|
||||||
|
self.piece.add_block(chunk, air, x + 1, y + 1, z, box_limit);
|
||||||
|
|
||||||
|
// Left side (West connection)
|
||||||
|
// self.piece.add_block(chunk, &bar_w, x, y, z, box_limit);
|
||||||
|
// self.piece.add_block(chunk, &bar_w, x, y + 1, z, box_limit);
|
||||||
|
|
||||||
|
// // Top beam (Connected East-West)
|
||||||
|
// self.piece.add_block(chunk, &bar_ew, x, y + 2, z, box_limit);
|
||||||
|
// self.piece.add_block(chunk, &bar_ew, x + 1, y + 2, z, box_limit);
|
||||||
|
// self.piece.add_block(chunk, &bar_ew, x + 2, y + 2, z, box_limit);
|
||||||
|
|
||||||
|
// // Right side (East connection)
|
||||||
|
// self.piece.add_block(chunk, &bar_e, x + 2, y + 1, z, box_limit);
|
||||||
|
// self.piece.add_block(chunk, &bar_e, x + 2, y, z, box_limit);
|
||||||
|
}
|
||||||
|
EntranceType::IronDoor => {
|
||||||
|
let stone = Block::STONE_BRICKS.default_state;
|
||||||
|
// let door_lower = Block::IRON_DOOR.default_state;
|
||||||
|
// let door_upper = Block::IRON_DOOR.default_state.with("half", "upper");
|
||||||
|
// let button_n = Block::STONE_BUTTON.default_state.with("facing", "north");
|
||||||
|
// let button_s = Block::STONE_BUTTON.default_state.with("facing", "south");
|
||||||
|
|
||||||
|
// Frame
|
||||||
|
self.piece.fill_with_outline(
|
||||||
|
chunk,
|
||||||
|
box_limit,
|
||||||
|
false,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
x,
|
||||||
|
y + 2,
|
||||||
|
z,
|
||||||
|
stone,
|
||||||
|
stone,
|
||||||
|
);
|
||||||
|
self.piece
|
||||||
|
.add_block(chunk, stone, x + 1, y + 2, z, box_limit);
|
||||||
|
self.piece.fill_with_outline(
|
||||||
|
chunk,
|
||||||
|
box_limit,
|
||||||
|
false,
|
||||||
|
x + 2,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
x + 2,
|
||||||
|
y + 2,
|
||||||
|
z,
|
||||||
|
stone,
|
||||||
|
stone,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Door
|
||||||
|
// self.piece.add_block(chunk, &door_lower, x + 1, y, z, box_limit);
|
||||||
|
// self.piece.add_block(chunk, &door_upper, x + 1, y + 1, z, box_limit);
|
||||||
|
|
||||||
|
// // Buttons
|
||||||
|
// self.piece.add_block(chunk, &button_n, x + 2, y + 1, z + 1, box_limit);
|
||||||
|
// self.piece.add_block(chunk, &button_s, x + 2, y + 1, z - 1, box_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn fill_forward_opening(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
left_right_offset: i32,
|
||||||
|
height_offset: i32,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
piece: Option<StrongholdPieceType>,
|
||||||
|
) {
|
||||||
|
if let Some(facing) = &self.piece.facing {
|
||||||
|
let bounding_box = self.piece.bounding_box;
|
||||||
|
let (nx, ny, nz) = match facing {
|
||||||
|
BlockDirection::North => (
|
||||||
|
bounding_box.min.x + left_right_offset,
|
||||||
|
bounding_box.min.y + height_offset,
|
||||||
|
bounding_box.min.z - 1,
|
||||||
|
),
|
||||||
|
BlockDirection::South => (
|
||||||
|
bounding_box.min.x + left_right_offset,
|
||||||
|
bounding_box.min.y + height_offset,
|
||||||
|
bounding_box.max.z + 1,
|
||||||
|
),
|
||||||
|
BlockDirection::West => (
|
||||||
|
bounding_box.min.x - 1,
|
||||||
|
bounding_box.min.y + height_offset,
|
||||||
|
bounding_box.min.z + left_right_offset,
|
||||||
|
),
|
||||||
|
BlockDirection::East => (
|
||||||
|
bounding_box.max.x + 1,
|
||||||
|
bounding_box.min.y + height_offset,
|
||||||
|
bounding_box.min.z + left_right_offset,
|
||||||
|
),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(next) = Self::piece_generator(
|
||||||
|
start,
|
||||||
|
collector,
|
||||||
|
random,
|
||||||
|
nx,
|
||||||
|
ny,
|
||||||
|
nz,
|
||||||
|
facing,
|
||||||
|
self.piece.chain_length,
|
||||||
|
piece,
|
||||||
|
) {
|
||||||
|
// IMPORTANT: The generator already adds it to the collector if successful.
|
||||||
|
// We just need to add it to the processing queue.
|
||||||
|
pieces_to_process.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn fill_nw_opening(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
height_offset: i32,
|
||||||
|
left_right_offset: i32,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
if let Some(facing) = &self.piece.facing {
|
||||||
|
let bounding_box = self.piece.bounding_box;
|
||||||
|
|
||||||
|
// Java switch(direction) logic translated to world coordinates
|
||||||
|
let (nx, ny, nz, next_facing) = match facing {
|
||||||
|
BlockDirection::North | BlockDirection::South => (
|
||||||
|
bounding_box.min.x - 1,
|
||||||
|
bounding_box.min.y + height_offset,
|
||||||
|
bounding_box.min.z + left_right_offset,
|
||||||
|
BlockDirection::West,
|
||||||
|
),
|
||||||
|
BlockDirection::West | BlockDirection::East => (
|
||||||
|
bounding_box.min.x + left_right_offset,
|
||||||
|
bounding_box.min.y + height_offset,
|
||||||
|
bounding_box.min.z - 1,
|
||||||
|
BlockDirection::North,
|
||||||
|
),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(next) = Self::piece_generator(
|
||||||
|
start,
|
||||||
|
collector,
|
||||||
|
random,
|
||||||
|
nx,
|
||||||
|
ny,
|
||||||
|
nz,
|
||||||
|
&next_facing,
|
||||||
|
self.piece.chain_length,
|
||||||
|
None, // Dynamic piece selection
|
||||||
|
) {
|
||||||
|
pieces_to_process.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fill_se_opening(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
height_offset: i32,
|
||||||
|
left_right_offset: i32,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
if let Some(facing) = &self.piece.facing {
|
||||||
|
let bounding_box = self.piece.bounding_box;
|
||||||
|
|
||||||
|
let (nx, ny, nz, next_facing) = match facing {
|
||||||
|
BlockDirection::North | BlockDirection::South => (
|
||||||
|
bounding_box.max.x + 1,
|
||||||
|
bounding_box.min.y + height_offset,
|
||||||
|
bounding_box.min.z + left_right_offset,
|
||||||
|
BlockDirection::East,
|
||||||
|
),
|
||||||
|
BlockDirection::West | BlockDirection::East => (
|
||||||
|
bounding_box.min.x + left_right_offset,
|
||||||
|
bounding_box.min.y + height_offset,
|
||||||
|
bounding_box.max.z + 1,
|
||||||
|
BlockDirection::South,
|
||||||
|
),
|
||||||
|
_ => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(next) = Self::piece_generator(
|
||||||
|
start,
|
||||||
|
collector,
|
||||||
|
random,
|
||||||
|
nx,
|
||||||
|
ny,
|
||||||
|
nz,
|
||||||
|
&next_facing,
|
||||||
|
self.piece.chain_length,
|
||||||
|
None,
|
||||||
|
) {
|
||||||
|
pieces_to_process.push(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
pub fn piece_generator(
|
||||||
|
start: &StructurePiece,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: &BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
piece_type: Option<StrongholdPieceType>,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
// 1. Hard limits
|
||||||
|
if chain_length > 50 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get bounds from the underlying StructurePiece of the start wrapper
|
||||||
|
let start_box = start.bounding_box;
|
||||||
|
if (x - start_box.min.x).abs() > 112 || (z - start_box.min.z).abs() > 112 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(piece_type) = piece_type {
|
||||||
|
let piece = Self::create_piece(
|
||||||
|
&piece_type,
|
||||||
|
collector,
|
||||||
|
random,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
orientation,
|
||||||
|
chain_length,
|
||||||
|
);
|
||||||
|
if let Some(piece) = piece {
|
||||||
|
collector.add_piece(piece.clone_box());
|
||||||
|
return Some(piece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Pick the piece
|
||||||
|
let picked_piece =
|
||||||
|
Self::pick_piece(collector, random, x, y, z, orientation, chain_length + 1);
|
||||||
|
|
||||||
|
if let Some(ref p) = picked_piece {
|
||||||
|
collector.add_piece(p.clone_box());
|
||||||
|
}
|
||||||
|
|
||||||
|
picked_piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pick_piece(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: &BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
let mut attempt = 0;
|
||||||
|
while attempt < 5 {
|
||||||
|
attempt += 1;
|
||||||
|
let mut j = random.next_bounded_i32(TOTAL_WEIGHT);
|
||||||
|
|
||||||
|
for piece_data in POSSIBLE_PIECES.iter() {
|
||||||
|
j -= piece_data.weight;
|
||||||
|
if j >= 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if piece_data.limit > 0 {
|
||||||
|
let type_to_check = piece_data.piece_type.as_structure_type();
|
||||||
|
|
||||||
|
let current_count = collector
|
||||||
|
.pieces
|
||||||
|
.iter()
|
||||||
|
.filter(|p| p.get_structure_piece().r#type == type_to_check)
|
||||||
|
.count();
|
||||||
|
|
||||||
|
if current_count >= piece_data.limit as usize {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let piece = Self::create_piece(
|
||||||
|
&piece_data.piece_type,
|
||||||
|
collector,
|
||||||
|
random,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
orientation,
|
||||||
|
chain_length,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some(p) = piece {
|
||||||
|
return Some(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback: Small Corridor
|
||||||
|
if let Some(bbox) = SmallCorridorPiece::create_box(collector, x, y, z, orientation) {
|
||||||
|
return Some(Box::new(SmallCorridorPiece::new(
|
||||||
|
chain_length,
|
||||||
|
bbox,
|
||||||
|
*orientation,
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::too_many_arguments)]
|
||||||
|
fn create_piece(
|
||||||
|
piece_type: &StrongholdPieceType,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: &BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
match piece_type {
|
||||||
|
StrongholdPieceType::FiveWayCrossing => {
|
||||||
|
FiveWayCrossingPiece::create(collector, random, x, y, z, *orientation, chain_length)
|
||||||
|
}
|
||||||
|
StrongholdPieceType::Corridor => {
|
||||||
|
CorridorPiece::create(collector, random, x, y, z, *orientation, chain_length)
|
||||||
|
}
|
||||||
|
StrongholdPieceType::SquareRoom => {
|
||||||
|
SquareRoomPiece::create(collector, random, x, y, z, *orientation, chain_length)
|
||||||
|
}
|
||||||
|
StrongholdPieceType::PortalRoom => {
|
||||||
|
PortalRoomPiece::create(collector, random, x, y, z, *orientation, chain_length)
|
||||||
|
}
|
||||||
|
StrongholdPieceType::SpiralStaircase => {
|
||||||
|
SpiralStaircasePiece::create(collector, random, x, y, z, *orientation, chain_length)
|
||||||
|
}
|
||||||
|
StrongholdPieceType::PrisonHall => {
|
||||||
|
PrisonHallPiece::create(collector, random, x, y, z, *orientation, chain_length)
|
||||||
|
}
|
||||||
|
StrongholdPieceType::LeftTurn => {
|
||||||
|
LeftTurnPiece::create(collector, random, x, y, z, *orientation, chain_length)
|
||||||
|
}
|
||||||
|
StrongholdPieceType::RightTurn => {
|
||||||
|
RightTurnPiece::create(collector, random, x, y, z, *orientation, chain_length)
|
||||||
|
}
|
||||||
|
StrongholdPieceType::Stairs => {
|
||||||
|
StairsPiece::create(collector, random, x, y, z, *orientation, chain_length)
|
||||||
|
}
|
||||||
|
StrongholdPieceType::ChestCorridor => {
|
||||||
|
ChestCorridorPiece::create(collector, random, x, y, z, *orientation, chain_length)
|
||||||
|
}
|
||||||
|
StrongholdPieceType::Library => {
|
||||||
|
LibraryPiece::create(collector, random, x, y, z, *orientation, chain_length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,262 @@
|
|||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::block_box::BlockBox,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructurePiece, StructurePieceBase, StructurePiecesCollector,
|
||||||
|
stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PortalRoomPiece {
|
||||||
|
pub piece: StrongholdPiece,
|
||||||
|
pub spawner_placed: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PortalRoomPiece {
|
||||||
|
pub fn create(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
_random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
let bounding_box = BlockBox::rotated(x, y, z, -4, -1, 0, 11, 8, 16, &orientation);
|
||||||
|
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdPortalRoom,
|
||||||
|
chain_length,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
|
||||||
|
Some(Box::new(Self {
|
||||||
|
piece,
|
||||||
|
spawner_placed: false,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for PortalRoomPiece {
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let randomizer = StoneBrickRandomizer;
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
let p = &self.piece;
|
||||||
|
let inner = &p.piece;
|
||||||
|
|
||||||
|
// 1. Main Shell (11x8x16)
|
||||||
|
inner.fill_outline_random(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
7,
|
||||||
|
15,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Entrance (Grates)
|
||||||
|
p.generate_entrance(chunk, &box_limit, EntranceType::Grates, 4, 1, 0);
|
||||||
|
|
||||||
|
// 3. Upper Walkways
|
||||||
|
inner.fill_outline_random(
|
||||||
|
1,
|
||||||
|
6,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
6,
|
||||||
|
14,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_outline_random(
|
||||||
|
9,
|
||||||
|
6,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
6,
|
||||||
|
14,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_outline_random(
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
1,
|
||||||
|
8,
|
||||||
|
6,
|
||||||
|
2,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_outline_random(
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
14,
|
||||||
|
8,
|
||||||
|
6,
|
||||||
|
14,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 4. Lava Pools (Side pools and central pool)
|
||||||
|
let lava = Block::LAVA.default_state;
|
||||||
|
inner.fill_outline_random(
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_outline_random(
|
||||||
|
8,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 1, 1, 1, 1, 1, 3, lava, lava);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 9, 1, 1, 9, 1, 3, lava, lava);
|
||||||
|
|
||||||
|
inner.fill_outline_random(
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
8,
|
||||||
|
7,
|
||||||
|
1,
|
||||||
|
12,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 4, 1, 9, 6, 1, 11, lava, lava);
|
||||||
|
|
||||||
|
// 5. Iron Bars (Directional panes)
|
||||||
|
// let bar_ns = Block::IRON_BARS.default_state.with("north", "true").with("south", "true");
|
||||||
|
// let bar_we = Block::IRON_BARS.default_state.with("west", "true").with("east", "true");
|
||||||
|
|
||||||
|
// for j in (3..14).step_by(2) {
|
||||||
|
// inner.fill_with_outline(chunk, &box_limit, false, 0, 3, j, 0, 4, j, &bar_ns, &bar_ns);
|
||||||
|
// inner.fill_with_outline(chunk, &box_limit, false, 10, 3, j, 10, 4, j, &bar_ns, &bar_ns);
|
||||||
|
// }
|
||||||
|
// for j in (2..9).step_by(2) {
|
||||||
|
// inner.fill_with_outline(chunk, &box_limit, false, j, 3, 15, j, 4, 15, &bar_we, &bar_we);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 6. Stairs leading to Portal
|
||||||
|
// let stairs_n = Block::STONE_BRICK_STAIRS.default_state.with("facing", "north");
|
||||||
|
// inner.fill_outline_random(4, 1, 5, 6, 1, 7, &randomizer, chunk, false, random, &box_limit);
|
||||||
|
// inner.fill_outline_random(4, 2, 6, 6, 2, 7, &randomizer, chunk, false, random, &box_limit);
|
||||||
|
// inner.fill_outline_random(4, 3, 7, 6, 3, 7, &randomizer, chunk, false, random, &box_limit);
|
||||||
|
|
||||||
|
// for k in 4..=6 {
|
||||||
|
// inner.add_block(chunk, &stairs_n, k, 1, 4, &box_limit);
|
||||||
|
// inner.add_block(chunk, &stairs_n, k, 2, 5, &box_limit);
|
||||||
|
// inner.add_block(chunk, &stairs_n, k, 3, 6, &box_limit);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 7. End Portal Frames and Eyes
|
||||||
|
let eyes: [bool; 12] = std::array::from_fn(|_| random.next_f32() > 0.9);
|
||||||
|
let all_eyes = eyes.iter().all(|&eye| eye);
|
||||||
|
|
||||||
|
// let f_north = Block::END_PORTAL_FRAME.default_state.with("facing", "north");
|
||||||
|
// let f_south = Block::END_PORTAL_FRAME.default_state.with("facing", "south");
|
||||||
|
// let f_east = Block::END_PORTAL_FRAME.default_state.with("facing", "east");
|
||||||
|
// let f_west = Block::END_PORTAL_FRAME.default_state.with("facing", "west");
|
||||||
|
|
||||||
|
// Helper to stringify eye boolean for state mapping
|
||||||
|
//let eye_str = |b: bool| if b { "true" } else { "false" };
|
||||||
|
|
||||||
|
// inner.add_block(chunk, &f_north.with("eye", eye_str(eyes[0])), 4, 3, 8, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_north.with("eye", eye_str(eyes[1])), 5, 3, 8, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_north.with("eye", eye_str(eyes[2])), 6, 3, 8, &box_limit);
|
||||||
|
|
||||||
|
// inner.add_block(chunk, &f_south.with("eye", eye_str(eyes[3])), 4, 3, 12, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_south.with("eye", eye_str(eyes[4])), 5, 3, 12, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_south.with("eye", eye_str(eyes[5])), 6, 3, 12, &box_limit);
|
||||||
|
|
||||||
|
// inner.add_block(chunk, &f_east.with("eye", eye_str(eyes[6])), 3, 3, 9, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_east.with("eye", eye_str(eyes[7])), 3, 3, 10, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_east.with("eye", eye_str(eyes[8])), 3, 3, 11, &box_limit);
|
||||||
|
|
||||||
|
// inner.add_block(chunk, &f_west.with("eye", eye_str(eyes[9])), 7, 3, 9, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_west.with("eye", eye_str(eyes[10])), 7, 3, 10, &box_limit);
|
||||||
|
// inner.add_block(chunk, &f_west.with("eye", eye_str(eyes[11])), 7, 3, 11, &box_limit);
|
||||||
|
|
||||||
|
if all_eyes {
|
||||||
|
let portal = Block::END_PORTAL.default_state;
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 4, 3, 9, 6, 3, 11, portal, portal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Spawner (Silverfish)
|
||||||
|
if !self.spawner_placed {
|
||||||
|
let pos = inner.offset_pos(5, 3, 6);
|
||||||
|
if box_limit.contains_pos(&pos) {
|
||||||
|
self.spawner_placed = true;
|
||||||
|
let spawner = Block::SPAWNER.default_state;
|
||||||
|
inner.add_block(chunk, spawner, 5, 3, 6, &box_limit);
|
||||||
|
// In a real implementation, you'd add the TileEntityData for Silverfish here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,194 @@
|
|||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::block_box::BlockBox,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructurePiece, StructurePieceBase, StructurePiecesCollector,
|
||||||
|
stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PrisonHallPiece {
|
||||||
|
piece: StrongholdPiece,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PrisonHallPiece {
|
||||||
|
pub fn create(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
// Main Outline 9x5x11 (0..8, 0..4, 0..10)
|
||||||
|
let bounding_box = BlockBox::rotated(x, y, z, -1, -1, 0, 9, 5, 11, &orientation);
|
||||||
|
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdPrisonHall,
|
||||||
|
chain_length,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
piece.entry_door = EntranceType::get_random(random);
|
||||||
|
|
||||||
|
Some(Box::new(Self { piece }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for PrisonHallPiece {
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_openings(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
// Prison hall usually only has one forward exit
|
||||||
|
self.piece
|
||||||
|
.fill_forward_opening(start, collector, random, 1, 1, pieces_to_process, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let randomizer = StoneBrickRandomizer;
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
let p = &self.piece;
|
||||||
|
let inner = &p.piece;
|
||||||
|
let air = Block::AIR.default_state;
|
||||||
|
|
||||||
|
// 1. Main Shell 9x5x11
|
||||||
|
inner.fill_outline_random(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
8,
|
||||||
|
4,
|
||||||
|
10,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
true,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Entrance (Z=0)
|
||||||
|
p.generate_entrance(chunk, &box_limit, self.piece.entry_door, 1, 1, 0);
|
||||||
|
|
||||||
|
// 3. Exit (Forward/Z=10) - Using fill_with_outline (cant_replace_air = false)
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 1, 1, 10, 3, 3, 10, air, air);
|
||||||
|
|
||||||
|
// 4. Cell Stone Separators (X=4)
|
||||||
|
// Note: Java passes 'false' for cant_replace_air here
|
||||||
|
inner.fill_outline_random(
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_outline_random(
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
3,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_outline_random(
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
7,
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
7,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
inner.fill_outline_random(
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
9,
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
9,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
false,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 5. Iron Bars with Directional Properties
|
||||||
|
// let bars_ns = Block::IRON_BARS.default_state.with("north", "true").with("south", "true");
|
||||||
|
// let bars_nse = bars_ns.with("east", "true");
|
||||||
|
// let bars_we = Block::IRON_BARS.default_state.with("west", "true").with("east", "true");
|
||||||
|
|
||||||
|
// for i in 1..=3 {
|
||||||
|
// inner.add_block(chunk, &bars_ns, 4, i, 4, &box_limit);
|
||||||
|
// inner.add_block(chunk, &bars_nse, 4, i, 5, &box_limit);
|
||||||
|
// inner.add_block(chunk, &bars_ns, 4, i, 6, &box_limit);
|
||||||
|
|
||||||
|
// inner.add_block(chunk, &bars_we, 5, i, 5, &box_limit);
|
||||||
|
// inner.add_block(chunk, &bars_we, 6, i, 5, &box_limit);
|
||||||
|
// inner.add_block(chunk, &bars_we, 7, i, 5, &box_limit);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // Top bars above the doors
|
||||||
|
// inner.add_block(chunk, &bars_ns, 4, 3, 2, &box_limit);
|
||||||
|
// inner.add_block(chunk, &bars_ns, 4, 3, 8, &box_limit);
|
||||||
|
|
||||||
|
// // 6. Iron Doors (2-block high structure)
|
||||||
|
// let door_bottom = Block::IRON_DOOR.default_state.with("facing", "west").with("half", "lower");
|
||||||
|
// let door_top = Block::IRON_DOOR.default_state.with("facing", "west").with("half", "upper");
|
||||||
|
|
||||||
|
// // Door 1
|
||||||
|
// inner.add_block(chunk, &door_bottom, 4, 1, 2, &box_limit);
|
||||||
|
// inner.add_block(chunk, &door_top, 4, 2, 2, &box_limit);
|
||||||
|
|
||||||
|
// // Door 2
|
||||||
|
// inner.add_block(chunk, &door_bottom, 4, 1, 8, &box_limit);
|
||||||
|
// inner.add_block(chunk, &door_top, 4, 2, 8, &box_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,124 @@
|
|||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::block_box::BlockBox,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructurePiece, StructurePieceBase, StructurePiecesCollector,
|
||||||
|
stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RightTurnPiece {
|
||||||
|
pub piece: StrongholdPiece,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RightTurnPiece {
|
||||||
|
pub fn create(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
// 5x5x5 box
|
||||||
|
let bounding_box = BlockBox::rotated(x, y, z, -1, -1, 0, 5, 5, 5, &orientation);
|
||||||
|
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdRightTurn,
|
||||||
|
chain_length,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
piece.entry_door = EntranceType::get_random(random);
|
||||||
|
|
||||||
|
Some(Box::new(Self { piece }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for RightTurnPiece {
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_openings(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
let facing = self.piece.piece.facing.unwrap_or(BlockDirection::North);
|
||||||
|
|
||||||
|
// For a Right Turn, we swap the logic from the Left Turn:
|
||||||
|
// If facing North/East, we go "South-East", otherwise "North-West"
|
||||||
|
if facing == BlockDirection::North || facing == BlockDirection::East {
|
||||||
|
self.piece
|
||||||
|
.fill_se_opening(start, collector, random, 1, 1, pieces_to_process);
|
||||||
|
} else {
|
||||||
|
self.piece
|
||||||
|
.fill_nw_opening(start, collector, random, 1, 1, pieces_to_process);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let randomizer = StoneBrickRandomizer;
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
let p = &self.piece;
|
||||||
|
let inner = &p.piece;
|
||||||
|
|
||||||
|
// 1. 5x5x5 Outline (Using randomizer)
|
||||||
|
// Note: Use your existing fill_outline_random for this part
|
||||||
|
inner.fill_outline_random(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
4,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
true,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Entrance (Z=0)
|
||||||
|
p.generate_entrance(chunk, &box_limit, self.piece.entry_door, 1, 1, 0);
|
||||||
|
|
||||||
|
// 3. Exit logic based on Facing
|
||||||
|
let air = Block::AIR.default_state;
|
||||||
|
let facing = inner.facing.unwrap_or(BlockDirection::North);
|
||||||
|
|
||||||
|
if facing == BlockDirection::North || facing == BlockDirection::East {
|
||||||
|
// Exit on X=4 wall
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 4, 1, 1, 4, 3, 3, air, air);
|
||||||
|
} else {
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 0, 1, 1, 0, 3, 3, air, air);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,187 @@
|
|||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::block_box::BlockBox,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructurePiece, StructurePieceBase, StructurePiecesCollector,
|
||||||
|
stronghold::{
|
||||||
|
EntranceType, StoneBrickRandomizer, StrongholdPiece, StrongholdPieceType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct SpiralStaircasePiece {
|
||||||
|
pub piece: StrongholdPiece,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
is_structure_start: bool,
|
||||||
|
// Start-specific fields
|
||||||
|
pub last_piece_data_idx: Option<usize>,
|
||||||
|
pub portal_room_exists: bool,
|
||||||
|
pub pieces: Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
}
|
||||||
|
impl Clone for SpiralStaircasePiece {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
// StrongholdPiece and other types must derive Clone
|
||||||
|
piece: self.piece.clone(),
|
||||||
|
orientation: self.orientation,
|
||||||
|
is_structure_start: self.is_structure_start,
|
||||||
|
last_piece_data_idx: self.last_piece_data_idx,
|
||||||
|
portal_room_exists: self.portal_room_exists,
|
||||||
|
// Use the clone_box helper we added to the trait!
|
||||||
|
pieces: self.pieces.iter().map(|p| p.clone_box()).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpiralStaircasePiece {
|
||||||
|
/// Matches Start(Random random, int i, int j)
|
||||||
|
pub fn new_start(random: &mut impl RandomImpl, x: i32, z: i32) -> Self {
|
||||||
|
let orientation = StructurePiece::get_random_horizontal_direction(random);
|
||||||
|
let bounding_box = BlockBox::create_box(x, 64, z, orientation.get_axis(), 5, 11, 5);
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdSpiralStaircase,
|
||||||
|
0,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
Self {
|
||||||
|
piece,
|
||||||
|
orientation,
|
||||||
|
is_structure_start: true,
|
||||||
|
last_piece_data_idx: None,
|
||||||
|
portal_room_exists: false,
|
||||||
|
pieces: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
let bounding_box = BlockBox::rotated(x, y, z, -1, -7, 0, 5, 11, 5, &orientation);
|
||||||
|
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdSpiralStaircase,
|
||||||
|
chain_length,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
piece.entry_door = EntranceType::get_random(random);
|
||||||
|
|
||||||
|
Some(Box::new(Self {
|
||||||
|
piece,
|
||||||
|
orientation,
|
||||||
|
is_structure_start: false,
|
||||||
|
last_piece_data_idx: None,
|
||||||
|
portal_room_exists: false,
|
||||||
|
pieces: Vec::new(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_portal_room(&self) -> bool {
|
||||||
|
self.portal_room_exists
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for SpiralStaircasePiece {
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
fn fill_openings(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
self.piece.fill_forward_opening(
|
||||||
|
start,
|
||||||
|
collector,
|
||||||
|
random,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
pieces_to_process,
|
||||||
|
Some(StrongholdPieceType::FiveWayCrossing),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let randomizer = StoneBrickRandomizer;
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
|
||||||
|
let p = &self.piece;
|
||||||
|
|
||||||
|
// 1. fillWithOutline (5x11x5 box)
|
||||||
|
p.piece.fill_outline_random(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
10,
|
||||||
|
4,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
true,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
p.generate_entrance(chunk, &box_limit, self.piece.entry_door, 1, 7, 0);
|
||||||
|
|
||||||
|
p.generate_entrance(chunk, &box_limit, EntranceType::Opening, 1, 1, 4);
|
||||||
|
|
||||||
|
// 4. Spiral Staircase Blocks
|
||||||
|
let stone = Block::STONE_BRICKS.default_state;
|
||||||
|
let slab = Block::SMOOTH_STONE_SLAB.default_state;
|
||||||
|
|
||||||
|
// Use the inner StructurePiece for block placement helpers
|
||||||
|
let inner = &p.piece;
|
||||||
|
inner.add_block(chunk, stone, 2, 6, 1, &box_limit);
|
||||||
|
inner.add_block(chunk, stone, 1, 5, 1, &box_limit);
|
||||||
|
inner.add_block(chunk, slab, 1, 6, 1, &box_limit);
|
||||||
|
inner.add_block(chunk, stone, 1, 5, 2, &box_limit);
|
||||||
|
inner.add_block(chunk, stone, 1, 4, 3, &box_limit);
|
||||||
|
inner.add_block(chunk, slab, 1, 5, 3, &box_limit);
|
||||||
|
inner.add_block(chunk, stone, 2, 4, 3, &box_limit);
|
||||||
|
inner.add_block(chunk, stone, 3, 3, 3, &box_limit);
|
||||||
|
inner.add_block(chunk, slab, 3, 4, 3, &box_limit);
|
||||||
|
inner.add_block(chunk, stone, 3, 3, 2, &box_limit);
|
||||||
|
inner.add_block(chunk, stone, 3, 2, 1, &box_limit);
|
||||||
|
inner.add_block(chunk, slab, 3, 3, 1, &box_limit);
|
||||||
|
inner.add_block(chunk, stone, 2, 2, 1, &box_limit);
|
||||||
|
inner.add_block(chunk, stone, 1, 1, 1, &box_limit);
|
||||||
|
inner.add_block(chunk, slab, 1, 2, 1, &box_limit);
|
||||||
|
|
||||||
|
// Final bottom blocks from Java source
|
||||||
|
inner.add_block(chunk, stone, 1, 1, 2, &box_limit);
|
||||||
|
inner.add_block(chunk, slab, 1, 1, 3, &box_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,256 @@
|
|||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::block_box::BlockBox,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructurePiece, StructurePieceBase, StructurePiecesCollector,
|
||||||
|
stronghold::{StoneBrickRandomizer, StrongholdPiece},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SquareRoomPiece {
|
||||||
|
piece: StrongholdPiece,
|
||||||
|
room_type: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SquareRoomPiece {
|
||||||
|
pub fn create(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
let bounding_box = BlockBox::rotated(x, y, z, -4, -1, 0, 11, 7, 11, &orientation);
|
||||||
|
|
||||||
|
// 2. Check world bounds and intersections
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Construct the piece
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdSquareRoom,
|
||||||
|
chain_length,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
|
||||||
|
let room_type = random.next_bounded_i32(5) as u32;
|
||||||
|
|
||||||
|
Some(Box::new(Self { piece, room_type }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for SquareRoomPiece {
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_openings(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
// 1. Forward Opening
|
||||||
|
// Java: this.fillForwardOpening((Start)start, holder, random, 4, 1);
|
||||||
|
self.piece.fill_forward_opening(
|
||||||
|
start,
|
||||||
|
collector,
|
||||||
|
random,
|
||||||
|
4, // left_right_offset
|
||||||
|
1, // height_offset
|
||||||
|
pieces_to_process,
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Left Opening (NW)
|
||||||
|
// Java: this.fillNWOpening((Start)start, holder, random, 1, 4);
|
||||||
|
self.piece.fill_nw_opening(
|
||||||
|
start,
|
||||||
|
collector,
|
||||||
|
random,
|
||||||
|
1, // height_offset
|
||||||
|
4, // left_right_offset
|
||||||
|
pieces_to_process,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. Right Opening (SE)
|
||||||
|
// Java: this.fillSEOpening((Start)start, holder, random, 1, 4);
|
||||||
|
self.piece.fill_se_opening(
|
||||||
|
start,
|
||||||
|
collector,
|
||||||
|
random,
|
||||||
|
1, // height_offset
|
||||||
|
4, // left_right_offset
|
||||||
|
pieces_to_process,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let randomizer = StoneBrickRandomizer;
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
let p = &self.piece;
|
||||||
|
let inner = &p.piece;
|
||||||
|
let air = Block::AIR.default_state;
|
||||||
|
|
||||||
|
// 1. Main Outline (10x6x10) - Matches Java: fillWithOutline(..., 0,0,0, 10,6,10, ...)
|
||||||
|
inner.fill_outline_random(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
6,
|
||||||
|
10,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
true,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Main Entrance (Z=0, aligned to X=4)
|
||||||
|
// Matches Java: generateEntrance(..., 4, 1, 0)
|
||||||
|
p.generate_entrance(chunk, &box_limit, self.piece.entry_door, 4, 1, 0);
|
||||||
|
|
||||||
|
// 3. Clear Exits
|
||||||
|
// Forward Exit (Z=10) -> fillWithOutline(..., 4,1,10, 6,3,10, AIR, AIR, false)
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 4, 1, 10, 6, 3, 10, air, air);
|
||||||
|
|
||||||
|
// Left Side Exit (X=0) -> fillWithOutline(..., 0,1,4, 0,3,6, AIR, AIR, false)
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 0, 1, 4, 0, 3, 6, air, air);
|
||||||
|
|
||||||
|
// Right Side Exit (X=10) -> fillWithOutline(..., 10,1,4, 10,3,6, AIR, AIR, false)
|
||||||
|
inner.fill_with_outline(chunk, &box_limit, false, 10, 1, 4, 10, 3, 6, air, air);
|
||||||
|
|
||||||
|
// 4. Room Specific Decorations
|
||||||
|
match self.room_type {
|
||||||
|
0 => {
|
||||||
|
let stone_brick = Block::STONE_BRICKS.default_state;
|
||||||
|
let smooth_slab = Block::SMOOTH_STONE_SLAB.default_state;
|
||||||
|
|
||||||
|
// Central Pillar
|
||||||
|
inner.add_block(chunk, stone_brick, 5, 1, 5, &box_limit);
|
||||||
|
inner.add_block(chunk, stone_brick, 5, 2, 5, &box_limit);
|
||||||
|
inner.add_block(chunk, stone_brick, 5, 3, 5, &box_limit);
|
||||||
|
|
||||||
|
// // Torches
|
||||||
|
// inner.add_block(chunk, &Block::WALL_TORCH.default_state.with("facing", "west"), 4, 3, 5, &box_limit);
|
||||||
|
// inner.add_block(chunk, &Block::WALL_TORCH.default_state.with("facing", "east"), 6, 3, 5, &box_limit);
|
||||||
|
// inner.add_block(chunk, &Block::WALL_TORCH.default_state.with("facing", "south"), 5, 3, 4, &box_limit);
|
||||||
|
// inner.add_block(chunk, &Block::WALL_TORCH.default_state.with("facing", "north"), 5, 3, 6, &box_limit);
|
||||||
|
|
||||||
|
// Floor Pattern (Slabs)
|
||||||
|
inner.add_block(chunk, smooth_slab, 4, 1, 4, &box_limit);
|
||||||
|
inner.add_block(chunk, smooth_slab, 4, 1, 5, &box_limit);
|
||||||
|
inner.add_block(chunk, smooth_slab, 4, 1, 6, &box_limit);
|
||||||
|
inner.add_block(chunk, smooth_slab, 6, 1, 4, &box_limit);
|
||||||
|
inner.add_block(chunk, smooth_slab, 6, 1, 5, &box_limit);
|
||||||
|
inner.add_block(chunk, smooth_slab, 6, 1, 6, &box_limit);
|
||||||
|
inner.add_block(chunk, smooth_slab, 5, 1, 4, &box_limit);
|
||||||
|
inner.add_block(chunk, smooth_slab, 5, 1, 6, &box_limit);
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
let stone_brick = Block::STONE_BRICKS.default_state;
|
||||||
|
|
||||||
|
// Ring of bricks
|
||||||
|
for i in 0..5 {
|
||||||
|
inner.add_block(chunk, stone_brick, 3, 1, 3 + i, &box_limit);
|
||||||
|
inner.add_block(chunk, stone_brick, 7, 1, 3 + i, &box_limit);
|
||||||
|
inner.add_block(chunk, stone_brick, 3 + i, 1, 3, &box_limit);
|
||||||
|
inner.add_block(chunk, stone_brick, 3 + i, 1, 7, &box_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Central Water Column
|
||||||
|
inner.add_block(chunk, stone_brick, 5, 1, 5, &box_limit);
|
||||||
|
inner.add_block(chunk, stone_brick, 5, 2, 5, &box_limit);
|
||||||
|
inner.add_block(chunk, stone_brick, 5, 3, 5, &box_limit);
|
||||||
|
inner.add_block(chunk, Block::WATER.default_state, 5, 4, 5, &box_limit);
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
let cobble = Block::COBBLESTONE.default_state;
|
||||||
|
|
||||||
|
// Cobblestone shelves/walls
|
||||||
|
for i in 1..=9 {
|
||||||
|
inner.add_block(chunk, cobble, 1, 3, i, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, 9, 3, i, &box_limit);
|
||||||
|
}
|
||||||
|
for i in 1..=9 {
|
||||||
|
inner.add_block(chunk, cobble, i, 3, 1, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, i, 3, 9, &box_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Central structure supports
|
||||||
|
inner.add_block(chunk, cobble, 5, 1, 4, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, 5, 1, 6, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, 5, 3, 4, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, 5, 3, 6, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, 4, 1, 5, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, 6, 1, 5, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, 4, 3, 5, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, 6, 3, 5, &box_limit);
|
||||||
|
|
||||||
|
// Pillars
|
||||||
|
for i in 1..=3 {
|
||||||
|
inner.add_block(chunk, cobble, 4, i, 4, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, 6, i, 4, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, 4, i, 6, &box_limit);
|
||||||
|
inner.add_block(chunk, cobble, 6, i, 6, &box_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
inner.add_block(chunk, Block::WALL_TORCH.default_state, 5, 3, 5, &box_limit);
|
||||||
|
|
||||||
|
// Upper floor (Oak Planks)
|
||||||
|
let oak = Block::OAK_PLANKS.default_state;
|
||||||
|
for i in 2..=8 {
|
||||||
|
inner.add_block(chunk, oak, 2, 3, i, &box_limit);
|
||||||
|
inner.add_block(chunk, oak, 3, 3, i, &box_limit);
|
||||||
|
|
||||||
|
// Holes for ladders/structure
|
||||||
|
if i <= 3 || i >= 7 {
|
||||||
|
inner.add_block(chunk, oak, 4, 3, i, &box_limit);
|
||||||
|
inner.add_block(chunk, oak, 5, 3, i, &box_limit);
|
||||||
|
inner.add_block(chunk, oak, 6, 3, i, &box_limit);
|
||||||
|
}
|
||||||
|
inner.add_block(chunk, oak, 7, 3, i, &box_limit);
|
||||||
|
inner.add_block(chunk, oak, 8, 3, i, &box_limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ladder
|
||||||
|
// let ladder = Block::LADDER.default_state.with("facing", "west");
|
||||||
|
// inner.add_block(chunk, &ladder, 9, 1, 3, &box_limit);
|
||||||
|
// inner.add_block(chunk, &ladder, 9, 2, 3, &box_limit);
|
||||||
|
// inner.add_block(chunk, &ladder, 9, 3, 3, &box_limit);
|
||||||
|
|
||||||
|
// Chest
|
||||||
|
// Note: Ensure you have `LootTables` defined or use a string ID
|
||||||
|
// p.add_chest(chunk, &box_limit, random, 3, 4, 8, LootTables::StrongholdCrossingChest);
|
||||||
|
inner.add_block(chunk, Block::CHEST.default_state, 3, 4, 8, &box_limit);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,124 @@
|
|||||||
|
use pumpkin_data::Block;
|
||||||
|
use pumpkin_util::{
|
||||||
|
BlockDirection,
|
||||||
|
math::block_box::BlockBox,
|
||||||
|
random::{RandomGenerator, RandomImpl},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
ProtoChunk,
|
||||||
|
generation::structure::{
|
||||||
|
piece::StructurePieceType,
|
||||||
|
structures::{
|
||||||
|
StructurePiece, StructurePieceBase, StructurePiecesCollector,
|
||||||
|
stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct StairsPiece {
|
||||||
|
pub piece: StrongholdPiece,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StairsPiece {
|
||||||
|
pub fn create(
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
random: &mut impl RandomImpl,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
z: i32,
|
||||||
|
orientation: BlockDirection,
|
||||||
|
chain_length: u32,
|
||||||
|
) -> Option<Box<dyn StructurePieceBase>> {
|
||||||
|
let bounding_box = BlockBox::rotated(x, y, z, -1, -7, 0, 5, 11, 8, &orientation);
|
||||||
|
|
||||||
|
if !StrongholdPiece::is_in_bounds(&bounding_box)
|
||||||
|
|| collector.get_intersecting(&bounding_box).is_some()
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut piece = StrongholdPiece::new(
|
||||||
|
StructurePieceType::StrongholdStairs,
|
||||||
|
chain_length,
|
||||||
|
bounding_box,
|
||||||
|
);
|
||||||
|
piece.piece.set_facing(Some(orientation));
|
||||||
|
piece.entry_door = EntranceType::get_random(random);
|
||||||
|
|
||||||
|
Some(Box::new(Self { piece }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StructurePieceBase for StairsPiece {
|
||||||
|
fn get_structure_piece(&self) -> &StructurePiece {
|
||||||
|
&self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut StructurePiece {
|
||||||
|
&mut self.piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_openings(
|
||||||
|
&self,
|
||||||
|
start: &StructurePiece,
|
||||||
|
random: &mut RandomGenerator,
|
||||||
|
collector: &mut StructurePiecesCollector,
|
||||||
|
pieces_to_process: &mut Vec<Box<dyn StructurePieceBase>>,
|
||||||
|
) {
|
||||||
|
self.piece
|
||||||
|
.fill_forward_opening(start, collector, random, 1, 1, pieces_to_process, None);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) {
|
||||||
|
let randomizer = StoneBrickRandomizer;
|
||||||
|
let box_limit = self.piece.piece.bounding_box;
|
||||||
|
let p = &self.piece;
|
||||||
|
let inner = &p.piece;
|
||||||
|
|
||||||
|
// 1. Outer Box 5x11x8
|
||||||
|
inner.fill_outline_random(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
10,
|
||||||
|
7,
|
||||||
|
&randomizer,
|
||||||
|
chunk,
|
||||||
|
true,
|
||||||
|
random,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Entrance (Top)
|
||||||
|
p.generate_entrance(chunk, &box_limit, self.piece.entry_door, 1, 7, 0);
|
||||||
|
|
||||||
|
// 3. Exit (Bottom - Opening only)
|
||||||
|
p.generate_entrance(chunk, &box_limit, EntranceType::Opening, 1, 1, 7);
|
||||||
|
|
||||||
|
// 4. Stairs Generation
|
||||||
|
let stair_state = Block::COBBLESTONE_STAIRS.default_state;
|
||||||
|
|
||||||
|
let stone_bricks = Block::STONE_BRICKS.default_state;
|
||||||
|
|
||||||
|
for i in 0..6 {
|
||||||
|
// Place the stair blocks
|
||||||
|
inner.add_block(chunk, stair_state, 1, 6 - i, 1 + i, &box_limit);
|
||||||
|
inner.add_block(chunk, stair_state, 2, 6 - i, 1 + i, &box_limit);
|
||||||
|
inner.add_block(chunk, stair_state, 3, 6 - i, 1 + i, &box_limit);
|
||||||
|
|
||||||
|
// Fill underneath the stairs for stability
|
||||||
|
if i < 5 {
|
||||||
|
inner.add_block(chunk, stone_bricks, 1, 5 - i, 1 + i, &box_limit);
|
||||||
|
inner.add_block(chunk, stone_bricks, 2, 5 - i, 1 + i, &box_limit);
|
||||||
|
inner.add_block(chunk, stone_bricks, 3, 5 - i, 1 + i, &box_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
|
Box::new((*self).clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,204 +1,240 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use pumpkin_data::Block;
|
use pumpkin_data::Block;
|
||||||
use pumpkin_util::{
|
use pumpkin_util::{math::position::BlockPos, random::RandomGenerator};
|
||||||
HeightMap,
|
|
||||||
math::{
|
|
||||||
block_box::BlockBox,
|
|
||||||
position::BlockPos,
|
|
||||||
vector3::{Axis, Vector3},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ProtoChunk,
|
ProtoChunk,
|
||||||
generation::{
|
generation::{
|
||||||
positions::chunk_pos::{get_center_x, get_center_z},
|
positions::chunk_pos::{get_center_x, get_center_z},
|
||||||
structure::structures::{
|
structure::{
|
||||||
StructureGenerator, StructurePiece, StructurePiecesCollector, StructurePosition, fill,
|
piece::StructurePieceType,
|
||||||
fill_downwards,
|
shiftable_piece::ShiftableStructurePiece,
|
||||||
|
structures::{
|
||||||
|
StructureGenerator, StructureGeneratorContext, StructurePiece, StructurePieceBase,
|
||||||
|
StructurePiecesCollector, StructurePosition,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Deserialize, Clone)]
|
#[derive(Deserialize)]
|
||||||
pub struct SwampHutGenerator;
|
pub struct SwampHutGenerator;
|
||||||
|
|
||||||
impl StructureGenerator for SwampHutGenerator {
|
impl StructureGenerator for SwampHutGenerator {
|
||||||
fn get_structure_position(
|
fn get_structure_position(
|
||||||
&self,
|
&self,
|
||||||
_seed: i64,
|
mut context: StructureGeneratorContext,
|
||||||
chunk_x: i32,
|
|
||||||
chunk_z: i32,
|
|
||||||
) -> Option<StructurePosition> {
|
) -> Option<StructurePosition> {
|
||||||
let x = get_center_x(chunk_x);
|
let x = get_center_x(context.chunk_x);
|
||||||
let z = get_center_z(chunk_z);
|
let z = get_center_z(context.chunk_z);
|
||||||
|
|
||||||
// TODO: random axis
|
|
||||||
let bounding_box = BlockBox::create_box(x, 64, z, Axis::X, 7, 7, 9);
|
|
||||||
|
|
||||||
let mut collector = StructurePiecesCollector::default();
|
let mut collector = StructurePiecesCollector::default();
|
||||||
collector.add_piece(Box::new(SwampHutPiece { bounding_box }));
|
collector.add_piece(Box::new(SwampHutPiece {
|
||||||
|
shiftable_structure_piece: ShiftableStructurePiece::new(
|
||||||
|
StructurePieceType::SwampHut,
|
||||||
|
x,
|
||||||
|
64,
|
||||||
|
z,
|
||||||
|
7,
|
||||||
|
7,
|
||||||
|
9,
|
||||||
|
StructurePiece::get_random_horizontal_direction(&mut context.random).get_axis(),
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
|
||||||
Some(StructurePosition {
|
Some(StructurePosition {
|
||||||
start_pos: BlockPos::new(x, 64, z),
|
start_pos: BlockPos::new(x, 64, z),
|
||||||
collector: collector.into(),
|
collector: Arc::new(collector.into()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct SwampHutPiece {
|
pub struct SwampHutPiece {
|
||||||
bounding_box: BlockBox,
|
shiftable_structure_piece: ShiftableStructurePiece,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructurePiece for SwampHutPiece {
|
impl StructurePieceBase for SwampHutPiece {
|
||||||
fn bounding_box(&self) -> &BlockBox {
|
fn clone_box(&self) -> Box<dyn StructurePieceBase> {
|
||||||
&self.bounding_box
|
Box::new(self.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn place(&self, chunk: &mut ProtoChunk, _seed: i64) {
|
fn place(&mut self, chunk: &mut ProtoChunk, _random: &mut RandomGenerator, _seed: i64) {
|
||||||
let min = self.bounding_box.min;
|
if !self
|
||||||
let origin_x = min.x;
|
.shiftable_structure_piece
|
||||||
let origin_z = min.z;
|
.adjust_to_average_height(chunk)
|
||||||
|
{
|
||||||
// Vanilla uses adjustToAverageHeight.
|
return;
|
||||||
let surface_y = chunk.get_top_y(&HeightMap::OceanFloorWg, origin_x + 3, origin_z + 3);
|
|
||||||
let origin_y = surface_y + 1;
|
|
||||||
|
|
||||||
let spruce_planks = &Block::SPRUCE_PLANKS.default_state;
|
|
||||||
let oak_log = &Block::OAK_LOG.default_state;
|
|
||||||
let oak_fence = &Block::OAK_FENCE.default_state;
|
|
||||||
let air = &Block::AIR.default_state;
|
|
||||||
|
|
||||||
// 1. THE FLOOR
|
|
||||||
fill(
|
|
||||||
origin_x + 1,
|
|
||||||
origin_y + 1,
|
|
||||||
origin_z + 1,
|
|
||||||
origin_x + 5,
|
|
||||||
origin_y + 1,
|
|
||||||
origin_z + 7,
|
|
||||||
spruce_planks,
|
|
||||||
chunk,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 2. THE CEILING
|
|
||||||
fill(
|
|
||||||
origin_x + 1,
|
|
||||||
origin_y + 4,
|
|
||||||
origin_z + 2,
|
|
||||||
origin_x + 5,
|
|
||||||
origin_y + 4,
|
|
||||||
origin_z + 7,
|
|
||||||
spruce_planks,
|
|
||||||
chunk,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 3. THE ENTRANCE PLATFORM
|
|
||||||
fill(
|
|
||||||
origin_x + 2,
|
|
||||||
origin_y + 1,
|
|
||||||
origin_z,
|
|
||||||
origin_x + 4,
|
|
||||||
origin_y + 1,
|
|
||||||
origin_z,
|
|
||||||
spruce_planks,
|
|
||||||
chunk,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Front Wall
|
|
||||||
fill(
|
|
||||||
origin_x + 2,
|
|
||||||
origin_y + 2,
|
|
||||||
origin_z + 2,
|
|
||||||
origin_x + 3,
|
|
||||||
origin_y + 3,
|
|
||||||
origin_z + 2,
|
|
||||||
spruce_planks,
|
|
||||||
chunk,
|
|
||||||
);
|
|
||||||
// Left Wall
|
|
||||||
fill(
|
|
||||||
origin_x + 1,
|
|
||||||
origin_y + 2,
|
|
||||||
origin_z + 3,
|
|
||||||
origin_x + 1,
|
|
||||||
origin_y + 3,
|
|
||||||
origin_z + 6,
|
|
||||||
spruce_planks,
|
|
||||||
chunk,
|
|
||||||
);
|
|
||||||
// Right Wall
|
|
||||||
fill(
|
|
||||||
origin_x + 5,
|
|
||||||
origin_y + 2,
|
|
||||||
origin_z + 3,
|
|
||||||
origin_x + 5,
|
|
||||||
origin_y + 3,
|
|
||||||
origin_z + 6,
|
|
||||||
spruce_planks,
|
|
||||||
chunk,
|
|
||||||
);
|
|
||||||
// Back Wall
|
|
||||||
fill(
|
|
||||||
origin_x + 2,
|
|
||||||
origin_y + 2,
|
|
||||||
origin_z + 7,
|
|
||||||
origin_x + 4,
|
|
||||||
origin_y + 3,
|
|
||||||
origin_z + 7,
|
|
||||||
spruce_planks,
|
|
||||||
chunk,
|
|
||||||
);
|
|
||||||
|
|
||||||
// 5. THE LOGS
|
|
||||||
let logs = [(1, 2), (5, 2), (1, 7), (5, 7)];
|
|
||||||
for (lx, lz) in logs {
|
|
||||||
fill(
|
|
||||||
origin_x + lx,
|
|
||||||
origin_y,
|
|
||||||
origin_z + lz,
|
|
||||||
origin_x + lx,
|
|
||||||
origin_y + 3,
|
|
||||||
origin_z + lz,
|
|
||||||
oak_log,
|
|
||||||
chunk,
|
|
||||||
);
|
|
||||||
fill_downwards(origin_x + lx, origin_y - 1, origin_z + lz, oak_log, chunk);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Placed at Y=2 (feet level) or Y=3 (eye level)
|
let box_limit = self.shiftable_structure_piece.piece.bounding_box;
|
||||||
let y_floor = origin_y + 2;
|
let p = &self.shiftable_structure_piece.piece;
|
||||||
let y_eye = origin_y + 3;
|
|
||||||
|
|
||||||
chunk.set_block_state(
|
let spruce_planks = Block::SPRUCE_PLANKS.default_state;
|
||||||
&Vector3::new(origin_x + 3, y_floor, origin_z + 6),
|
let oak_log = Block::OAK_LOG.default_state;
|
||||||
Block::CRAFTING_TABLE.default_state,
|
let oak_fence = Block::OAK_FENCE.default_state;
|
||||||
|
let air = Block::AIR.default_state;
|
||||||
|
|
||||||
|
p.fill_with_outline(
|
||||||
|
chunk,
|
||||||
|
&box_limit,
|
||||||
|
false,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
1,
|
||||||
|
7,
|
||||||
|
spruce_planks,
|
||||||
|
spruce_planks,
|
||||||
);
|
);
|
||||||
chunk.set_block_state(
|
p.fill_with_outline(
|
||||||
&Vector3::new(origin_x + 4, y_floor, origin_z + 6),
|
chunk,
|
||||||
Block::CAULDRON.default_state,
|
&box_limit,
|
||||||
|
false,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
2,
|
||||||
|
5,
|
||||||
|
4,
|
||||||
|
7,
|
||||||
|
spruce_planks,
|
||||||
|
spruce_planks,
|
||||||
);
|
);
|
||||||
chunk.set_block_state(
|
p.fill_with_outline(
|
||||||
&Vector3::new(origin_x + 1, y_eye, origin_z + 5),
|
chunk,
|
||||||
|
&box_limit,
|
||||||
|
false,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
spruce_planks,
|
||||||
|
spruce_planks,
|
||||||
|
);
|
||||||
|
|
||||||
|
p.fill_with_outline(
|
||||||
|
chunk,
|
||||||
|
&box_limit,
|
||||||
|
false,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
3,
|
||||||
|
2,
|
||||||
|
spruce_planks,
|
||||||
|
spruce_planks,
|
||||||
|
);
|
||||||
|
p.fill_with_outline(
|
||||||
|
chunk,
|
||||||
|
&box_limit,
|
||||||
|
false,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
6,
|
||||||
|
spruce_planks,
|
||||||
|
spruce_planks,
|
||||||
|
);
|
||||||
|
p.fill_with_outline(
|
||||||
|
chunk,
|
||||||
|
&box_limit,
|
||||||
|
false,
|
||||||
|
5,
|
||||||
|
2,
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
3,
|
||||||
|
6,
|
||||||
|
spruce_planks,
|
||||||
|
spruce_planks,
|
||||||
|
);
|
||||||
|
p.fill_with_outline(
|
||||||
|
chunk,
|
||||||
|
&box_limit,
|
||||||
|
false,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
7,
|
||||||
|
4,
|
||||||
|
3,
|
||||||
|
7,
|
||||||
|
spruce_planks,
|
||||||
|
spruce_planks,
|
||||||
|
);
|
||||||
|
|
||||||
|
p.fill_with_outline(chunk, &box_limit, false, 1, 0, 2, 1, 3, 2, oak_log, oak_log);
|
||||||
|
p.fill_with_outline(chunk, &box_limit, false, 5, 0, 2, 5, 3, 2, oak_log, oak_log);
|
||||||
|
p.fill_with_outline(chunk, &box_limit, false, 1, 0, 7, 1, 3, 7, oak_log, oak_log);
|
||||||
|
p.fill_with_outline(chunk, &box_limit, false, 5, 0, 7, 5, 3, 7, oak_log, oak_log);
|
||||||
|
|
||||||
|
p.add_block(chunk, oak_fence, 2, 3, 2, &box_limit);
|
||||||
|
p.add_block(chunk, oak_fence, 3, 3, 7, &box_limit);
|
||||||
|
p.add_block(chunk, air, 1, 3, 4, &box_limit);
|
||||||
|
p.add_block(chunk, air, 5, 3, 4, &box_limit);
|
||||||
|
p.add_block(chunk, air, 5, 3, 5, &box_limit);
|
||||||
|
p.add_block(
|
||||||
|
chunk,
|
||||||
Block::POTTED_RED_MUSHROOM.default_state,
|
Block::POTTED_RED_MUSHROOM.default_state,
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
&box_limit,
|
||||||
);
|
);
|
||||||
|
p.add_block(
|
||||||
|
chunk,
|
||||||
|
Block::CRAFTING_TABLE.default_state,
|
||||||
|
3,
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
&box_limit,
|
||||||
|
);
|
||||||
|
p.add_block(chunk, Block::CAULDRON.default_state, 4, 2, 6, &box_limit);
|
||||||
|
p.add_block(chunk, oak_fence, 1, 2, 1, &box_limit);
|
||||||
|
p.add_block(chunk, oak_fence, 5, 2, 1, &box_limit);
|
||||||
|
|
||||||
// Fences & Gaps
|
// let stairs_n = Block::SPRUCE_STAIRS.default_state.with("facing", "north");
|
||||||
chunk.set_block_state(&Vector3::new(origin_x + 2, y_eye, origin_z + 2), oak_fence); // Above the door
|
// let stairs_e = Block::SPRUCE_STAIRS.default_state.with("facing", "east");
|
||||||
chunk.set_block_state(&Vector3::new(origin_x + 3, y_eye, origin_z + 7), oak_fence); // Back window
|
// let stairs_w = Block::SPRUCE_STAIRS.default_state.with("facing", "west");
|
||||||
chunk.set_block_state(
|
// let stairs_s = Block::SPRUCE_STAIRS.default_state.with("facing", "south");
|
||||||
&Vector3::new(origin_x + 1, y_floor, origin_z + 1),
|
|
||||||
oak_fence,
|
|
||||||
); // Balcony left
|
|
||||||
chunk.set_block_state(
|
|
||||||
&Vector3::new(origin_x + 5, y_floor, origin_z + 1),
|
|
||||||
oak_fence,
|
|
||||||
); // Balcony right
|
|
||||||
|
|
||||||
// Clear window air (Java: addBlock AIR at 1, 3, 4 and 5, 3, 4/5)
|
// p.fill_with_outline(chunk, &box_limit, false, 0, 4, 1, 6, 4, 1, &stairs_n, &stairs_n);
|
||||||
chunk.set_block_state(&Vector3::new(origin_x + 1, y_eye, origin_z + 4), air);
|
// p.fill_with_outline(chunk, &box_limit, false, 0, 4, 2, 0, 4, 7, &stairs_e, &stairs_e);
|
||||||
chunk.set_block_state(&Vector3::new(origin_x + 5, y_eye, origin_z + 4), air);
|
// p.fill_with_outline(chunk, &box_limit, false, 6, 4, 2, 6, 4, 7, &stairs_w, &stairs_w);
|
||||||
chunk.set_block_state(&Vector3::new(origin_x + 5, y_eye, origin_z + 5), air);
|
// p.fill_with_outline(chunk, &box_limit, false, 0, 4, 8, 6, 4, 8, &stairs_s, &stairs_s);
|
||||||
|
|
||||||
|
// p.add_block(chunk, &stairs_n.with("shape", "outer_right"), 0, 4, 1, &box_limit);
|
||||||
|
// p.add_block(chunk, &stairs_n.with("shape", "outer_left"), 6, 4, 1, &box_limit);
|
||||||
|
// p.add_block(chunk, &stairs_s.with("shape", "outer_left"), 0, 4, 8, &box_limit);
|
||||||
|
// p.add_block(chunk, &stairs_s.with("shape", "outer_right"), 6, 4, 8, &box_limit);
|
||||||
|
|
||||||
|
for i in [2, 7] {
|
||||||
|
for j in [1, 5] {
|
||||||
|
p.fill_downwards(chunk, oak_log, j, -1, i, &box_limit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if !self.has_witch {
|
||||||
|
// let world_coords = p.get_world_coords(2, 2, 5);
|
||||||
|
// if box_limit.contains(world_coords.0, world_coords.1, world_coords.2) {
|
||||||
|
// self.has_witch = true;
|
||||||
|
// // TODO: chunk.add_entity(Witch, world_coords)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// TODO: self.spawn_cat(chunk, &box_limit);
|
||||||
|
}
|
||||||
|
fn get_structure_piece(&self) -> &super::StructurePiece {
|
||||||
|
&self.shiftable_structure_piece.piece
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_structure_piece_mut(&mut self) -> &mut super::StructurePiece {
|
||||||
|
&mut self.shiftable_structure_piece.piece
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -302,18 +302,22 @@ impl Level {
|
|||||||
self.shut_down_chunk_system.store(true, Ordering::Relaxed);
|
self.shut_down_chunk_system.store(true, Ordering::Relaxed);
|
||||||
self.level_channel.notify();
|
self.level_channel.notify();
|
||||||
|
|
||||||
{
|
let handles: Vec<_> = {
|
||||||
let mut lock = self.thread_tracker.lock().unwrap();
|
let mut lock = self.thread_tracker.lock().unwrap();
|
||||||
log::info!("Wait {} jobs stop", lock.len());
|
log::info!("Shutting down {} jobs", lock.len());
|
||||||
while let Some(i) = lock.pop() {
|
lock.drain(..).collect()
|
||||||
log::info!(
|
};
|
||||||
"Waiting Thread {:?} {} stop",
|
|
||||||
i.thread().id(),
|
for handle in handles {
|
||||||
i.thread().name().unwrap_or("unknown")
|
log::info!(
|
||||||
);
|
"Waiting for thread {:?} ({}) to stop",
|
||||||
i.join().unwrap();
|
handle.thread().id(),
|
||||||
|
handle.thread().name().unwrap_or("unknown")
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(e) = handle.join() {
|
||||||
|
log::error!("Thread panicked during execution: {:?}", e);
|
||||||
}
|
}
|
||||||
log::info!("All Thread stop");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log::info!("Wait chunk system tasks stop");
|
log::info!("Wait chunk system tasks stop");
|
||||||
|
|||||||
@ -33,7 +33,7 @@ impl NoteBlock {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn get_note_pitch(note: u16) -> f32 {
|
fn get_note_pitch(note: u16) -> f32 {
|
||||||
2.0f64.powf((f64::from(note) - 12.0) / 12.0) as f32
|
2.0f32.powf((f32::from(note) - 12.0) / 12.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_state_with_instrument(
|
async fn get_state_with_instrument(
|
||||||
|
|||||||
@ -44,8 +44,8 @@ impl LookControl {
|
|||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
z,
|
z,
|
||||||
mob.get_max_look_yaw_change() as f32,
|
mob.get_max_look_yaw_change(),
|
||||||
mob.get_max_look_pitch_change() as f32,
|
mob.get_max_look_pitch_change(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ impl LookControl {
|
|||||||
let navigator = mob_entity.navigator.lock().await;
|
let navigator = mob_entity.navigator.lock().await;
|
||||||
if !navigator.is_idle() {
|
if !navigator.is_idle() {
|
||||||
let entity = &mob_entity.living_entity.entity;
|
let entity = &mob_entity.living_entity.entity;
|
||||||
let max_head_rotation = mob.get_max_head_rotation() as f32;
|
let max_head_rotation = mob.get_max_head_rotation();
|
||||||
entity.head_yaw.store(clamp_angle(
|
entity.head_yaw.store(clamp_angle(
|
||||||
entity.head_yaw.load(),
|
entity.head_yaw.load(),
|
||||||
entity.body_yaw.load(),
|
entity.body_yaw.load(),
|
||||||
|
|||||||
@ -66,16 +66,16 @@ pub trait Mob: EntityBase + Send + Sync {
|
|||||||
rand::rng()
|
rand::rng()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_max_look_yaw_change(&self) -> i32 {
|
fn get_max_look_yaw_change(&self) -> f32 {
|
||||||
10
|
10.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_max_look_pitch_change(&self) -> i32 {
|
fn get_max_look_pitch_change(&self) -> f32 {
|
||||||
40
|
40.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_max_head_rotation(&self) -> i32 {
|
fn get_max_head_rotation(&self) -> f32 {
|
||||||
75
|
75.0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_mob_entity(&self) -> &MobEntity;
|
fn get_mob_entity(&self) -> &MobEntity;
|
||||||
|
|||||||
@ -1027,16 +1027,15 @@ impl Player {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[expect(clippy::cast_precision_loss)]
|
|
||||||
pub async fn progress_motion(&self, delta_pos: Vector3<f64>) {
|
pub async fn progress_motion(&self, delta_pos: Vector3<f64>) {
|
||||||
// TODO: Swimming, gliding...
|
// TODO: Swimming, gliding...
|
||||||
if self.living_entity.entity.on_ground.load(Ordering::Relaxed) {
|
if self.living_entity.entity.on_ground.load(Ordering::Relaxed) {
|
||||||
let delta = (delta_pos.horizontal_length() * 100.0).round() as i32;
|
let delta = (delta_pos.horizontal_length() * 100.0).round() as f32;
|
||||||
if delta > 0 {
|
if delta > 0.0 {
|
||||||
if self.living_entity.entity.sprinting.load(Ordering::Relaxed) {
|
if self.living_entity.entity.sprinting.load(Ordering::Relaxed) {
|
||||||
self.add_exhaustion(0.1 * delta as f32 * 0.01).await;
|
self.add_exhaustion(0.1 * delta * 0.01).await;
|
||||||
} else {
|
} else {
|
||||||
self.add_exhaustion(0.0 * delta as f32 * 0.01).await;
|
self.add_exhaustion(0.0 * delta * 0.01).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user